Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

The recommended approach for running a Seam application on Apache Tomcat is to infuse Tomcat with the Embedded JBoss distribution. This turns JBoss AS inside out, making Tomcat the outer web container and JBoss AS the nested Java EE container. Naturally, you would expect an application that runs on JBoss AS to also run on this hybrid container. However, you may need to run a Seam application on a vanilla Tomcat installation. It turns that this configuration is not that difficult to setup. In this article, we'll be taking a normal seam-gen project and deploying it to Tomcat after performing some modifications.

There are three obstacles we need to overcome to deploy a Seam project to vanilla Tomcat. The first is the JDBC data source, the second is transactions, and the third is library dependencies. Let's begin with the data source.

Setting up a JDBC data source

The typical peristence configuration in a Seam application assumes that the application server will manage the JDBC connection and make it available through JNDI. Although there's no UI to configure it, Tomcat can satisify this contract. The JDBC data source is configured as a Tomcat resource. Although a shared configuration is possible, you have the option of embedding the data source configuration inside the application. That's the approach we'll take here for portability.

Create the file tomcat-context-dev.xml in the resources folder and add an empty <Context> element.

<?xml version="1.0" encoding="UTF-8"?>
<Context>
</Context>

Next set the context path of the application, which in our example is /vehicles:

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/vehicles">
</Context>

Finally, add the following resource definition, using the proper credentials and JDBC driver for your application's database. Some sensible defaults are shown here.

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/projectName">
   <Resource auth="Container"
      name="vehiclesDatasource"
      type="javax.sql.DataSource"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost/projectName"
      username="user"
      password="secret"
      maxActive="100"
      maxIdle="30"
      maxWait="10000"/>
</Context>

Now let's get the build to put this file in the right place. It needs to end up as META-INF/context.xml in the root of the WAR. Add the following command to the end of the war target in the build.xml file:

<copy file="${basedir}/resources/tomcat-context-${profile}.xml"
   tofile="${war.dir}/META-INF/context.xml"/>

Next, you need to get your persistence unit configuration (i.e., META/persistence.xml) to reference this resource in JNDI. Tomcat binds resources to the java:comp/env JNDI namespace, so the value of the name attribute in the resource is appended to this namespace. We also need to use a non-JTA datasource, which I'll explain later.

Open up the resources/persistence-dev.xml file and make the following changes:

  1. Remove the transaction-type attribute
  2. Remove the <jta-data-source> element
  3. Remove the hibernate.transaction.manager_lookup_class <property> element
  4. Add a <non-jta-data-source> element that matches the name of the JDBC resource you configured above

The top part of the descriptor should match the exerpt below:

<?xml version="1.0" encoding="UTF-8"?>
<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" 
   version="1.0">
   <persistence-unit name="projectName">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <non-jta-data-source>java:comp/env/projectNameDatasource</non-jta-data-source>
      ...
   </persistence-unit>
</persistence>

The persistence unit configuration hinted at our transaction obstacle. Without significant research and trial and error, Tomcat does not support JTA. Fortunately, all JPA providers are capable of managing transactions using the transaction exposed by the database connection. This type of transaction is called a resource-local transaction. Seam's transaction management can be configured to use the resource-local transaction rather than JTA. Keep in mind, though, that you lose the ability to control transactions across multiple resources. But that is one of the prices you pay for using Tomcat.

Switching to resource location transactions

The first step is to inform the persistence unit to create persistence contexts that manage their own transactions, which we have already done. The second step is to tell Seam to use the persistence context's transaction, or entity transaction. To make this switch, register the org.jboss.seam.transaction.entityTransaction component in the Seam compoennt descriptor. This requires importing the appropriate namespace:

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   ...
   xmlns:tx="http://jboss.com/products/seam/transaction"
   xsi:schemaLocation="
      ...
      http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.1.xsd
      http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">
   ...
   <tx:entity-transaction/>
</components>

There's only one step left, the libraries.

Adding the extra libraries

You need to package Hibernate and it's dependencies, as well as some core Java EE APIs since these things are not provided by Tomcat out of the box. Open up the deployed-jars.list file in the root of the project and add the following lines:

# JPA and Hibernate
commons-logging.jar
commons-collections.jar
cglib.jar
antlr.jar
asm*jar
log4j.jar
dom4j.jar
javassist.jar
hibernate.jar
hibernate-annotations.jar
hibernate-commons-annotations.jar
hibernate-entitymanager.jar
hibernate-validator.jar
jboss-common-core.jar
persistence-api.jar
# JSF
jsf-api.jar
jsf-impl.jar
# JTA
jta.jar
# JSTL
jstl.jar

The project is ready! Now you just need to create the archive and copy it to the Tomcat deployment directory. First, build the archive.

ant archive

Then copy dist/projectName.war to the $TOMCAT_HOME/webapps directory. If you want to use an exploded archive, begin by staging the archive:

ant stage

Then copy the exploded-archives/vehicles.war directory to $TOMCAT_HOME/webapps/vehicles.

Now you have a seam-gen project running on a vanilla Tomcat installation!

A note about logging

One of the main differences between Tomcat and JBoss AS that many people struggle with is how logging is handled, specifically commons-logging. In Tomcat, logging can be configured from within the application, thus allowing you to have different appenders and log levels per application. JBoss AS centralized logging so you only need a single log configuration file.

When deploying to JBoss AS or Tomcat with Embedded JBoss, you configure all logging in one place. Deploying a Seam application to Tomcat without Embedded JBoss works just like any other WAR file deployed to Tomcat. So you simply add log4j.xml (or log4j.properties) to your classpath (WEB-INF/classes) and you can configure logging however you please!