As you might have read in previous posts, one of our favorite web application stacks consists of JBoss Seam. While combining this with our favorite tooling suite consisting of Eclipse and Maven, we had to realize that Seam has a much longer tradition with Ant, and Maven support is only slowly moving in. Today I want to share some ideas on how to tackle the combination issues.
While JBoss Tools provides a nice code hot deployment feature when working with Seam on JBoss, things can get a little hairy when using a Maven build with the project. Seam uses a special classloader and relies on monitoring the WEB-INF/dev folder for changed classes - where Maven is completely unaware of this and uses the standard WEB-INF/classes folder to deploy all classes in the project. More than once I've seen the situation where JBoss Tools and Maven got in each others way, ending with JBoss rejecting to restart the application due to "duplicate components". The reason was that Eclipse hot deployed classes to WEB-INF/dev where Seam expects them, but Maven being completely unaware of it packaging all classes in the common WEB-INF/classes folder.
In order to avoid trouble and also to allow using hot deployment from a command line build, I experimented with several approaches to achieve hot deployment with Maven. The main challenges here are:
- Distinguishing compiler output: The Maven compiler plugin by default compiles everything in the same output folder. We need to be able to distinguish between hot and regular deployable code.
- Publish updated resources to JBoss, including domain model classes, seam components as well as XHTML pages.
- Make sure Seam unit tests still compile.
- Hook into the compile phase and compile the source code to a dedicated directory. The source code location is configurable as well as the output directory.
- Remove outdated hot deployable class files from the JBoss deployment. This is necessary for the Seam classloader to pick up the changes, only updating the file is not enough.
<plugin>
<groupId>com.ctp.seam.maven</groupId>
<artifactId>maven-hotdeploy-plugin</artifactId>
<version>0.1.0</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.source.version}</target>
<hotdeployOutputDirectory>${directory.hotdeployables}</hotdeployOutputDirectory>
<includes>
<include>**/session/**</include>
</includes>
<deployDirectory>
${directory.deploy.jboss}/${build.finalName}.${project.packaging}
</deployDirectory>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
The main additions to a regular compiler plugin setup are:
- This variant uses the common /src/main/java source folder and uses in- and excludes to distinguish the hot deployable sources. Alternatively you can set the sourceDirectory configuration value pointing to a dedicated source folder (similar as JBoss Tools does the setup for you). Don't forget that this requires then the Build Helper Maven plugin to add source folders to your regular build.
- The deployDirectory is a reference to your exploded application directory inside the JBoss deploy folder. This is needed in order to remove outdated hot deployable classes.
- You can specify the hotdeployOutputDirectory directory where the code should be compiled to. Note that this defaults to the value shown in the example - feel free to skip this part. The plugin will compile into this folder after appending a WEB-INF/dev to the path.
- Reconfiguring the WAR plugin so the webappDirectory points inside our JBoss deploy folder. Maven uses this folder to assemble the WAR file, which perfectly corresponds to an exploded deployment. Additionally we define our hot deployment compilation folder as a webResource, which will copy our compilation exactly to the WEB-INF/dev folder in the exploded deployment.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${directory.hotdeployables}</directory>
<filtering>false</filtering>
</resource>
</webResources>
<webappDirectory>
${directory.deploy.jboss}/${build.finalName}.${project.packaging}
</webappDirectory>
</configuration>
</plugin>
- Similarly, we have to add our output folder to the test classpath. This is simplest done by adding it as a resource folder, which will copy the classes to the target/test-classes directory before the test classes are compiled:
<testResources>
<testResource>
<directory>${directory.hotdeployables}/WEB-INF/dev</directory>
<filtering>false</filtering>
</testResource>
...
</testResources>
If you want to run a similar luxury setup as you get from JBoss Tools, you can add a Maven builder to your project. Simply go to the Eclipse Project properties and click "New" on the "Builders" properties. The new Maven build should look like the following:
Make sure that the following conditions apply to your Eclipse setup:
- The JRE you have configured is actually a JDK - otherwise the builder will fail. Once you see that the builder does not fail, you can also switch off the console allocation in the "Common" tab.
- Also take care that you always build with your specific hot deploy profile - otherwise make sure to clean before using the build again.
- Assert that Eclipse uses a dedicated output folder and not the Maven target/classes.
Unfortunately when activating the builder Eclipse might prompt you when editing files and (depending on the speed of your machine) hang for a second or two after saving a file. Still, this is much better than waiting a minute for your JBoss server to restart.
Happy coding!
13 comments:
Is it possible to get this working in EAR type seam project?
I haven't looked at this yet (EARs will probably get out of fashion with EJB 3.1 anyway ;-), but from the WAR perspective this gets just deployed inside the [application].ear folder in the JBoss deploy directory.
In terms of the sample POM, it might be enough to just tweak the directory.deploy.jboss property to point inside the exploded EAR directory. I guess you also need to add a similar profile to the EAR module (resp a common one in the parent POM), using the unpackTypes configuration.
Additional note to the article: Seam 2.1.1.GA includes improved hot deployment, and the solution outlined here might not work as expected (still to be verified :-)
Hi Thomas,
g8 work. Can you verify if it still works with seam 2.1.1.GA?
Hi Rafique
So far I didn't run into issues using the plugin with 2.1.1.GA. Some funky effects popped up with hot deployed session components, but browsing the Seam source code it looked much like this is related to the new hot deploy implementation.
BTW: Just commited a new plugin version yesterday. The new version deals better with an Eclipse Maven project and has a simplified configuration. Unfortunately the documentation didn't make it to the Google code wiki yet :-) but I'll be working on it next week.
Thanks Thomas for your quick response. I will be looking forward to it and meanwhile check out the old one.
Hi Thomas,
This is great stuff, I have a problem though, I need to filter web.xml with maven properties at build time. I added the following in my war plugin config:
<webResources>
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
<targetPath>WEB-INF</targetPath>
<filtering>true</filtering>
</resource>
</webResources>
but the filtering does not happen when running hotdeploy:exploded goal. Do you have any pointers? The filtering works fine for other resources in src/main/resources but I cannot put web.xml in src/main/resources because these files are copied under WEB-INF/classes ...
Hi Guillaume
Try to add
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
to the hotdeploy configuration if you have to filter web.xml. Seams to be a quite new option for the WAR plugin (since Maven 2.0.9 I think). At least in my quick test run here it worked fine.
Hope this helps...
Has anyone tried to make this work for an ear type seam project? If so I would greatly appreciate any pointers. In my configuration, I have many application modules(as jars) packed inside the ear along with the war and ejb jars.
Took a while, but here we go with the EAR project support.
Hello,
I have mysterious problem and I can't do my work. I'm changing the source code and the hotdeploy:explode task generate result file with old date (from today have all *.class files date 2009-08-31 21:35. The parent 2009-09-04 14:39. I have deleted the target directory, called clean but the problem is still alive.
Can somebody point me to good direction ? Where could be problem ?
Thanks Mila
Can you file this on the google code issues, including plugin configuration, plugin version, and maven version?
Please also describe which class files get out of date (the hot deployable, the regular, or the one that end up in WEB-INF/dev, /classes).
I have to apologize, the fault was on my side. I have do synchronization from my college and I have copied the compiled class file (and libraries) to the /src/main/webapp/WEB-INF folder.
So, the plugin work great.
Thanks for yours time.
Mila
Oh I see - had that issue also once with a Maven project and WEB-INF/lib.
Anyway, thanks for posting the cause of the issue!
Post a Comment