Saturday, July 26, 2008

Building Seam Apps for Tomcat with JBoss Tools

One of the things I really like about the Seam framework is how much easier J2EE development is, particularly with respect to pulling the various components together. With Seam, it's very easy to build applications that utilize JSF, EJBs, and JPA. However, it's often preferable to develop more lightweight applications. While EJB3 has made enterprise development much more lightweight than in the past, there is still considerable overhead associated with it, particularly when you make use of a full-blown J2EE application server like JBoss. Sometimes, it's better to utilize a simpler approach and deploy your application using a Servlet Container like Tomcat.

The JBoss Tools project goes a long way towards making the actual development process with Seam much more user friendly. JBoss Tools provides a set of Eclipse plugins designed to work seamlessly (pun intended) with the framework. One of the drawbacks of JBoss Tools, however, is its lack of proper integration when creating a project you wish to deploy to Tomcat. In this post, I'll show you the changes you'll need to make to your Eclipse projects if you want to deploy your application to Tomcat.

To start, I'll assume that you have already downloaded Tomcat and defined a Server for it in Eclipse. It's very important to remember that you need Tomcat 6, in order to have support for the Servlet 2.5 specification. Once you've downloaded and setup Tomcat in Eclipse, you're ready to create your JBoss Tools project.

Create your JBoss Tools Seam project, just as you would a regular Seam project, by clicking File->New->Seam Web Project. This will start the new project wizard. However, when selecting your Target Runtime, select your Apache Tomcat 6.0 runtime, as shown in the image below.



Click next, and select whatever project facets you may need. Click next through the wizard and fill in the appropriate fields according to your project needs, until you get to the following screen.



In this screen, be sure to select a WAR file for your deployment, as Tomcat only understands WARs, not EARs. Click Finish, and your project will be created. Now, we need to make a couple of changes to support Tomcat in our project. We'll start by ensuring that the necessary Seam libraries are a part of your project. The following libraries need to be placed in your WEB-INF/lib directory:

  • antlr.jar
  • asm.jar
  • cglib.jar
  • commons-beanutils.jar
  • common-collections.jar
  • commons-digester.jar
  • commons-lang.jar
  • commons-logging.jar
  • dom4j.jar
  • hibernate-annotations.jar
  • hibernate-commons-annotations.jar
  • hibernate-entitymanager.jar
  • hibernate.jar
  • hibernate-validator.jar
  • javassist.jar
  • jboss-archive-browsing.jar
  • jboss-el.jar
  • jboss-seam-debug.jar
  • jboss-seam.jar
  • jboss-seam-ui.jar
  • jsf-api.jar
  • jsf-facelets.jar
  • jsf-impl.jar
  • jstl.jar
  • jta.jar
  • persistence-api.jar
  • richfaces-api.jar
  • richfaces-impl.jar
  • richfaces-ui.jar
Once these libraries are in place, your project will build successfully and deploy correctly to Tomcat. Now, we need to look at the Resource configuration to support JPA for object persistence. First, we need to create a context.xml file and place it your META-INF directory. This is the file that Tomcat uses when deploying your project to setup the context root, as well as any resources that need to be made available to your project. With a JBoss deployment, your database settings are placed in a separate file, like MyProject-ds.xml. With Tomcat, we will place all of these settings in our context.xml file. Once completed, it will look like this:


<?xml version="1.0" encoding="UTF-8"?>
<Context crossContext="true" debug="5" docBase="myproject" path="/myproject" reloadable="true">
<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" maxActive="20"
maxIdle="10" maxWait="-1" name="jdbc/myproject type="javax.sql.DataSource"
url="jdbc:mysql://localhost:3306/myproject"
username="myuser" password="mypassword" />
</Context>


We also need to modify the persistence.xml file used by JPA. The JNDI name that Tomcat utilizes differs from what JBoss uses. Instead of "java:/", our datasource needs to start with "java:comp/env". Our persistence.xml file will now look like this:


<persistence xmlns="http://java.sun.com/xml/ns/persistence" xsi="http://www.w3.org/2001/XMLSchema-instance" 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="MyProject" type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:comp/env/jdbc/MyProjectDatasource</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
</properties>
</persistence-unit>
</persistence>


Lastly, we need to modify some settings in our components.xml file. First, remove the jndi-pattern="@jndiPattern@" attribute from the following line:


<core:init debug="true" pattern="@jndiPattern@">


At this point, we should be ready to deploy our applicaion and test it out. Take a good look at the log files in case your application doesn't deploy correctly. However, if you followed the above steps, everything should work as expected.



2 comments:

archy said...

Hello WhoAmI

I followed your guide accurately but I'm getting the following exception during the tomcat startup:

SEVERE: Exception sending context initialized event to listener instance of class org.jboss.seam.servlet.SeamListener
org.jboss.seam.InstantiationException: Could not instantiate Seam component: tpjtEntityManagerFactory
at org.jboss.seam.Component.newInstance(Component.java:2096)
at org.jboss.seam.contexts.Contexts.startup(Contexts.java:304)
at org.jboss.seam.contexts.Contexts.startup(Contexts.java:278)
at org.jboss.seam.contexts.ServletLifecycle.endInitialization(ServletLifecycle.java:112)
at org.jboss.seam.init.Initialization.init(Initialization.java:727)
at org.jboss.seam.servlet.SeamListener.contextInitialized(SeamListener.java:34)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: javax.persistence.PersistenceException: org.hibernate.HibernateException: Could not locate TransactionManager
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:737)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:121)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:60)
at org.jboss.seam.persistence.EntityManagerFactory.createEntityManagerFactory(EntityManagerFactory.java:81)
at org.jboss.seam.persistence.EntityManagerFactory.startup(EntityManagerFactory.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
at org.jboss.seam.util.Reflections.invokeAndWrap(Reflections.java:138)
at org.jboss.seam.Component.callComponentMethod(Component.java:2209)
at org.jboss.seam.Component.callCreateMethod(Component.java:2124)
at org.jboss.seam.Component.newInstance(Component.java:2084)
... 20 more
Caused by: org.hibernate.HibernateException: Could not locate TransactionManager
at org.hibernate.transaction.JNDITransactionManagerLookup.getTransactionManager(JNDITransactionManagerLookup.java:26)
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:325)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:915)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:730)
... 34 more
Caused by: javax.naming.NameNotFoundException: Name TransactionManager is not bound in this Context
at org.apache.naming.NamingContext.lookup(NamingContext.java:770)
at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:137)
at javax.naming.InitialContext.lookup(Unknown Source)
at org.hibernate.transaction.JNDITransactionManagerLookup.getTransactionManager(JNDITransactionManagerLookup.java:23)
... 38 more

Any ideas about what might be the reason?

Thanks in advance.

Bapi said...

while getting the Caused by: org.hibernate.HibernateException: Could not locate TransactionManager error.

you do have a transaction manager lookup configured in your file. This is automatically put in by jboss-tools even though u didn’t specify any such thing.
i.e.
hibernate.transaction.manager_lookup_class = org.hibernate.transaction.JBossTransactionManagerLookup

If you are not using the JBossTransactionManagerLookup, then remove this property declaration.