Elegant ways to separate configuration from WAR in Tomcat

12,453

Solution 1

If I have a specific set of beans that I'd like to configure, and this configuration must be separated from the WAR file, I usually do the following:

In applicationContext.xml:

<!-- here you have a configurer based on a *.properties file -->
<bean id="configurer" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="file://${configDir}/configuration.properties"/>
    <property name="ignoreResourceNotFound" value="false" />
    <property name="ignoreUnresolvablePlaceholders" value="false" />
    <property name="searchSystemEnvironment" value="false" />
</bean>

<!-- this is how you can use configuration properties -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="${smtp.host}"/>
</bean>

In configuration.properties:

smtp.host=smtp.your-isp.com

You also need to start Tomcat with -DconfigDir=/path/to/configuration/directory

Solution 2

If you are using Spring 3, you can take advantage of the Spring Expression Language. Let's say you have two applications app1.war and app2.war and they require a properties file named config.properties. The applications will be deployed with context paths /app1 and /app2.

Create two directories app1 and app2 in a common directory, eg. C:\myConfig\app1 and C:\myConfig\app2.

Put config.properties inside app1 and another config.properties inside app2.

Then create a file ${CATALINA_HOME}/conf/[enginename]/[hostname]/context.xml.default with the contents:

context.xml.default:

<Context>
  <Parameter name="myConfigDirectory" value="C:/myConfig" override="false"/>
</Context>

The parameter myConfigDirectory will be available to all the applications on the host. It is better to create this parameter in context.xml.default rather than in server.xml, because the file can be changed later without restarting tomcat.

In the applicationContext.xml inside war you can access config.properties using the SpEL expression: "#{contextParameters.myConfigDirectory + servletContext.contextPath}/config.properties", so for example you can write:

applicationContext.xml:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="file:#{contextParameters.myConfigDirectory + servletContext.contextPath}/config.properties" />
</bean>

The expression will get expanded to C:/myConfig/app1 for application with contextPath /app1, and C:/myConfig/app2 for application with contextPath /app2. This will make the applications access the config.properties file based on their contextPath.

Share:
12,453
Nigel
Author by

Nigel

Updated on June 06, 2022

Comments

  • Nigel
    Nigel almost 2 years

    I am trying to find the best way to pass complex configurations in a Spring webapp running in Tomcat. Currently I use JNDI to pass data sources and strings from the Tomcat context into the webapp, and this works well.

    But, lets say I need to select the implementation of a notification service. There is no way that Spring can conditionally select which bean to instantiate (although in the past I have used a JNDI string to import a predefined configuration of beans by setting contextConfigLocation).

    I've also seen many webapps which supply a configuration tool which will create a custom WAR file. In my opinion this is bad form, if for no other reason than it prevents the redeployment of WARs from upstream without many checks to ensure all the configuration has been re-applied.

    Ideally I would be able to supply a Spring XML file which existed on the filesystem, outside of the webapp. But, the spring import directive does not seem to resolve ${} variables, making it impossible to supply customisations.

    Are there any techniques I can employ here to properly separate complex configuration from the webapp?

  • gustafc
    gustafc over 14 years
    Wouldn't it be even "eleganter" to use a classpath: URI to the properties file, so you can just drop it somewhere in the classpath and avoid fiddling with the Tomcat startup parameters?
  • Nigel
    Nigel over 14 years
    Well, is there a way to get something onto the Tomcat classpath without it being in the WAR? I know there is shared/lib or common/lib is there a classes equivalent?
  • Nigel
    Nigel over 14 years
    Note to self, in Tomcat 6.0 it is ${catalina.home}/lib. Thanks for the assistance, I think I this will work.