Is it possible to create a JNDI tomcat resource with multiple names (synonyms, aliases)?
Something like
<Resource
name="jdbc/product-ds"
alias="jdbc/product-cfg-ds"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
...
/>
I need this, because there are two modules which use the same DataSource, but with different JNDI name. The simplest solution will be to sync those names, but unfortunately it's not possible at the moment.
You can do this. It took me a while to work out the correct sequence. What you need to do is define the jdbc/product-ds in your server.xml (tomcat/conf/server.xml) in the GlobalNamingResources section kind of like this:
<GlobalNamingResources>
<Resource name="jdbc/product-ds " auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#127.0.0.1:1521:mysid"
username="scott" password="tiger" maxActive="20" maxIdle="10"
maxWait="-1"/>
</GlobalNamingResources>
Then you can rename this in your server context.xml (tomcat/conf/context.xml)
Like this:
<ResourceLink
name="jdbc/product-cfg-ds"
global="jdbc/product-ds"
type="javax.sql.DataSource"/>
The global name is then renamed for all applications deployed on the the server. I don't think the global jdbc/product-ds will be available in any application, if you did want it you'd need to add:
<ResourceLink
name="jdbc/product-ds"
global="jdbc/product-ds"
type="javax.sql.DataSource"/>
Related
I am working on a tomcat 7 webapp that I recently inherited. We are working on migrating from Tomcat 5.5.
The webapp uses a tomcat realm to handle a combination of ldap/sql authentication.
When I define my context.xml as follows
<Context docBase="*******" reloadable="false">
<Realm className="com.******.tomcat.auth.LdapSqlRealm"
****
/>
<Resource name="jdbc/*****"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="SELECT 1"
validationInterval="30000"
timeBetweenEvictionRunsMillis="60000"
maxActive="15"
maxIdle="15"
maxWait="30000"
initialSize="10"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="true"
minEvictableIdleTimeMillis="60000"
numTestsPerEvictionRun="2"
jmxEnabled="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
username="*****"
password="*****"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="*****"
/></Context>
I can see my realm initializing in the logs, but when I go to authenticate (using basic) it doesn't use my realm.
If I define the realm in the server.xml file it works just fine.
Any thoughts on why I can't define it in the context.xml.
Our context.xml file is actually located in cont/Catalina/localhost/*****.xml
I have tried starting from scratch with simple realms, or extensions of RealmBase and they all do the same thing.
Thanks,
Travis
Turns out that I had an extra <Context /> tag in my <Host /> tag in my server.xml which was messing up my context.xml file.
Little Santi tipped me off by suggesting a vanilla build of tomcat, which I didn't end up doing, but I did a compare of server.xml from the two and spotted the issue right away. Wish I had thought of it sooner.
I have an application that uses the built-in Tomcat connection pool, and for the most part it works. A problem arises when I'm trying to use another pool to get a different set of connection from the same database, but from a different username/password (It's an oracle database that uses 2 usernames to access different namespaces of tables and function).
The first pool is accepted, but for the second one, I'm getting this error
15:09:47.157 [http-nio-8081-exec-5] ERROR com.applicationname.providers.ConnectionManager - NamingException in MyDataSource
javax.naming.NameNotFoundException: Name [appdb_two] is not bound in this Context. Unable to find [appdb_two].
at org.apache.naming.NamingContext.lookup(NamingContext.java:818) ~[catalina.jar:8.0.15]
Here is my configuration:
server.xml
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users-->
<Resource name="UserDatabase"
auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
<Resource name="jdbc/appdb_two"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"/>
<Resource name="jdbc/appdb_one"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"/>
</GlobalNamingResources>
context.xml
<ResourceLink name="jdbc/appdb_two"
auth="Container"
username="DBONE"
password="xxxx"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:#xx.xxx.xxx.xxx:1521:XE"
initialSize="20"
maxActive="50"
maxIdle="20"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"/>
<ResourceLink name="jdbc/appdb_one"
auth="Container"
username="DBTWO"
password="xxxx"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:#xx.xxx.xxx.xxx:1521:XE"
initialSize="20"
maxActive="50"
maxIdle="20"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"/>
...
I think you're looking up the second datasource via name "appdb_two", but should be using "jdbc/appdb_two" - it's hard to see from the stacktrace alone, code for lookup would be helpful.
Also check that your web.xml has references to both data sources (<resource-ref> elements).
I am trying to set up the database connection properties using JNDI for a Spring web application.
I am considering two approaches as below:
Approach 1:
In your Spring configuration you may have something like:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/facs"/>
Then in your webapp /META-INF/context.xml file you should have something similar too:
<?xml version='1.0' encoding='utf-8'?>
<!-- antiResourceLocking="true" -->
<Context path="/podd-apn"
reloadable="true"
cachingAllowed="false"
antiResourceLocking="true"
>
<Resource name="jdbc/facs"
type="javax.sql.DataSource" username="${database.username}" password="${database.password}"
driverClassName="org.postgresql.Driver"
url="${database.url}"
maxActive="8" maxIdle="4"
global="jdbc/facs"
/>
</Context>
And in your web.xml you should something like:
<!-- JNDI -->
<resource-ref>
<description>FACs Datasource</description>
<res-ref-name>jdbc/facs</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Approach 2:
Setup in the Spring context like this:
<jee:jndi-lookup id="dbDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
You can declare the JNDI resource in Tomcat's server.xml using something like this:
<GlobalNamingResources>
<Resource name="jdbc/DatabaseName" auth="Container" type="javax.sql.DataSource"
username="dbUsername" password="dbPasswd"
url="jdbc:postgresql://localhost/dbname"
driverClassName="org.postgresql.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"/>
</GlobalNamingResources/>
And reference the JNDI resource from Tomcat's web context.xml like this:
<ResourceLink name="jdbc/DatabaseName"
global="jdbc/DatabaseName"
type="javax.sql.DataSource"/>
My question is where is the best place to keep database properties? Should they be placed in server.xml or context.xml?
Also, if I have 2 databases, should I use two configs?
Also, is it best practice to directly place them in either server.xml or context.xml? Or do I need to configure through Tomcat Manager GUI console?
Thanks!
I prefer a third approach that takes the best from
Approach 1 and Approach 2 described by user1016403.
Approach 3
Save database properties on the server.xml
reference the server.xml database properties from the web application META-INF/context.xml
Approach 3 benefits
While the first point is useful for security reasons the second point is useful for referencing server properties value from the web application, even if server properties values will change.
Moreover decoupling resource definitions on the server from their use by the web application makes such configuration scalable across organizations with various complexity where different teams work on different tiers/layers: the server administrators team can work without conflicting with developers team if the administrator shares the same JNDI name with the developer for each resource.
Approach 3 implementation
Define the JNDI name jdbc/ApplicationContext_DatabaseName.
Declare the jdbc/ApplicationContext_DatabaseName's various properties and values in Tomcat's server.xml using something like this:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContext_DatabaseName" auth="Container" type="javax.sql.DataSource"
username="dbUsername" password="dbPasswd"
url="jdbc:postgresql://localhost/dbname"
driverClassName="org.postgresql.Driver"
initialSize="5" maxWait="5000"
maxActive="120" maxIdle="5"
validationQuery="select 1"
poolPreparedStatements="true"/>
</GlobalNamingResources/>
Link the jdbc/ApplicationContext_DatabaseName's properties from web application META-INF/context.xml by an application-private JNDI context java:comp/env/ specified in the name attribute:
<Context path="/ApplicationContext" ... >
<!--
"global" attribute links to GlobalNamingResources in the ${catalina.base}/conf/server.xml (server administrator team)
"name" attribute is relative to the application-private JNDI context java:comp/env/ and is looked up from the java web application (application developer team)
-->
<ResourceLink global="jdbc/ApplicationContext_DatabaseName" name="jdbc/DatabaseName" type="javax.sql.DataSource"/>
</Context>
Finally, in order to use the JNDI resource, specify the JNDI name jdbc/DatabaseName in web application's deployment descriptor:
<resource-ref>
<description>DatabaseName's Datasource</description>
<res-ref-name>jdbc/DatabaseName</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and in Spring context:
<jee:jndi-lookup id="DatabaseNameDataSource"
jndi-name="jdbc/DatabaseName"
expected-type="javax.sql.DataSource" />
Approach 3 drawbacks
If the JNDI name gets changed then both the server.xml and the META-INF/context.xml will have to be edited and a deploy would be necessary; nevertheless this scenario is rare.
Approach 3 variations
Many data sources used by one web application
Simply add configurations to Tomcat's server.xml:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContext_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContext_DatabaseName2" ... />
...
</GlobalNamingResources/>
Add link web application META-INF/context.xml by an application-private JNDI context java:comp/env/ specified in the name attribute:
<Context path="/ApplicationContext" ... >
<ResourceLink global="jdbc/ApplicationContext_DatabaseName1" name="jdbc/DatabaseName1" ... />
<ResourceLink global="jdbc/ApplicationContext_DatabaseName2" name="jdbc/DatabaseName2" ... />
...
</Context>
Finally add JNDI resources usage in web application's deployment descriptor:
<resource-ref>
<description>DatabaseName1's Datasource</description>
<res-ref-name>jdbc/DatabaseName1</res-ref-name> ...
</resource-ref>
<resource-ref>
<description>DatabaseName2's Datasource</description>
<res-ref-name>jdbc/DatabaseName2</res-ref-name> ...
</resource-ref>
...
and in Spring context:
<jee:jndi-lookup id="DatabaseName1DataSource"
jndi-name="jdbc/DatabaseName1" ... />
<jee:jndi-lookup id="DatabaseName2DataSource"
jndi-name="jdbc/DatabaseName2" ... />
...
Many data sources used by many web application on the same server
Simply add configuration to Tomcat's server.xml:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContextX_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContextX_DatabaseName2" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName1" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName2" ... />
...
</GlobalNamingResources/>
the others configuration should be deducible from previous variation case.
Many data sources to the same database used by many web application on the same server
In such case a Tomcat's server.xml configurations like:
<GlobalNamingResources>
<Resource name="jdbc/ApplicationContextX_DatabaseName" ... />
<Resource name="jdbc/ApplicationContextY_DatabaseName" ... />
ends up in two different web applications META-INF/context.xml like:
<Context path="/ApplicationContextX" ... >
<ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>
and like:
<Context path="/ApplicationContextY" ... >
<ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>
so someone might be worried about the fact that the same name="jdbc/DatabaseName" is looked up, and then used, by two different applications deployed on the same server: this is not a problem because the jdbc/DatabaseName is an application-private JNDI context java:comp/env/, so ApplicationContextX by using java:comp/env/ can't (by design) look up the resource linked to global="jdbc/ApplicationContextY_DatabaseName".
Of course if you felt more relaxed without this worry you might use a different naming strategy like:
<Context path="/ApplicationContextX" ... >
<ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/applicationXprivateDatabaseName" ... />
</Context>
and like:
<Context path="/ApplicationContextY" ... >
<ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/applicationYprivateDatabaseName" ... />
</Context>
YOUR_APP.xml file
I prefer Approach 2 (put everything (not only some attribute in the config), but instead of placing them in the global server.xml or global context.xml you should place it in the application-specific context.xml.default YOUR_APP.xml file in your Tomcat.
The YOUR_APP.xml file is located in $catalinaHome/conf/<engine>/<host> (for example conf/Catalina/localhost/YOUR_APP.xml).
The configuration in application specific YOUR_APP.xml is only available for the specific application.
See the guide published by MuleSoft. And see the official documentation, Tomcat Configuration Reference, page for The Context Container
Version 9
Version 8
To quote that documentation:
Individual Context elements may be explicitly defined:
• …
• In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The context path and version will be derived from the base name of the file (the file name less the .xml extension).
• …
Approach 4
Instead of using JNDI I work with .properties files and build complex object during program initialization instead on configuration time.
You already use Spring and it is easy construct DataSource by:
<context:property-placeholder location="classpath:app.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#${db.host}:${db.port}:${db.user}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.pass}"/>
</bean>
I completely agree with Ralph with using deployment descriptor in $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xmlbut instead JNDI I like plain key-value file!
With Spring injecting above properties into bean fields are easy:
#Value("${db.user}") String defaultSchema;
instead of JNDI:
#Inject ApplicationContext context;
Enviroment env = context.getEnvironment();
String defaultSchema = env.getProperty("db.user");
Note also that EL allow this (default values and deep recursive substitution):
#Value('${db.user:testdb}') private String dbUserName;
<property name='username' value='${db.user.${env}}'/>
To externalize .properties file I use modern Tomcat 7 that has org.apache.catalina.loader.VirtualWebappLoader:
<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
virtualClasspath="/srv/web/app/"/>
So your devops fill virtualClasspath with local external full paths which is separate per application and put local app.propertiesto that dir.
See also:
Adding a directory to tomcat classpath
Can I create a custom classpath on a per application basis in Tomcat
Externalizing Tomcat webapp config from .war file
How to read a properties file outside my webapp context in Tomcat
Configure Tomcat to use properties file to load DB connection information
Externalize Tomcat configuration
step 1: context.xml
<Context path="/projectname">
<Resource auth="Container"
driverClassName="com.mysql.jdbc.Driver"
logAbandoned="true"
maxActive="100" ``
maxIdle="30"
maxWait="10000"
name="refname"
removeAbandoned="true"
removeAbandonedTimeout="60"
type="javax.sql.DataSource"
url="jdbc:mysql://localhost:8080/dbname"
username="root"
password="root"/>
</Context>
Step 2 : web.xml
<resource-ref>
<description>DB Connection</description>
<res-ref-name>refname</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Step 3 : create a class to get connection
Connection connection = null;
Context context = (Context) new InitialContext().lookup("java:comp/env");
DataSource ds = (DataSource) context.lookup("refname");
connection = ds.getConnection();
Everything is set
You also can use JNDI URL support for different application configuration for test, integration test, production.
<Context>
...
<Resource auth="Container" factory="com.benasmussen.jndi.url.URLFactory"
name="url/MyUrl" type="java.net.URL" url="file:///your/path/to/file"/>
...
</Context>
<jee:jndi-lookup id="myUrl" jndi-name="java:comp/env/url/MyUrl" expected-type="java.net.URL" />
Check out the GitHub project Tomcat JNDI URL Support to enable JNDI URL support for Tomcat servers.
On one developer workstation running Eclipse Helios SR2, Windows 7 and Tomcat 6.0.32 we have a very strange case of duplicate JNDI connection pools
Running tomcat from Eclipse
server.xml
>
<Context docBase="path to web app" path="/ds-web" reloadable="true">
<Resource
name="jdbc/ds"
username="ds"
password="pass"
type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/ds"
auth="Container"
driverClassName="org.postgresql.Driver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
logAbandoned="true"
maxActive="30"
maxIdle="10"
maxWait="1000"
removeAbandoned="true"
removeAbandonedTimeout="60"
validationQuery="SELECT 1"
testOnBorrow="true"
testOnReturn="true"/>
</Context>
When start server, on the console we see the following 3 times in a row
AbandonedObjectPool is used
(org.apache.commons.dbcp.AbandonedObjectPool#11aa58b)
LogAbandoned: true
RemoveAbandoned: true
RemoveAbandonedTimeout: 60
Application then fails to find the JNDI resource
If we remove the <Resource> in server.xml, then the console shows no connection pool is created at all
On another developer machine with the same hardware and OS we do not have this problem
Any ideas?
Thanks
Marc
My suggestion, Copy the whole <Context> from server.xml and create a blank context.xml inside your web application META-INF folder and paste the <Context> copied from server.xml there.
Restart your application and see if this works.
We re-installed postgresql on the machine and the problem got solved somehow.
We're thinking something in the original postgresql config had been messed up and tomcat was failing to connect to the BD somehow. I guess tomcat was simply "trying 3 times", thus the triple output.
Another unsolved mystery...
move
<Resource
name="jdbc/ds"
username="ds"
password="pass"
type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/ds"
auth="Container"
driverClassName="org.postgresql.Driver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
logAbandoned="true"
maxActive="30"
maxIdle="10"
maxWait="1000"
removeAbandoned="true"
removeAbandonedTimeout="60"
validationQuery="SELECT 1"
testOnBorrow="true"
testOnReturn="true"/>
to server.xml namely the
<GlobalNamingResources>
element
in your conf/context.xml file you would instead specify
<ContextLink name="jdbc/ds" global="jdbc/ds"/>
and this way, the three contexts will share the same pool.
How can we configure JNDI using tomcat server similar to JBoss server using jboss-web.xml?
Please help me on this?
I want to know which file we need to write it? or is there any programmatic way to do this?
Thanks in Advance,
Pravin
Write a context.xml
<Context>
<Resource name="jdbc/dbConnection"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="j<url to database>"
username="user"
password="pwdt"
validationQuery="select 1"
removeAbandoned="true"
removeAbandonedTimeout="120"
maxWait="60"
maxActive="20"
maxIdle="10" />
</Context>
Here's a JNDI HOWTO from Apache on how to configure JNDI on Tomcat 6.
Related topic.
If you want a JNDI data source or something else, read the docs. They are quite detailed.