Saturday, June 26, 2010

maven release management

Introduction

Following up on my previous article about Maven, this one will dive into the deep and unleash the wonders of performing release management using Maven.

For release management you need some kind of source repository; this article will assume you will be using Subversion.


What is release management?

There are plenty of definitions out there, mine is as follows:

"to regulate, document and safely store source code, versions and releases".

Broken down into sections:

- source code is version managed (subversion)
- software is 'released', creating for example a milestone tag in subversion
- binary artifacts (ears usually) are also version managed
- version releases end up in a maven repository so their artifacts can be referenced from mavenized projects
- software projects are checked for compile problems and unit test failures through an automated build server

We won't be discussing any build server in this article as it is not really maven specific, but I'm going to make sure everything else is covered!


Hooking in the Nexus

A good place to store our own maven artifacts upon releasing them is in a Nexus repository. I'll leave it to the Nexus documentation to guide you how to set one up, but the least I can tell you is how to configure one in your maven POM. You'll want to put the following into your parent pom:

<distributionManagement>
    <repository>
      <id>releases</id>
      <name>Local release directory</name>
      <url>http://yournexushost/nexus/content/repositories/releases</url>
    </repository>
    <snapshotRepository>
      <id>snapshots</id>
      <name>Local snapshot directory</name>
      <url>http://yournexushost/nexus/content/repositories/snapshots</url>
    </snapshotRepository>
  </distributionManagement>

This piece of configuration assumes that you will be creating a 'releases' and a 'snapshots' repository in your Nexus server. This makes sense because in Maven we will be dealing with exactly those two types of artifacts. I'll leave it up to you if you really want snapshot versions of your projects to actually end up in Nexus.


If you followed the Nexus instruction guide (IE. you did some setup in your settings.xml also!), then you should be up and running to have Maven use Nexus!


Subversion on the commandline


To let maven do subversion stuff, you'll need to have a commandline subversion client installed. There are a few; I suggest to use SlikSVN.

Maven will do "non-interactive" commits, meaning it will not ask you for a username and password when it tries to commit to SVN. To tell Slik which username and password you want to use always, do an SVN commit from the commandline once; Slik will then ask you for your credentials. When you have them set, Maven will automatically adopt them and you won't have to pass them along to Maven when you perform releases.

Note: be sure that the SVN command can be invoked without a path; add the Slik binary directory to your PATH environmental variable!


Subversion and maven: friends for life

The first thing we will want to take care of is to tell Maven where our project is stored in your subversion environment. We'll want to tell it where it can get the source code, and where to store tags. You can do this with some simple configuration in your parent pom.

<scm>
    <connection>scm:svn:http://svn.path.to.your.project/myproject/trunk</connection>
    <developerConnection>scm:svn:http://svn.path.to.your.project/myproject/trunk</developerConnection>
  </scm>

This tiny piece of information tells Maven where you will be committing source files to - in this case to the trunk of our imaginary project. You could also specify a branch if you would want that. Maven SCM is a neat plugin that allows you to connect to far more than Subversion by the way - I'd explore the maven documentation on this topic.


The actual magic is performed by the maven release plugin - that is the one we'll be configuring.

<plugin>
        <artifactId>maven-release-plugin</artifactId>
        <configuration>
          <tagBase>http://svn.path.to.your.project/myproject/tags</tagBase>
          <preparationGoals>clean install</preparationGoals>
        </configuration>
      </plugin>

A vital piece of information is the tagbase; this is where we will be storing our release versions in subversion. Also, the preparationGoals tells Maven what it should do before we do an actual release; we will want it to do a clean install to be 100% sure that we are working with a clean up to date project.

And we're there actually, Maven is now setup to do some wicked release management steps for us.

Maven version management

Not subversion, but the actual versions of your project and its modules. In order for release management to work, you MUST adhere to the rules maven lays down for you.

Luckily, there is only one rule. During development, the version of your application will be 'version-SNAPSHOT'. The important part here is the -SNAPSHOT. For example, you can have version 1.0-SNAPSHOT. This literally translates to 'version 1.0 that is still in development', thus not released yet. If you do not work with snapshot version during development, you are not going to be able to use maven release, as one of its steps is to promote the snapshot versions to release versions!


Preparing a Maven release

To do the actual release, we need to go to the command line. Before we continue, you should know what Maven is going to do for us.

The first step of a release is to prepare it. You do this by invoking the following command under the parent pom:

mvn release:prepare

  • First of all, it will check if you still have external snapshot references in your dependencies; you should resolve those to release versions before you perform the actual release.

    Also, Maven will check your project against what is in Subversion to see if there are no differences. You'll find you cannot do a release if there is but one difference somewhere; make sure to update and commit your project!
  • The first thing that maven will do (after the clean install) is ask you which versions of the module you will be committing. Maven will turn all snapshot versions of your project's parent and modules into release versions automatically. Usually you can just <enter> through these to keep the default values.
  • The second thing maven is going to ask you is the tag name you will give your release; this is the exact name of the project as it will end up in your tags folder of the subversion repository. Choose it well; usually companies have a naming scheme for this. Be sure to put the current version number of the project into it at least.
  • The third thing maven will ask you are the next snapshot versions of your parent and modules if you have a logical version number (such as 1.X), maven will automatically generate a correct next minor version number, thus you can <enter> through the suggestions to keep them.

Phew. Once all that is done, maven will perform some magic which will generate a lot of output. If everything is alright, you'll get a build succesful and you'll have a fresh new tag in your subversion tree, plus the poms in your subversion repository will have their versions incremented to the next snapshot version. Hurray! But we're not done yet!


Performing a Maven release

We have to invoke one additional command, and that is:

mvn release:perform

This command will check out the tag we just created from the subversion repository, and it will try to compile/unit test/package it (depending on what you have setup). Afterwards if that succeeds, the release is uploaded to the nexus repository under the right group, artifact and version.

And that is all! You may want to take the packaged binaries now and store them away safely - for example I opt to put all release binaries in a releases subversion repository!


Resume and rollback

If for some reason something blows up during the two release phases, you have two options available to you.

a) fix the problem.
If you can, you can use the -Dresume=true switch to make Maven pick up where it left off. For example:

mvn release:prepare -Dresume=true


b) rollback
The action of rollback is to undo the damage that Maven has already done. After the rollback your project will be back in the state as if you didn't invoke the release command.

mvn release:rollback


Adding unit testing to the mix

No release management cycle is complete without automated unit testing. Maven, again, jumps to the rescue. Maven has a plugin called Surefire that can automatically invoke JUnit or TestNG unit tests that are part of our project. All we need to do, is tell maven that they exist.

First of all, we need to add the surefire plugin definition to our parent pom:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.5</version>
        <childDelegation>true</childDelegation>
        <useSystemClassLoader>true</useSystemClassLoader>
        <argLine>-Dsun.lang.ClassLoader.allowArraySyntax=true</argLine>
      </plugin>


If you want to use TestNG, you have to add the TestNG dependency to your project.

<dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>5.8</version>
      <scope>test</scope>
      <classifier>jdk15</classifier>
    </dependency>

Also don't forget to add the dependency to any module (EJB, WAR) that you have unit tests in.


Now that surefire is setup, it will invoke test cases that are part of your project. By default, Maven will add resources in the following directories to the test classpath:

module/src/test/java
module/src/test/resources

This is the maven default and you should follow it; however if you want to override the convention, you can have control over it by adding the following configuration to your module pom:

<build>
  ...
  <testSourceDirectory>src/test/java</testSourceDirectory>
  <testResources>
    <testResource>
      <filtering>true</filtering>
      <directory>src/test/resources</directory>
    </testResource>
  </testResources>
  </build>

Through the testResources you can also include and exclude certain file patterns - for example if you only want to add XML files from the test resources, do the following:

<testResource>
      <filtering>true</filtering>
      <directory>src/test/resources</directory>
      <includes>
       <include>**/*.xml</include>
      </includes>
    </testResource>

Try it out: upon a mvn clean install you should now see any unit tests in your project being invoked. Not only that, but if a unit test fails your build will fail (this is something you actually want)! Also, you can run tests by invoking the following command:

mvn clean test

(I always add the clean step to be sure you won't be using out of date classes - leave it out at your own discretion).

Unit test not running?
Surefire has a naming convention. Check this documentation page for more information:

Surefire documentation


Reporting

The final thing you may want to add is report and documentation generation. For example the surefire reporting is especially useful when trying to figure out why a testcase blew up - the report will contain the generated error.

Add the following configuration to your parent pom:

<build>
   <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>2.7</version>
      </plugin>
   </plugins>
  </build>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-report-plugin</artifactId>
        <configuration>
          <showSuccess>true</showSuccess>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <configuration>
          <aggregate>true</aggregate>
        </configuration>
      </plugin>
    </plugins>
  </reporting>

Adding the javadoc plugin allows you to build documentation for your project using a simple maven command:

mvn javadoc:javadoc


Conclusion


I hope I have given you the tools you need to setup release management in your own environment. Yes it requires some work and you need to wire some tools (usually with lengthy manuals) together to get a benefit out of using Maven, but in the end it is very much worth it - I can guarantee that.

As a side note, I would like to point out the existence of Continuum, which is a build server you can use to automatically test Mavenized project builds. Continuous integration is really that final icing on the cake that can make your release management cycle complete. But I have to warn you: setting up Continuum is no picnic. You may want to explore some of the alternatives that are available, such as Hudson.

Good luck!

Sunday, June 13, 2010

maven2 - a copy pastable template

Introduction

Maven; a wonderful tool to manage your project dependencies and builds with. It is also a source of great confusion and often is not used as the right tool for the job. I'll repeat what several people have already said before: Maven is NOT a build tool like ANT.

What Maven does give you is easy dependency management - in stead of shipping the dependencies with your project, they are downloaded from central repositories. To make this even more manageable, you can put up a tool like Nexus, which acts like a proxy server between your local maven projects and the big bad outside world. In stead of downloading dependencies off the internet, you'll be downloading them off of Nexus (which in turn might download it from the net if it doesn't have it yet).

Maven can also be used to build, test and package your project and even take that final step of applying release management to it, creating a tag in an SVN repository and possibly putting the resulting artifacts in Nexus so it may be used in other projects.

Maven is great, but it has a learning curve. I hope to make that learning curve slightly less steep by providing a clear and logical template for maven project setups that you can copy and adapt for yourself. I have applied this structure to all projects I have done so far; be it a simply library jar to a full blown enterprise application with ear, ejb and war modules. It just works!

In this document I'll give the full JavaEE project template; you can cut out what you don't need yourself.

Repositories

To be able to work with Maven, you need repositories. Repositories hold the dependencies that Maven can then download on demand. Once downloaded a dependency exists in your local repository, which is a directory on your harddisk. This prevents Maven from having to download dependencies over and over again, seriously speeding up your builds.

Out of the box Maven knows about the maven central repository, which is found here: http://repo1.maven.org/maven2/

I'd say about 80% of the dependencies you need can be found here. Due to licensing issues certain dependencies are blocked from entering the maven central and will exist in vendor specific repositories. Two useful ones are:

1. The java.net nexus: https://maven.java.net
3. The JBoss repository: http://repository.jboss.org/nexus/content/groups/public-jboss/

The Jboss repository is of course useful for JBoss dependencies such as JBossAS itself, JBPM, Drools, etc. There are also plenty of the more obscure dependencies to be found there. Note that this is a newer repository; there is also an older one that is no longer updated. Always check that you have the above repository configured.

The java.net nexus replaces the old java.net repository which used to be documented here. All dependencies from the old repository have been migrated to Maven central which is a -very- good thing. You should only configure the Java.net nexus if you need a dependency that is in there.

The java.net Maven documentation has a nice guide how to setup additional repositories.


The pom structure

Maven works with files called 'poms'. These are XML formatted meta data files that tell Maven how to do its magic and where to put files. Generally you will want to keep these files as sparse as possible; Maven defines 'convention over configuration' and it is wise to stick to the maven way of doing things, in stead of enforcing your own rules. I will assume that you actually do follow the convention.

Maven poms can follow a hierarchy; this is more useful than you can imagine. I will right now tell you what hierarchy the template I am describing here will follow:

parent pom
The parent pom is the 'gatekeeper' of the project; it defines which modules are part of the project and it will define which dependencies we will be using, plus which versions of these dependencies. It is important to keep that kind of information in the parent, because otherwise you can end up with version conflicts between the modules.

Module poms
Module poms will result in the actual 'deliverables' of our project. In terms of a JavaEE project that will be an ear, zero or more EJB modules and zero or more WAR modules.


On disc, that might look like this:

project_dir
  • pom.xml {parent}
  • myejb_dir
    • src/main/java
    • pom.xml
  • mywar_dir
    • src/main/java
    • pom.xml
  • myear_dir
    • pom.xml

Quite simple, but very effective.

Groups and artifacts


To refer to dependencies, Maven defines them in 'groups', 'artifacts' and 'versions' (optionally also a classifier, but I wouldn't worry too much about that). For example, if we would want to include JBPM in our application, we would declare a dependency like this:

<dependency>
 <groupId>org.jbpm.jbpm3</groupId>
 <artifactId>jbpm-jpdl</artifactId>
 <version>3.2.9</version>
</dependency>

How to know what the groupId and artifactId of a certain library are? I generally use the trick of looking the dependency up in the maven repository itself using google. For example searching for "maven 2 jbpm3" gives the following url as a result:

Jboss maven repository.

Now under the jbpm3/jbpm-jdpl subdirectory you'll find all the jbpm3 versions, including our 3.2.9 version. The full path thus becomes:

org/jbpm/jbpm3/jbpm-jpdl/3.2.9

To get the groupID, artifactID and version, you simply strip off elements from right to left.

Version=3.2.9
artifactId=jbpm-jpdl

And what is left is the groupId, replacing the slashes with dots.

groupId=org.jbpm.jbpm3

If you know this little trick, it is quite easy! All you need is the name of the API you want to use and Google.


the parent pom


Through the parent pom we will do dependency management, and more specifically we'll manage the versions of those dependencies. But the minimal thing we'll do is define what modules our project is built out of.

First of all, the basic header that all poms should have.

<?xml version="1.0" encoding="UTF-8"?>
<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">
  
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.myproject</groupId>
  <artifactId>myproject-parent</artifactId>
  <name>Project name</name>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

Here we define our groupId and the artifactId of the parent. For our modules we'll use the same groupId, but different artifactIds of course.

The version follows a maven standard: a SNAPSHOT release is an 'in development' release; remove the -SNAPSHOT for a stable production release. (the maven release management functions can automatically do that).

The 'pom' packaging type we specify here basically refers to the fact that this is a parent pom - it doesn't lead to a specific product, it is used to manage modules.


Next, we define which modules our project has.

<modules>
    <module>myproject-ejb</module>
    <module>myproject-web</module>
    <module>myproject-ear</module>
  </modules>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.mycompany.project</groupId>
        <artifactId>project-ejb</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <type>ejb</type>
      </dependency>
      <dependency>
        <groupId>com.mycompany.project</groupId>
        <artifactId>project-web</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <type>war</type>
      </dependency>
      <!-- more dependencies here -->
    </dependencies>
  </dependencyManagement>

Three modules, as discussed. This not only tells maven which modules are part of the project, but also in which subdirectories the modules will be stored - the subdirectories must match the names as they are written here.

The dependencymanagement section basically says 'if you adopt me in your project, you are taking these specific versions of these dependencies and their transitive dependencies with you as well'. That's why we don't put the EAR module, that module is of no consequence to the outside world.

Finally, we declare which dependencies we want to manage for our project. You will typically put all dependencies here that you want to use in your EJB, WAR and EAR modules. In the place of the more dependencies here above, put the dependencies, versions and default scope of all dependencies your project will use. Example:

<!-- more dependencies here -->
    ...
    <dependency>
      <groupId>org.jbpm.jbpm3</groupId>
      <artifactId>jbpm-jpdl</artifactId>
      <version>3.2.9</version>
    </dependency>
    </dependencyManagement>
</project>

Note that declaring the dependencies here (in the dependencyManagement section) will not actually add them to the project dependencies yet; think of these declarations only as 'in this project, the modules might use these libraries and I want them all to use these specific version, plus I want to have them at this scope by default'.

Dependency scope

You can provide another dependency configuration element: the scope. In my own projects I tend to use three scopes:

compile (default)
This means that the dependency is a transient dependency needed at compile time AND runtime of the application. Generally this will cause the dependency to be packaged with your application, depending on what the artifact type is. Since this is the default scope, when you don't provide scope information the dependency will be in the compile scope.

provided
This scope tells maven 'I need this dependency during compile time, but during runtime it will already be in the working environment'. A good example of this is the JavaEE dependency: when you deploy your web application the server will already have the libraries needed, so you certainly never ever want to deploy dependencies such as the servlet-api with your application. the provided scope can help you to manage this.

test
The test scope is the same as the provided scope; be it that the libraries will only be put on the classpath with running JUnit or TestNG unit tests. Dependencies such as JUnit/TestNG itself and Mock APIs used for testing purposes will need to be put in the test scope.

Managing and maintaining scopes is a piece of responsibility you HAVE to take when you start to use Maven; putting libraries on the wrong scope can have annoying or even disastrous results. Minimally it will lead to classpath pollution. Please, take the time and effort to put the dependencies on the right scope. You only have to provide that information in your parent pom, your modules will adopt it.

<dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>5.9</version>
      <scope>test</scope>
    </dependency>


the EJB pom

EJB and WAR poms are very much alike. I'll illustrate the EJB pom and then list what is different for the WAR pom.

First of all, the common header.

<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">

  <modelVersion>4.0.0</modelVersion>
  <artifactId>myproject-ejb</artifactId>
  <groupId>com.mycompany.myproject</groupId>
  <name>Myproject EJB layer</name>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>ejb</packaging>

This should speak for itself. We are building an EJB jar, so that's the packaging type. For ease, I would keep the groupId the same as the one of your parent (in fact, use the same groupId for every module).

Now we add a new bit to the POM header.

<parent>
    <artifactId>myproject-parent</artifactId>
    <groupId>com.mycompany.myproject</groupId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

In the parent we say what modules are part of the project, in the modules we explicitly say which parent the module has, plus its version. This strongly ties the parent to the module and will allow you to link to any specific versions of the project's artifacts (as long as the versions end up in some global repository, such as a Nexus).

The minimum you'll want to add to the modules is which dependencies they have. As discussed because you have already defined the dependencies and the default scope in the parent pom, you don't need to specify any versions or scope in your modules.

<dependencies>
   <dependency>
      <groupId>org.jbpm.jbpm3</groupId>
      <artifactId>jbpm-jpdl</artifactId>
   </dependency>
   ...
 </dependencies>

Note that the tag name is dependencies, NOT dependencyManagement. Everything that you declare in a dependencies block are actual dependencies part of the module. That's the difference:

dependencyManagement: these dependencies I don't want to include right now, but I am going to use them somewhere and I want to use these versions.
dependencies: these dependencies I am going to use right here in this module.

Besides dependencies, you may also want to declare build properties in your EJB/WAR poms. Such as the following:

<build>
    <finalName>myproject-ejb</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>
    <defaultGoal>install</defaultGoal>
    <resources>
      <resource>
        <filtering>true</filtering>
        <directory>src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <filtering>true</filtering>
        <directory>src/test/resources</directory>
      </testResource>
      <testResource>
        <directory>src/main/java</directory>
      </testResource>
    </testResources>
  </build>


Now this build definition is actually quite unnecessary, as it configures the default convention that Maven implies. Any maven project will have its source files in the following structure:

PROJECT_DIR
/pom.xml
/myproject-ejb/pom.xml
/myproject-ejb/src/main/java
/myproject-ejb/src/main/resources
/myproject-ejb/src/test/java
/myproject-ejb/src/test/resources
/myproject-web/pom.xml
/myproject-web/src/main/java
/myproject-web/src/main/webapp
/myproject-web/src/main/resources
/myproject-web/src/test/java
/myproject-web/src/test/resources
/myproject-ear/pom.xml


So there is a split between application source files and test source files. Also there is a logical split between source files (java) and any other type of file such as images and configuration files (resources).

Even if you are not using maven, this is a very logical, flat and easy path structure to use. I use it for all my projects now, even ones I don't manage with Maven.

One last thing. By default Maven assumes EJB 2.1 spec. You will want to override that by manually declaring the EJB plugin in your parent pom, like this:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-ejb-plugin</artifactId>
      <inherited>true</inherited>
      <configuration>
        <ejbVersion>3.1</ejbVersion>
      </configuration>
    </plugin>
    
  </plugins>
</build>

We'll cover more plugins a little later.

Now you could have slapped packaging type 'jar' on your EJB module and be done with it. There is a good reason to make your poms 'by the book' however; the fact that you configure your EJB module to be of packaging type EJB can influence tools built on top of Maven. A good example is the m2eclipse plugin of Eclipse which synchronizes your Eclipse project settings with the project poms; if you neglect the packaging type the project won't receive the EJB facet; if you neglect the plugin definition to specify the exact version, the wrong version of the facet will be configured by the plugin.

More information on Maven in Eclipse you can read in my article on the subject.

the WAR module


The WAR module is very much the same as the EJB module, with some notable differences.

<packaging>war</packaging>

Easy enough, we'll be creating a war file so that is the packaging type.

<dependencies>
    <dependency>
      <groupId>com.mycompany.myproject</groupId>
      <artifactId>myproject-ejb</artifactId>
      <type>ejb</type>
      <scope>provided</scope>
    </dependency>
    ...
  </dependencies>

Here the EJB module is defined as a dependency in the WAR module - this should make sense about now; you'll be wanted to use the EJBs in your WAR module, so you'll want it on the WAR classpath during compile time. To not confuse maven however, you have to declare the dependency as provided - after all, the EJB jar will certainly be there when you deploy your application!


the EAR module

The EAR module consists of nothing but a pom that will tell the maven EAR plugin how to generate it.

<packaging>ear</packaging>

Standard header (including the parent declaration), only we are packaging an EAR.

<build>
    <finalName>myapp</finalName>
    <defaultGoal>install</defaultGoal>
    <plugins>
      <plugin>
        <artifactId>maven-ear-plugin</artifactId>
        <configuration>
          <displayName>Myproject EAR module</displayName>
          <description>Myproject EAR module</description>
          <defaultLibBundleDir>lib</defaultLibBundleDir>
          <modules>
            <ejbModule>
              <groupId>com.mycompany.myproject</groupId>
              <artifactId>myproject-ejb</artifactId>
            </ejbModule>
            <webModule>
              <groupId>com.mycompany.myproject</groupId>
              <artifactId>mycompany-web</artifactId>
              <contextRoot>/myproject</contextRoot>
            </webModule>
            ...
        </modules>
        </configuration>
      </plugin>
   </plugins>
  </build>

Basically this piece of configuration defines how the application.xml of the EAR will be generated. The defaultLibBundleDir must not be forgotten; if you leave it out all dependencies will end up in the root of the ear, where they will NOT end up on the classpath of the EAR by default. They must go to the lib subdirectory for that to happen.

As you can see, you define the modules of the application. For EJB modules you will get some generated name (as apposed to the finalName you gave the EJB for some strange reason); to force a specific filename on the EJB you can add the bundleFileName element, like this:

<ejbModule>
     <groupId>com.mycompany.myproject</groupId>
     <artifactId>myproject-ejb</artifactId>
     <bundleFileName>myproject-ejb.jar</bundleFileName>
   </ejbModule>

You can do the same for war modules, although in my experience war modules do adopt their finalName in the EAR.


You must also declare the modules as dependencies, to get the dependency management going:

<dependencies>
    <dependency>
      <groupId>com.mycompany.myproject</groupId>
      <artifactId>myproject-ejb</artifactId>
      <type>ejb</type>
    </dependency>
    <dependency>
      <groupId>com.mycompany.myproject</groupId>
      <artifactId>myproject-web</artifactId>
      <type>war</type>
    </dependency>
    ...
  </dependencies>
</project>

It seems a little excessive to have to do this - I'm sure the Maven developers had a good reason though.

And thats basically it, you now have working templates for creating a standard JEE application!


Transitive dependencies revisited

Dependency management is the main feature of Maven, and we're not quite there yet with our understanding of it. Let me go a little deeper.

You know that dependencies at the compile scope are transitive dependencies. But what does that mean to Maven?

A lot actually! Lets create an example just how important it is to properly manage your dependencies and their scopes. Say we want to use JBPM in our EJB module. This means that when we deploy our EAR, the JBPM jar needs to end up in the lib subdirectory of the EAR for it to work during runtime.

To let Maven do that for us, do the following. In your parent pom, activate dependency management on JBPM:

<!-- in the parent pom -->
<dependencyManagement>
...
<dependency>
 <groupId>org.jbpm.jbpm3</groupId>
 <artifactId>jbpm-jpdl</artifactId>
 <version>3.2.9</version>
</dependency>
</dependencyManagement>

We've already seen that, now we have told maven that whenever we refer to JBPM, we want version 3.2.9 of it and we want it to be on the compile scope by default.

Now in your EJB, add JBPM to the dependency list:

<!-- in the EJB pom -->
  <dependencies>
   <dependency>
      <groupId>org.jbpm.jbpm3</groupId>
      <artifactId>jbpm-jpdl</artifactId>
   </dependency>
   ...
 </dependencies>

Also nothing new. But now we've made JBPM a transitive dependency of the EJB module - Maven now knows: whenever you add the EJB module as a dependency, JBPM has to be deployed with it!

And that is exactly what happens when we put the EJB in our EAR, like this:

<!-- in the EAR pom -->
  <dependencies>
    <dependency>
      <groupId>com.mycompany.myproject</groupId>
      <artifactId>myproject-ejb</artifactId>
      <type>ejb</type>
    </dependency>
    ...
  </dependencies>

And right there it happens! The EJB is a dependency of the EAR module, and right now Maven will automatically deploy the transitive JBPM dependency with your EAR, putting it in a lib subdirectory (remember: because of the defaultLibBundleDir element we added to the configuration). This is very important that it does this, as all jars in the lib subdirectory of an EAR are automatically deployed by the server, you don't have to configure anything more to make it happen. Slick!

Similarly, transitive dependencies of a WAR module will automatically be put in the WEB-INF/lib directory by Maven when you let it build the war file. It doesn't get any better than that I'd say.

Sharing dependencies between the EJB and the WAR

It might happen you need the same library in an EJB module and in a WAR module. Because the library is a transitive dependency of the EJB, it will end up in the EAR as I discussed in the previous paragraph.

Fact of the matter is, all EAR libraries are also visible to the WAR modules, so letting the library be put in the WAR also is overkill. The solution?

Declare the dependency as provided in the WAR, overriding the compile scope as it is determined by the parent. In this case it is true, because the jar will be provided to the WAR through the EAR file. That cleans up a duplicate library mess.

That is a basic rule by the way: scope and version of a dependency CAN be overridden in module poms by adding version and scope tags to the dependency. You just should minimize it only to cases where it is really necessary, like this particular one. In a sane project environment only the parent will contain version declarations.


Controlling compiler settings

The convention over configuration concept that Maven follows has resulted in many configuration options not being present in the template I have put before you thus far. You can have finegrained control over many of the maven plugins that are available. Most of them you can easily work out from the online maven documentation; to give you an idea I'll show you how to control compiler settings. For example, lets see how you can make Maven output Java 5 compatible classes. You can declare this in the parent so all the modules adopt it.

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>

Check out the maven documentation to know which plugins are available and what configuration properties they have!

So what if you don't create a JEE project?


It's flexible, Bill.

Say you want to create a maven project for a library - one jar, nothing more, nothing less. You could define a parent and a single jar module, or you could skip the parent altogether and just create a single pom with jar packaging. Its up to you - just be sure to think about what you are doing. Maven is a wonderful tool, but it isn't a magic wand that can do everything. ANT however can do a lot more, and did you know you can invoke ANT scripts from within Maven?


Closing thoughts: Fine grained dependency management


The dependency management system of Maven is fantastic. But it can lead to problems also. Once you start to attach dependencies to your project, you'll find that additional dependencies start popping up all over the place. These are transient dependencies of your own transient dependencies - sometimes they are correct runtime dependencies of the libraries you use, other times they are the result of not defining the proper scopes. JBPM is a good example of this; when you include it in your project you'll automatically get such dependencies as Lucene and Jackrabbit as a gift, while they are absolutely not needed during runtime!

You'll want to keep your classpath clean, and that will take a little micromanagement. When you declare a dependency in your parent pom, you can also add exclusions to the dependency. Its easy, like this:

<dependencies>
    <dependency>
      <groupId>org.jbpm.jbpm3</groupId>
      <artifactId>jbpm-jpdl</artifactId>
      <version>3.2.9</version>
      <exclusions>
       <exclusion>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-core<artifactId>
       </exclusion>
      </exclusions>
    </dependency>
    ...
  </dependencies>

Its annoying, but usually you'll only have to figure this out once and then it will become a copy-paste action among your projects. The question is, how do you figure out where all the transitive dependencies are coming from? Maven comes to the rescue. Simply invoke the following command under your parent or a module:

mvn dependency:tree

When invoked from the parent, that will display all dependencies in all modules in a nice tree structure, showing exactly where all the dependencies and their transitive dependencies are coming from. If you invoke this under a module you'll only get the dependencies in that module.


That's it!

I haven't even covered half of what is possible using Maven in this article. That was not my intention either. I hope I have given you a clean and easy to use template for your own projects that you can copy and abuse.

When I have found some dependable webspace I'll put up a download of a 'complete' maven project. In a future article I'll also describe release management using Maven - it would have made this article to confusing.

Wednesday, June 9, 2010

JBPM 3.2.6 on JBoss 5.1

Introduction


Note: This article is now basically obsolete. I refer you to my jbpm 3.2.9 installation guide. I let this article be to preserve the knowledge inside it.


JBPM is a 'Business Process Management' API. This API allows you to define program logic as a process, providing a vastly different and more visible approach to implementing application flow.

This document will describe setting up JBPM 3.2.6 on JBoss 5.1. The question you may ask yourself is: WHY do I document this? There is already a JBPM 4 out there!

Indeed there is and the JBPM4 API is very slick indeed. It deploys like a charm on JBoss 5.1, the API is very clean and in less than 5 minutes I had a database oriented test case running that was stepping through a process.

But there are a few things that I don't like about JBPM 4.

  • the tools are only half finished and quite buggy
  • the original 'brain' of JBPM has left Redhat and is now working on a competing open source API under the name of 'Activiti'.

Activiti press release.

This API is still heavily in development though, and thus not very useful yet. I am keeping my eye on it however.

And apparently Redhat also already has JBPM5 in development; my guess is that JBPM4.x is given up for lost for now and the focus for the tool development will be on JBPM5. A message on the main jbpm website called 'JBPM is still alive and kicking' seems to back this up.


All is fine and well, but I want a working JBPM API that DOES have functional tools. So I stick with my good old JBPM 3.2.X, which has served me well over the last year (with some help from me). The trouble is that this API is quite old already and developed with JBossAS 4 in mind. It just doesn't work out of the box on JBoss 5.1.

This document will tell you how you CAN make it work.


A note about JBPM 3 minor versions

If you look online, you'll find that there is actually also a JBPM 3.3.1. This version is *not* newer than JBPM 3.2.6! At one point in time JBPM 3 was forked into two paths; the JBPM 3.2.X path and the JBPM 3.3.X path. The 3.3.X path is binary compatible with the JBPM 3.2.X path, but also has an additional tool called the GWT-console, which is a flashier management interface built on top of google web toolkit.

I don't know why, but the 3.3.X path was not maintained as far as the 3.2.X path. I recommend sticking with 3.2.6 as it contains a few bugfixes that are very much worth it.

JBPM 3.2.6 under the hood


The engine
When I talk about "JBPM", I actually talk about three different components;

  • JBPM API; the api you can program against in your applications to be able to invoke and manage processes.
  • JBPM MANAGER; a management application package that you deploy into JBossAS; the manager application holds the deployed processes and has a management interface in which you can see deployed process definitions and the state of process instances. It also allows you to deploy new process definitions.
  • JBPM TOOLS; part of the "JBoss Tools" Eclipse plugin set, the JBPM tools give you a neat visual process designer application, also with the ability to directly deploy processes to a JBPM manager instance (requiring a running JBoss instance of course).

par files

JBPM 3 defines a binary type called a 'par' file. This is in fact nothing more than a zip file with the process classes and the process definition file inside it. The JBPM process designer can create these files for you if you want; You can also use the following ANT task, modifying it to your own taste/environment.

<zip destfile="${project.build.directory}/PROCESS_NAME-${project.version}.par">
  <fileset dir="${project.build.directory}" includes="**/PROCESS_NAME/handlers/*" />
  <fileset dir="${process.src.root}/PROCESS_NAME" includes="*.*" />
</zip>


This contains the following properties, as used in a maven pom file:

project.version = maven project version (1.0.0 for example)
process.src.root = PROJECT_BASE_DIR/src/main/process
project.build.directory = PROJECT_BASE_DIR/target/classes

I tend to put the node handler classes for a process in a package with the same name as the process and in a subpackage 'handlers'; this means that for example if I create a process 'myprocess', the package would be myapp.myprocess.handlers. Adjust according to your own taste.

Maven compile dependencies


To be able to compile code that uses JBPM, you need no more than the following maven dependency.

<dependency>
        <groupId>org.jbpm.jbpm3</groupId>
        <artifactId>jbpm-jpdl</artifactId>
        <version>3.2.6.SP1</version>
        <exclusions>
          <exclusion>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
          </exclusion>
          <exclusion>
            <groupId>org.apache.jackrabbit</groupId>
            <artifactId>jackrabbit-core</artifactId>
          </exclusion>
          <exclusion>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
          </exclusion>
          <exclusion>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
          </exclusion>
          <exclusion>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant-launcher</artifactId>
          </exclusion>
          <exclusion>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
          </exclusion>
          <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
          </exclusion>
          <exclusion>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
          </exclusion>
          <exclusion>
            <groupId>bsh</groupId>
            <artifactId>bsh</artifactId>
          </exclusion>
        </exclusions>
      </dependency>

I add all the exclusions to prevent polluting the classpath; most of these dependencies are absolutely not needed to just use JBPM. The exception to the rule is Hibernate: JBPM uses Hibernate internally for persistence actions. I exclude it anyway because you may be using your own, newer version of Hibernate already.


Installing JBPM 3.2.6

This part is quite easy actually; JBPM comes with an installer. Just download JBPM from the JBoss sourceforge download page. This download gives you an executable jar, so double click to execute it.

The installer will ask you a few questions; where to install the JBPM runtime (choose any directory you want), which database do you use, which version of JBoss are you using (doesn't really matter, but choose Jboss 5), path to the JBoss server instance, etc. Unfortunately the JBPM 3 installer does not come with support for Oracle yet, but I'll show you how to set that up in a moment. If your database is not listed, simply choose Hypersonic SQL for now.

If everything is correct, you'll have an installed JBPM runtime folder and your JBoss instance's deploy directory now contains a subdirectory 'jbpm'.


Setting up the JBPM manager to work with Oracle


Of course, install an oracle driver into the instance's lib directory. To make JBPM talk with oracle, do the following steps.

1. delete the deploy\jbpm\jbpm-hsql-db.xml file
2. create your own JBoss oracle datasource with name 'JbpmDS' (case sensitive!)
3. Open file deploy\jbpm\jbpm-service.sar\hibernate.cfg.xml. Change the hibernate dialect to 'org.hibernate.dialect.Oracle9Dialect'.

That is all on the configuration side. Now we need to get some tables into the schema you point to in the datasource. Luckily the runtime installation comes with the script you need; simply invoke the file RUNTIME_INSTALL_DIR/database/jbpm.jpdl.oracle.sql.

Finally to be able to work with the manager we need some users and roles. You can execute the following statements to get a few defaults:

INSERT INTO JBPM_ID_GROUP VALUES(1,'G','user','security-role',NULL);
INSERT INTO JBPM_ID_GROUP VALUES(2,'G','manager','security-role',NULL);
INSERT INTO JBPM_ID_GROUP VALUES(3,'G','admin','security-role',NULL);

INSERT INTO JBPM_ID_USER VALUES(1,'U','manager','','manager');
INSERT INTO JBPM_ID_USER VALUES(2,'U','user','','user');
INSERT INTO JBPM_ID_USER VALUES(3,'U','shipper','','shipper');
INSERT INTO JBPM_ID_USER VALUES(4,'U','admin','','admin'); 

INSERT INTO JBPM_ID_MEMBERSHIP VALUES(1,'M',NULL,NULL,1,1);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(2,'M',NULL,NULL,1,2);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(3,'M',NULL,NULL,1,3);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(4,'M',NULL,NULL,2,1);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(5,'M',NULL,NULL,3,1);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(6,'M',NULL,NULL,4,1);
INSERT INTO JBPM_ID_MEMBERSHIP VALUES(7,'M',NULL,NULL,4,3);

All done! But when you now try to run the server instance, things will unfortunately break down with many deployment errors. We are not done yet, we need to hack some code!


Fixing the JMS queues

JBoss 5.1 has a new JMS provider. To be able to make JBPM work, we need to modify the file deploy\jbpm\jbpm-destinations-service.xml; replace the queue declarations with the following:

<mbean code="org.jboss.jms.server.destination.QueueService"
    name="jboss.messaging.destination:service=Queue,name=JbpmJobQueue"
    xmbean-dd="xmdesc/Queue-xmbean.xml">
    <depends optional-attribute-name="ServerPeer">
      jboss.messaging:service=ServerPeer
    </depends>
    <depends>jboss.messaging:service=PostOffice</depends>
  </mbean>

  <mbean code="org.jboss.jms.server.destination.QueueService"
    name="jboss.messaging.destination:service=Queue,name=JbpmCommandQueue"
    xmbean-dd="xmdesc/Queue-xmbean.xml">
    <depends optional-attribute-name="ServerPeer">
      jboss.messaging:service=ServerPeer
    </depends>
    <depends>jboss.messaging:service=PostOffice</depends>
  </mbean>

And that is all.


Fixing jbpm-enterprise.jar


The main problem we need to fix here is the way that the JNDI lookups are done - it tries to use the java:comp/env space, which is off-limits unfortunately. We need to patch some source files to use the real local JNDI names to get things working. Luckily we know these JNDI names; they are defined in the jboss.xml descriptor file.

First of all, locate the 'src' folder of your JBPM runtime installation folder. It holds a file 'jbpm-enterprise-sources.jar'. This is the source code you need, so unpack that so that you can modify and build the code into a jar - I created a simple maven Eclipse project for it.

you need to make modifications to the following files.

org/jbpm/msg/jms/JmsMessageServiceFactory.java

Change this:

String connectionFactoryJndiName = "java:comp/env/jms/JbpmConnectionFactory";
String destinationJndiName = "java:comp/env/jms/JobQueue";
String commandDestinationJndiName = "java:comp/env/jms/CommandQueue";

to this:

String connectionFactoryJndiName = "java:JmsXA";
String destinationJndiName = "queue/JbpmJobQueue";
String commandDestinationJndiName = "queue/JbpmCommandQueue";

org/jbpm/scheduler/ejbtimer/EntitySchedulerServiceFactory.java

Change this:

String timerEntityHomeJndiName = "java:comp/env/jbpm/TimerEntityBean";

to this:

String timerEntityHomeJndiName = "java:jbpm/TimerEntityBean";


org/jbpm/scheduler/ejbtimer/TimerServiceBean.java
In the ejbTimeout() method, change this:

String localCommandServiceJndiName = "java:comp/env/ejb/LocalCommandServiceBean";

To this:

String localCommandServiceJndiName = "java:jbpm/LocalCommandServiceBean";

org/jbpm/scheduler/ejbtimer/EjbSchedulerServiceFactory.java

Change this:

String timerServiceHomeJndiName = "java:comp/env/ejb/LocalTimerServiceBean";

To this:

String timerServiceHomeJndiName = "java:jbpm/LocalTimerServiceBean";


org/jbpm/ejb/impl/TimerEntityBean.java

In the ejbActivate() method change this:

LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) initial.lookup("java:comp/env/ejb/LocalCommandServiceBean");

To this:

LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) initial.lookup("java:jbpm/CommandServiceBean");

Build the new jbpm-enterprise.jar. Remove the exploded jbpm-enterprise.jar directory from the deploy\jbpm directory in JBoss and put your own jbpm-enterprise.jar file in there to replace it (be sure to rename your own jar to jbpm-enterprise.jar if the name is different).


Now try to start the JBoss server instance; if you followed all my instructions to the letter, you should now be able to boot up your server instance without exceptions and be able to see http://localhost:8080/jbpm-console/ working!

One more thing to be done is to configure security in JBoss.


Configuring the user credentials


Open up JBOSS_HOME/server/instance/conf/login-config.xml. Add the following content somewhere at the end:

<application-policy name = "jbpm">
      <authentication>
        <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
                      flag="required">
          <module-option name="dsJndiName">java:/JbpmDS</module-option>
          <module-option name="principalsQuery">
             SELECT PASSWORD_ FROM JBPM_ID_USER WHERE NAME_=?
          </module-option>
          <module-option name="rolesQuery">
             SELECT g.NAME_ ,'Roles'
             FROM JBPM_ID_USER u,
                  JBPM_ID_MEMBERSHIP m,
                  JBPM_ID_GROUP g
             WHERE g.TYPE_='security-role'
               AND m.GROUP_ = g.ID_
               AND m.USER_ = u.ID_
               AND u.NAME_=?
          </module-option>
        </login-module>
      </authentication>
    </application-policy>

Be sure to check the datasource JNDI name against your own environment - JbpmDS should connect to a schema/database with the JBPM tables in it. The above is for an Oracle environment, but is should work on other DBMS systems also.

Now you should be able to login in the JBPM manager webapp.


Installing JBoss-Tools into Eclipse.


Now that JBossAS is all setup, we need to do some work in Eclipse to be able to add a JBPM process to a project. Note that I assuming you are using Eclipse 3.5.2 or higher. Also you need the JEE version of Eclipse.

The thing we need is our good friend, JBoss Tools. This wonderful set of Eclipse plugins can give you great joy, if you take the time to learn how to work with it. Jboss Tools contains far more tools beyond JBPM; in this document I'll limit it to the JBPM tools.

In order to install JBoss tools you just need to point the Eclipse software installer to the download site http://download.jboss.org/jbosstools/updates/stable/galileo/. This will present you with quite an impressive list of available plugins; I suggest you install at least these ones:

  • JBossAS tools
  • JPBM3 tools SDK
  • Jboss BPEL editor SDK

This will give you tools for deploying to a JBoss server and the JBPM 3 visual process editor.

After installation (and restarting Eclipse) we still have one setup step to go. We should configure our JBPM runtime. Click:

window -> preferences -> JBoss JBPM -> runtime locations

Click Add and add the runtime by selecting the directory where you installed it. Also give it a descriptive name. After adding it make it the default runtime by checking the checkbox in front of the name and click the apply button.

To be sure, select jBPM 3 -> server deployment. Make sure that the following information is filled in here;

server name = localhost
server port = 8080
server deployer = /jbpm-console/upload


Okay done! You can now try it out if it all works; in any project click:

new -> other -> JBoss jBPM -> jBPM 3 Process Definition

Give your process definition a name and if all is correct, you'll be presented with the JBPM visual process editor! To continue with your JBPM adventures, I can now safely leave you in the hands of the JBoss documentation. Perhaps I will create another article about process development and deployment and I'll certainly write one on unit testing them.


JBPM and classpath isolation


Sometimes you have to work in an environment where classpath isolation is turned off. You'll find that when you have a JSF based application running next to the jbpm-manager in such situations, you get a strange 'jbpm configuration is null' error when you try to see a page. Opening the jbpm-console application will magically fix that issue.

If you are in this situation you have one of two options.

- turn on classpath isolation (really, you should do that!)
- patch your web module so that it can play nice with the jbpm-manager application.

To do this you need to add two dependencies to your war module:

- jsf-console-jbpm4jsf (maven groupId = org.jbpm.jbpm3, artifactId = jsf-console-jbpm4jsf, version = version 3.2.6.SP1)
- JBoss gravel (maven groupId = org.jboss.gravel, artifactId = gravel, version = 1.0.0.GA)

Then to magically fix the problem, add a file 'jbpm4jsf-config.xml' to the WEB-INF of your war, with the following default content:

<?xml version="1.0" encoding="UTF-8"?>
<jbpm4jsf-config xmlns="http://jbpm.org/jbpm4jsf/config">

    
    <authentication-service>
        <use-jsf-actor>true</use-jsf-actor>
    </authentication-service>

    <!-- This entry facilitates the usage of process image URLs -->
    <process-file-matcher>
        <pattern>^/app/pi/(\d+).jpg$</pattern>
        <file-name>processimage.jpg</file-name>
        <content-type>image/jpg</content-type>
    </process-file-matcher>

</jbpm4jsf-config>

Now boot JbossAS and open a webpage of your application: problem solved.

Tuesday, June 8, 2010

Setting up JBoss Seam2 on JBoss 4.2.X and 5.1

Introduction


website

Jboss Seam is a "helper" API for JSF - much like Richfaces gives you web 2.0 capabilities in the form of rich editing components and Ajax integration, Seam works more on the backend side, lowering the need for boilerplate XML configuration and bringing the service layer (EJBs) directly to the view layer. If you actually want the latter is up to you; I'm not much of a fan of it.

This document will describe the steps needed to get Seam up and running in both JBoss 4.2.X and JBoss 5.1 environments - I document this because I had to piece this together from many different sources and even a little hacking in the source code.

Recommend Seam version for JBoss 4.2.X: 2.1.2
Recommend Seam version for JBoss 5.1: 2.2.1.CR2

Seam comes in four jars: seam, seam-debug, seam-ui and seam-jul.


Configuration

To make Seam available to your application in either JBoss 4.2.X or JBoss 5.1, you need to do some boilerplate configuration setup. I'll attack them one at a time. I'm assuming you know how to create a web application, so I'll only provide snippets.

WEB-INF/web.xml
First of all, make sure you are using the servlet 2.5 spec. Add the following configuration items:

<!-- note: seam filter should be at the top -->
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>

<filter>
<filter-name>Seam Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>Seam Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Seam Resource Servlet</servlet-name>
<url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>



The above is for "full seam support". You may not need this however.

  • Seam filter: only needed when you are going to declare @Filter annotated objects, or use one of the built-in filters (redirect filter, multipart filter, etc.); otherwise it is quite useless.
  • Seam resource servlet: only needed when you are going to use Seam resource resolving; if not then you do not need to declare this at all.

The less you configure, the less overhead you have.


WEB-INF/faces-config.xml

This assumes you will use facelets; I would highly recommend that over JSPs.

<application>
  <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>

<lifecycle>
  <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>

WEB-INF/components.xml
The components.xml file can be used to configure the default JNDI pattern of EJBs. This is how you would configure it for JBoss:

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
            xmlns:core="http://jboss.com/products/seam/core"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation=
              "http://jboss.com/products/seam/core 
               http://jboss.com/products/seam/core-2.1.xsd
               http://jboss.com/products/seam/components 
               http://jboss.com/products/seam/components-2.1.xsd">

  <core:init jndi-pattern="EARNAME/#{ejbName}/local"/>
  <core:manager conversation-timeout="900000"/>
</components>


Replace EARNAME with the name of your ear, naturally. This is because under JBoss the default local JNDI name for EJBs is earname/EjbBeanName/local.

In Jboss 4.2.x, by default Seam provides you transaction management in managed beans. This means that in the managed bean itself you will have an active transaction. You may want this or not; if you want to turn it off, add this attribute to the core:init line of the components.xml file:

<core:init jndi-pattern="ear/#{ejbName}/local" transaction-management-enabled="false"/>

In JBoss 5.1 however, you have to explicitely turn it on by setting the above attribute transaction-management-enabled to true. Its as simple as that.


META-INF/ejb-jar.xml
That is enough to get seam going, but in order to make it possible for Seam to instrument EJBs, you'll need to install an interceptor. Put a file META-INF/ejb-jar.xml in your EJB jar:


<?xml version="1.0"?>
<ejb-jar version="3.0" 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/ejb-jar_3_0.xsd">
  <description>jboss51test</description>
  <display-name>jboss51test</display-name>
  <interceptors>
    <interceptor>
      <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
    </interceptor>
  </interceptors>

  <assembly-descriptor>
    <interceptor-binding>
      <ejb-name>*</ejb-name>
      <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
    </interceptor-binding>
  </assembly-descriptor>

</ejb-jar>

Note: this ejb-jar.xml is created to work with JBoss 5.1; I'm not 100% sure if this will work on JBoss 4.2.X. If it doesn't, change the header to this:

<?xml version="1.0"?>
<ejb-jar>

If you do not install this interceptor, Seam will not be able to inject or outject beans into/from EJBs (IE. @In and @Out annotations won't work in them).

Personally, I'm not a big fan of doing Seam injections in EJBs anyway - it leads to bad design. Seam components should be in the war, but if you would want to inject them into EJBs, they would need to be in the ejb module. Yuck.

So my advice would be: skip the EJB interceptor and don't do injections into EJBs at all. But hey, if you must do it, now you know how to.


seam.properties
Something you must not forget - any application module (EJB, WAR) that you want Seam to instrument, you must put an empty seam.properties file in its classpath. For EJB jars this means the file has to end up in the root of the jar, for WAR files it means the file has to be in the WEB-INF/classes directory. If you forget to do this, Seam managed components are not going to work!


JBoss 5.1 patch

Out of the box, without any patches, you may run into continuous redployment problems on JBoss 5.1 when using Seam2 in your application and you hot-deploy it. This is apparently due to a configuration mistake that was supposed to be corrected in JBoss 5.2 - development on the 5.X branch has been halted in favor of JBoss 6 however. So we need to correct ourselves.

In order to be safe, you should make the following configuration changes.

JBOSS_HOME/server/SERVER_INSTANCE/conf/bootstrap/profile.xml.
Change this:

<bean name="DefaultDeploymentRepositoryFactory" class="org.jboss.system.server.profileservice.repository.DefaultDeploymentRepositoryFactory">
 <property name="deploymentFilter"><inject bean="DeploymentFilter" /></property>
 <property name="checker"><inject bean="StructureModificationChecker" /></property>
 </bean>

to this:

<bean name="DefaultDeploymentRepositoryFactory" class="org.jboss.system.server.profileservice.repository.DefaultDeploymentRepositoryFactory">
 <property name="deploymentFilter"><inject bean="DeploymentFilter" /></property>
 <property name="checker"><inject bean="MetaDataStructureModificationChecker" /></property>
 </bean>


JBOSS_HOME/server/SERVER_INSTANCE/deployers/seam.deployer/META-INF/seam-deployers-jboss-beans.xml.
Remove the following line:

<bean name="SeamMTMatcher" class="org.jboss.seam.integration.microcontainer.deployers.SeamTempModificationTypeMatcher"/>

This should prevent hot-deployed seam applications from continuously redeploying.


Seam debug: servlets beware

An issue I ran into recently is the use of the seam debugging features - this is done by installing a filter. However when developing a servlet of your own, you might run into a nasty no file extension in servlet path error. This is caused by the seam debug feature when an exception occurs in your servlet. The trouble is that the real exception is never printed or logged anywhere when this happens, making debugging actually impossible.

For this reason I've actually ditched the seam debugging altogether - it never did really add any benefit for me anyway.


That's it!

You are all setup to use Seam in your application now, be it on JBoss 4.2.X or JBoss 5.1! There is still one thing more to cover, and that is the special case of a server environment without classpath isolation.


Seam and classpath isolation


Because of the way that Seam hooks into the system (quite deep), it has issues with running in an environment where it appears in the classpath twice. This will happen in server environments where classpath isolation is not active. Although it is not recommended to run in an environment like that (far too much chance of library clashes), like me you may end up in that very situation anyway because of bad application design.

This does not work well with Seam because of the fact that the main seam library itself contains a META-INF/faces-config.xml file - this basically forces Seam on any web component that is active in the server instance, even web components of other ears that only use pure JSF without Seam. That does not go down well with many (deployment) exceptions as a result.

The solution: patch seam by removing the META-INF/faces-config.xml from both the main seam jar and the seam-debug jar. The configuration in these files is still needed, so in the web application that uses Seam you'll have to add the following configuration lines to faces-config.xml:

<factory>
      <application-factory>org.jboss.seam.jsf.SeamApplicationFactory</application-factory>
   </factory>

   <application>
      <navigation-handler>org.jboss.seam.jsf.SeamNavigationHandler</navigation-handler>
      <view-handler>org.jboss.seam.jsf.SeamViewHandler</view-handler>
      <state-manager>org.jboss.seam.jsf.SeamStateManager</state-manager>
      <el-resolver>org.jboss.seam.el.SeamELResolver</el-resolver>
      <message-bundle>org.jboss.seam.core.SeamResourceBundle</message-bundle>
   </application>

   <lifecycle>
      <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
   </lifecycle>

   <lifecycle>
      <phase-listener>org.jboss.seam.debug.jsf.SeamDebugPhaseListener</phase-listener>
   </lifecycle>

   <lifecycle>
       <phase-listener>org.jboss.seam.document.DocumentStorePhaseListener</phase-listener>
   </lifecycle>

    <application>
        <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

Basically what you would already put in the faces-config.xml, plus everything that was declared in the files inside the seam jars. If you apply this patch then Seam will play nice with other web modules.

But of course the real fix is to enable classpath isolation!