Chapter 26. Seam on OC4J

OC4J(用于Java的Oracle容器)11g(目前是一个“技术预览”版本)是Oracle的JEE 5应用服务器。 我们将从Seam自带的酒店预订(Hotel Booking)的示例应用程序开始,来了解它的构建和部署,接着了解如何部署seam-gen生成的工程。 这个工程将集成Seam、RichFaces Ajax和组件、Seam Security (包含Drools)、Facelets和Hibernate提供的JPA。

本节要求你使用OC4J 11g技术预览版(不是OC4J 10g)。你可以从 http://www.oracle.com/technology/tech/java/oc4j/11/ 下载OC4J 11g。

26.1. jee5/booking 实例

jee5/booking 实例基于一个(运行在JBoss AS中的)酒店预订的例子。 它是被设计为Glassfish开箱即用,但是可以很容易地构建它,并运行在OC4J上。

26.1.1. 预订酒店实例的依赖包

首先,让我们来看一下预订实例的依赖包。有了这方面的知识,我们就可以了解由于OC4J的引入,带来了哪些额外的依赖包。

  • jboss-seam.jar — 我们把这个声明为一个EJB3模块(为什么呢?Seam需要能够和容器管理事务CMT交互;它实现为一个EJB3的有状态会话Bean。)

  • jboss-el.jar

  • jboss-seam-ui.jar — Seam的JSF Control,依赖Apache的commons-beanutils

  • jboss-seam-debug.jar

  • jsf-facelets.jar

  •  richfaces-api.jar — 它需要Apache commons-digester和commons-beanutils

  • richfaces-impl.jarrichfaces-ui.jar — 需要Apache commons-digester和commons-beanutils

26.1.2. OC4J需要的额外依赖包

  • Hibernate — 当然,我们决定使用Hibernate作为JPA提供者(而不是OC4J中带有的TopLink Essentials)

    为了使用Hibernate作为JPA提供者,你需要三个jar包(hibernate3.jarhibernate-annotations.jarhibernate-entitymanager.jar)和他们的依赖包(jboss-common.jarjboss-archive-browsing.jarejb3-persistence.jar)。 你可以在Seam发行包的 hibernate/lib 目录中找到这些jar包。

  • thirdparty-all.jar — 是Seam所依赖的第三方库的集合(如javassist)

在大多数应用程序服务器中运行Seam(例如JBoss AS或是Glassfish),你只需要包含一些你真正需要的依赖包 (例如:如果你使用Seam Text,你就需要包含ANTLR);但是,在OC4J中,由于它“奇异的”classloading,你必须始终包含它们:

  • antlr-2.7.6.jar — Seam Text需要的。(在本例中没有用到)。

  • jbpm-jpdl.jar — Seam的JBPM集成所需要的(在本例中没有用到)。

  • Drools — Seam Security所需要的。尽管我们没有通过Drools使用Seam Security,但还是必须包含它。 Drools由5个jar包组成 - drools-core-4.0.0.jardrools-compiler-4.0.0.jarjanino-2.5.7.jarmvel14-1.2rc1.jarantlr-runtime-3.0.jar。在本例中没有用到Drools集成。

26.1.3. 配置文件的改变

只有一些地方需要更改:

web.xml

你需要在 web.xml 中声明所有的EJB。这是许多JEE 5应用程序服务器的一个无聊要求 - 例如OC4J和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

你需要提供一个正确的配置给你的JPA实现。我们是使用Hibernate,由于OC4J还是绑定老的ANTLR,我们需要使用可选的查询工厂,我们也想使用OC4J的事务管理器:

<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. 构建 jee5/booking 实例

  1. 在工程中修改以下文件:

    • build.xml — 反注释OC4J相关的依赖包

    • resources/META-INF/persistence.xml — 注释掉Glassfish属性,反注释OC4J的属性

  2. 通过运行 ant 来构建demo应用程序。最终构建成 dist/jboss-seam-jee5.ear

  3. hsqldb.jar 复制到OC4J中: cp ../../seam-gen/lib/hsqldb.jar $ORACLE_HOME/j2ee/home/applib/ (由于OC4J并没有嵌入式的数据库,因此我们决定使用HSQLDB)。

26.2. 部署Seam应用程序到OC4J中

这个迷你型的教程(有点乏味)描述了将一个JEE 5应用程序部署到OC4J中所需的步骤。 假设你正在部署使用了嵌入式的hsqldb数据库的 jee5/booking 实例。 为了部署其他的应用程序,你需要更改数据源名称和应用程序名称。

  1. 下载和解压OC4J

  2. 请确认你已经设置了 $JAVA_HOME$ORACLE_HOME 作为环境变量 ($ORACLE_HOME这个是指解压后OC4J的目录名称)。 想了解更多有关安装OC4J的信息,请参考OC4J发布包中的 Readme.txt

  3. 编辑OC4J数据源 $ORACLE_HOME/j2ee/home/config/data-sources.xml,并在 <data-sources> 中增加

    <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>

    persistence.xml 里,jndi-name 被用作 jta-data-source

  4. 编辑 $ORACLE_HOME/j2ee/home/config/server.xml ,在 <application-server>中增加

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

    为了让事情简单些,就采用你给项目所用的相同名字。

  5. 编辑 $ORACLE_HOME/j2ee/home/config/default-web-site.xml,在 <web-site> 中增加

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

    root 就是你将输入到Web浏览器用来访问应用程序的上下文路径。

  6. 将应用程序复制到OC4J:cp dist/jboss-seam-jee5.ear $ORACLE_HOME/j2ee/home/applications/

  7. 启动OC4J:$ORACLE_HOME/bin/oc4j -start

    如果是第一次启动OC4J,您将被要求设置管理员密码

  8. http://localhost:8888/seam-jee5 中检验应用程序

  9. 你可以通过在服务器运行的控制台上按下 CTRL-C 来停止服务器。

26.3.  将一个使用 seam-gen 创建的应用程序部署到OC4J中。

接下来的说明假设你正在使用命令行的方式和一个简单的文本编辑器,当然你也可以使用自己熟悉的IDE - seam-gen 项目,同时支持Eclipse和Netbeans。

我们从使用 seam-gen 来创建一个非常简单的应用程序开始。 seam-gen 使用Hibernate Tools 来将数据库的Scheam反向工程为JPA的实体Bean; 它也可以生成Seam应用程序的框架组件和JSF的CRUD(创建、读取、更新和删除)视图。 这个教程使用MySQL数据库(当然你也可以使用任何其他的数据库,更改相应的SQL就可以了);安装、配置和运行MySQL,然后创建数据库,并带有一些范例数据。

接下来,在Seam的目录中运行 ./seam setup

> ./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

输入 ./seam new-project 来创建你的工程,并 cd 进入到刚创建的工程里。

输入 ./seam generate-entities 来运行创建的实体,Seam应用程序框架类和相关的视图。

我们现在需要对生成的工程进行一些修改。让我们从配置文件开始:

resources/META-INF/persistence-dev.xml
  • 更改 jta-data-sourcejdbc/__oc4jExample (当在 data-sources.xml 创建数据源时,用这个作为 jndi-name

  • 添加上述的属性:

    <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"/>
  • 删除JBoss AS中暴露EntityManagerFactory的方法:

    <property
     name="jboss.entity.manager.factory.jndi.name"
     value="java:/oc4j-exampleEntityManagerFactory">
  • 同样地,如果你想使用先前的文件部署到OC4J中,就需要更改 persistence-prod.xml

resources/META-INF/jboss-app.xml

你可以删除这个文件,由于我们没有部署到JBoss AS中(jboss-app.xml 用于在JBoss AS中激活classloading隔离)。

resources/*-ds.xml

你可以删除这些文件,由于我们也没有部署到JBoss AS中 (在JBoss AS中这些文件定义了数据源,在OC4J中,你必须编辑主要的 data-sources.xml 文件。)

resources/WEB-INF/components.xml
  • 激活CMT(container managed transaction:容器管理事务—)集成 - 添加 <transaction:ejb-transaction /> 组件, 和它的命名空间声明 xmlns:transaction="http://jboss.com/products/seam/transaction"

  • jndi-pattern 更改为 java:comp/env/oc4j-example/#{ejbName}/local

  • 我们想在我们的应用程序当中使用Seam的MPC(Managed Persistence Context:持久化上下文管理)。 不幸的是,OC4J没有以JNDI形式暴露EntityManagerFactory,但是Seam提供了一个内置的管理器组件:

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

    接下来我们需要告知Seam来使用它,因此我们更改注入实体管理器工厂的 managed-persistence-context

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

在这里,你需要声明你所有的EJB。记住包括Seam的容器管理事务集成:

<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
改变默认的target来完成(我们不打算覆盖OC4J的自动部署)。

现在,让我们来添加一些额外的依赖包:

  • Hibernate —

    • 将Seam发行包中 hibernate/lib 目录下所有的jar文件拷贝到 oc4j-example/lib 目录下: cp ../jboss-seam/hibernate/lib/*.jar lib/

    • 更改 build.xml,将它们包含在ear包中 - 要添加的这些包括在其他备份库下方:

      <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 — 更改 build.xml 包含它 - 添加这个:

    <include name="lib/thirdparty-all.jar" />
  • antlr-2.7.6.jar — 更改 build.xml 包含它 - 添加这个:

    <include name="lib/antlr-*.jar" />
  • 由于我们使用Drools来提供Seam Security规则,我们需要添加在Eclipse JDT编译器中(在JBoss AS中你不需要这个;再说一次,这是由于OC4J的classloading造成的):

    • cp ../jboss-seam/seam-gen/lib/org.eclipse.jdt.core*.jar lib/
    • 更改 build.xml 将它们包含在ear包中:

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

你应该类似于下面这样结束:

<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>

最后,让我们将 User 实体连接到Seam Security中 (我们有一个包含username 列和 password 列的 User 表)。 我们将使验证器变成一个无状态会话Bean(毕竟OC4J也是EJB3的容器!):

    • 添加 @Stateless 注解。

    • 将类重命名为 AuthenticatorAction

    • 创建一个具名为 Authenticator 的接口,并由 AuthenticatorAction 来实现这个接口(EJB3要求会话Bean要有一个本地接口)。 使用 @Local 来注解这个接口,然后增加一个与AuthenticatorAction 中的 authenticate 方法同样签名的方法。

    @Name("authenticator") @Stateless public class
                AuthenticatorAction implements Authenticator {
    @Local public interface Authenticator {
      public boolean authenticate();
    }
  1. 使用 @PersistenceContext 注解来注入EntityManager:

    @PersistenceContext private EntityManager entityManager;
  2. 实现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. 然后在 web.xml 中添加EJB3的引用:

    <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>

现在你可以继续并定制你自己的应用程序了。

26.3.1. seam-gen之类的应用程序的OC4J部署描述符

为了使用上述部署指令来部署你的应用程序,要结合使用这些部署描述符:

$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" />