41

I am having trouble with configuring a webapp in Tomcat 7. In my WAR file, there is a properties file myApp/WEB-INF/classes/myProps.props, and it contains environment-specific properites. I am trying to override that configuration file on the server, so that the same WAR file will deploy to multiple environments.

I heard there was a way to do this using replacement config files in tomcat/conf/Catalina/myApp. This is the method I am having trouble figuring out.

Also, myApp.war is one of many running on the same Tomcat server, and it does not run as localhost. I want to be able to solve this problem for several of the webapps.

Server version: Apache Tomcat/7.0.23
Server built:   Nov 20 2011 07:36:25
Server number:  7.0.23.0
OS Name:        Linux
StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
mfa
  • 5,017
  • 2
  • 23
  • 28
  • I tried to change the order of the tags in order to put "tomcat configuration linux", instead of "linux tomcat configuration", in order to fix the page title that now says "linux" instead of "tomcat". It seems it's not easy to do it, or I can't do it. – Valerio Bozz Mar 27 '20 at 16:25

4 Answers4

70

Your tomcat/conf/Catalina/<host> can contain context descriptors that let you configure lots of things including defining "environment entries", which are accessible from Java via JNDI. There are lots of ways to go about using it. Personally, I set an environment entry which is the file system path to my properties file. My app is built to check for this entry, and if it doesn't exist, look for the file on the classpath instead. That way, in dev, we have the dev properties right there on the classpath, but when we build and deploy, we point it to an external file.

There's good documentation for configuring a context on the Tomcat website. See the Defining a Context section on details of how to create the file and where to put it.

As an example, if your host is named myHost and your app is a war file named myApp.war in the webapps directory, then you could create tomcat/conf/Catalina/myHost/myApp.xml with this content:

<Context>
    <Environment name="configurationPath" value="/home/tomcat/myApp.properties" type="java.lang.String"/>
</Context>

Then from your code, you'd do a JNDI lookup on java:comp/env/configurationPath (95% certainty here) to get that string value.

StackzOfZtuff
  • 2,534
  • 1
  • 28
  • 25
Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • 1
    @Ryan Stewart Can you provide an example of how the above configuration be used in java code? – Kaizar Laxmidhar Oct 23 '15 at 10:03
  • @Ryan Stewart Can you help me to fix similar problem here http://stackoverflow.com/questions/36446968/read-common-external-property-file-in-java-webapp-and-java-normal-app – School Boy Apr 06 '16 at 15:53
48

I like .properties files instead of

  • JNDI - why build complex object during program configuration instead of initialization time?
  • system properties - you can't separately configure several instances of same WAR in single Tomcat
  • context parameters - they accessible only in javax.servlet.Filter, javax.servlet.ServletContextListener which my be inconvenient

Tomcat 7 Context hold Loader element. According to docs deployment descriptor (what in <Context> tag) can be placed in:

  • $CATALINA_BASE/conf/server.xml - bad - require server restarts in order to reread config
  • $CATALINA_BASE/conf/context.xml - bad - shared across all applications
  • $CATALINA_BASE/work/$APP.war:/META-INF/context.xml - bad - require repackaging in order to change config
  • $CATALINA_BASE/work/[enginename]/[hostname]/$APP/META-INF/context.xml - nice, but see last option!!
  • $CATALINA_BASE/webapps/$APP/META-INF/context.xml - nice, but see last option!!
  • $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xml - best - completely out of application and automatically scanned for changes!!!

Context can hold custom Loader org.apache.catalina.loader.VirtualWebappLoader (available in modern Tomcat 7, you can add own separate classpath to your .properties), and Parameter (accessed via FilterConfig.getServletContext().getInitParameter(name)) and Environment (accessed via new InitialContext().lookup("java:comp/env").lookup("name")):

<Context docBase="${basedir}/src/main/webapp"
         reloadable="true">
    <!-- http://tomcat.apache.org/tomcat-7.0-doc/config/context.html -->
    <Resources className="org.apache.naming.resources.VirtualDirContext"
               extraResourcePaths="/WEB-INF/classes=${basedir}/target/classes,/WEB-INF/lib=${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
    <Loader className="org.apache.catalina.loader.VirtualWebappLoader"
            virtualClasspath="${basedir}/target/classes;${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
    <JarScanner scanAllDirectories="true"/>

    <Parameter name="min" value="dev"/>
    <Environment name="app.devel.ldap" value="USER" type="java.lang.String" override="true"/>
    <Environment name="app.devel.permitAll" value="true" type="java.lang.String" override="true"/>
</Context>

If you use Spring and it's XML config:

<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>

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}}'/>

See also:

NOTE With extending classpath to live directory you also allowed to externilize any other configs, like logging, auth, atc. I externilize logback.xmlin such way.

UPDATE Tomcat 8 change syntax for <Resources> and <Loader> elements, corresponding part now look like:

<Resources>
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   webAppMount="/WEB-INF/classes" base="${basedir}/target/classes" />
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   webAppMount="/WEB-INF/lib" base="${basedir}/target/${project.build.finalName}/WEB-INF/lib" />
</Resources>
Community
  • 1
  • 1
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
  • Curious: if you've got logback.xml in `src/main/resources` and also externally, what would you use in tomcat 8 to use the external config - would it be PostResources, PreResources or something else? – Prasoon Jun 01 '17 at 21:38
  • EE spec mandates behavior with container Classloaders. Some framewords break standard but most follow. So classpath would be as it specified in `Context` and usually first hit is used. – gavenkoa Jun 01 '17 at 21:53
  • Tried that @gavenkoa and it works with Tomcat8, however, noticed the following things: * If the app is undeployed and tomcat is restarted, it results in errors as `$App.xml` exists but tomcat tries to load the Resources to the path which no longer exists * Unable to deploy the app at `/` now. Wanted to deploy the spring boot generated war file on Tomcat 8, and made the change to `conf/server.xml` to have the context path as empty, but this no longer works with any other context path except the App name. – Prasoon Jun 02 '17 at 08:02
1

You can try to place your configuration (properties file) in Apache Tomcat\lib in JAR file and remove it from the web application. When the Tomcat's class loader won't find your config in webapp it will try to find in "lib" directory. So you can externalize your configuration just moving the config to global lib dir (it's shared among other webapps).

bmoc
  • 652
  • 7
  • 7
  • Thank you, but this doesn't quite do what I need it to. The war file needs to run on developer's machines which have no config of their own. – mfa Dec 23 '12 at 17:31
  • 1
    If you are using tomcat maven plugin, you can probably provide custom config to tomcat, so the developers would have it. – bmoc Dec 23 '12 at 18:53
0

I just added a setenv.bat or setenv.sh script in the bin folder of tomcat. Set the classpath variable like

set CLASSPATH=my-propery-folder
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
baliman
  • 588
  • 2
  • 8
  • 27