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.xml
in 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>