Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Externalizing Tomcat webapp config from .war file

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 
like image 608
mfa Avatar asked Dec 19 '12 16:12

mfa


People also ask

Where do I put properties file in Tomcat?

It doesn't "go in Tomcat". It goes in the web application (WAR). The best place to put a properties file would be in the webapp's WEB-INF/classes directory, which is part of the application classpath.

How do you read Properties filed outside war?

Provide the file name using context param or java system parameter. But, it is again property file location is war dependent. You should change war file to change propert file location. If i am correct the requirement was to change the props in the file , not the file as such.

Which folder under the Tomcat server installation should you deploy your .WAR file to?

Perhaps the simplest way to deploy a WAR file to Tomcat is to copy the file to Tomcat's webapps directory. Copy and paste WAR files into Tomcat's webapps directory to deploy them. Tomcat monitors this webapps directory for changes, and if it finds a new file there, it will attempt to deploy it.

How do I run a WAR file in Tomcat?

Another thing we can do is to deploy it by simply dropping it into the $CATALINA_HOME\webapps directory of any Tomcat instance. If the instance is running, the deployment will start instantly as Tomcat unpacks the archive and configures its context path.


2 Answers

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.

like image 93
Ryan Stewart Avatar answered Oct 05 '22 02:10

Ryan Stewart


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:

  • Adding a directory to tomcat classpath
  • Can I create a custom classpath on a per application basis in Tomcat
  • How to read a properties file outside my webapp context in Tomcat
  • Configure Tomcat to use properties file to load DB connection information
  • Should you set up database connection properties in server.xml or context.xml
  • Externalize Tomcat configuration

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> 
like image 45
gavenkoa Avatar answered Oct 05 '22 01:10

gavenkoa