Externalize properties in Tomcat - java

I would like to configure a series of environment-dependant settings as an external resource, so that the same WAR artifact can be configured in any application server.
In Glassfish I rememeber that I just created a .properties file using the built-in Properties Resource JNDI Factory, and it worked like a charm, but there's no such thing in Tomcat.
I tried defining a java.net.URL and a java.lang.String to define just the path that this file is held in, but I get the following error:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/c://app.properties]
So what would be a good way to create an environment-agnostic set of properties for my application?
PS: this is how I define my properties in Spring:
<jee:jndi-lookup id="appProperties" jndi-name="java:comp/env/url/urlAppProperties" />
<bean id="application-properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>#{ appProperties }</value>
</list>
</property>
</bean>

You can add external configuration values to the JNDI context using Tomcat's resource configuration elements. As described in the Tomcat documentation, you can define a <GlobalNamingResources> element in your server.xml listing a set of configuration values which are available to all contexts (web applications) on that server:
Example taken from the Tomcat docs:
<GlobalNamingResources ...>
...
<Environment name="maxExemptions" value="10"
type="java.lang.Integer" override="false"/>
...
</GlobalNamingResources>
These environment variables are then available via JDNI in the java:comp/env context. In order to use such variables in a web application, you have to link to them in the individual context.xml configuration files using the <ResourceLink> element.
<Context>
<ResourceLink name="maxExemptions" global="maxExemptions" type="java.lang.Integer"/>
</Context>
With this set up, you can access these values through the standard JNDI API.

Related

Read PropertyPlaceholderConfigurer classpath Spring with Jboss

Using PropertyPlaceholderConfigurer to externalize spring-configuration
properties.
Added following code to spring-servlet.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:environment.properties</value>
</property>
</bean>
Filter to be externalized from spring-security.xml
<security:custom-filter position="AUTH_FILTER" ref="${filter}" />
filter value is present in environment.properties
environment.properties file is present inside Jboss modules and is readable from code using resource bundle.
But, with these changes somehow properties file is not getting loaded and following error is thrown while publishing code.
Caused by: java.lang.IllegalArgumentException: Could not resolve
placeholder 'filter' in string value "${filter}"
PS:
Also tried hardcoding path as <value>file:${jboss.home.dir}/modules/system/layers/base/configuration/main/environment.properties</value> but, dosen't seems to be working.
I think your problem is that your spring-servlet.xml is not linked to your spring-security.xml. So spring-security.xml has no knowledged of the PropertyPlaceholderConfigurer.
IMO, you should configure PropertyPlaceholderConfigurer in a properties-context.xml (for instance) so you can import this new file into your spring-servlet.xml and spring-security.xml as following:
<import resource="classpath:properties-context.xml" />

Connecting Tomcat to standalone Artemis Broker via JNDI

I want to define a jms ConnectionFactory as global resource in tomcat server.xml to connect my webapps to standalone Artemis server.
Unfortunately I couldn't find any official or clean manual for such a common use case that describes required libraries and configurations.
Can somebody show me an example?
I couldn't find any native Artemis based solution. Artemis server is ActiveMQ client compatible. So we can follow ActiveMQ manual for this purpose.
Add ActiveMQ client libraries to $CATALINA_HOME/lib
geronimo-jms_2.0_spec-1.0-alpha-2.jar
activemq-client-5.14.3.jar
geronimo-j2ee-management_1.1_spec-1.0.1.jar
hawtbuf-1.11.jar
slf4j-api-1.7.6.jar
Add global resource to $CATALINA_HOME/conf/server.xml
<Resource auth="Container"
name="jms/ConnectionFactory"
type="org.apache.activemq.ActiveMQConnectionFactory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="tcp://127.0.0.1:61616"
brokerName="MyActiveMQBroker"/>
Add resource link to $CATALINA_HOME/conf/context.xml
<ResourceLink name="/ConnectionFactory"
global="jms/ConnectionFactory"
type="javax.jms.ConnectionFactory"/>
Look up connection factory in app context
<bean id="jmsConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/ConnectionFactory" />
<property name="proxyInterface" value="javax.jms.ConnectionFactory"/>
</bean>

Create Portable JDBC Connection Pool in Glassfish

I'm trying to deploy my project to AWS Elastic Beanstalk, using Docker with preconfigured Glassfish.
Since the project uses JPA, and EJB to create a transactional persistence unit; I create a database connection pool in the Glassfish admin console, then supply the JNDI string to persistence.xml.
All good in my local machine, Amazon won't allow you to use the admin console. I've read this post, which discusses generally how to configure Glassfish with asadmin, but I've found it very hard to follow and sounds very invasive.
Is there a one-size-fits-all solution to create a database connection pool that is automatically allocated on any server I deploy my application to?
Yes. Although this is unfortunately under-documented.
You can create an application scoped connection pool, that Glassfish creates on the fly, when you depoly your war file to it. It will destroy it when undeployed. Though, it is only available to this application on the server.
Here's what you should do. Create a file named glassfish-resources.xml like this (for MySql):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" name="MySQLPool" res-type="javax.sql.DataSource">
<property name="user" value="someUser"></property>
<property name="password" value="aPassword"></property>
<property name="databaseName" value="aDatabase"></property>
<property name="serverName" value="some.string.you got.from.amazon.rds.amazonaws.com"></property>
<property name="portNumber" value="3306"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="MySQLPool" jndi-name="jdbc/MySQLPool"></jdbc-resource>
</resources>
And drop it into your WEB-INF directory. You should also place the database connection dependency jar in the lib directory.
Now since this isn't a global JNDI reference, as it is only available to this application, you should access it in persistence.xml with java:app prefix, like this:
<jta-data-source>java:app/jdbc/MySQLPool</jta-data-source>

Should you set up database connection properties in server.xml or context.xml

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.

finding a property using Spring and Webapp properties placeholder

This is a "simple" problem and I am seeking both a how-to and/or a you're-dumb-don't-do-that. I am open to both.
I am building a war file and want the structure to be:
WEB-INF/
properties/
<my properties files>
classes/
...
spring/
<my spring files>
Is that dumb? I know that I can access the properties files though the property-placeholder but I'd rather not nest the properties in the classes section - it doesn't make sense to me.
So the Spring file looks like this:
<context:property-placeholder location="classpath:properties/*.properties" />
if I want to access them in the classes area. I thought that
<context:property-placeholder location="properties/*.properties" />
would let me just put the directory under WEB-INF directly...am I wrong (ps I think I am :) ).
Any advice?
This should work
<context:property-placeholder location="WEB-INF/properties/*.properties" />
WEB-INF is not the root of the of the web-app, so you need to add WEB-INF to the path.
spring-context-3.1.xsd
<xsd:attribute name="location" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The location of the properties file to resolve placeholders against, as a Spring
resource location: a URL, a "classpath:" pseudo URL, or a relative file path.
Multiple locations may be specified, separated by commas. If neither location nor properties-ref is
specified, placeholders will be resolved against system properties.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
You can't do it the way you want since the classpath for the Classloader will be the /classes directory and any jars in the /lib directory. This is the standard configuration for a war file.
Wars and ears have specific configurations which you have to follow for the files to be valid. If you think about it, it would make it difficult to have different vendors provide web containers that could deploy the same war file if there was no standard format. There is a pretty informative page here.
To achieve something similar to what you want, you can simply have directories of /classes/properties and /classes/spring and look them up appropriately from your classpath ("classpath:properties/myfile.properties).
I am not sure what you want to achieve. Here the method I use to inject the properties from a basic properties file to a bean:
In the spring files (XML bean definitions), I add the reference to my property file (myfile.properties):
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:myfile.properties" />
</bean>
and then I add my references to the properties (db.url is the URL address for my database connection, I kept only the bean properties referenced in my property file).
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"><value>${db.url}</value></property>
<property name="username"><value>${db.login}</value></property>
<property name="password"><value>${db.password}</value></property>
</bean>
By default, if the property is not defined in the property file, Spring uses the System Properties (this behaviour can be changed).

Categories