JBoss AS 5 和 GlassFish 支持已添加到 seam-gen

发布者:    |       Seam

我终于完成了添加 GlassFish 支持到 seam-gen 的修改(文档见这里这里),并将其整合到 Seam 项目中(JBSEAM-1619)。在集成更改的过程中,我设法关闭了剩余的一些差距,并为 JBoss AS 5 添加了支持!

让我告诉你,调整配置和构建以支持所有十二种部署变体(GlassFish V2/V3 的 EAR 和 WAR 存档、JBoss AS 4.2.x 和 JBoss AS 5.0.x 的爆炸式和打包格式)是一项相当大的任务;(如果你考虑我为 RichFaces 和 ICEfaces 项目添加了支持,则这个数字要翻倍)。但令人惊讶的是,现在它们都能直接工作(除了部署 EAR 项目到 GlassFish 时需要进行的少量配置更改)。

持久性悖论

最大的挑战是持久性单元配置。这源于持久性单元的打包要求在 EAR 和 WAR 之间有所不同。更复杂的是,应用程序服务器在解释和强制执行这些要求方面存在差异。我想花一些时间解释这种情况以及我是如何找到平衡的,因为这对理解项目构建现在的工作方式很重要。

让我们先回顾一下事情原来的设置。JPA 规范中有一个期望,即在 Java EE 环境中,应用程序服务器将引导在 META-INF/persistence.xml 中定义的持久性单元。当然,如果这是使用 JPA 的唯一方式,那就相当有限了。因此,提供了一个 API,允许应用程序在 Java SE 环境中(或在容器不支持此期望的情况下)手动加载持久性单元。可以理解的是,随着供应商努力使他们的应用程序服务器符合 Java EE 5(第一个包含 JPA 的规范),这一期望需要一些时间才能得到满足。

对于不支持加载持久化单元的容器,无论是特定存档类型(EAR或WAR)还是一般情况,Seam可以介入并接管这项任务。这就是以下组件定义的目的,该定义在Seam组件描述符(即components.xml)中定义。

<persistence:entity-manager-factory name="entityManagerFactory" persistence-unit-name="example"/>

以下属性值persistence-unit-name属性与在META-INF/persistence.xml中定义的持久化单元之一名称匹配。

在JBoss AS 5之前的版本中,JBoss AS不支持在WAR中加载持久化单元,因此需要将此任务委托给Seam。对于任何版本的Tomcat也是如此。GlassFish更符合规范。它将加载持久化单元,但前提是它已按如下方式在WEB-INF/web.xml中的持久化单元引用中声明:

<persistence-unit-ref>
    <persistence-unit-ref-name>example/pu</persistence-unit-ref-name>
    <persistence-unit-name>example</persistence-unit-name>
</persistence-unit-ref>

持久化单元引用还有另一个目的。在持久化单元加载后,EntityManagerFactory实例将使用以下属性的值绑定到JNDIpersistence-unit-ref-namejava:comp/env命名空间(在此示例中,完整的JNDI名称为java:comp/env/example/pu)。在版本5之前,JBoss AS不支持这种JNDI绑定。为了提供一个临时的解决方案,Hibernate可以提供在私有JBoss AS命名空间下将EntityManagerFactory绑定到JNDI的功能java:/。如果META-INF/persistence.xml中存在以下属性,则此功能被激活

<property name="jboss.entity.manager.factory.jndi.name" value="java:/exampleEntityManagerFactory"/>

JBoss AS 5支持将EntityManagerFactory绑定到JNDI的合规方法,但有一个副作用。它会自动加载在META-INF/persistence.xml中定义的所有持久化单元,无论它们是否在持久化单元引用中声明。这个事实让很多人感到困惑,并且是现在在JBoss AS 5上大多数Seam部署失败的根本原因。总之,最好只定义你打算加载的持久化单元,然后让容器加载它们。

到目前为止,讨论仅适用于WAR部署。对于EAR来说,事情变得更加复杂。技术上,持久化单元(描述符和类)应打包在其自己的JAR文件中,然后将其打包在WAR的WEB-INF/lib目录下,或者直接打包在EAR中。然而,持久化单元也可以打包在EJB JAR中。无论哪种方式,你都必须非常清楚自己在做什么,才能正确设置所有引用,以便正确加载持久化单元,并将其绑定到JNDI,以便WAR可以访问它。当应用服务器按预期工作,以下持久化单元配置就足够了

<persistence-unit-ref>
    <persistence-unit-ref-name>example/pu</persistence-unit-ref-name>
    <persistence-unit-name>../example.jar#example</persistence-unit-name>
</persistence-unit-ref>

注意持久化单元名称现在包括包含持久化单元描述符和类的JAR文件在EAR中的相对位置。由于某种原因,GlassFish不理解此配置,因此我已包含一个临时的解决方案(请参阅seam-gen EAR项目的resources/WEB-INF/web.xml中的说明)。

为了总结各种变体,我已整理了一个矩阵,说明根据存档类型和环境处理持久化单元以及它们在seam-gen项目中如何加载。

WAR EAR
JBoss AS 4.2 JBoss AS 5.0 GlassFish JBoss AS 4.2 JBoss AS 5.0 GlassFish
Seam 容器启动 容器启动 容器启动 容器启动 容器启动
依赖于持久化单元引用
JNDI绑定机制 不适用 两者 标准 专有 两者 标准
自动检测实体

正如您所想象的,处理所有这些变化需要来自构建的大量支持。幸运的是,我能够利用Seam的一个特性来满足这些需求。您可以在Seam组件描述符中使用Ant风格的占位符,Seam将使用类路径根目录下的components.properties文件中定义的替换值来替换它们。但是,替换值也必须是动态的,以支持各种环境。因此,我在该文件中也使用了Ant风格的占位符,这些占位符由Ant构建替换。因此,这里发生了一个两阶段替换。下面是components-dev.properties文件的内容

jndiPattern=@ejbJndiPattern@
debug=true
seamBootstrapsPu=@seamBootstrapsPu@
seamEmfRef=@seamEmfRef@
puJndiName=@puJndiName@

下面是从这个模板生成的components.properties文件,用于针对名为example的EAR项目中的JBoss AS 5构建

jndiPattern=example/#{ejbName}/local
debug=true
seamBootstrapsPu=false
seamEmfRef=#{null}
puJndiName=java:comp/env/example/pu

下面是为针对名为example的WAR项目中的JBoss AS 4构建准备的components.properties文件

jndiPattern=example/#{ejbName}/local
debug=true
seamBootstrapsPu=true
seamEmfRef=#{entityManagerFactory}
puJndiName=#{null}

这些占位符被应用于Seam组件描述符中的以下两个组件定义

<persistence:managed-persistence-context name="entityManager" auto-create="true"
                       entity-manager-factory="@seamEmfRef@"
                   persistence-unit-jndi-name="@puJndiName@"/>

<persistence:entity-manager-factory name="entityManagerFactory"
                   persistence-unit-name="@projectName@"
                               installed="@seamBootstrapsPu@"/>

嗯!这涵盖了JPA配置。有关Java EE和Seam中持久化单元配置的更多详细信息,请查看《Seam in Action》第9章。现在我想简要提及支持部署到JBoss AS 5和GlassFish的新目标。

JBoss AS 5和GlassFish目标

与之前一样,seam-gen项目的构建默认支持JBoss AS。但现在,我可以通过检查JBoss AS 5安装目录(从jboss.home解析)来支持JBoss 4和JBoss 5,以确定目标JBoss AS的版本。我只是检查JAR文件的存在。

GlassFish确实有一套自己的目标。所有与GlassFish相关的目标都位于项目根目录下的glassfish-build.xml文件中。此文件被导入到主Ant构建中。所有GlassFish目标都以前缀gf-开头gf-并紧密地反映了JBoss AS的目标。还有控制GlassFish服务器的目标。以下是新目标的简要总结,这些目标在glassfish-readme.txt文件中有详细文档

gf-start - Starts GlassFish
gf-debug - Starts GlassFish in debug mode
gf-stop - Stops GlassFish
gf-reboot - Restarts GlassFish
gf-deploy-datasource - Deploys the datasource and connection pool to GlassFish
gf-explode - Deploys the exploded archive to GlassFish (restarts application if already deployed)
gf-hotdeploy - Hot deploys Java classes, Seam components, and web resources
gf-deploy - Deploys the packaged archive to GlassFish
gf-undeploy - Undeploys the exploded or packaged archive from GlassFish
gf-stage - Prepares an exploded archive targeting GlassFish
gf-archive - Prepares a packaged archive targeting GlassFish
gf-prepare - Prepares GlassFish for a seam-gen project deployment (calls gf-deploy-hibernate)
gf-deploy-hibernate - Deploys Hibernate as a JPA provider to GlassFish

您不需要做任何事情就可以启用对GlassFish的支持。它与对JBoss AS的支持一起提供。我在工作于GlassFish支持的过程中终于关闭了一个差距,那就是explode EAR的部署。GlassFish一直因为包含EJBs的jboss-seam.jar而窒息,因此被认为是EJB模块。事实证明,GlassFish不支持打包和展开存档的混合。因此,当您部署展开的EAR时,还需要展开jboss-seam.jar文件,构建会为您处理。

我必须做出的唯一权衡是,在GlassFish中期望Hibernate是JPA提供程序(注意部署Hibernate的目标)。切换到另一个JPA提供程序并不难,但可以说,Seam与Hibernate配合最好。

增长的Ivy

Seam项目在库管理方面以其比较老式而闻名。所有JAR文件都只是被丢弃到lib目录中,并在需要时从中挑选。这使得共享seam-gen项目非常困难,因为它太大。我在这篇条目中解释了如何使用Ivy来管理seam-gen项目的依赖项。seam-gen现在将该配置作为扩展提供。要启用它,您只需运行以下seam-gen目标

seam add-ivy

目前,仅支持RichFaces项目,但支持ICEfaces项目只需对依赖项进行一些更改。

总结

现在您可以看到,seam-gen不再局限于JBoss世界。对于早期采用者来说,JBoss AS 5——JBoss的骄傲和喜悦——现在得到了支持。将Ivy添加到项目中有助于使其更加轻量级,提供了一种轻松下载源JAR的方式,并有助于记录正在使用的JAR版本。只需从SVN检查Seam,构建并享受即可!


回到顶部