Tuesday, June 22, 2010

Running Seam 2.0 on Tomcat

Java Server Faces or JSF is a component-based MVC framework designed for rapid development. It includes predefined UI components and event model. The framework enables the integration of third party components. One of my favorites is RichFaces which provides an array of rich components and skinability.

JBoss Seam is a powerful enterprise web framework for seamless integration between JSF and EJB. Seam supports several features not in the JSF such as conversation scope, JBPM integration, navigation enhancement, just to name a few (Some features are supported since JSF 2.0).

If you had worked on JSF project without using Seam, you might see some exception complaining your session is closed when rendering Hibernate objects. With Seam, you can inject the Hibernate session (or EntityManager) into your bean. Seam will manage the session for you. It saves some tedious plumbing codes to to use Data Transfer Object.

However, I am not the fan of EJB as it is heavyweight. And you would need to deploy on J2EE compliance server such as JBoss. It takes comparatively longer to start up than our old friend Tomcat. So I began my research on how to deploy my web application on Tomcat without sacrificing the features I want in Seam. The first attempt was to use embedded JBoss in Tomcat. It works, but... it still gave me some exceptions during startup and I like to make deployment easier. Why can't I just deploy war file on Tomcat without any change? Fortunately, here is the solution. I assume readers have knowledge on how to configure JPA and Seam.

persistence.xml in the META-INF
<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">

  <persistence-unit name="testdb" type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <name="hibernate.connection.driver_class" value="org.gjt.mm.mysql.Driver"/>
      <name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
      <name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1/test"/>
      <name="hibernate.connection.username" value="test"/>
      <name="hibernate.connection.password" value="test"/>
      <name="hibernate.default_schema" value="test"/>
      <name="hibernate.bytecode.use_reflection_optimizer" value="false"/>
      <name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
      <name="show_sql" value="false"/>

      <name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
      <name="hibernate.c3p0.acquire_increment" value="1"/>
      <name="hibernate.c3p0.idle_test_period" value="120"/>
      <name="hibernate.c3p0.timeout" value="60"/>
      <name="hibernate.c3p0.max_size" value="50"/>
      <name="hibernate.c3p0.min_size" value="1"/>
      <name="hibernate.c3p0.max_statements" value="0"/>
      <name="hibernate.c3p0.preferredTestQuery" value="select 1;"/>
    </properties>
  </persistence-unit>
</persistence>

components.xml in Seam
<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:transaction="http://jboss.com/products/seam/transaction"
xmlns:security="http://jboss.com/products/seam/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ui="http://jboss.com/products/seam/ui"
xsi:schemaLocation=
"http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd 
http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.1.xsd
http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.1.xsd 
http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd 
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">

  <core:init debug="false" transaction-management-enabled="false"/>
  <core:manager conversation-timeout="120000"/>
  <core:resource-loader>
  <core:bundle-names>
    <value>org.sheng.bundle.messages</value>
  </core:bundle-names>
  </core:resource-loader>

  <persistence:entity-manager-factory name="entityManagerFactory" persistence-unit-name="testdb"/>
  <persistence:managed-persistence-context name="entityManager" auto-create="true" entity-manager-factory="#{entityManagerFactory}"/>
  <transaction:entity-transaction entity-manager="#{entityManager}"/>
  <factory name="session" scope="STATELESS" auto-create="true" value="#{entityManager.delegate}"/>
</components>

One thing to notice is the persistence-unit-name needs to match the one in persistence.xml. If you are using JPA, just remove the factory tag and inject your entityManager. Now you could simply drop the war file in the webapps folder of your tomcat. The version I use at the time of this writing is 6.0.26.