Tuesday, February 27, 2007

From maven to mvn : Part 3 -- Filtering Resources & Timestamps

Good morning all!

We left off yesterday with an application that will build and tests that will pass. We even have a war file sitting in our target directory. Before I try to deploy that, however, there are some loose ends I want to clean up.

Our goals for Part 3 are:
- @foo@ (or ${foo}) replacement
- attempt to deploy the war

M1 grew out of a set of ant build scripts. As such, filter-when-copying worked pretty much like you would expect from ant. In other words, any @foo@ text in a filter-when-copying resource would be replaced by the foo property value. M2 uses the newer ${foo} syntax and that's probably a good thing.

The first thing I want to do is identify the files I need to change:
  grep -r '@.*@' src/ | grep -v /.svn | grep -v /images/

It turns out that I'm only using a few properties. One no-brainer is the application version that comes from the pom. The others are application specific properties I need to deal with.

So, @pom.artifactId@ should simply become ${pom.artifactId}. Enabling filters is easy enough to do by adding <filtering>true</filtering> to the appropriate resource tag(s).

That gets me very close but not quite done... You see, I'm filtering /WEB-INF/geronimo-web.xml. The one that gets copied into target/test-classes (because of my pom's testResources as discussed in Part 2) is fine. However, the one in target/contacts-1.0-SNAPSHOT (which is managed by the war plugin) is not filtered. Hrm...

It turns out that the war plugin is responsible for managing that bit of the project. Well, that makes sense. So, to change it's behavior we need to configure the plugin:

<build>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
Now if you read the plugin documentation about this topic you will probably reach the conclusion that I'm doing it wrong. That is probably the correct conclusion. What I should do is create a ${basedir}/otherResources directory and put the to-be-filtered things there. Well, as with my dependency ick, I'm simply going to defer that to later. I still haven't quite decided for myself whether I like the idea of separating the resources from everything else anyway...

So, now I have my ${pom.*} properties filtered but I still have my application-specific properties to contend with. In particular, I have a myApp.timestamp property supplied by the venerable ant <tstamp/> tag that I slam into footer.jsp to display the build time (very handy for our QA folks.) It turns out that getting the timestamp (oh so easy with m1) is a bit of a trick with m2. Thanks to some good folks on the forum for pointing out a clever use of the build number plugin.

First -- tell maven where to find the plugin:
<pluginRepositories>
<pluginRepository>
<id>tlc</id>
<name>TLC Repository</name>
<url>http://commons.ucalgary.ca/pub/m2</url>
</pluginRepository>
</pluginRepositories>

Second -- configure the plugin to provide a timestamp:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>maven-buildnumber-plugin</artifactId>
<version>0.9.4</version>
<configuration>
<format>{0,date,yyyy-MM-dd HH:mm:ss}</format>
<items>
<item>timestamp</item>
</items>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
</configuration>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>
In my case I also have to add an <include...> to my filtering since I want my timestamp in footer.jsp. With all of that in place I now get the date/time in my footer.jsp via ${buildNumber}. It isn't nearly so easy or flexible as with m1 but right now I'm interested in results, not cleverness. Perhaps I'll find something better in the future. (Or maybe write my own timestamp plugin!)

(BTW: If you have simple, static properties they are very easy to define in an external file or directly in pom.xml as described by the link above.)

OK, so now I'm filtering and building and testing and packaging so I should have a deployable war. I'm not brave enough to try to deploy via m2 (perhaps another day) so I'll fire up my application server of choice and deploy the war manually. Nothing really to write about here. You probably don't care. I just thought you would like to know that it actually deploys correctly.

You will have to excuse me now; there are some other, non-maven things I need to address this afternoon. (Yes, it is afternoon now. What began in the morning has bled over the international lunch line due to the usual distractions.) If my meeting schedule allows I'll pickup Part 4 tomorrow and try to do a bit of cleanup on my dependencies.