Many web applications are configured via web.xml.

Primary examples of this are Comet web application, which are configured via a ServletContextAttributeListener: you may want different listener classes depending on the enviroment you’re working in.

Another example is where Spring configuration files are provided via the contextConfigLocation context parameter.

Here I want to show how to do property substitution in web.xml based on Maven2 profiles.

Let’s start from the case where you don’t need property substitution.
These are your web.xml and pom.xml files:

// web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<listener>
<listener-class>com.acme.CometInitializer</listener-class>
</listener>
</webapp>
// pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.15</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
</connector>
</connectors>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>

As you can see, the pom.xml file specifies the Jetty plugin configuration, with no particular configuration needed: default values are good.
The web.xml file specifies a listener class that initializes your Comet web application.

Often times, however, you want your initializer to configure the application differently, for example by stubbing some functionality; as the initializer is complex, you write a different class.

At this point, you want to be able to specify a Maven2 profile that modifies the web.xml with the suitable class (default or stubbed).
Here’s how you do it:

// web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<listener>
<listener-class>${initializer.class}</listener-class> (1)
</listener>
</webapp>
// pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<build>
<resources> (2)
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
<includes>
<include>web.xml</include>
</includes>
<filtering>true</filtering>
<targetPath>${project.build.directory}</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.15</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
</connector>
</connectors>
<webXml>${project.build.directory}/web.xml</webXml> (3)
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
<plugin> (4)
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webXml>${project.build.directory}/web.xml</webXml>
</configuration>
</plugin>
</plugins>
</build>
<profiles> (5)
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<filters>
<filter>${basedir}/src/main/filters/default.properties</filter>
</filters>
</build>
</profile>
<profile>
<id>stubs</id>
<build>
<filters>
<filter>${basedir}/src/main/filters/stubbed.properties</filter>
</filters>
</build>
</profile>
</profiles>
</project>
// default.properties
initializer.class=com.acme.CometInitializer
// stubbed.properties
initializer.class=com.acme.CometStubbedInitializer

Let’s review the changes step by step.

  1. We replaced the listener class with a property in the web.xml file.
  2. We added a resources section in the pom.xml file to reference both the standard resource location and the web.xml file.
    For the web.xml file we specified that the property-substituted version of the file must go in the standard maven output directory (by default ${basedir}/target, referenced as ${project.build.directory}).
  3. We added the <webXml> element in the Jetty plugin configuration, referencing the property-substituted version of web.xml.
  4. We added the configuration for the war plugin, so that building the war will reference the property-substituted version of web.xml.
  5. We created two profiles that reference different filter files, which will contain the property values to be substituted. The standard Maven2 location for filter files is src/main/filters.

Now you can build your project as before by specifying the profile if you need the stubbed version, like this:

// Normal build
$ mvn clean install
// Stubbed build
$ mvn -Pstubbed clean install
// Stubbed build with Jetty plugin
$ mvn -Pstubbed jetty:run

Enjoy !