Chapter 26. Seam on OC4J

OC4J (Oracle Containers for Java) 11g (currently a "Technology Preview" release) is Oracle's JEE 5 application server. We will will start by looking at the building and deploying the Hotel Booking example application which comes with Seam, and then at deploying a project generated by seam-gen. This project will integrate Seam, RichFaces Ajax and components, Seam Security (with Drools), Facelets and JPA provided by Hibernate.

This section requires you to use OC4J 11g Technology Preview (not OC4J 10g). You can download OC4J 11g from http://www.oracle.com/technology/tech/java/oc4j/11/.

26.1. The jee5/booking example

The jee5/booking example is based on the Hotel Booking example (which runs on JBoss AS). Out of the box it is designed to run on Glassfish, but it's easy to build it for OC4J.

26.1.1. Booking Example Dependencies

First, lets look at the dependencies of the booking example. Armed with this knowledge we can look at the extra dependencies requirements that OC4J adds.

  • jboss-seam.jar — We declare this as an EJB3 module (why? well Seam needs to be able to interact with container managed transactions; this is implemented as an EJB3 Stateful Session Bean)

  • jboss-el.jar

  • jboss-seam-ui.jar — Seam's JSF controls depend on Apache's commons-beanutils

  • jboss-seam-debug.jar

  • jsf-facelets.jar

  • richfaces-api.jar — which requires Apache commons-digester and commons-beanutils

  • richfaces-impl.jar and richfaces-ui.jar — which requires Apache commons-digester and commons-beanutils

26.1.2. Extra dependencies required by OC4J

  • Hibernate — of course, we decided to use Hibernate as the JPA provider (rather than TopLink Essentials which ships with OC4J).

    To use Hibernate as your JPA provider you need three jars (hibernate3.jar, hibernate-annotations.jar, hibernate-entitymanager.jar) and their dependencies (jboss-common.jar, jboss-archive-browsing.jar and ejb3-persistence.jar). You can find these in the hibernate/lib directory in the Seam distribution.

  • thirdparty-all.jar — a selection of third party libraries on which Seam depends (like javassist).

Running Seam on most application servers (such as JBoss AS or Glassfish) you only need to include the dependencies for those bits of Seam you actually use (e.g. if you use Seam Text you need to include ANTLR); but, on OC4J, due to its "interesting" classloading you must always include them:

  • antlr-2.7.6.jar — needed for Seam Text (not used in the example).

  • jbpm-jpdl.jar — needed for Seam's JBPM integration (not used in the example).

  • Drools — needed for Seam Security. We aren't using Seam security with Drools, but have to include it. Drools consists of 5 jars - drools-core-4.0.0.jar, drools-compiler-4.0.0.jar, janino-2.5.7.jar, mvel14-1.2rc1.jar and antlr-runtime-3.0.jar. Drools integration is not used in the example.

26.1.3. Configuration file changes

There are just a few changes to be made:

web.xml

you need to declare all your ejb's in the web.xml. This is a silly requirement of a number of JEE 5 application servers - for example OC4J and Glassfish.

<ejb-local-ref>
   <ejb-ref-name>
      jboss-seam-jee5/AuthenticatorAction/local
   </ejb-ref-name>
   <ejb-ref-type>Session</ejb-ref-type>
   <local-home/>
   <local>
      org.jboss.seam.example.booking.Authenticator
   </local>
   <ejb-link>AuthenticatorAction</ejb-link>
</ejb-local-ref>
persistence.xml

you need to provide the correct configuration for your JPA implementation. We are using Hibernate and due to OC4J bundling an old ANTLR, we need to use an alternative query factory, we also want to use the OC4J transaction manager:

<property 
   name="hibernate.query.factory_class"
   value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory" />
<property
   name="hibernate.transaction.manager_lookup_class"
   value="org.hibernate.transaction.OrionTransactionManagerLookup" />

26.1.4. Building the jee5/booking example

  1. Modify the following files in the project:

    • build.xml — Un-comment the OC4J-related libraries

    • resources/META-INF/persistence.xml — Comment out the Glassfish properties and un-comment the OC4J properties.

  2. Build the demo app by running ant. The build target is dist/jboss-seam-jee5.ear

  3. Copy hsqldb.jar to OC4J: cp ../../seam-gen/lib/hsqldb.jar $ORACLE_HOME/j2ee/home/applib/ (OC4J doesn't come with an embedded database so we decided to use HSQLDB)

26.2. Deploying a Seam application to OC4J

This mini-tutorial describes the (fairly tedious) steps required to deploy a JEE 5 application to OC4J. It assumes you are deploying the jee5/booking example, using the embedded hsqldb database. To deploy another application you would need to alter the datasource and application name.

  1. Download and unzip OC4J

  2. Make sure you have $JAVA_HOME and $ORACLE_HOME set as environment variables ($ORACLE_HOME is the directory to which you unzip OC4J). For further information on installing OC4J, consult the Readme.txt distributed with OC4J

  3. Edit the OC4J datasource $ORACLE_HOME/j2ee/home/config/data-sources.xml and, inside <data-sources>, add

    <managed-data-source
       connection-pool-name="jee5-connection-pool"
       jndi-name="jdbc/__default"
       name="jee5-managed-data-source" />
    <connection-pool name="jee5-connection-pool">
       <connection-factory
          factory-class="org.hsqldb.jdbcDriver" 
          user="sa"
          password="" 
          url="jdbc:hsqldb:." />
    </connection-pool>

    The jndi-name is used as the jta-data-source in persistence.xml.

  4. Edit $ORACLE_HOME/j2ee/home/config/server.xml and, inside <application-server>, add

    <application name="jboss-seam-jee5"
     path="../../home/applications/jboss-seam-jee5.ear"
     parent="default" 
     start="true" />

    To keep things simple use the same names as you used for project.

  5. Edit $ORACLE_HOME/j2ee/home/config/default-web-site.xml, and, inside <web-site>, add

    <web-app application="jboss-seam-jee5"
     name="jboss-seam-jee5" 
     load-on-startup="true"
     root="/seam-jee5" />

    The root is the context path you will put into your web browser to access the application.

  6. Copy the application to OC4J: cp dist/jboss-seam-jee5.ear $ORACLE_HOME/j2ee/home/applications/

  7. Start OC4J: $ORACLE_HOME/bin/oc4j -start

    You will be asked to set the admin password if this is the first time you've started OC4J

  8. Checkout the app at: http://localhost:8888/seam-jee5

  9. You can stop the server by pressing CTRL-C in the console on which the server is running.

26.3.  Deploying an application created using seam-gen to OC4J

The following explanation assumes you are using the command line and a simple text editor, but of course you can use your favourite IDE - seam-gen projects come with support for Eclipse and Netbeans.

We start by creating a pretty simple application using seam-gen. seam-gen uses Hibernate Tools to reverse engineer a database schema to JPA entity beans; it also genereates Seam Application Framework components and JSF views for CRUD. This tutorial uses MySQL (but of course you could use any database, altering the SQL as appropriate); install, configure and run MySQL, then create a database with some sample data.

Next, run ./seam setup in the seam directory.

> ./seam setup
Buildfile: build.xml

setup:
    [echo] Welcome to seam-gen :-)
    [input] Enter your Java project workspace (the directory that contains your Seam projects) [/home/pmuir/workspace] [/home/pmuir/workspace]

    [input] Enter your JBoss home directory [/home/pmuir/java/jboss-4.2.1.GA] [/home/pmuir/java/jboss-4.2.1.GA]

    [input] Enter the project name [oc4j-example] [oc4j-example]

    [input] Is this project deployed as an EAR (with EJB components) or a WAR (with no EJB support) [ear]  ([ear], war, )

    [input] Enter the Java package name for your session beans [org.jboss.seam.tutorial.oc4j.action] [org.jboss.seam.tutorial.oc4j.action]

    [input] Enter the Java package name for your entity beans [org.jboss.seam.tutorial.oc4j.model] [org.jboss.seam.tutorial.oc4j.model]

    [input] Enter the Java package name for your test cases [org.jboss.seam.tutorial.oc4j.test] [org.jboss.seam.tutorial.oc4j.test]

    [input] What kind of database are you using? [mysql]  (hsql, [mysql], oracle, postgres, mssql, db2, sybase, enterprisedb, )

    [input] Enter the Hibernate dialect for your database [org.hibernate.dialect.MySQLDialect] [org.hibernate.dialect.MySQLDialect]

    [input] Enter the filesystem path to the JDBC driver jar [lib/mysql.jar] [lib/mysql.jar]

    [input] Enter JDBC driver class for your database [com.mysql.jdbc.Driver] [com.mysql.jdbc.Driver]

    [input] Enter the JDBC URL for your database [jdbc:mysql:///oc4j] [jdbc:mysql:///oc4j]

    [input] Enter database username [user] [user]

    [input] Enter database password [password] [password]

    [input] skipping input as property hibernate.default_schema.new has already been set.
    [input] Enter the database catalog name (it is OK to leave this blank) [] []

    [input] Are you working with tables that already exist in the database? [y]  ([y], n, )

    [input] Do you want to drop and recreate the database tables and data in import.sql each time you deploy? [n]  (y, [n], )

    [propertyfile] Updating property file: /home/pmuir/workspace/jboss-seam/seam-gen/build.properties
    [echo] Installing JDBC driver jar to JBoss server
    [echo] Type 'seam new-project' to create the new project

BUILD SUCCESSFUL

Type ./seam new-project to create your project and cd to the newly created project.

Type ./seam generate-entities to run create the entities, the Seam Application Framework classes and the relevant views.

We now need to make some changes to the generated project. Let's start with the configuration files:

resources/META-INF/persistence-dev.xml
  • Alter the jta-data-source to be jdbc/__oc4jExample (and use this as the jndi-name when creating the data source in data-sources.xml).

  • Add the properties (described above):

    <property name="hibernate.query.factory_class"
       value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory" />
    <property name="hibernate.transaction.manager_lookup_class"
       value="org.hibernate.transaction.OrionTransactionManagerLookup" />
    <property name="hibernate.transaction.flush_before_completion" 
       value="true"/>
    <property name="hibernate.cache.provider_class" 
       value="org.hibernate.cache.HashtableCacheProvider"/>
  • Remove the JBoss AS specific method of exposing the EntityManagerFactory:

    <property 
     name="jboss.entity.manager.factory.jndi.name" 
     value="java:/oc4j-exampleEntityManagerFactory">
  • You'll need to alter persistence-prod.xml as well if you want to deploy to OC4J using the prod profile.

resources/META-INF/jboss-app.xml

You can delete this file as we aren't deploying to JBoss AS (jboss-app.xml is used to enable classloading isolation in JBoss AS)

resources/*-ds.xml

You can delete these file as we aren't deploying to JBoss AS (these files define datasources in JBoss AS, in OC4J you have to edit the master data-sources.xml file)

resources/WEB-INF/components.xml
  • Enable container managed transaction integration - add the <transaction:ejb-transaction /> component, and it's namespace declaration xmlns:transaction="http://jboss.com/products/seam/transaction"

  • Alter the jndi-pattern to java:comp/env/oc4j-example/#{ejbName}/local

  • We want to use a Seam Managed Persistence Context in our application. Unfortunately OC4J doesn't expose the EntityManagerFactory in JNDI, but Seam provides a built-in manager component:

    <persistence:entity-manager-factory
     auto-create="true" 
     name="oc4jEntityManagerFactory"
     persistence-unit-name="oc4j-example" />

    We then need to tell Seam to use it, so we alter the managed-persistence-context injecting the Entity Manager Factory:

    <persistence:managed-persistence-context
     name="entityManager"
     auto-create="true"
     entity-manager-factory="#{oc4jEntityManagerFactory}" />
                    
resources/WEB-INF/web.xml

You need to declare all your EJBs here. Remember to include the Seam container managed transaction integration:

<ejb-local-ref>
   <ejb-ref-name>
      oc4j-example/EjbSynchronizations/local
   </ejb-ref-name>
   <ejb-ref-type>Session</ejb-ref-type>
   <local>
      org.jboss.seam.transaction.LocalEjbSynchronizations
   </local>
   <ejb-link>EjbSynchronizations</ejb-link>
</ejb-local-ref>
build.xml
Change the default target to archive (we aren't going to cover automatic deployment to OC4J).

Now, lets add in the extra dependencies:

  • Hibernate —

    • Copy the jars from hibernate/lib directory in the Seam distribution oc4j-example/lib: cp ../jboss-seam/hibernate/lib/*.jar lib/

    • Alter the build.xml to include them in the ear - add these includes underneath the other libraries being copies:

      <include name="lib/hibernate-annotations.jar" />
      <include name="lib/hibernate-entitymanager.jar" />
      <include name="lib/hibernate3.jar" />
      <include name="ejb3-peristence.jar" />
      <include name="lib/jboss-archive-browsing.jar" />
      <include name="lib/jboss-common.jar" />
  • thirdparty-all.jar — alter the build.xml to include it - add this include:

    <include name="lib/thirdparty-all.jar" />
  • antlr-2.7.6.jar — alter the build.xml to include it - add this include:

    <include name="lib/antlr-*.jar" />
  • As we are using Drools to provide Seam Security rules, we need to add in Eclipse JDT compiler (you don't need this on JBoss AS; again this is due to OC4J's classloading):

    • cp ../jboss-seam/seam-gen/lib/org.eclipse.jdt.core*.jar lib/
    • Alter the build.xml to include them in the ear:

      <include name="lib/org.eclipse.jdt.core*.jar" />

You should end up with something like:

<fileset dir="${basedir}">
   <!-- other libraries added by seam-gen -->
   <include name="lib/hibernate-annotations.jar" />
   <include name="lib/hibernate-entitymanager.jar" />
   <include name="lib/hibernate3.jar" />
   <include name="lib/jboss-archive-browsing.jar" />
   <include name="lib/jboss-common.jar" />
   <include name="lib/thirdparty-all.jar" />
   <include name="lib/antlr-*.jar" />
   <include name="lib/org.eclipse.jdt.core*.jar" />
</fileset>

Finally, lets link our User entity into Seam Security (we have a User table with a username column and a password column). We're going to make our authentictor a Stateless Session Bean (OC4J is a EJB3 container after all!):

    • Add the @Stateless annotation.

    • Rename the class to AuthenticatorAction

    • Create an interface called Authenticator which AuthenticatorAction implements (EJB3 requires session beans to have a local interface). Annotate the interface with @Local, and add a single method with same signature as the authenticate in AuthenticatorAction.

    @Name("authenticator") @Stateless public class
                AuthenticatorAction implements Authenticator {
    @Local public interface Authenticator { 
      public boolean authenticate(); 
    }
  1. Use @PersistenceContext to inject an EntityManager:

    @PersistenceContext private EntityManager entityManager;
  2. Implement authenticate:

    public boolean authenticate() {
       List &lt;User&gt; users = entityManager .createQuery("select u from User u where 
       u.username = #{identity.username} and 
       u.password = #{identity.password}") .getResultList();
       if (users.size() == 1) {
          identity.addRole("admin"); 
          return true; 
       } else {
          return false; 
       } 
    }
  3. And then add the EJB3 reference to web.xml:

    <ejb-local-ref>
       <ejb-ref-name>
          oc4j-example/AuthenticatorAction/local
       </ejb-ref-name>
       <ejb-ref-type>Session</ejb-ref-type>
       <local>
          org.jboss.seam.tutorial.oc4j.action.Authenticator
       </local>
       <ejb-link>AuthenticatorAction</ejb-link>
    </ejb-local-ref>

Now you can go on and customize your application.

26.3.1. OC4J Deployment Descriptors for the seam-gen'd application

To deploy your application use the deployment instructions above in conjunction with these deployment descriptors:

$ORACLE_HOME/j2ee/home/config/data-sources.xml
<managed-data-source
   connection-pool-name="oc4j-example-connection-pool"
   jndi-name="jdbc/__oc4jExample"
   name="oc4j-example-managed-data-source" />
<connection-pool
   name="oc4j-example-connection-pool">
   <connection-factory
      factory-class="com.mysql.jdbc.Driver"
      user="username" 
      password="password"
      url="jdbc:mysql:///oc4j" />
</connection-pool>
$ORACLE_HOME/j2ee/home/config/server.xml
<application name="oc4j-example"
 path="../../home/applications/oc4j-example.ear"
 parent="default"
 start="true" />
$ORACLE_HOME/j2ee/home/config/default-web-site.xml
<web-app application="oc4j-example"
 name="oc4j-example" 
 load-on-startup="true"
 root="/oc4j-example" />