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!

1 comment:

  1. Thank you. It was useful. Just one more thing, that might be forgotten:
    When updating seam version don't forget to change xsd locations in all configuration files. For example: from http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.1.xsd to http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.2.xsd

    ReplyDelete