Build the enterprise with EJB 3, JBoss Seam, and Maven 2

Enterprise resource management with JBoss Seam and Maven 2

JBoss Seam is the extension framework that made JSF a viable alternative to Struts, and Maven 2 is the build tool that convinced you to leave Ant for good. The problem is, these two cool tools don't play well together. Or do they? Agile consultant Michael Nyika shows you how to integrate Seam and Maven 2 in your enterprise development projects using EJB 3.

If you're using Apache Ant to create EJB 3-based EAR projects as a final artifact, you may find the configuration files complicated to set up. Ant introduces complexity to the process of integrating multiple resources from many locations, especially because there's no way to see what the targets are doing on first glance. Moreover, Ant targets are often grouped together in a hierarchy, so that certain targets are executed only when others are successful.

Maven 2 does a better job of modularizing the components that make up an EJB 3 project. From EAR configuration files to EJBs to WAR files, a well-known group of projects always represents the individual components to be used. Many developers view Maven 2 as too complicated, however, and stick with Ant even for complex enterprise projects where Maven is clearly the better choice. One of the most powerful features of Maven 2, resource placement, is also one of the least understood.

In this article we'll take a realistic, hands-on approach to learning about enterprise resource management with Maven 2. The example application, built on EJB 3, uses JSF for the presentation tier. The glue between the two frameworks is JBoss Seam, which facilitates smooth collaboration between EJB 3's resources (session beans and entity beans) and JSF's (managed beans and JSF views). In addition to learning the basics of enterprise resource placement in Maven 2, you'll learn where Seam-specific resources fit into Maven's seemingly rigid structuring of project archetypes (JAR, WAR, POM, EAR, EJB, and so on).

Development environment and prerequisites

To develop the example application I used JDK 1.6 update 4 with JBoss Seam version 2.0.1 and Apache Maven version 2.0.8. My development platform is Linux OpenSuSE 10.3 and my JBoss Application Server is at version 4.2.2. To facilitate easier access to binary executables such as mvn, run.sh, and so on, I set environment variables such as $JBOSS_HOME and $JAVA_HOME on localhost.

This article requires some knowledge of JSF and using Maven as a build tool. You don't need to know how JBoss Seam works to follow along, but you should be familiar with EJB concepts.

The example application

The example application for this article simulates a screen in which a user new to an arbitrary Web site registers three simple bits of information: a formal username, his or her full name, and the password to be associated with the username. Seam is important in this example because it uses a plain old Java object (the POJO used to hold the user's information) implicitly from within EJB's stateless session bean. Unlike JSF applications where Seam is absent, there is no need in the sample application to explicitly reference the POJO as a managed bean (which would in turn need to obtain a reference to the stateless bean that handles the actual user registration).

Figure 1 shows an overall view of the Maven project as seen from a filesystem explorer window. It shows the overall directory structure, including the parent POM project.

Figure 1. Overall view of the Maven project (click to enlarge)

The different elements composing the project structure break down as follows:

  • The EAR directory represents the enterprise archive Maven project containing all EAR packaging information, as well as the elements of the artifact that will ultimately be deployed. It should not be confused with the parent directory (the directory that will contain projects that will be eventually packaged up within the EAR file). The EAR project is just another project as far as the parent project is concerned.

  • The EJBs directory is a directory representing the application-specific location where Enterprise JavaBeans live.

  • The servlets directory is a special Maven project that is a container for (potentially) multiple Web application archives bundled up in the single EAR file. We'll examine this later, and see that its pom configuration is similar in type to the parent pom configuration.

  • The jboss-seam directory represents the JBoss Seam EJB project. This project was built from a part of the JBoss Seam source download. I'll explain this project in more detail later in the article.

Getting started

Let's dive into the first section of the project structure, the project POM file, which is shown in Listing 1.

Listing 1. Parent project POM file

<project>

   <modelVersion>4.0.0</modelVersion>
   <groupId>root</groupId>
   <version>1.0</version>
   <artifactId>project</artifactId>
   <packaging>pom</packaging>
   <name>project</name>
   <modules>
      <module>servlets</module>
      <module>ejbs</module>
      <module>jboss-seam</module>
      <module>ear</module>
   </modules>
   <dependencyManagement>
      <dependencies>
        <dependency>
            <groupId>root.project.servlets</groupId>
            <artifactId>servlet</artifactId>
            <version>1.0</version>
            <type>war</type>
         </dependency>
         <dependency>
            <groupId>root.project</groupId>
            <artifactId>ejbs</artifactId>
            <version>1.0</version>
            <type>ejb</type>
         </dependency>
    <dependency>
            <groupId>root.project</groupId>
            <artifactId>jboss-seam</artifactId>
            <version>2.0</version>
            <type>ejb</type>
         </dependency>
      </dependencies>
   </dependencyManagement>
</project>

First off, you'll note that the project packaging type is pom. Multiple modules are also listed here that correspond directly to the directories (projects) seen in Figure 1. The dependency management section is particularly helpful because it lists the exact nature of the elements that compose the modules listed here, not the modules themselves.

Take a look at the first dependency in the dependency management section, the servlet artifact. The groupId may be confusing at first. It gets its appropriate value first from the parent project's groupId (<groupId>root</groupId>), then from the artifactId (<artifactId>project</artifactId>), and finally from the name of the parent project that houses all possible Web servlet projects (servlets, in this case). The groupId would seem to be a pointer to the project that would house this particular Web application artifact, hence <groupId>root.project.servlets</groupId>.

Because the root context of any Web application must be unique (among all others within a running Web container), the artifactId will vary accordingly, if the version number is the same for all Web projects. This assumes that the POM files for the individual Web projects do not have a <finalName> configuration within the default <build> section of the configuration file. The servlets module is, of course, packaged as a WAR file, hence the war packaging.

The EJB and jboss-seam projects are both deployed as EJBs (hence the packaging ejb); again, the groupIds seem to be pointers, from the parent project's perspective, to the location of the EJB and Seam directories excluding the directory names, hence <groupId>root.project</groupId>. The Seam project has a version number of 2.0 simply because we are dealing with JBoss Seam's Candidate Release version 2.0.1.

EAR project POM file and file structure

Listing 2 offers a quick glance into the POM file for the EAR project.

Listing 2. EAR project POM file

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project</groupId>
   <artifactId>ear</artifactId>
   <packaging>ear</packaging>
   <version>1.0</version>
   <name>ear assembly</name>
   <dependencies>
      <dependency>
         <groupId>root.project</groupId>
         <artifactId>ejbs</artifactId>
         <type>ejb</type>
    <version>1.0</version>
      </dependency>
      <dependency>
         <groupId>root.project.servlets</groupId>
         <artifactId>servlet</artifactId>
         <type>war</type>
    <version>1.0</version>
      </dependency>
    <dependency>
         <groupId>root.project</groupId>
         <artifactId>jboss-seam</artifactId>
     <version>2.0</version>
         <type>ejb</type>
      </dependency>
     <dependency>
         <groupId>jboss</groupId>
         <artifactId>jboss-el</artifactId>
     <version>2.0</version>
         <type>jar</type>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <artifactId>maven-ear-plugin</artifactId>
            <configuration>
        <applicationXml>${artifactId}/src/main/resources/application.xml</applicationXml>
        <jboss>${artifactId}/src/main/resources/jboss-app.xml</jboss>
        <defaultLibBundleDir>lib</defaultLibBundleDir>
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                  </manifest>
               </archive>
        <modules>
        <ejbModule>
               <groupId>root.project</groupId>
            <artifactId>ejbs</artifactId>
                </ejbModule>
        <ejbModule>
               <groupId>root.project</groupId>
            <artifactId>jboss-seam</artifactId>
                </ejbModule>
        <webModule>
               <groupId>root.project.servlets</groupId>
            <artifactId>servlet</artifactId>
               <contextRoot>/servlet</contextRoot>
             </webModule>
        </modules>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

In the dependencies section, all three dependencies listed in the parent project's pom section are also found here in the exact same manner, with the exception of the JBoss Expression Language JAR dependency. There is a slight change in a dependency property: instead of packaging, we find type, with the same values (ejb, war, and now jar).

Further down, the configuration of the maven-ear-plugin is key, as you can see in Listing 3.

Listing 3. Configuring the maven-ear-plugin

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project</groupId>
   <artifactId>ear</artifactId>
   <packaging>ear</packaging>
   <version>1.0</version>
   <name>ear assembly</name>
   <dependencies>
      <dependency>
         <groupId>root.project</groupId>
         <artifactId>ejbs</artifactId>
         <type>ejb</type>
    <version>1.0</version>
      </dependency>
      <dependency>
         <groupId>root.project.servlets</groupId>
         <artifactId>servlet</artifactId>
         <type>war</type>
    <version>1.0</version>
      </dependency>
    <dependency>
         <groupId>root.project</groupId>
         <artifactId>jboss-seam</artifactId>
     <version>2.0</version>
         <type>ejb</type>
      </dependency>
     <dependency>
         <groupId>jboss</groupId>
         <artifactId>jboss-el</artifactId>
     <version>2.0</version>
         <type>jar</type>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <artifactId>maven-ear-plugin</artifactId>
            <configuration>
        <applicationXml>${artifactId}/src/main/resources/application.xml</applicationXml>
        <jboss>${artifactId}/src/main/resources/jboss-app.xml</jboss>
        <defaultLibBundleDir>lib</defaultLibBundleDir>
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                  </manifest>
               </archive>
        <modules>
        <ejbModule>
               <groupId>root.project</groupId>
            <artifactId>ejbs</artifactId>
                </ejbModule>
        <ejbModule>
               <groupId>root.project</groupId>
            <artifactId>jboss-seam</artifactId>
                </ejbModule>
        <webModule>
               <groupId>root.project.servlets</groupId>
            <artifactId>servlet</artifactId>
               <contextRoot>/servlet</contextRoot>
             </webModule>
        </modules>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

The standard J2EE EAR deployment file, application.xml, is located at ${artifactId}/src/main/resources/application.xml, which in our scenario translates to ear/src/main/resources/application.xml. Similarly, jboss-app.xml, a JBoss Application Server-specific file, is located here at ear/src/main/resources/jboss-app.xml. The defaultLibBundleDir's value of lib refers to the name of the directory in which all JARs not already packaged into the WAR file (or files) will be held.

The rest of the file lists the various EJB and WAR file projects in the plugin's specific format. Note that the context root of the single existing Web application, servlet, is /servlet.

Listing 4 looks into the application.xml EJB configuration file.

Listing 4. application.xml

<?xml version="1.0" encoding="UTF-8"?>
<application 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/application_5.xsd"
             version="5">
    <display-name>Seam Registration</display-name>
    <module>
        <web>
            <web-uri>servlet-1.0.war</web-uri>
            <context-root>/servlet-1.0</context-root>
        </web>
    </module>
    <module>
        <ejb>ejbs-1.0.jar</ejb>
    </module>
    <module>
        <ejb>jboss-seam-2.0.jar</ejb>
    </module>
   </application>

The Web module entries (with context root value), ejbs-1.0.jar and Seam JARs, both deployed as EJBs, are now familiar. The display name, Seam Registration, is specific to the packaged example that you'll see later, but this name should really represent the name of the application in general.

Listing 5 shows the jboss-app.xml configuration file.

Listing 5. jboss-app.xml

<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE jboss-app
    PUBLIC "-//JBoss//DTD J2EE Application 4.2//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-app_4_2.dtd">
<jboss-app>
      <loader-repository>
      seam.jboss.org:loader=servlet-1.0
      </loader-repository>
</jboss-app>

This file is very short but contains information essential to making JBoss Seam work. It is a JBoss-specific file that configures the final artifact type, the EAR file, for JBoss deployment. A loader-repository entry of seam.jboss.org:loader=servlet-1.0 refers to the packaged WAR file as the repository for Seam resources. This entry can be placed in the maven-ear-plugin configuration in the EAR pom file.

Note that seam.jboss.org is a Seam-specific classloader. It acts as a way for Seam to disassociate artifacts bundled with the application from those that exist within the JBoss application server's shared artifacts or JARs (for example, those that reside in the default server's lib directory or in JBoss's general lib directory). Also note that the repository may just as well have been a reference to the ear-1.0.ear file as to the larger containing artifact.

EJBs project POM and file structure

Figure 2 is a breakdown of the internal structure of the EJB project folder.

Figure 2. The EJBs project folder (click to enlarge)

The resources directory has three very important files that are core to the EJB 3 mechanism, especially because Seam is now an integral part of the project architecture. Figure 3 illustrates the layout of the resources folder.

Figure 3. The layout of the resources folder (click to enlarge)

Note the explicit META-INF directory. Maven will not create this for you if it is missing (in case the EJB project structure was created manually, for instance), so it is recommended that Maven's archetype plugin be used to create Maven project types. All files in this directory will end up in the root of the JAR file.

In the ejb-jar.xml descriptor file, shown in Listing 6, the application attempts to bind Seam's Interceptor class to one or more EJB modules deployed within the EAR file. This means that we can have multiple EJB JARs deployed and packaged separately for functional reasons (perhaps the EJBs were produced from build systems other than Maven and are required in the current EAR, but Seam does not need to know about those modules) and removed from the Interceptor's list of modules to manage.

Listing 6. ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar 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"
         version="3.0">
   <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>

Seam's standard interceptor class, org.jboss.seam.ejb.SeamInterceptor, is declared in Listing 6. In the assembly descriptor, the * would indicate all packaged EJB modules. You would need to explicitly declare all EJB modules under the control of the interceptor. (It's a shame that you can't exclude EJB modules by name; the descriptor file would be a lot shorter.)

Listing 7 shows the persistence.xml file, which is really the application's gateway into the application server's data source configurations. You can see that Hibernate is the default persistence provider, with a reference to a configured data source (in the JBoss application server). The file is short, with no real reference to Seam-specific details.

Listing 7. persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">
   <persistence-unit name="userDatabase">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:/DefaultDS</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      </properties>
   </persistence-unit>
</persistence>

Note that Seam.properties is an empty file, but it needs to be present because Seam will not work without it.

Figure 4 is a look at the finally packaged ejbs.jar module.

Figure 4. The ejbs.jar module file structure (click to enlarge)

And Listing 8 shows the pom file for the ejb-jar project.

Listing 8. pom.xml

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project</groupId>
   <artifactId>ejbs</artifactId>
   <packaging>ejb</packaging>
   <version>1.0</version>
   <name>enterprise java beans</name>
    <build>
  <sourceDirectory>src/main/java</sourceDirectory>
      <plugins>
         <plugin>
            <artifactId>maven-ejb-plugin</artifactId>
            <configuration>
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                  </manifest>
               </archive>
            </configuration>
         </plugin>
     <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>
    <dependencies>
   ...
  </dependencies>
</project>

Note that the dependencies section was moved below the build configuration and tree-collapsed to help you better understand the maven-ejb-plugin configuration.

JBoss Seam (core source) POM and file structure

Our Maven project is based on the Registration example that comes with Seam's 2.0.1CR1 download. The core source files were extracted, compiled, and packaged as an EJB. Apart from the large number of source files, the remainder of the project structure features only a few more configuration files. Figure 5 offers an overall view.

Figure 5. Seam project structure (click to enlarge)

You'll note that this looks similar to the EJBs project structure. The components.xml file in the resources/META-INF directory, shown in Listing 9, contains nothing more than a listing of all Seam-specific components that will be included (and loaded) as part of the framework.

Listing 9. components.xml

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">
  <import>org.jboss.seam.core</import>
  <import>org.jboss.seam.transaction</import>
  <import>org.jboss.seam.framework</import>
  <import>org.jboss.seam.web</import>
  <import>org.jboss.seam.faces</import>
  <import>org.jboss.seam.international</import>
  <import>org.jboss.seam.theme</import>
  <import>org.jboss.seam.pageflow</import>
  <import>org.jboss.seam.bpm</import>
  <import>org.jboss.seam.jms</import>
  <import>org.jboss.seam.mail</import>
  <import>org.jboss.seam.security</import>
  <import>org.jboss.seam.captcha</import>
</components>

ejb-jar.xml in this Seam project is actually a file with the entry <ejb-jar></ejb-jar> and nothing more. Because this is the project that will provide Seam components to the rest of the structure, it needs no entries.

One of the more interesting files is faces-config.xml, shown in Listing 10. One point that might strike your is that this is a file that ordinarily appears within a Web project -- specifically, in the WEB-INF directory. You'd also expect the Seam-specific entries within it to be configured by the developer within the application-specific JSF faces-config.xml file. Actually, both of these statements are true, but with a special caveat. This particular file contains the default components Seam will use at system startup -- navigation handlers, view handlers, phase listeners, and so on.

Listing 10. faces-config.xml

<?xml version="1.0"?>
<faces-config version="1.2"
              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-facesconfig_1_2.xsd">
   <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>
</faces-config>

The Javamail.Providers file, shown in Listing 11, is of no consequence in this example. However, if you ever need to add mailing providers to an application, this would be the file to configure to make Seam mail-aware.

Listing 11. Javamail.Providers

# See http://java.sun.com/products/javamail/javadocs/javax/mail/Session.html

protocol=mock; type=transport; class=org.jboss.seam.mock.MockTransport; vendor=JBoss Seam Integration Tests;

The jboss-seam source POM file, shown in Listing 12, looks pretty standard and not that different from what we've already seem. Again, the dependencies section has been tree-collapsed. Because Seam is being compiled from source, there are multiple dependencies, all of whose binaries can be found in the Seam zip (or tar.gz) lib folder, once unpacked.

Listing 12. jboss-seam source POM file

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project</groupId>
   <artifactId>jboss-seam</artifactId>
   <packaging>ejb</packaging>
   <version>2.0</version>
   <name>jboss seam </name>
   <dependencies>
   ...
  </dependencies>
   <build>
  <sourceDirectory>src/main/java</sourceDirectory>
      <plugins>
         <plugin>
            <artifactId>maven-ejb-plugin</artifactId>
            <configuration>
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                  </manifest>
               </archive>
            </configuration>
         </plugin>
     <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>
</project>

Servlet (parent) and child project POM and file structures

The final subproject to be examined is the Web archive resource container (servlets) and its associated children. Let's first explore the overall structure, shown in Figure 6.

Figure 6. Seam servlets container structure (click to enlarge)

In some cases you may have multiple servlet* directories -- servlet1, servlet2, servlet3 -- with different root contexts, so that there would be one or more WAR files packaged up in the EAR file. Listing 13 shows the configuration of the parent servlets project.

Listing 13. Servlets POM file

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project</groupId>
   <artifactId>servlets</artifactId>
   <packaging>pom</packaging>
   <name>servlets</name>
   <version>1.0</version>
     <modules>
      <module>servlet</module>
   </modules>
   </project>

All potential WAR projects are declared serially in this file. No other configuration is required and certainly no Seam properties are needed. Within the single servlet child project, we find a POM file with the structure shown in Listing 14.

Listing 14. Servlet child project POM file

<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>root.project.servlets</groupId>
   <artifactId>servlet</artifactId>
   <packaging>war</packaging>
   <name>servlet</name>
   <parent>
      <groupId>root.project</groupId>
      <artifactId>servlets</artifactId>
      <version>1.0</version>
   </parent>
    <dependencies>
 <dependency>
         <groupId>commons</groupId>
         <artifactId>commons-beanutils</artifactId>
     <version>1.0</version>
 </dependency>
<dependency>
         <groupId>jboss</groupId>
         <artifactId>jboss-seam-ui</artifactId>
     <version>2.0</version>
 </dependency>
<dependency>
         <groupId>jboss</groupId>
         <artifactId>jboss-seam-debug</artifactId>
     <version>2.0</version>
 </dependency>
<dependency>
         <groupId>jsf</groupId>
         <artifactId>jsf-facelets</artifactId>
     <version>1.2MR1</version>
 </dependency>
 <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
     <version>3.8.2</version>
     <scope>test</scope>
 </dependency>
 <dependency>
         <groupId>root.project</groupId>
         <artifactId>ejbs</artifactId>
     <version>1.0</version>
     <scope>provided</scope>
 </dependency>
</dependencies>
<build>
<testSourceDirectory>src/main/test</testSourceDirectory>
</build>
</project>

Two significant aspects of this file should be noted. First, there is the compulsory parent declaration and a reference to the servlets project. (By compulsory, I mean that both the declaration of the module in the parent POM and the declaration of the parent in the servlet child's POM fuse the relationship, as far as Maven is concerned, which helps us avoid complications during the build.) Secondly, the Seam binaries (UI and Debug) can also be found from the downloaded sources lib directory.

The web-app directory contains XHTML documents (because Facelets are being used): A single page for registering a user and a confirmation page to display the information that was registered. The WEB-INF directory contains the final pieces of the Seam puzzle; its contents are shown in Figure 7.

Figure 7. The WEB-INF directory (click to enlarge)

The components.xml file

You can see the components.xml file in Listing 15. The entry ear-1.0/#{ejbName}/local ties the name of the EAR file as part of the context for EJB names in the JNDI namespace. This is one way of knowing which EJBs are tied to a particular enterprise artifact among many deployed on the same application server instance. The final EAR file will be named ear-1.0.ear, because there is no <finalName> configuration in the EAR project POM file.

Listing 15. components.xml

<?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.0.xsd
                 http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">

    <core:init jndi-pattern="ear-1.0/#{ejbName}/local"/>
</components>

The application's faces-config.xml file, shown in Listing 16, simply reiterates the default lifecycle phase listener, and states that the facelets view handler is the proper handler for the application's XHTML views.

Listing 16. faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2"
              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-facesconfig_1_2.xsd">
    <!-- Facelets support -->
    <application>
        <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>
<lifecycle>
    <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>
</faces-config>

The only Seam-related entry in the web.xml file, shown in Listing 17, concerns the Seam listener. All other entries look pretty standard. A single empty Seam.properties file is placed in the classes directory.

Listing 17. web.xml

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

    <!-- Seam -->
    <listener>
        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
    </listener>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <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>
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
</web-app>

Running the example

Having completely explored the structure of the example application, we can now run it from the command line and then deploy the application into JBoss to see the results. Begin by running the mvn clean package command, as shown in Figure 8.

Figure 8. Running mvn clean package (click to enlarge)

You should see a message indicating that the build was successful, as in Figure 9.

Figure 9. Success! (click to enlarge)

There should be about 495 source files to compile in the jboss-seam project, as you can see from the snapshot of the Maven command-line output in Figure 10.

Figure 10. 495 source files to compile (click to enlarge)

Now, start up the JBoss application server and copy the EAR file to the deploy directory of the default server, as shown in Figure 11.

Figure 11. Copying the EAR file to JBoss (click to enlarge)

This shouldn't reveal any problems. You should see a message like the one in Figure 12.

Figure 12. JBoss started successfully (click to enlarge)

Open up a browser page and enter http://localhost:8080/servlet-1.0 as the URL. You'll see the registration page, shown in Figure 13.

Figure 13. Sample application (click to enlarge)

There's no real database behind the application. The source code reveals that state is stored within the EJB entity upon submission. Seam maps JSF's view components to the EJB session beans and simply uses bijection to print state values from the entity on the confirmation page. Enter jadams, John Adams, and Independence as the Username, Real Name, and Password respectively, as in Figure 14.

Figure 14. Entering your registration values (click to enlarge)

Then click Register. You'll get the result shown in Figure 15.

Figure 15. Registration complete (click to enlarge)

In conclusion

Many Java developers balk at the more rigorous project structure of Maven 2 as compared to Ant, but in the long run Ant's simplicity is its undoing. This is particularly true for enterprise projects such as the example used for this article, which must be able to store, manage, and persist many different types of resources.

As you've seen in this article, it is not hard to use Maven 2 as the build tool for Seam-based applications. This is good to know because JBoss Seam is often used for integrating EJB 3 and JSF, in turn two of the most commonly used (if not always popular) frameworks in enterprise development. JBoss Seam played three important roles in the example application: First, it influenced the application through various JAR distributions; second, it acted as an EJB 3 component; third, it fed into controlling major parts of the Web application in general, alleviating much of the plumbing associated with JSF Web structures. In all of these roles Seam was an indispensable part of the application, and well worth integrating.

Though you might find the thought of integrating Seam into your Maven 2 build process daunting, actually doing it isn't so hard. There's nothing about Maven 2 or JBoss Seam that makes the two mutually exclusive. In fact, they work beautifully together -- once introduced.

Michael Nyika is an Agile/Lean consultant with Stelligent, Inc., a Northern Virginia-based agile consultancy firm specializing in testing. He develops software in a number of dynamic languages, such as Ruby and Groovy, and is a passionate advocate of open source software.

Learn more about this topic