SeamFramework.orgCommunity Documentation

Chapter 28. Configuring Seam and packaging Seam applications

28.1. Basic Seam configuration
28.1.1. Integrating Seam with JSF and your servlet container
28.1.2. Using facelets
28.1.3. Seam Resource Servlet
28.1.4. Seam servlet filters
28.1.5. Integrating Seam with your EJB container
28.1.6. Don't forget!
28.2. Using Alternate JPA Providers
28.3. Configuring Seam in Java EE 5
28.3.1. Packaging
28.4. Configuring Seam in J2EE
28.4.1. Boostrapping Hibernate in Seam
28.4.2. Boostrapping JPA in Seam
28.4.3. Packaging
28.5. Configuring Seam in Java SE, without JBoss Embedded
28.6. Configuring Seam in Java SE, with JBoss Embedded
28.6.1. Installing Embedded JBoss
28.6.2. Packaging
28.7. Configuring jBPM in Seam
28.7.1. Packaging
28.8. Configuring SFSB and Session Timeouts in JBoss AS
28.9. Running Seam in a Portlet
28.10. Deploying custom resources

Configuration is a very boring topic and an extremely tedious pastime. Unfortunately, several lines of XML are required to integrate Seam into your JSF implementation and servlet container. There's no need to be too put off by the following sections; you'll never need to type any of this stuff yourself, since you can just copy and paste from the example applications!

First, let's look at the basic configuration that is needed whenever we use Seam with JSF.

Seam doesn't need any servlet filters for basic operation. However, there are several features which depend upon the use of filters. To make things easier, Seam lets you add and configure servlet filters just like you would configure other built-in Seam components. To take advantage of this feature, we must first install a master filter in web.xml:


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

The Seam master filter must be the first filter specified in web.xml. This ensures it is run first.

The Seam filters share a number of common attributes, you can set these in components.xml in addition to any parameters discussed below:

Note that the patterns are matched against the URI path of the request (see HttpServletRequest.getURIPath()) and that the name of the servlet context is removed before matching.

Adding the master filter enables the following built-in filters.

This filter allows Seam to apply URL rewriting for views based on configuration in the pages.xml file. This filter is not activate by default, but can be activated by adding the configuration to components.xml:


<web:rewrite-filter />

Rewriting occurs based on rewrite patterns found for views in pages.xml. Seam URL rewriting does both incoming and outgoing URL rewriting based on the same pattern. Here's a simple pattern:



<page view-id="/home.xhtml">
    <rewrite pattern="/home" />
</page>

In this case, any incoming request for /home will be sent to /home.xhtml. More interestingly, any link generated that would normally point to /home.seam will instead be rewritten as /home. Rewrite patterns only match the portion of the URL before the query parameters. So, /home.seam?conversationId=13 and /home.seam?color=red will both be matched by this rewrite rule.

Rewrite rules can take these query paramters into consideration, as shown with the following rules.



<page view-id="/home.xhtml">
    <rewrite pattern="/home/{color}" />
    <rewrite pattern="/home" />
</page>

In this case, an incoming request for /home/red will be served as if it were a request for /home.seam?color=red. Similarly, if color is a page parameter an outgoing URL that would normally show as /home.seam?color=blue would instead be output as /home/blue. Rules are processed in order, so it is important to list more specific rules before more general rules.

Default Seam query parameters can also be mapped using URL rewriting, allowing for another option for hiding Seam's fingerprints. In the following example, /search.seam?conversationId=13 would be written as /search-13.



<page view-id="/search.xhtml">
    <rewrite pattern="/search-{conversationId}" />
    <rewrite pattern="/search" />
</page>

Seam URL rewriting provides simple, bidirectional rewriting on a per-view basis. For more complex rewriting rules that cover non-seam components, Seam applications can continue to use the org.tuckey URLRewriteFilter or apply rewriting rules at the web server.

We need to apply the SeamInterceptor to our Seam components. The simplest way to do this across an entire application is to add the following interceptor configuration in ejb-jar.xml:


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

Seam needs to know where to go to find session beans in JNDI. One way to do this is specify the @JndiName annotation on every session bean Seam component. However, this is quite tedious. A better approach is to specify a pattern that Seam can use to calculate the JNDI name from the EJB name. Unfortunately, there is no standard mapping to global JNDI defined in the EJB3 specification, so this mapping is vendor-specific. We usually specify this option in components.xml.

For JBoss AS, the following pattern is correct:


<core:init jndi-name="myEarName/#{ejbName}/local" />

Where myEarName is the name of the EAR in which the bean is deployed.

Outside the context of an EAR (when using the JBoss Embeddable EJB3 container), the following pattern is the one to use:


<core:init jndi-name="#{ejbName}/local" />

You'll have to experiment to find the right setting for other application servers. Note that some servers (such as GlassFish) require you to specify JNDI names for all EJB components explicitly (and tediously). In this case, you can pick your own pattern ;-)

In an EJB3 environment, we recommend the use of a special built-in component for transaction management, that is fully aware of container transactions, and can correctly process transaction success events registered with the Events component. If you don't add this line to your components.xml file, Seam won't know when container-managed transactions end:


<transaction:ejb-transaction/>

Seam comes packaged and configured with Hibernate as the default JPA provider. If you require using a different JPA provider you must tell seam about it.

Telling seam about a different JPA provider can be be done in one of two ways:

Update your application's components.xml so that the generic PersistenceProvider takes precedence over the hibernate version. Simply add the following to the file:


<component name="org.jboss.seam.persistence.persistenceProvider" 
           class="org.jboss.seam.persistence.PersistenceProvider"
           scope="stateless">
</component>

If you want to take advantage of your JPA provider's non-standard features you will need to write you own implementation of the PersistenceProvider. Use HibernatePersistenceProvider as a starting point (don't forget to give back to the community :). Then you will need to tell seam to use it as before.


<component name="org.jboss.seam.persistence.persistenceProvider" 
           class="org.your.package.YourPersistenceProvider">
</component>

All that is left is updating the persistence.xml file with the correct provider class, and what ever properties your provider needs. Don't forget to package your new provider's jar files in the application if they are needed.

If you're running in a Java EE 5 environment, this is all the configuration required to start using Seam!

Once you've packaged all this stuff together into an EAR, the archive structure will look something like this:

my-application.ear/
    jboss-seam.jar
    lib/
        jboss-el.jar
    META-INF/
        MANIFEST.MF
        application.xml
    my-application.war/
        META-INF/
            MANIFEST.MF
        WEB-INF/
            web.xml
            components.xml
            faces-config.xml
            lib/
                jsf-facelets.jar
                jboss-seam-ui.jar
        login.jsp
        register.jsp
        ...
    my-application.jar/
        META-INF/
            MANIFEST.MF
            persistence.xml
        seam.properties
        org/
            jboss/
                myapplication/
                    User.class
                    Login.class
                    LoginBean.class
                    Register.class
                    RegisterBean.class
                    ...

You should declare jboss-seam.jar as an ejb module in META-INF/application.xml; jboss-el.jar should be placed in the EAR's lib directory (putting it in the EAR classpath.

If you want to use jBPM or Drools, you must include the needed jars in the EAR's lib directory.

If you want to use facelets (our recommendation), you must include jsf-facelets.jar in the WEB-INF/lib directory of the WAR.

If you want to use the Seam tag library (most Seam applications do), you must include jboss-seam-ui.jar in the WEB-INF/lib directory of the WAR. If you want to use the PDF or email tag libraries, you need to put jboss-seam-pdf.jar or jboss-seam-mail.jar in WEB-INF/lib.

If you want to use the Seam debug page (only works for applications using facelets), you must include jboss-seam-debug.jar in the WEB-INF/lib directory of the WAR.

Seam ships with several example applications that are deployable in any Java EE container that supports EJB 3.0.

I really wish that was all there was to say on the topic of configuration but unfortunately we're only about a third of the way there. If you're too overwhelmed by all this tedious configuration stuff, feel free to skip over the rest of this section and come back to it later.

Seam is useful even if you're not yet ready to take the plunge into EJB 3.0. In this case you would use Hibernate3 or JPA instead of EJB 3.0 persistence, and plain JavaBeans instead of session beans. You'll miss out on some of the nice features of session beans but it will be very easy to migrate to EJB 3.0 when you're ready and, in the meantime, you'll be able to take advantage of Seam's unique declarative state management architecture.

Seam JavaBean components do not provide declarative transaction demarcation like session beans do. You could manage your transactions manually using the JTA UserTransaction or declaratively using Seam's @Transactional annotation. But most applications will just use Seam managed transactions when using Hibernate with JavaBeans.

The Seam distribution includes a version of the booking example application that uses Hibernate3 and JavaBeans instead of EJB3, and another version that uses JPA and JavaBeans. These example applications are ready to deploy into any J2EE application server.

It is possible to use Seam completely outside of an EE environment. In this case, you need to tell Seam how to manage transactions, since there will be no JTA available. If you're using JPA, you can tell Seam to use JPA resource-local transactions, ie. EntityTransaction, like so:


<transaction:entity-transaction entity-manager="#{entityManager}"/>

If you're using Hibernate, you can tell Seam to use the Hibernate transaction API like this:


<transaction:hibernate-transaction session="#{session}"/>

Of course, you'll also need to define a datasource.

A better alternative is to use JBoss Embedded to get access to the EE APIs.

JBoss Embedded lets you run EJB3 components outside the context of the Java EE 5 application server. This is especially, but not only, useful for testing.

The Seam booking example application includes a TestNG integration test suite that runs on JBoss Embedded via SeamTest.

The booking example application may even be deployed to Tomcat.

Embedded JBoss must by installed into Tomcat for Seam applications to run correctly on it. Embedded JBoss runs with JDK 5 or JDK 6 ( see Section 38.1, “JDK Dependencies” for details on using JDK 6). Embedded JBoss can be downloaded here. The process for installing Embedded JBoss into Tomcat 6 is quite simple. First, you should copy the Embedded JBoss JARs and configuration files into Tomcat.

  • Copy all files and directories under the Embedded JBoss bootstrap and lib directories, except for the jndi.properties file, into the Tomcat lib directory.

  • Remove the annotations-api.jar file from the Tomcat lib directory.

Next, two configuration files need to be updated to add Embedded JBoss-specific functionality.

  • Add the Embedded JBoss listener to conf/server.xml. It should appear after all other listeners in the file.

    
    <Listener className="org.jboss.embedded.tomcat.EmbeddedJBossBootstrapListener"/>
  • WAR file scanning should be enabled by adding a listener to conf/context.xml.

    
    <Listener className="org.jboss.embedded.tomcat.WebinfScanner"/>

For more configuration options, please see the Embedded JBoss Tomcat integration wiki entry.

Seam's jBPM integration is not installed by default, so you'll need to enable jBPM by installing a built-in component. You'll also need to explicitly list your process and pageflow definitions. In components.xml:


<bpm:jbpm>
    <bpm:pageflow-definitions>
        <value>createDocument.jpdl.xml</value>
        <value>editDocument.jpdl.xml</value>
        <value>approveDocument.jpdl.xml</value>
    </bpm:pageflow-definitions>
    <bpm:process-definitions>
        <value>documentLifecycle.jpdl.xml</value>
    </bpm:process-definitions>
</bpm:jbpm>

No further special configuration is needed if you only have pageflows. If you do have business process definitions, you need to provide a jBPM configuration, and a Hibernate configuration for jBPM. The Seam DVD Store demo includes example jbpm.cfg.xml and hibernate.cfg.xml files that will work with Seam:


<jbpm-configuration>

  <jbpm-context>
    <service name="persistence">
       <factory>
          <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
             <field name="isTransactionEnabled"><false/></field>
          </bean>
       </factory>
    </service>
    <service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
    <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
    <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
    <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
    <service name="authentication" 
             factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
  </jbpm-context>

</jbpm-configuration>

The most important thing to notice here is that jBPM transaction control is disabled. Seam or EJB3 should control the JTA transactions.

It is very important that the timeout for Stateful Session Beans is set higher than the timeout for HTTP Sessions, otherwise SFSB's may time out before the user's HTTP session has ended. JBoss Application Server has a default session bean timeout of 30 minutes, which is configured in server/default/conf/standardjboss.xml (replace default with your own configuration).

The default SFSB timeout can be adjusted by modifying the value of max-bean-life in the LRUStatefulContextCachePolicy cache configuration:


<container-cache-conf>
    <cache-policy>org.jboss.ejb.plugins.LRUStatefulContextCachePolicy</cache-policy>
    <cache-policy-conf>
        <min-capacity>50</min-capacity>
        <max-capacity>1000000</max-capacity>
        <remover-period>1800</remover-period>

        <!-- SFSB timeout in seconds; 1800 seconds == 30 minutes -->
        <max-bean-life>1800</max-bean-life>  

        <overager-period>300</overager-period>
        <max-bean-age>600</max-bean-age>
        <resizer-period>400</resizer-period>
        <max-cache-miss-period>60</max-cache-miss-period>
        <min-cache-miss-period>1</min-cache-miss-period>
        <cache-load-factor>0.75</cache-load-factor>
    </cache-policy-conf>
</container-cache-conf>

The default HTTP session timeout can be modified in server/default/deploy/jbossweb-tomcat55.sar/conf/web.xml for JBoss 4.0.x, or in server/default/deploy/jboss-web.deployer/conf/web.xml for JBoss 4.2.x. The following entry in this file controls the default session timeout for all web applications:


<session-config>
    <!-- HTTP Session timeout, in minutes -->
    <session-timeout>30</session-timeout>
</session-config>

To override this value for your own application, simply include this entry in your application's own web.xml.

If you want to run your Seam application in a portlet, take a look at the JBoss Portlet Bridge, an implementation of JSR-301 that supports JSF within a portlet, with extensions for Seam and RichFaces. See http://labs.jboss.com/portletbridge for more.

Seam scans all jars containing /seam.properties, /META-INF/components.xml or /META-INF/seam.properties on startup for resources. For example, all classes annotated with @Name are registered with Seam as Seam components.

You may also want Seam to handle custom resources. A common use case is to handle a specific annotation and Seam provides specific support for this. First, tell Seam which annotations to handle in /META-INF/seam-deployment.properties:

# A colon-separated list of annotation types to handle
org.jboss.seam.deployment.annotationTypes=com.acme.Foo:com.acme.Bar

Then, during application startup you can get hold of all classes annotated with @Foo:

@Name("fooStartup")
@Scope(APPLICATION)
@Startup
public class FooStartup {

   @In("#{deploymentStrategy.annotatedClasses['com.acme.Foo']}")
   private Set<Class<Object>> fooClasses;
   
   @In("#{hotDeploymentStrategy.annotatedClasses['com.acme.Foo']}")
   private Set<Class<Object>> hotFooClasses;

   @Create
   public void create() {
      for (Class clazz : fooClasses) {
         handleClass(clazz);
      }
      for (Class clazz : hotFooClasses) {
         handleClass(clazz);
      }
   }

}

You can also handle any resource. For example, you process any files with the extension .foo.xml. To do this, we need to write a custom deployment handler:

public class FooDeploymentHandler implements DeploymentHandler {
        
   private Set<InputStream> files = new HashSet<InputStream>();
        
   public String getName() {
      return "fooDeploymentHandler";
   }
   
   public Set<InputStream> getFiles() {
      return files;
   }
   
   public void handle(String name, ClassLoader classLoader) {
      if (name.endsWith(".foo.xml")) {
         files.add(classLoader.getResourceAsStream(name));
      }
   }
}

Here we are just building a list of any files with the suffix .foo.xml.

Then, we need to register the deployment handler with Seam. In /META-INF/seam-deployment.properties:

# For standard deployment
org.jboss.seam.deployment.deploymentHandlers=com.acme.FooDeploymentHandler
# For hot deployment
org.jboss.seam.deployment.hotDeploymentHandlers=com.acme.FooDeploymentHandler

You can register multiple deployment handler using a comma separated list.

Seam uses deployment handlers internally to install components and namespaces, therefore the handle() is called too early in inside Seam bootstrap to normally be useful. However, you can easily access the deployment handler during an APPLICATION scoped component's startup:

@Name("fooStartup")
@Scope(APPLICATION)
@Startup
public class FooStartup {

   @In("#{deploymentStrategy['fooDeploymentHandler']}")
   private MyDeploymentHandler myDeploymentHandler;
   
   @In("#{hotDeploymentStrategy['fooDeploymentHandler']}")
   private MyDeploymentHandler myHotDeploymentHandler;

   @Create
   public void create() {
      for (InputStream is : myDeploymentHandler.getFiles()) {
         handleFooXml(is);
      }
      for (InputStream is : myHotDeploymentHandler.getFiles()) {
         handleFooXml(is);
      }
   }

}