Integrating Seam with Maven, Netbeans and GlassFish
The application that resulted from my previous article on Maven, Netbeans and GlassFish uses Hibernate for JPA and Facelets for JSF. This is an ideal situation to make the move to Seam. In the past few weeks I have spent some time on using Seam in GlassFish applications but I haven’t been very successful. However, the jpa example that is shipped with the Seam 2.0.0 GA distribution shows that working with Seam on Glassfish can be very simple. I just built the GlassFish war suing ant and deployed that war to GlassFish. The application just ran without any problems at all! Today I finally managed to build a Seam application for GlassFish. Moving that application to Maven then was very easy. This article will tell you how to do it.
I will assume that you have followed the
instructions for
creating a Maven enabled Hibernate/Facelets application in Netbeans. If
you haven’t then that’s no problem. This article should provide you
with enough hints to enable Seam on any Enterprise Application created
with Netbeans.
Moving the EJB module to Seam
The number of jars that are needed to enable Seam on the EJB module is very very small: just 1. It is the jboss-seam.jar file. The reason only this jar is needed, is that the jars, that the jboss-seam.jar depends on, already have been installed into the GlassFish lib directory when we moved from Toplink Essentials to Hibernate. These jars are dom4j.jar, el-api.jar, hibernate-validator.jar and javassist.jar. Well, the el-api.jar file classes are actually contained in the GlassFish javaee.jar file. Besides these jars, the jboss-seam.jar file also depends on the jboss-el.jar file. We won’t need this jar for the application that we’ll build in this article, but you may want to include that jar anyway in case you’d want to use EL expressions in your entity classes.
Please note
that I have updated the list of jars files needed for Hibernate in my
previous article. You should now also copy the hibernate-validator.jar
file from the Hibernate EntityManager distribution to the GlassFish lib
directory. If you haven’t done so, please do it now!
Before we can make Maven download the seam jars, we need to add another repository to our pom.xml file. Since this repository will also be used by the WAR module, I have included it in the Parent Pom Module pom.xml file. The lines to add are these
<repository>
<id>jboss.org</id>
<name>jboss.org</name>
<url>http://repository.jboss.com/maven2</url>
</repository>
Next, these lines in the EJB module pom.xml file will make Maven download the needed jars
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<version>2.0.0.GA</version>
<exclusions>
<exclusion>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
</exclusion>
<exclusion>
<artifactId>el-api</artifactId>
<groupId>javax.el</groupId>
</exclusion>
<exclusion>
<artifactId>javassist</artifactId>
<groupId>jboss</groupId>
</exclusion>
</exclusions>
</dependency>
At this stage you may want to Clean And Build the project so the jars get downloaded and Netbeans is capable of locating the classes in the jars. Once done we can annotate our DataBean session bean with the @Name annotation. Besides that, we can switch over to Seam managed persistency instead of container managed persistency. This means that we need to replace the @PersistenceContext annotation with the @In annotation.
Besides that we’ll need to adjust the DataBean
class to conform the Java Bean design guidelines. These guidelines
state that Beans contain (private) member variables with getters and
setters. To be able to keep on using the "countEmployees" call in our
index.xhtml file, we’ll need to introduce a (private) int
countEmployees and rename the countEmployees() method to
getCountEmployees(). Please note that renaming the method in the
DataBean class means that you’ll have to rename the countEmployees
method definition in the DataLocal interface as well!
The final DataBean class looks like this
package nl.amis.maven.enterprise.ejb.session;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
@Stateless
@Name("dataBean")
public class DataBean implements DataLocal {
@In EntityManager em;
private int countEmployees;
public int getCountEmployees() {
return em.createNamedQuery("Employees.getAll").getResultList().size();
}
}
That nearly completes the modifications that the EJB module needs. We only need to create two more files, one of which is empty, and modify our persistence.xml file. Lets start with the empty file.
Expand the Other Sources -> resources -> META-INF node. Right click the META-INF node and select New -> Other … and choose Properties File from the Other category. Name this file "seam" (without the queotes and without the .properties extension) and click Finish. This file will trigger Seam to initiate and load all classes and resources in the EJB module.
While we’re in this node, let’s create the second file. This file will be an XML file (New -> Other … and choose XML document from the XML Category) and name this file "ejb-jar". This file will contain some Seam specific interceptor definitions. The contents of this file should be
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
version = "3.0"
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">
<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>
Finally, we need to modify the persistence.xml file to define Seam managed persistency. Double click the persistence.xml file in the META-INF node and make the contents look like this
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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">
<persistence-unit name="MavenEnterpriseApplicationEjbPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>jdbc/hr</non-jta-data-source>
<properties/>
</persistence-unit>
</persistence>
Please note two important things. first of all, we switched from transaction type JTA to RESOURCE_LOCAL. This means that Seam will be in charge of the transactions etc. This means that we will need to modify our data source definition from jta-data-source to non-jta-data-source. Besides that I slightly modified the persistence-unit name. It was MavenEnterpriseApplication-ejbPU but unfortunately Seam won’t accept the dash ("-") in the name.
That’s it for the EJB module.
Moving the WAR module to Seam
Moving the WAR project to Seam also isn’t very complicated. First we need to include the needed dependencies in our pom.xml file. The WAR module needs two of them: jboss-seam-ui.jar and jboss-el.jar. Again, these two jars depend on several jars that already have been installed in the GlassFish lib dir, so we may exclude them. Add these lines to your pom.xml file
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam-ui</artifactId>
<version>2.0.0.GA</version>
<exclusions>
<exclusion>
<artifactId>commons-beanutils</artifactId>
<groupId>commons-beanutils</groupId>
</exclusion>
<exclusion>
<artifactId>dom4j</artifactId>
<groupId>dom4j</groupId>
</exclusion>
<exclusion>
<artifactId>el-api</artifactId>
<groupId>javax.el</groupId>
</exclusion>
<exclusion>
<artifactId>javassist</artifactId>
<groupId>jboss</groupId>
</exclusion>
<exclusion>
<artifactId>jboss-seam</artifactId>
<groupId>org.jboss.seam</groupId>
</exclusion>
<exclusion>
<artifactId>jsf-api</artifactId>
<groupId>javax.faces</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-el</artifactId>
<version>2.0.0.GA</version>
<exclusions>
<exclusion>
<artifactId>el-api</artifactId>
<groupId>javax.el</groupId>
</exclusion>
</exclusions>
</dependency>
Clean and Build the project so the jars get downloaded. Doing so will make Maven resolve all dependencies. This will make Maven see that the EJB module depends on jboss-seam.jar and this library will show up in the list. however, this jar is not needed here. If you like you can exclude it by adding these lines to the EJB modulle dependency in the WAR module pom.xml file
<exclusions>
<exclusion>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
</exclusion>
</exclusions>
Since Seam will be in charge on injecting (and outjecting) bean references, we won’t need the Managed Bean reference in the faces-config.xml file anymore. So, expand the Web Pages -> WEB-INF node and remove the managed bean code from the faces-config.xml file. For Seam to be able to resolve the bean references correctly, the web.xml file also needs to be modified. Open the file and add these lines at the top of the file
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
Finally, Seam needs another configuration file called components.xml. This file will, among onther things, enable Seam to setup the persistence unit and to find the session bean in our EJB module. Simply create another XML Document in the WEB-INF directory and name it "components". This is the components.xml file I stole from the Seam jpa example
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:persistence="http://jboss.com/products/seam/persistence"
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/persistence http://jboss.com/products/seam/persistence-2.0.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">
<persistence:entity-manager-factory name="MavenFaceletsSeamEjbPU"/>
<persistence:managed-persistence-context name="em"
auto-create="true"
entity-manager-factory="#{MavenFaceletsSeamEjbPU}"/>
<core:init jndi-pattern="java:comp/env/#{ejbName}" />
</components>
Please note that the persistence name should be the same as in the persistence.xml file in your EJB module. Besides that, Seam won’t be able to resolve the JNDI names of session beans on GlassFish. I’m not sure why this is so, but Seam NEEDS the jndi-pattern line. The pattern can be anything you like. For me the "java:comp/env/#{ejbName}" pattern will make the DataBean bean be registered under the JNDI name "java:comp/env/DataBean" as this GlassFish log shows
Component: org.jboss.seam.core.init, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.core.Init
Installing components…
Component: MavenEnterpriseApplicationEjbPU, scope: APPLICATION, type: JAVA_BEAN, class: org.jboss.seam.persistence.EntityManagerFactory
Component: dataBean, scope: STATELESS, type: STATELESS_SESSION_BEAN, class: nl.amis.maven.enterprise.ejb.session.DataBean, JNDI: java:comp/env/DataBean
Component: em, scope: CONVERSATION, type: JAVA_BEAN, class: org.jboss.seam.persistence.ManagedPersistenceContext
There is one more catch to resolve. Since our session bean is packaged in a jar file, the reference to the bean needs to be made explicit in web.xml. This can be done with a "ejb-local-ref" element like this
<ejb-local-ref>
<ejb-ref-name>DataBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>nl.amis.maven.enterprise.ejb.session.DataLocal</local>
</ejb-local-ref>
That’s it! The ejb-ref-name element makes sure that the "#{ejbName}" part in the jndi-pattern line in the components.xml file is referring to the right value. Please note that this needs to be done for ALL beans that are packaged in the EJB module jar and that need to be references from your xhtml pages. That was easy right?
O wait, one more thing and then we’re finished. Promise. We need to modify the index.xhtml file to make use of the DataBean bean instead of the NumberBackingBean. So, open the index.xhtml file and modify the EL expression to
#{dataBean.countEmployees}
Once more rebuild the application and now fire the exec:exec goal on the EAR module. Once your application has been deployed, browse to e.g. http://localhost:8280/MavenEnterpriseApplication-war/index.jsf and you should once more see
Next steps
Since
Seam now is in charge of the transaction and Entity Manager management,
the Hibernate jars are no longer needed in the GlassFish lib directory.
You may remove them and then restart GlassFish. This will mean that you
need to deploy those jars with your application. This, of course, can
be done by modifying the pom.xml files and add the correct
dependencies. To do this, I used the list below in the EJB pom.xml and
made no modifications to the WAR pom.xml file. Please note that the
list below only replaces the Hibernate dependencies. No modifications
are needed for the Seam dependencies. Adding the dependencies to the
EJB pom.xml will make Maven include the jars both in the EJB jar and in
the WAR thus making sure that both modules have the jars they need. It
would help to clean up the redundant jars but I’ll leave that up to you
to do
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.3.1.ga</version>
<exclusions>
<exclusion>
<artifactId>jboss-common-core</artifactId>
<groupId>jboss</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>3.0.0.ga</version>
</dependency>
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-attrs</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<artifactId>avalon-framework</artifactId>
<groupId>avalon-framework</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>logkit</artifactId>
<groupId>logkit</groupId>
</exclusion>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>concurrent</groupId>
<artifactId>concurrent</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
<exclusions>
<exclusion>
<artifactId>xml-apis</artifactId>
<groupId>xml-apis</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>javassist</artifactId>
<version>3.3.ga</version>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jboss-archive-browsing</artifactId>
<version>5.0.0alpha-200607201-119</version>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb-api</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>