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>
<Resource name="jdbc/ApplicationContextX_DatabaseName" ... /> <Resource name="jdbc/ApplicationContextY_DatabaseName" ... />
If the resources were connection pools, would this give you two separate pools, one per webapp? Whereas, if I linked from both webapps to one resource, there would be just one connection pool, correct? Any reasons to prefer one to the other? (separate DB connection pools one per webapp vs one connection pool shared by all webapps)? Thanks. – Pipe