Chapter 28. Configuring Seam and packaging Seam applications

Configuration can be complex and tedious, but for the most part you will not need to write configuration data from scratch. Only a few lines of XML are required to integrate Seam with your JavaServer Faces (JSF) implementation and Servlet container, and for the most part you can either use seam-gen to start your application, or simply copy and paste from the example applications provided with Seam.

28.1. Basic Seam configuration

First, the basic configuration required whenever Seam is used with JSF.

28.1.1. Integrating Seam with JSF and your servlet container

First, define a Faces Servlet.
<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>
(You can adjust the URL pattern as you like.)
Seam also requires the following entry in your web.xml file:
<listener> 
  <listener-class>org.jboss.seam.servlet.SeamListener</listener-class> 
</listener>
This listener is responsible for bootstrapping Seam, and for destroying session and application contexts.
Some JSF implementations do not implement server-side state saving correctly, which interferes with Seam's conversation propagation. If you have problems with conversation propagation during form submissions, try switching to client-side state saving. To do so, add the following to web.xml:
<context-param> 
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name> 
  <param-value>client</param-value> 
</context-param>
The JSF specification is unclear about the mutability of view state values. Since Seam uses the JSF view state to back its PAGE scope, this can be problematic. If you use server-side state saving with the JSF-RI (JSF Reference Implementation), and you want a page-scoped bean to retain its exact value for a given page view, you must specify the context parameter as follows:
<context-param> 
  <param-name>com.sun.faces.serializeServerState</param-name> 
  <param-value>true</param-value> 
</context-param>
If this is not specified, a page-scoped component will contain the latest value of the page, and not the value of the "back" page, when the "back" button is used. (See this specification issue for details.) This setting is not enabled by default because serializing the JSF view with every request lowers overall performance.

28.1.2. Using Facelets

To use the recommended Facelets over JavaServer Pages (JSP), add the following lines to faces-config.xml:
<application> 
  <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> 
</application>
Then, add the following lines to web.xml:
<context-param> 
  <param-name>javax.faces.DEFAULT_SUFFIX</param-name> 
  <param-value>.xhtml</param-value> 
</context-param>

28.1.3. Seam Resource Servlet

The Seam Resource Servlet provides resources used by Seam Remoting, CAPTCHAs (see the Security chapter) and some JSF UI controls. Configuring the Seam Resource Servlet requires the following entry in web.xml:
<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>

28.1.4. Seam Servlet filters

Seam does not require Servlet filters for basic operation, but there are several features that depend upon filter use. Seam lets you add and configure Servlet filters as you would configure other built-in Seam components. To use this configuration method, you 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>
To ensure that it is run first, the Seam master filter must be the first filter specified in web.xml.
The Seam filters share a number of common attributes, which can be set in components.xml, along with any parameters discussed below:
  • url-pattern — Specifies which requests are filtered. The default is all requests. url-pattern is a pattern which allows a wildcard suffix.
  • regex-url-pattern — Specifies which requests are filtered. The default is all requests. regex-url-pattern is a true regular expression match for request path.
  • disabled — Disables a built in filter.
These patterns are matched against the URI path of the request (see HttpServletRequest.getURIPath()), and the name of the Servlet context is removed before matching occurs.
Adding the master filter enables the following built-in filters:

28.1.4.1. Exception handling

This filter is required by most applications, and provides the exception mapping functionality in pages.xml. It also rolls back uncommitted transactions when uncaught exceptions occur. (The web container should do this automatically, but this does not occur reliably in some application servers.)
By default, the exception handling filter will process all requests, but you can adjust this behavior by adding a <web:exception-filter> entry to components.xml, like so:
<components xmlns="http://jboss.com/products/seam/components" 
            xmlns:web="http://jboss.com/products/seam/web"> 
  <web:exception-filter url-pattern="*.seam"/> 
</components>

28.1.4.2. Conversation propagation with redirects

This filter allows Seam to propagate the conversation context across browser redirects. It intercepts any browser redirects and adds a request parameter that specifies the Seam conversation identifier.
The redirect filter processes all requests by default, but this behavior can also be adjusted in components.xml:
<web:redirect-filter url-pattern="*.seam"/>

28.1.4.3. URL rewriting

This filter lets Seam apply URL rewriting for views, depending on its configuration in pages.xml. This filter is not active by default, but can be activated by adding the following configuration to components.xml:
<web:rewrite-filter view-mapping="*.seam"/>
The view-mapping parameter must match the Servlet mapping defined for the Faces Servlet in the web.xml file. If omitted, the rewrite filter assumes the pattern *.seam.

28.1.4.4. Multipart form submissions

This feature is required when you use the Seam file upload JSF control. It detects multipart form requests and processes them according the multipart/form-data specification (RFC-2388). Add the following to components.xml to override settings:
<web:multipart-filter create-temp-files="true" 
     max-request-size="1000000" url-pattern="*.seam"/>
  • create-temp-files — If true, uploaded files are written to a temporary file, rather than being held in memory. This can be important if you expect large file uploads. By default, this is set to false.
  • max-request-size — If the size of a file upload request exceeds this value, the request will be aborted. The default setting is 0 (no size limit). (The size of a file upload is determined by reading the Content-Length header in the request.)

28.1.4.5. Character encoding

This filter sets the character encoding of submitted form data. It is not installed by default, and requires an entry in components.xml to enable it:
<web:character-encoding-filter encoding="UTF-16" 
     override-client="true" url-pattern="*.seam"/>
  • encoding — The type of encoding to use.
  • override-client — If set to true, the request encoding will be set to that specified by encoding, regardless of whether the request specifies a particular encoding. If set to false, the request encoding will only be set if the client has not already specified the request encoding. By default, this is set to false.

28.1.4.6. RichFaces

If RichFaces is used in your project, Seam automatically installs the RichFaces AJAX filter before all other built-in filters, so there is no need to add it to web.xml manually.
The RichFaces Ajax filter is installed only if the RichFaces JARs are present in your project.
To override the default settings, add the following entry to components.xml. The options are the same as those specified in the RichFaces Developer Guide:
<web:ajax4jsf-filter force-parser="true" enable-cache="true" 
     log4j-init-file="custom-log4j.xml" url-pattern="*.seam"/>
  • force-parser — forces all JSF pages to be validated by RichFaces's XML syntax checker. If false, only AJAX responses are validated and converted to well-formed XML. Setting force-parser to false improves performance, but can provide visual artifacts on AJAX updates.
  • enable-cache — enables caching of framework-generated resources, such as JavaScript, CSS, images, etc. When developing custom JavaScript or CSS, setting this to true prevents the browser from caching the resource.
  • log4j-init-file — is used to set up per-application logging. A path, relative to web application context, to the log4j.xml configuration file should be provided.

28.1.4.7. Identity Logging

This filter adds the authenticated username to the log4j mapped diagnostic context, so that it can be included in formatted log output by adding %X{username} to the pattern.
By default, the logging filter processes all requests. You can adjust this behavior by adding a <web:logging-filter> entry to components.xml, like so:
<components xmlns="http://jboss.com/products/seam/components" 
            xmlns:web="http://jboss.com/products/seam/web"> 
  <web:logging-filter url-pattern="*.seam"/> 
</components>

28.1.4.8. Context management for custom servlets

Requests that are sent directly to Servlets other than the JSF Servlet are not processed in the JSF life cycle, so Seam provides a Servlet filter that can be applied to any other Servlet requiring access to Seam components.
This filter lets custom Servlets interact with Seam contexts. It sets up Seam contexts at the beginning of each request, and removes them at the end of the request. This filter should never be applied to the JSF FacesServlet — Seam uses the phase listener to manage context in a JSF request.
This filter is not installed by default, and must be enabled in components.xml:
<web:context-filter url-pattern="/media/*"/>
The context filter expects the conversation ID of any conversation context to be defined in the conversationId request parameter. You are responsible for ensuring that this is included in the request.
You are also responsible for ensuring that any new conversation ID propagates back to the client. Seam exposes the conversation ID as a property of the built in component conversation.

28.1.4.9. Adding custom filters

Seam can install your filters for you. This allows you to specify your filter's placement in the chain — the Servlet specification does not provide a well-defined order when you specify your filters in web.xml. Add a @Filter annotation to your Seam component. (Your Seam component must implement javax.servlet.Filter.)
@Startup 
@Scope(APPLICATION) 
@Name("org.jboss.seam.web.multipartFilter") 
@BypassInterceptors 
@Filter(within="org.jboss.seam.web.ajax4jsfFilter") 
public class MultipartFilter extends AbstractFilter {...}
Adding the @Startup annotation makes the component available during Seam start up. Bijection is not available here (@BypassInterceptors), and the filter should be further down the chain than the RichFaces filter (@Filter(within="org.jboss.seam.web.ajax4jsfFilter")).

28.1.5. Integrating Seam with your EJB container

EJB components in a Seam application are managed by both Seam and the EJB container. Seam resolves EJB component references, manages the lifetime of stateful session bean components, and participates in each method call via interceptors. To integrate Seam with your EJB container, you must first configure the interceptor chain.
Apply the SeamInterceptor to your Seam EJB components. This interceptor delegates to a set of built-in server-side interceptors that handle operations like bijection, conversation demarcation, and business process signals. 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>
You must tell seam where to find session beans in JNDI. You could do this by specifying a @JndiName annotation on every session bean Seam component. A better approach is to specify a pattern with which Seam can calculate the JNDI name from the EJB name. However, the EJB3 specification does not define a standard method of mapping to global JNDI — this mapping is vendor-specific, and can also depend upon your naming conventions. We specify this option in components.xml:
For JBoss Enterprise Application Platform, the following pattern is correct:
<core:init jndi-name="earName/#{ejbName}/local" />
Here, earName is the name of the EAR in which the bean is deployed. Seam replaces #{ejbName} with the name of the EJB, and the final segment represents the type of interface (local or remote).
When outside the EAR context (for example, when using the JBoss Embeddable EJB3 container), the first segment is dropped, since there is no EAR, which leaves us with the following pattern:
<core:init jndi-name="#{ejbName}/local" />
This process looks complicated, but in reality comprises few steps.
First, we will talk about how the EJB component is transferred to JNDI. To avoid using XML, JBoss Enterprise Application Platform uses the aforementioned pattern (that is, EAR name/EJB name/interface type) to automatically assign an EJB component a global JNDI name. The EJB name will be the first non-empty value out of the following:
  • the <ejb-name> element in ejb-jar.xml,
  • the name attribute in the @Stateless or @Stateful annotation, or
  • the simple name of the bean class.
For example, assume that you have the following EJB bean and interface defined:
package com.example.myapp; 
import javax.ejb.Local; 

@Local 
public class Authenticator { 
  boolean authenticate(); 
} 


package com.example.myapp; 
import javax.ejb.Stateless; 

@Stateless 
@Name("authenticator") 
public class AuthenticatorBean implements Authenticator { 
  public boolean authenticate() { ... } 
}
Assuming that your EJB bean class is deployed in an EAR named myapp, the global JNDI name assigned on the JBoss Enterprise Application Platform will be myapp/AuthenticatorBean/local. You can refer to this EJB component as a Seam component with the name authenticator, and Seam will use the JNDI pattern (or the @JndiName annotation) to locate it in JNDI.
For other application servers, you must declare an EJB reference for your EJB so that it is assigned a JNDI name. This does require some XML, and means that you must establish your own JNDI naming convention so that you can use the Seam JNDI pattern. It may be useful to follow the JBoss convention.
You must define the EJB references in two locations when using Seam with a non-JBoss application server. If you look up the Seam EJB component with JSF (in a JSF view, or as a JSF action listener) or a Seam JavaBean component, then you must declare the EJB reference in web.xml. The EJB reference that would be required for our example is:\
<ejb-local-ref> 
  <ejb-ref-name>myapp/AuthenticatorBean/local</ejb-ref-name> 
  <ejb-ref-type>Session</ejb-ref-type> 
  <local>org.example.vehicles.action.Authenticator</local> 
</ejb-local-ref>
This reference covers most uses of the component in a Seam application. If you want to be able to inject a Seam EJB component into another Seam EJB component with the @In annotation, you must define this EJB reference in a second location: ejb-jar.xml. This is slightly more complicated.
When Seam looks for a Seam EJB component to satisfy an injection point defined with @In, the component will only be found if it is referenced in JNDI. JBoss automatically registers EJBs to the JNDI so that they are always available to the web and EJB containers. Other containers require you to define your EJBs explicitly.
Application servers that adhere to the EJB specification require that EJB references are always explicitly defined. These cannot be declared globally — you must specify each JNDI resource for an EJB component individually.
Assuming that you have an EJB with a resolved name of RegisterAction, the following Seam injection applies:
@In(create = true) Authenticator authenticator;
For this injection to work, you must also establish the link in ejb-jar.xml, like so:
<ejb-jar> 
  <enterprise-beans> 
    <session> 
      <ejb-name>RegisterAction</ejb-name> 
      <ejb-local-ref> 
      <ejb-ref-name>myapp/AuthenticatorAction/local</ejb-ref-name> 
      <ejb-ref-type>Session</ejb-ref-type> 
      <local>com.example.myapp.Authenticator</local> 
      </ejb-local-ref> 
    </session> 
  </enterprise-beans> 
  
  ...
   
</ejb-jar>
The component is referenced here just as it was in web.xml. Identifying it here brings the reference into the EJB context, where it can be used by the RegisterAction bean. You must add one reference for each injection (via @In) of one Seam EJB component into another Seam EJB component. You can see an example of this setup in the jee5/booking example.
It is possible to inject one EJB into another with the @EJB annotation, but this injects the EJB reference rather than the Seam EJB component instance. Because Seam's interceptor is invoked on any method call to an EJB component, and using @EJB only invokes Seam's server-side interceptor chain, some Seam features will not work with @EJB injection. (Seam's state management and Seam's client-side interceptor chain, which handles security and concurrency, are two affected features.) When a stateful session bean is injected using the @EJB annotation, it will not necessarily bind to the active session or conversation, either, so we recommend injecting with @In.
Some application servers (such as Glassfish) require you to specify JNDI names for all EJB components explicitly, sometimes more than once. You may also need to alter the JNDI pattern used in Seam, even if you follow the JBoss Enterprise Application Platform naming convention. For example, in Glassfish the global JNDI names are automatically prefixed with java:comp/env, so you must define the JNDI pattern as follows:
<core:init jndi-name="java:comp/env/earName/#{ejbName}/local" />
For transaction management, we recommend using a special built-in component that is fully aware of container transactions, and can correctly process transaction success events registered with the Events component. To tell Seam when container-managed transactions end, add the following line to your components.xml file:
<transaction:ejb-transaction/>

28.1.6. Remember

The final requirement for integration is that a seam.properties, META-INF/seam.properties or META-INF/components.xml file be placed in any archive in which your Seam components are deployed. For web archive (WAR) files, place a seam.properties file inside the WEB-INF/classes directory in which your components are deployed.
Seam scans any archive with seam.properties files for Seam components at start up. The seam.properties file can be empty, but it must be included so that the component is recognized by Seam. This is a workaround for Java Virtual Machine (JVM) limitations — without the seam.properties file, you would need to list every component explicitly in components.xml.