Wednesday, September 14, 2011

Filter web.xml with Maven

Maven (build manager for Java projects) has a feature called "filtering". As is generally known you can replace with Maven any variables defined with placeholders like ${my.variable}. Such variables can be placed in property files or any other resource files like CSS, JavaScript, (X)HTML. I normally place placeholders in web.xml and replace them by real values while Maven runs with a certain profile. My default Maven profile is e.g. "development". Another profile is "release". To build artefacts with "release" profile I run
mvn -Prelease clean install
Option -P is for profile you want to build software with. How to set up filtering of web.xml exactly? At first we need to create a directory "filter" under "src/main" and place two (or more) property files under "src/main/filter". In order to demonstrate filtering for two profiles I'm going to create development.properties and release.properties. So, our structure looks now as follows:


Let us define the content of development.properties as
jsf.faceletsRefreshPeriod=2
jsf.resourceUpdateCheckPeriod=2
and the content of release.properties as
jsf.faceletsRefreshPeriod=-1
jsf.resourceUpdateCheckPeriod=-1
The next step is to modify web.xml. web.xml gets two placeholders:
...
<context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>${jsf.faceletsRefreshPeriod}</param-value>
</context-param>
<context-param>
    <param-name>com.sun.faces.resourceUpdateCheckPeriod</param-name>
    <param-value>${jsf.resourceUpdateCheckPeriod}</param-value>
</context-param>
...
The last step is to modify pom.xml. Actually we don't need two profiles. Let us only define the "release" profile and assume default case without any profiles as "development".
<profiles>
    <profile>
        <id>release</id>
        <properties>
            <webapp.filter>release</webapp.filter>
        </properties>
    </profile>
</profiles>
Important thing is to define a property "webapp.filter" for "release" profile. For default case we have also to add this line to pom.xml (somewhere at the end):
<properties>
    <webapp.filter>development</webapp.filter>
</properties>
To make it working together with maven-war plugin (in this case) we need to activate filtering feature. That means, before WAR archive gets packed, placeholders in web.xml should be already replaced by real values from property files. This task is simple:
<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <executions>
        <execution>
            <id>war</id>
            <phase>package</phase>
            <goals>
                <goal>war</goal>
            </goals>
            <configuration>
                ...
                <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
                <filters>
                    <filter>${basedir}/src/main/filter/${webapp.filter}.properties</filter>
                </filters>
            </configuration>
        </execution>
    </executions>
</plugin>
We leverages here the variable ${webapp.filter} which points to file name depends on currently active profile. That's all. You can now run builds with
// build for development
mvn clean install

// build for release
mvn -Prelease clean install
There is only one issue when using Jetty Maven plugin and let it runs with
mvn jetty:run
Jetty looks as default under source directory and scans src/main/webapp/WEB-INF/web.xml. But located there web.xml has placeholders. How to solve that? A solution is easy. This is a bonus part of this post. We can leverages overrideDescriptor tag and specify a XML file with our own parts of web.xml which overwrite the same parts in src/main/webapp/WEB-INF/web.xml when Jetty running.
<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <configuration>
        <webAppConfig>
            ...
            <overrideDescriptor>src/main/resources/overrideweb.xml</overrideDescriptor>
        </webAppConfig>
    </configuration>
</plugin>
The content of overrideweb.xml is (we normally need settings for default "development" profile when Jetty running)
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
         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">

    <context-param>
        <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
        <param-value>2</param-value>
    </context-param>
    <context-param>
        <param-name>com.sun.faces.resourceUpdateCheckPeriod</param-name>
        <param-value>2</param-value>
    </context-param>
    <context-param>
        <param-name>com.sun.faces.injectionProvider</param-name>
        <param-value>com.sun.faces.vendor.WebContainerInjectionProvider</param-value>
    </context-param>
    <context-param>
        <param-name>com.sun.faces.spi.AnnotationProvider</param-name>
        <param-value>com.sun.faces.config.AnnotationScanner</param-value>
    </context-param>
    
    <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener>
</web-app>
Happy filtering!

2 comments:

  1. Very useful. thanks.

    ReplyDelete
  2. Thanks, it's very useful.

    However it doesn't work in Eclipse then.
    Proper Maven configuration which works in Maven and Eclipse as well is here: https://github.com/sonatype/m2eclipse-wtp/blob/master/org.maven.ide.eclipse.wtp.tests/projects/WebResourceFiltering/example-web/pom.xml#L98

    ReplyDelete

Note: Only a member of this blog may post a comment.