迁移指南

JBoss 企业级应用程序平台 6.1

适用于 JBoss 企业版应用程序平台 6

版 3

Sande Gilda

Darrin Mison

David Ryan

摘要

本书是从以前的 JBoss 企业级应用程序平台移植应用程序的指南。

Preface

第 1 章 简介

1.1. 关于移植指南

JBoss 企业级应用程序平台 6 是一个快速的、轻量级的、功能强大的 Java EE 6 规格的实现。它的架构基于模块化服务容器并按需要启用服务。由于这个新的架构,运行在 JBoss EAP 5 上的应用程序需要进行修改以运行在 JBoss EAP 6 上。
本指南的目的是为在 JBoss EAP 6 上成功运行和部署 JBoss EAP 5.1 应用程序需要进行的修改编写文档。它提供了如何解决部署和运行时问题的信息,以及如何防止程序行为发生变化的信息。这是移植到新平台的第一步。一旦应用程序成功地部署和运行了,你就可以开始计划升级单独的组件以使用 JBoss EAP 6 的新功能和特征。

第 2 章 准备移植

2.1. 准备移植

既然应用服务器的结构和以前不一样,在试图移植应用程序之前,你可能想进行一些研究和规划。
  1. 查看 JBoss EAP 6 里的新功能以及不同之处

    这个版本里已经修改了大量的内容,可能会影响到 JBoss EAP 5 应用程序的开发。这包括对文件目录结构、脚本、部署配置、类加载和 JNDI 查找的修改。详情请参考 第 2.2 节 “查看 JBoss EAP 6 里的新功能以及不同之处”
  2. 阅读关于开发应用程序起步的文档

    请务必阅读《JBoss EAP 6 开发指南》里的《开发应用程序起步》章节:https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/。它包含下列重要的信息:
    • Java EE 6
    • 新的模块化类加载系统
    • 文件结构的改变
    • 如何下载和安装 JBoss 企业版应用程序平台 6
    • 如何下载和安装 JBoss Developer Studio
    • 如何为你的开发环境配置 Maven
    • 如何下载和运行产品附带的 Quickstart 例程。
  3. 分析和理解你的应用程序

    每个应用程序都是独特的,在移植前你必须彻底了解现有应用程序的相关组件和架构。

重要

在修改应用程序之前,请确保以进行了备份。

2.2. 查看 JBoss EAP 6 里的新功能以及不同之处

简介

下面是 JBoss EAP 6 和以前版本的显著不同的列表。

基于模块的类加载
在 JBoss EAP 5 里,类加载架构是层次结构的。而在 JBoss EAP 6 里,类加载基于 JBoss 模块。这提供了真正的应用程序隔离,隐藏了服务器实现类,且只加载应用程序所需的类。类加载并行具有更高的性能。针对 JBoss EAP 5 编写的应用程序必须进行修改以指定模块依赖关系,且在某些情况下需要重新打包归档文件。关于更多的信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的《类加载与模块》章节里的《类加载与模块概述》
域管理
在 JBoss EAP 6 里,服务器可以作为独立服务器或者以受管域运行。在受管域里,你可以一次配置整个服务器组,从而在整个服务器网络里保持配置的同步。虽然这应该不会影响为以前版本构建的应用程序,但它可以简化对多个服务器的部署的管理。关于更多的信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 管理和配置指南》里的《关于受管域》章节。

注意

下面的 JBoss 企业级产品里不支持域模式:
  • JBoss Portal Platform 6
部署配置
独立服务器和受管域
JBoss EAP 5 使用基于配置集的部署配置。这些配置集位于 EAP_HOME/server/ 目录。应用程序经常会包含多个配置文件以用于安全性、数据库、资源适配器和其他配置。在 JBoss EAP 6 里,部署配置是通过使用一个文件实现的。这个文件用来配置部署使用的所有服务和子系统。独立服务器是使用 EAP_HOME/standalone/configuration/standalone.xml 文件来配置的。对于运行在受管域里的服务器,服务器使用 EAP_HOME/domain/configuration/domain.xml 文件来配置。包含在多个 JBoss EAP 5 配置文件里的信息必须移植到新的单个配置文件里。
部署顺序
JBoss EAP 6 的部署使用快速的、并发的初始化,从而提高了性能和效率。在多数情况下,应用服务器能够自动提前决定依赖关系并选择最有效的部署策略。然而,由多个模块组成的 JBoss EAP 5 的应用程序部署为 EAR 并使用传统的 JNDI 查找而不是 CDI 注入或 resource-ref 条目,这样就可能要求修改相关配置。
目录结构和脚本
如之前提到的,JBoss EAP 6 不再使用基于配置集的部署配置,所以不再有 EAP_HOME/server/ 目录了。用于独立服务器的配置文件现在位于 EAP_HOME/standalone/configuration/ 目录而部署位于 EAP_HOME/standalone/deployments/ 目录。而对于运行在受管域里的服务器,配置文件位于 EAP_HOME/domain/configuration/ 目录而部署位于 EAP_HOME/domain/deployments/ 目录里。
在 JBoss EAP 5 里,Linux 脚本 EAP_HOME/bin/run.sh 或 Windows 脚本 EAP_HOME/bin/run.bat 用来启动服务器。而在 JBoss EAP 6 里,服务器启动脚本依赖于你运行服务器的方式。Linux 脚本 EAP_HOME/bin/standalone.sh 或 Windows 脚本 EAP_HOME/bin/standalone.bat 用于启动独立服务器。Linux 脚本 EAP_HOME/bin/domain.sh 或Windows 脚本 EAP_HOME/bin/domain.bat 用于启动受管域。
JNDI 查找
JBoss EAP 6 现在使用标准化的可移植的 JNDI 命名空间。为 JBoss EAP 5 编写的使用 JNDI 查找的应用程序必须进行修改以符合新的标准化 JNDI 命名空间格式。关于 JNDI 命名语法的详情,请参考 第 3.1.8.2 节 “可移植的 JNDI 命名语法”
关于其他的信息,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的《开发应用程序起步》章节里的《JBoss EAP 6 里新的和已修改的特征》“。

第 3 章 移植你的应用程序

3.1. 多数应用程序要求的修改

3.1.1. 复查多数程序所要求的修改

JBoss EAP 6 里的类加载和配置修改将影响几乎所有的应用程序。JBoss EAP 6 也使用标准的可移植的 JNDI 语法。这些修改将影响多数程序,所以我们推荐你在移植应用程序前先复查下面的信息。

3.1.2. 类加载的修改

3.1.2.1. 根据类加载的变化更新应用程序

在 JBoss EAP 6 里,模块化类加载是一个明显的变化,它将影响几乎所有的程序。在移植应用程序之前,请先查看下面的信息。
  1. 首先,查看你的应用程序的软件包结构及其依赖关系。详情请参考 第 3.1.2.3 节 “根据类加载的修改更新应用程序的依赖关系”
  2. 如果你的应用程序使用了日志,你需要制定正确的模块依赖关系。请查看 第 3.1.4.1 节 “修改日志依赖关系” 里的相关细节。
  3. 由于模块化类加载的修改,你可能需要修改 EAR 或 WAR 的软件包结构。详情请参考 第 3.1.5.1 节 “修改 EAR 和 WAR 的打包”

3.1.2.2. 了解模块依赖关系

总结

模块只能访问自己的类,以及它具有显性或隐性依赖关系的任何模块上的类。

过程 3.1. 了解模块依赖关系

  1. 了解隐性依赖关系

    服务器里的部署者隐性地自动添加一些常用的模块依赖关系,如javax.apisun.jdk。这使得类在运行时对于部署可见,并让开发人员不用显性地添加依赖关系。关于如何和何时添加这些隐性的依赖关系,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的『类记载和模块』章节的『隐性模块依赖关系』
  2. 理解显性依赖关系

    对于其他类,模块必须显性地制定,否则缺失的依赖关系会导致部署或运行时错误。如果缺失了依赖关系,你可以在服务器日志里看到 ClassNotFoundExceptionsNoClassDefFoundErrors 跟踪信息。如果多于一个模块加载了相同的 JAR 或一个模块加载的类扩展了不同模块加载的类,你会在服务器日志里看到 ClassCastExceptions。要显性地制定依赖关系,请修改 MANIFEST.MF 或创建一个 JBoss 专有的部署描述符文件 jboss-deployment-structure.xml。关于模块依赖关系的更多信息,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/《JBoss EAP 6 开发指南》里的『开发应用程序起步』章节的『类记载和模块概述』

3.1.2.3. 根据类加载的修改更新应用程序的依赖关系

总结

JBoss EAP 6 里的类加载和以前版本的很不一样。类加载现在基于 JBoss Modules 项目。它不是一个加载所有 JAR 到普通 Class path 里的单一的、层级的类加载器,而是每个库都成为一个模块,链接它所依赖的模块。EAP 6 里的部署也是模块,它们不能访问应用服务器里 JAR 中定义的类,除非在这些类上显性地定义了依赖关系。应用服务器里定义的一些模块依赖关系是自动设置的。例如,如果你在部署一个 Java EE 应用程序,Java EE API 的依赖关系将被自动添加到你的模块里。关于自动添加的模块的完整列表,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》『类加载和模块』章节里的『隐性模块依赖关系』

任务

当你移植程序到 EAP 6 时,由于模块化类加载的变化,你可能需要执行一个或多个下列任务:

3.1.3. 配置文件的修改

3.1.3.1. 创建或修改控制 JBoss EAP 6.0 里类加载的文件

总结

由于 JBoss EAP 6 里对于使用模块化类加载的修改,你可能需要修改或创建一个或多个文件来添加依赖关系或阻止对自动依赖关系的加载。关于类加载和类加载次序的更多信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的『类加载与模块』章节。

下列文件用于控制 JBoss EAP 6 的类加载。
jboss-web.xml
如果你已经在 jboss-web.xml 文件里定义了一个 <class-loading> 元素,你需要删除它。JBoss EAP 5 里使用的这个行为现在是 EAP 6 里的默认类加载行为,所以不是必需的了。如果你没有删除这个元素,你会在服务器日志里看到一个 ParseError 和 XMLStreamException。
下面是 jboss-web.xml 文件里已注释的 <class-loading> 元素的例子。
<!DOCTYPE jboss-web PUBLIC
    "-//JBoss//DTD Web Application 4.2//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-web_4_2.dtd">
<jboss-web>  
<!-- 
    <class-loading java2ClassLoadingCompliance="false">
        <loader-repository>
            seam.jboss.org:loader=MyApplication
            <loader-repository-config>java2ParentDelegation=false</loader-repository-config>
        </loader-repository>
    </class-loading>
 -->
 </jboss-web>


MANIFEST.MF
手动编辑的
根据你的应用程序使用的组件或模块,你可能需要添加一个或多个以来关系到这个文件里。你可以添加它们为 Dependencies Class-Path 条目。
下面是开发人员编辑的 MANIFEST.MF 例子:
Manifest-Version: 1.0
Dependencies: org.jboss.logmanager
Class-Path: OrderManagerEJB.jar

如果你修改了这个文件,请确保在文件结尾包含一个新行符号。
用 Maven 生成的
如果你使用 Maven,你需要修改你的 pom.xml 文件以生成用于 MANIFEST.MF 的依赖关系。如果你的应用程序使用了 EJB 3.0,你可能在 pom.xml 文件里有如下内容:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-ejb-plugin</artifactId>
    <configuration>
        <ejbVersion>3.0</ejbVersion>
    </configuration>
</plugin>

如果 EJB 3.0 代码使用了 org.apache.commons.log,你需要在 MANIFEST.MF 文件里有这个以来关系。要生成这个以来关系,请添加 <plugin> 元素到 pom.xml 文件里:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-ejb-plugin</artifactId>
    <configuration>
        <ejbVersion>3.0</ejbVersion>
        <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
        </archive>
    </configuration>
</plugin>

在上面的例子里,src/main/resourcres/MANIFEST.MF 文件只需要包含以来关系条目:
Dependencies: org.apache.commons.logging
Maven 将生成完整的 MANIFEST.MF 文件:
Manifest-Version: 1.0
Dependencies: org.apache.commons.logging
jboss-deployment-structure.xml
这个文件是 JBoss 专有的部署描述符,它可以用来对类加载的细颗粒度控制。就像 MANIFEST.MF 一样,这个文件可以用来添加依赖关系。它也可以阻止添加自动依赖关系、定义额外的模块、修改 EAR 部署的隔离类加载行为,以及在模块里添加其他的资源根目录。
下面是一个 jboss-deployment-structure.xml 文件,它添加 JSF 1.2 模块的依赖关系且阻止了 JSF 2.0 模块的自动加载。
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
  <deployment>
      <dependencies>
        <module name="javax.faces.api" slot="1.2" export="true"/>
              <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
      </dependencies>
  </deployment>
  <sub-deployment name="jboss-seam-booking.war">
    <exclusions>
        <module name="javax.faces.api" slot="main"/>
              <module name="com.sun.jsf-impl" slot="main"/>
      </exclusions>
      <dependencies>
        <module name="javax.faces.api" slot="1.2"/>
              <module name="com.sun.jsf-impl" slot="1.2"/>
      </dependencies>
  </sub-deployment>
</jboss-deployment-structure>
关于这个文件的其他信息,请参考 第 3.1.3.2 节 “jboss-deployment-structure.xml”
application.xml
在以前的 JBoss EAP 版本里,你通过 jboss-app.xml 控制 EAR 里部署的顺序。现在不是这样了。Java EE6 规格在 application.xml 文件里提供了 <initialize-in-order> 元素来控制 EAR 里的 Java EE 模块的部署顺序。
在大多数情况下,你不许要指定部署顺序。如果你的应用程序使用了依赖关系注入和 resource-refs 来引入外部模块的组件,多数情况下都不要求 <initialize-in-order> 元素,因为应用服务器能够隐性地确定正确和优化的组件顺序。
让我们假设你有一个包含 myBeans.jarmyApp.war 位于 myApp.ear 内部的应用程序。 myApp.war@EJB 里的 servlet 从 myBeans.jar 注入一个 bean。在这个例子里,应用服务器懂得如何确保 EJB 组件在 servlet 启动之前可用,而你不需要使用 <initialize-in-order> 元素。
然而,如果这个 servlet 使用了如下的传统 JNDI 查找风格的远程引用来访问 bean,你可能需要指定模块的顺序。
init() {
  Context ctx = new InitialContext();
  ctx.lookup("TheBeanInMyBeansModule");
}
在这种情况下,服务器不能确定 EJB 组件处于 myBeans.jar 里,你需要强制 myBeans.jar 里的组件被初始化并在 myApp.war 里的组件之前启动。为此,你可以设置 <initialize-in-order> 元素为 true 并指定 application.xml 文件里的 myBeans.jarmyApp.war 的顺序。
下面是一个使用 <initialize-in-order> 元素来控制部署顺序的例子。 myBeans.jarmyApp.war 文件之前被部署。
<application xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="6"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
             http://java.sun.com/xml/ns/javaee/application_6.xsd">
    <application-name>myApp</application-name>
    <initialize-in-order>true</initialize-in-order>
    <module>
        <ejb>myBeans.jar</ejb>
    </module>
    <module>
        <web>
            <web-uri>myApp.war</web-uri>
            <context-root>myApp</context-root>
        </web>
    </module>
</application>
application.xml 文件的 schema 可以在这里找到:http://java.sun.com/xml/ns/javaee/application_6.xsd

注意

你应该意识到设置 <initialize-in-order> 元素为 true 会减慢部署速度。我们更倾向于使用依赖注入或 resource-refs 来定义正确的依赖关系,既然它们使容器在优化部署时更具灵活性。
jboss-ejb3.xml
jboss-ejb3.xml 部署描述符替换 jboss.xml 以覆盖和添加 Java EE 定义的 ejb3-jar.xml 里提供的功能。这个新文件和 jboss.xml 兼容,而目前的部署里已经忽略了jboss.xml
login-config.xml
login-config.xml 文件不再用于安全配置。现在安全性是在服务器配置文件的 <security-domain> 元素里配置。这个文件是 standalone/configuration/standalone.xml,如果你是在受管域里运行服务器, 则是 domain/configuration/domain.xml

3.1.3.2. jboss-deployment-structure.xml

jboss-deployment-structure.xml 是 JBoss EAP 6 的一个新的可选的部署描述符。这个部署描述符提供了对部署里的类加载的控制。
这个部署描述符的 XML schema 位于 EAP_HOME/docs/schema/jboss-deployment-structure-1_2.xsd

3.1.3.3. 新模块化类加载系统的软件包资源

总结

在以前的 JBoss EAP 版本里,WEB-INF/ 里面的所有资源都添加到了 WAR classpath。在 JBoss EAP 6 里,web 应用程序的 artifact 只从 WEB-INF/classesWEB-INF/lib 目录进行加载。没有在指定位置打包应用程序 artifact 会导致 ClassNotFoundException, NoClassDefError 或运行时错误。

要解决这些类加载的错误,你必须修改应用程序归档的结构或定义一个自定义模块。

修改资源打包
要使资源只对应用程序可用,你必须将属性文件、JAR 或其他具有 WAR 的 artifact 移至WEB-INF/classes/WEB-INF/lib/ 目录来进行绑定。详情请参考 第 3.1.3.4 节 “修改 ResourceBundle 属性的位置”
创建自定义模块
如果你想使自定义的资源对于运行在 JBoss EAP 服务器上的所有应用程序可用,你必须创建一个自定义的模块。关于这个途径的详情,请参考 第 3.1.3.5 节 “创建自定义模块”

3.1.3.4. 修改 ResourceBundle 属性的位置

总结

在以前的 JBoss EAP 版本里,EAP_HOME/server/SERVER_NAME/conf/ 目录位于 classpath 里且可为应用程序所用。要使属性为 EAP 6 里的应用程序的 classpath 所用,你必须将其打包至你的应用程序里。

过程 3.2. 

  1. 如果你在开发 WAR 归档,你必须将这些属性包裹至 WAR 的 WEB-INF/classes/ 目录里。
  2. 如果你想要 EAP 里所有组件都可以访问这些属性,那你必须将其打包到 JAR 的根目录并将 JAR 放入 EAR 的 lib/ 目录下。

3.1.3.5. 创建自定义模块

下面的步骤描述了如何创建自定义模块以使书性文件和其他资源对于运行在 JBoss EAP 服务器上的所有应用程序所用。

过程 3.3. 创建自定义模块

  1. 创建和填充 module/ 目录结构。
    1. EAP_HOME/module 目录下创建一个目录结构来包含文件和 JAR。例如:
      $ cd EAP_HOME/modules/ $ mkdir -p myorg-conf/main/properties
      
      
    2. 将属性文件移到你在前一步骤里创建的 EAP_HOME/modules/myorg-conf/main/properties/ 目录里。
    3. EAP_HOME/modules/myorg-conf/main/ 目录里创建一个包含下列 XML 内容的 module.xml 文件:
      <module xmlns="urn:jboss:module:1.1" name="myorg-conf">
          <resources>
              <resource-root path="properties"/>
          </resources>
      </module>
      
      
  2. 修改服务器配置文件里的 ee 子系统。你可以使用 JBoss CLI 或手动编辑这个文件。
    • 按照下列步骤使用 JBoss CLI 来修改服务器配置文件。
      1. 启动服务器并连接至管理 CLI。
        • 对于 Linux,输入下列命令:
          $ EAP_HOME/bin/jboss-cli.sh --connect
          $ Connected to standalone controller at localhost:9999
          
        • 对于 Windows,输入下列命令:
          C:\>EAP_HOME\bin\jboss-cli.bat --connect
          C:\> Connected to standalone controller at localhost:9999
          
      2. 要在 ee 子系统里创建 myorg-conf<global-modules> 元素,输入下列命令:
        /subsystem=ee:write-attribute(name=global-modules, value=[{"name"=>"myorg-conf","slot"=>"main"}])
        
        你应该看到下面的结果:
        {"outcome" => "success"}
        
    • 如果你向手动编辑服务器配置文件,请按照下列步骤进行。
      1. 停止服务器并打开服务器配置文件。如果你使用的是独立服务器,这个文件是 EAP_HOME/standalone/configuration/standalone.xml,如果是受管域,这个文件是 EAP_HOME/domain/configuration/domain.xml
      2. 找到 ee 子系统并为 myorg-conf 添加全局模块。下面是 ee 子系统元素的例子,已经进行修改并包含了 myorg-conf 元素:
        <subsystem xmlns="urn:jboss:domain:ee:1.0" >            
            <global-modules>
                <module name="myorg-conf" slot="main" />            
            </global-modules>
        </subsystem>
        
        
  3. 假设你复制了一个名为 my.properties 的文件到正确的模块位置,你现在可以使用下面的代码来加载属性文件了:
    Thread.currentThread().getContextClassLoader().getResource("my.properties");
    

3.1.4. 日志的修改

3.1.4.1. 修改日志依赖关系

总结

JBoss LogManager 支持所有日志框架的前端,所以你可以保持现有的日志代码或移至新的 JBoss 日志框架。不管用哪种方法,因为模块化类加载有改变,你可能需要修改你的应用程序来添加所需的依赖关系。

3.1.4.2. 为第三方日志框架更新应用程序代码

总结

在 JBoss EAP 6 里,在默认情况下都已经添加了常见的第三方框架如 Apache Commons Loggin、Apache log4j、SLF4J 和 Java Logging 的日志依赖关系。然而,如果你在使用 log4j,你不需要使用日志子系统来配置处理程序(appender),你需要排除 JBoss EAP log4j 模块。

过程 3.5. 配置 JBoss EAP 6 以使用 log4j.properties 或 log4j.xml 文件

  1. 用下列内容创建一个 jboss-deployment-structure.xml 文件:
    <jboss-deployment-structure>
        <deployment>
            <!-- Exclusions allow you to prevent the server from automatically adding some dependencies -->
            <exclusions>
                <module name="org.apache.log4j" />
            </exclusions>
        </deployment>
    </jboss-deployment-structure>
    
    
  2. 如果你在部署 WAR 文件,将 jboss-deployment-structure.xml文件放入 META-INF/ 目录或者 WEB-INF/,如果是部署 EAR 文件则放入 META-INF/
  3. log4j.propertieslog4j.xml 文件包含在 EAR 的 lib/ 目录、或者 WAR 的 WEB-INF/classes/ 目录里。
  4. 部署你的应用程序

注意

如果你选择使用 log4j 配置文件,你将无法在运行时再修改 log4j 日志配置。

3.1.4.3. 修改代码以使用新的 JBoss Logging 框架

总结

要使用新的框架,请修改你的导入和代码:

过程 3.6. 修改代码和依赖关系以使用新的 JBoss Logging 框架

  1. 修改你的导入和日志代码

    下面是一个使用新的 JBoss Logging 框架的例子:
    import org.jboss.logging.Level;
    import org.jboss.logging.Logger;
    
    private static final Logger logger = Logger.getLogger(MyClass.class.toString());
    
    if(logger.isTraceEnabled()) {
        logger.tracef("Starting...", subsystem);
    }
    
  2. 添加日志依赖关系

    包含 JBoss Logging 类的 JAR 位于名为 org.jboss.logging 的模块。你的 MANIFEST-MF 文件应该类似要:
    Manifest-Version: 1.0
    Dependencies: org.jboss.logging
    

3.1.5. 应用程序包的修改

3.1.5.1. 修改 EAR 和 WAR 的打包

总结

当你移植应用程序时,由于模块化类加载的修改,你可能需要修改 EAR 或 WAR 的打包结构。模块依赖关系是以特定的顺序加载的:

  1. 系统依赖关系
  2. 用户依赖关系
  3. 本地资源
  4. 部署间的依赖关系

过程 3.7. 修改归档的打包

  1. 打包 WAR

    WAR 是一个模块,它里面的所有类都用相同的类加载器加载。这意味着打包在 WEB-INF/lib/ 目录里的类将和 WEB-INF/classes 里的一样被对待。
  2. 打包 EAR

    EAR 由多个模块组成。EAR/lib/ 目录是一个单个的模块且 EAR 里每个 WAR 或 EJB JAR 都是一个独立的模块。除非定义了显性的依赖关系,类不能访问 EAR 里其他模块里的类。子部署总是对父部署有着自动的依赖关系,这让它们可以访问 EAR/lib/ 目录里的类。然而,子部署并不总由自动的依赖关系来允许它们访问彼此。这个行为是通过设置 ee 子系统配置里的 <ear-subdeployments-isolated> 元素来控制的:
    <subsystem xmlns="urn:jboss:domain:ee:1.0" >            
      <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
    </subsystem>
    
    在默认情况下,它被设置为 false,这允许子部署查看属于 EAR 里其他子部署的类。
    关于类加载的更多信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss 开发指南》里的『类加载和模块』章节。

3.1.6. 数据源和资源适配器配置的修改

3.1.6.1. 根据配置的变化更新应用程序

在 JBoss EAP 5 里,服务和子系统是在多个文件里配置的。而在 JBoss EAP 6 里,配置主要是在一个文件里完成的。如果你的应用程序使用了任何下面的资源或服务,你就可能需要修改配置。
  1. 如果你的应用程序使用了数据源,请参考: 第 3.1.6.2 节 “更新数据源配置”
  2. 如果你的应用程序使用了 JPA 且捆绑了 Hibernate JAR,请参考下列移植选项: 第 3.1.6.4 节 “为 Hibernate 或 JPA 配置数据源”
  3. 如果你的应用程序使用了资源适配器,请参考: 第 3.1.6.5 节 “更新资源适配器配置”
  4. 关于如何配置基本安全性,请参考: 第 3.1.7.1 节 “应用程序安全性的修改”

3.1.6.2. 更新数据源配置

总结

在以前的 JBoss EAP 版本里,JCA 数据源配置在一个后缀为 *-ds.xml 的文件里定义。这个文件然后部署在服务器的 deploy/ 下或和应用程序一起打包。JDBC 驱动会被复制到 server/lib/ 目录或打包在应用程序的 WEB-INF/lib/ 目录里。虽然这种配置数据源的方法仍被支持,我们不推荐在产品环境里使用它,因为 JBoss 的管理根据已不支持。

在 JBoss EAP 6 里,数据源是在服务器配置文件里进行配置的。如果 JBoss EAP 实例运行在受管域里,数据源是在 domain/configuration/domain.xml 文件里配置。如果 JBoss EAP 实例以独立服务器运行,那么数据源是在 standalone/configuration/standalone.xml file 里配置。以这种方式配置的数据源可以用 JBoss 管理接口(包括 Web 管理控制台和命令行接口)进行管理和控制。这些工具使得在受管域上管理部署和配置多个服务器更为容易。
下面的内容描述了如何修改你的数据源配置,从而可以通过不同的管理工具进行管理和支持。
移植到 JBoss EAP 6 的可管理数据源配置。

JDBC 4.0 兼容的驱动可以作为部署或核心模块来安装。JDBC 4.0 兼容的驱动包含一个 META-INF/services/java.sql.Driver 文件,它制定驱动类名。非 JDBC 4.0 兼容的驱动需要其他的步骤。关于如何兼容 JDBC 4.0 驱动和更新你的数据源配置为 Web 管理控制台和 CLI 管理的配置,请参考 第 3.1.6.3 节 “安装和配置 JDBC 驱动”

如果你的应用程序使用了 Hibernate 或 JPA,它可能要求进行额外的修改。详情请参考 第 3.1.6.4 节 “为 Hibernate 或 JPA 配置数据源”
使用 IronJacamar 移植工具来转换配置数据

你可以参考 第 4.1.6 节 “使用 IronJacamar 工具来移植数据源和资源适配器配置”。这个工具将 *-ds.xml 风格的配置文件转换成 JBoss EAP 6 需要的格式。

3.1.6.3. 安装和配置 JDBC 驱动

总结

JDBC 驱动可以下列两种方式之一安装到容器里。

  • 作为部署
  • 作为核心模块
每种方式各自的优缺点如下。

在 JBoss EAP 6 里,数据源是在服务器配置文件里进行配置的。如果 JBoss EAP 实例运行在受管域里,数据源是在 domain/configuration/domain.xml 文件里配置。如果 JBoss EAP 实例以独立服务器运行,那么数据源是在 standalone/configuration/standalone.xml file 里配置。Schema 引用信息两种模式都是一样的,你可以在 JBoss EAP 6 的 doc/ 里找到。我们在这里假设服务器是作为独立服务器运行的,而数据源是在 standalone.xml 文件里进行配置的。

过程 3.8. 安装和配置 JDBC 驱动

  1. 安装 JDBC 驱动

    1. 将 JDBC 驱动作为部署安装

      这是我们推荐的安装驱动的方法。当 JDBC 驱动作为部署安装时,它会被部署为常规的 JAR。如果 JBoss EAP 实例作为独立服务器运行,请将兼容 JDBC 4.0 的 JAR复制到 EAP_HOME/standalone/deployments/ 目录。如过服务器是运行在受管域里,请将 JAR 复制到 EAP_HOME/domain/deployments/ 目录。
      下面是一个安装为独立服务器的部署的 MySQL JDBC 驱动示例:
      $cp mysql-connector-java-5.1.15.jar EAP_HOME/standalone/deployments/
      任何兼容 JDBC 4.0 的驱动都自动会被承认且根据名称和版本安装至系统里。兼容 JDBC 4.0 的 JAR 都包含一个名为 META-INF/services/java.sql.Driver 的文本文件,它指定驱动类的名称。如果这个驱动不兼容 JDBC 4.0,它可以用下列方法来部署:
      • 创建并添加 java.sql.Driver 文件到 META-INF/services/ 路径下的 JAR。这个文件应该包含驱动类的名称,例如:
        com.mysql.jdbc.Driver
      • 在部署目录里创建一个 java.sql.Driver 文件。对于作为独立服务器运行的 JBoss EAP 6 实例,这个文件应该位于:EAP_HOME/standalone/deployments/META-INF/services/java.sql.Driver。如果服务器位于受管域里,那么这个文件应该是:EAP_HOME/domain/deployments/META-INF/services/java.sql.Driver
      这种方法的优点是:
      • 因为不需要定义模块,所以这是最简单的方法。
      • 当服务器运行在受管域里时,使用这个方法的部署将自动传播到域里的所有服务器。这意味着管理员不需要手动分发这个驱动 JAR。
      这种方法的缺点是:
      • 如果 JDBC 驱动由多个 JAR 组成,如驱动 JAR 以及依赖的许可证 JAR 或本地化 JAR,你不能将驱动安装为部署。你必须将其安装为核心模块。
      • 如果这个驱动不兼容 JDBC 4.0,你必须创建一个包含驱动类名的文件并导入至 JAR 或覆盖到 deployments/ 目录。
    2. 将 JDBC 驱动安装为核心模块

      要将 JDBC 驱动安装为核心模块,你必须在 EAP_HOME/modules/ 下创建一个文件路径结构。这个结构包含 JDBC 驱动 JAR,任何其他供应商许可证或本地化 JAR,以及一个定义模块的 module.xml 文件。
      • 将 MySQL JDBC 驱动安装为核心模块

        1. 创建一个目录结构 EAP_HOME/modules/com/mysql/main/
        2. main/ 子目录里,创建一个包含下列为 MySQL JDBC 驱动定义的 module.xml 文件:
          <?xml version="1.0" encoding="UTF-8"?>
          <module xmlns="urn:jboss:module:1.0" name="com.mysql">
          <resources>
              <resource-root path="mysql-connector-java-5.1.15.jar"/>
          </resources>
          <dependencies>
              <module name="javax.api"/>
          </dependencies>
          </module>
          
          
          
          模块名 “com.mysql”,映射了这个模块的目录结构。<dependencies> 元素用来指定这个模块对其他模块的依赖关系。在这种情况下,所有的 JDBC 数据源都依赖于在另一个名为 javax.api 的模块里定义的 Java JDBC API。这个模块位于 modules/system/layers/base/javax/api/main/ 目录。

          注意

          确保你在 module.xml 文件的开头没有留下一个空格,否则你会遇到 "New missing/unsatisfied dependencies" 错误。
        3. 复制 MySQL JDBC 驱动 JAR 到 EAP_HOME/modules/com/mysql/main/ 目录:
          $ cp mysql-connector-java-5.1.15.jar EAP_HOME/modules/com/mysql/main/
      • 将 IBM DB2 JDBC 驱动和 license JAR 安装为核心模块

        这个例子只是用来演示如何部署要求 JDBC Driver JAR 之外的 JAR 的驱动。
        1. 创建目录结构 EAP_HOME/modules/com/ibm/db2/main/
        2. main/ 子目录里,创建一个包含下列用于 IBM DB2 JDBC 驱动和许可证的模块定义的 module.xml 文件:
          <?xml version="1.0" encoding="UTF-8"?>
          <module xmlns="urn:jboss:module:1.1" name="com.ibm.db2">
            <resources>
              <resource-root path="db2jcc.jar"/>
              <resource-root path="db2jcc_license_cisuz.jar"/>
            </resources>
            <dependencies>
              <module name="javax.api"/>
              <module name="javax.transaction.api"/>
            </dependencies>
          </module>
          
          

          注意

          确保你在 module.xml 文件的开头没有留下一个空格,否则你会遇到 "New missing/unsatisfied dependencies" 错误。
        3. 复制 JDBC 驱动和 license JAR 到 EAP_HOME/modules/com/ibm/db2/main/ 目录里。
          $ cp db2jcc.jar EAP_HOME/modules/com/ibm/db2/main/
          $ cp db2jcc_license_cisuz.jar EAP_HOME/modules/com/ibm/db2/main/
      这种方法的优点是:
      • 当 JDBC 驱动由多个 JAR 组成时,这是唯一的途径。
      • 用这个方法,不兼容 JDBC 4.0 的驱动可以无需修改驱动 JAR 或创建覆盖文件就进行安装。
      这种方法的缺点是:
      • 通过设置模块要更为困难。
      • 你必须手动将模块复制到运行在受管域里的每个服务器里。
  2. 配置数据源

    1. 添加数据库驱动

      在相同的文件的 <drivers> 元素了添加 <driver> 元素。它也包含了之前的 *-ds.xml 文件里定义的一些数据源信息。
      首先确定驱动 JAR 是否兼容 JDBC 4.0。兼容 JDBC 4.0 的 JAR 包含一个指定驱动类名的 META-INF/services/java.sql.Driver。这个服务器使用这个文件来在 JAR 里查找驱动类的名称。兼容 JDBC 4.0 的 JAR 不要求 <driver-class> 元素,因为在 JAR 里它已被指定。下面是一个 JDBC 4.0 兼容的 MySQL 驱动的 driver 元素示例:
      <driver name="mysql-connector-java-5.1.15.jar" module="com.mysql"/>
      
      不兼容 JDBC 4.0 的驱动要求一个 <driver-class> 属性来确定驱动类,这是因为没有 META-INF/services/java.sql.Driver 文件来指定驱动类名。下面是一个不兼容 JDBC 4.0 的 MySQL 驱动的 driver 元素示例:
      <driver name="mysql-connector-java-5.1.15.jar" module="com.mysql"><driver-class>com.mysql.jdbc.Driver</driver-class></driver>
      
      
    2. 创建数据源

      standalone.xml 文件的 <datasources> 部分创建一个 <datasource> 元素。这个文件包含之前的 *-ds.xml 文件里定义的大部分数据源信息。

      重要

      要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
      下面是 standalone.xml 文件里的一个 MySQL 数据源元素示例:
      <datasource jndi-name="java:/YourDatasourceName" pool-name="YourDatasourceName">
        <connection-url>jdbc:mysql://localhost:3306/YourApplicationURL</connection-url>
        <driver>mysql-connector-java-5.1.15.jar</driver>
        <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
        <pool>
          <min-pool-size>100</min-pool-size>
          <max-pool-size>200</max-pool-size>
        </pool>
        <security>
          <user-name>USERID</user-name>
          <password>PASSWORD</password>
        </security>
        <statement>
          <prepared-statement-cache-size>100</prepared-statement-cache-size>
          <share-prepared-statements/>
        </statement>
      </datasource>
      
      

3.1.6.4. 为 Hibernate 或 JPA 配置数据源

如果你的应用程序时用了 JPA 且捆绑了 Hibernate JAR,你可能会想要使用 JBoss EAP 6 附带的 Hibernate。要使用这个版本的 Hibernate,你必须从应用程序里删除旧的 Hibernate 捆绑。

过程 3.9. 删除 Hibernate 捆绑

  1. 从你的应用程序的库目录里将 Hibernate JAR 删除。
  2. 当不再需要 <hibernate.transaction.manager_lookup_class> 元素时,在 persistence.xml 里将其删除或注释。

3.1.6.5. 更新资源适配器配置

总结

在以前的应用服务器版本里,资源适配器配置是在带有后缀 *-ds.xml 的文件里定义的。在 JBoss EAP 6 里,资源适配器是在服务器配置文件里配置的。如果你是在受管域里运行的,配置文件是 EAP_HOME/domain/configuration/domain.xml。如果你运行的是独立服务器,配置文件是 EAP_HOME/standalone/configuration/standalone.xml。Schema 引用信息两种模式都是一样的,你可以在这里找到:资源适配器描述

重要

要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
定义资源适配器

资源适配器描述符信息是在服务器配置文件中的下列 subsystem 元素里定义的:

<subsystem xmlns="urn:jboss:domain:resource-adapters:1.0"/>
你将使用资源适配器 *-ds.xml 文件里定义的相同的一些信息。

下面是服务器配置文件里的资源适配器元素示例:
<resource-adapters>
  <resource-adapter>
    <archive>multiple-full.rar</archive>
    <config-property name="Name">ResourceAdapterValue</config-property>
    <transaction-support>NoTransaction</transaction-support>
    <connection-definitions>
      <connection-definition
      class-name="org.jboss.jca.test.deployers.spec.rars.multiple.MultipleManagedConnectionFactory1"
      enabled="true" jndi-name="java:/eis/MultipleConnectionFactory1"
      pool-name="MultipleConnectionFactory1">
    <config-property name="Name">MultipleConnectionFactory1Value</config-property>
      </connection-definition>
      <connection-definition
      class-name="org.jboss.jca.test.deployers.spec.rars.multiple.MultipleManagedConnectionFactory2"
      enabled="true" jndi-name="java:/eis/MultipleConnectionFactory2"
      pool-name="MultipleConnectionFactory2">
    <config-property name="Name">MultipleConnectionFactory2Value</config-property>
      </connection-definition>
    </connection-definitions>
    <admin-objects>
      <admin-object
      class-name="org.jboss.jca.test.deployers.spec.rars.multiple.MultipleAdminObject1Impl"
      jndi-name="java:/eis/MultipleAdminObject1">
    <config-property name="Name">MultipleAdminObject1Value</config-property>
      </admin-object>
      <admin-object class-name="org.jboss.jca.test.deployers.spec.rars.multiple.MultipleAdminObject2Impl"
      jndi-name="java:/eis/MultipleAdminObject2">
    <config-property name="Name">MultipleAdminObject2Value</config-property>
      </admin-object>
      </admin-objects>
  </resource-adapter>
</resource-adapters>

3.1.7. 安全性的修改

3.1.7.1. 应用程序安全性的修改

配置基本验证的安全性

UsersRolesLoginModule 在 classpath 里查找书性文件。在以前的 JBoss EAP 版本里,放在 EAP_HOME/server/SERVER_NAME/conf/ 下的属性文件位于 classpath 上且可以轻易地被 UsersRolesLoginModule 找到。在 JBoss EAP 6 里,目录结构已经发生了变化。属性文件必须打包在应用程序里,使其在 classpath 上可用。

重要

要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
要配置基本验证的安全性,请在 standalone/configuration/standalone.xmldomain/configuration/domain.xml 服务器配置文件里的 security-domains 下添加一个新的安全域:
<security-domain name="example">
    <authentication>
        <login-module code="UsersRoles" flag="required">
            <module-option name="usersProperties" 
                    value="${jboss.server.config.dir}/example-users.properties"/>
            <module-option name="rolesProperties" 
                    value="${jboss.server.config.dir}/example-roles.properties"/>
        </login-module>
    </authentication>
</security-domain>
如果 JBoss EAP 6 实例作为独立服务器运行, ${jboss.server.config.dir} 指向 EAP_HOME/standalone/configuration/ 目录。如果它运行在受管域里, ${jboss.server.config.dir} 指向 EAP_HOME/domain/configuration/ 目录。
修改安全域的名称

在 JBoss EAP 6 里,安全域名称里不再使用前缀 java:/jaas/

  • 对于 Web 应用程序,你必须从 jboss-web.xml 里的安全域配置里删除这个前缀。
  • 对于企业级应用程序,你必须从 jboss-ejb3.xml 里的安全域配置里删除这个前缀。这个文件已经替换了 JBoss EAP 6 里的 jboss.xml

3.1.8. JNDI 的修改

3.1.8.1. 更新应用程序 JNDI 命名空间的名称

总结

EJB 3.1 introduced a standardized global JNDI namespace and a series of related namespaces that map to the various scopes of a Java EE application. The three JNDI namespaces used for portable JNDI lookups are java:global, java:module, and java:app. Applications that use JNDI lookups must be changed to follow the new standardized JNDI namespace convention.

JNDI 映射示例

前一版本里的 JNDI 命名空间示例以及如何在 JBoss EAP 6 里指定可以在这里找到: 第 3.1.8.5 节 “以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的”

3.1.8.2. 可移植的 JNDI 命名语法

总结

有四种逻辑命名空间,每个都具有自己的作用域。其中三种用于可移植的 JNDI 查找。下表详述了何时和如何使用这些命名空间。

表 3.1. 可移植的 JNDI 命名空间

JNDI 命名空间 描述
java:global
这个命名空间里的名称是应用程序服务器实例里部署的所有应用程序共享的。你可以使用这个命名空间里的名称来查找远程 EJB。
下面是一个 java:global 命名空间的示例:java:global/jboss-seam-booking/jboss-seam-booking.jar/HotelBookingAction
java:module
这个命名空间里的名称是由部署在模块里的所有组件共享的,例如单一 EJB 模块或 web 模块中所有组件里的所有 EJB。
下面是一个 java:module 命名空间的示例:java:module/HotelBookingAction!org.jboss.seam.example.booking.HotelBooking
java:app
这个命名空间里的名称是由单个应用程序里的所有模块里的所有组件共享的,例如相同 EJB 文件里的 WAR 或 EJB JAR 文件将可以访问 java:app 命名空间里的资源。
下面是一个 java:app 命名空间的示例:java:app/jboss-seam-booking.jar/HotelBookingAction
你在 EE.5.2.2 章节里可以找到关于 JNDI 命名上下文的更多信息,如 "JSR 316: JavaTM Platform, Enterprise Edition (Java EE) Specification, v6" 里的"Application Component Environment Namespaces"。你也可以在这里下载相关规格:http://jcp.org/en/jsr/detail?id=316

3.1.8.3. 复核 JNDI 命名空间规则

总结

JBoss EAP 6 已经改进了 JNDI 命名空间,不只是为应用服务器里绑定的每个名称提供可预测和一致的规则,也防止了将来的兼容性问题。这意味着如果名称不遵循新的规则,应用程序里的当前命名空间也会出现问题。

命名空间应该遵循下列规则:

  1. 未限定的相对名称如 DefaultDSjdbc/DefaultDS 应该根据上下文限定于 java:comp/envjava:module/envjava:jboss/env
  2. 未限定的绝对名称如 /jdbc/DefaultDS 应该限定于 java:jboss/root
  3. 限定的绝对名称如 java:/jdbc/DefaultDS 应该和上面未限定的绝对名称采用相同的方式进行限定。
  4. java:jboss 命名空间在整个 AS 服务器实例间进行共享。
  5. 带有 java: 前缀的相对名称必须位于下列 5 个命名空间中的一个:compmoduleappglobal 或私有的 jboss。任何以 java:xxx 开始的名称里的 xxx 不匹配上面的 5 个命名空间都将导致无效名称错误。

3.1.8.4. 修改应用程序以遵循新的 JNDI 命名规则

  • 下面是一个 JBoss EAP 5.1 里的 JNDI 查找示例。这些代码是在一个初始方法里找到的。
    private ProductManager productManager;
    try {
        context = new InitialContext();
        productManager = (ProductManager) context.lookup("OrderManagerApp/ProductManagerBean/local");
    } catch(Exception lookupError) {
        throw new ServletException("Unable to find the ProductManager bean", lookupError);
    }
    
    请注意查找名称是 OrderManagerApp/ProductManagerBean/local
  • 下面是在 JBoss EAP 6 编写相同查找的示例:
    @EJB(lookup="java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager")
    private ProductManager productManager;
    
    查找值现在被定义为成员变量并使用新的可移植的 java:app JNDI 命名空间名 java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager

3.1.8.5. 以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的

表 3.2. 

JBoss EAP 5.x 里的命名空间 JBoss EAP 6 里的命名空间 其他注释
OrderManagerApp/ProductManagerBean/local java:module/ProductManagerBean!services.ejb.ProductManager EE6 标准绑定,只可以在相同模块里访问
OrderManagerApp/ProductManagerBean/local java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager EE6 标准绑定,只可以在相同应用程序里访问
OrderManagerApp/ProductManagerBean/local java:global/OrderManagerApp/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager EE6 标准绑定,可以全局访问
java:comp/UserTransaction java:comp/UserTransaction 非 EE 线程可以访问,例如你的应用程序直接创建的线程
java:comp/UserTransaction java:jboss/UserTransaction 可全局访问,如果 java:comp/UserTransaction 不可用则使用它
java:/TransactionManager java:jboss/TransactionManager
java:/TransactionSynchronizationRegistry java:jboss/TransactionSynchronizationRegistry

3.2. 依赖于应用程序架构和组件的修改

3.2.1. 复查依赖于应用程序架构和组件的修改

如果你的应用程序使用了下面的技术或组件,在移植到 JBoss EAP 6 时你可能需要修改应用程序。
Hibernate 和 JPA
如果你的应用程序使用了 Hibernate 或 JPA,你的应用程序可能需要进行修改。详情请参考: 第 3.2.2.1 节 “更新使用 Hibernate 和/或 JPA 的应用程序”
REST
如果你的应用程序使用了 JAX-RS,你应该意识到 JBoss EAP 6 会自动设置 RESTEasy,所以你不需要再进行配置。详情请参考 第 3.2.4.1 节 “配置 JAX-RS 和 RESTEasy 的修改”
LDAP
在 JBoss EAP 6 里,LDAP 安全区的配置是不一样的。如果你的应用程序使用了 LDAP,请参考: 第 3.2.5.1 节 “配置 LDAP Security Realm 的修改”
Messaging
JBoss EAP 6 里不再包含 JBoss Messaging。如果你的应用程序使用了 JBoss Messaging,你需要用 HornetQ 替换 JBoss Messaging 代码。下面是相关的信息: 第 3.2.6.3 节 “移植你的应用程序以将 HornetQ 用作 JMS 提供者”
群集
在 JBoss EAP 6 里启用群集的方式已经修改了,详情请参考: 第 3.2.7.1 节 “修改应用程序以用于群集环境”
服务风格的部署
虽然 JBoss EAP 6 不再使用服务风格的描述符,容器还是这种风格的部署而无需进行修改。关于部署的信息,请参考: 第 3.2.8.1 节 “更新使用服务风格部署的应用程序”
远程调用
如果你的应用程序进行了远程调用,你仍可以使用 JNDI 为你的 Bean 查找代理并在返回的代理上进行调用。关于语法和命名空间的修改,请参考: 第 3.2.9.1 节 “将进行远程调用的 JBoss EAP 5 应用程序移植到 JBoss EAP 6。”
Seam 2.2
如果你的应用程序使用了 Seam 2.2,对于可能需要的修改,请参考: 第 3.2.11.1 节 “移植 Seam 2.2 归档到 JBoss EAP 6”
Spring
如果你的用程序使用了 Spring,请参考: 第 3.2.12.1 节 “移植 Spring 应用程序”
其他可能影响应用程序移植的修改
对于 JBoss EAP 6 里可能影响你的应用程序的其他修改,请参考: 第 3.2.13.1 节 “熟悉其他可能影响到移植的修改”

3.2.2. Hibernate 和 JPA 的修改

3.2.2.2. 修改使用 Hibernate 和 JPA 的应用程序的配置

总结

如果你的应用程序包含一个 persistence.xml 文件或代码使用了注解 @PersistenceContext@PersistenceUnit,JBoss EAP 6 会在部署时进行检测并假设应用程序使用了 JPA。这会隐性地添加 Hibernate 4 以及一些其他的依赖关系到应用程序的 classpath。

如果你的应用程序使用了 Hibernate 3,在大多数情况下,你将可以切换到 Hibernate 4 且成功运行。然而,如果你在部署应用程序时看到了 ClassNotFoundExceptions,你可以用下列方法来解决:

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 3.12. 配置应用程序

  1. 复制所需的 Hibernate 3 JAR 到你的应用程序库。

    你可以复制包含缺失类的特定 Hibernate 4 JAR 到应用程序的 lib/ 目录,或者使用其他方法添加它们到 classpath 来解决这个问题。在某些情况下,由于混合使用 Hibernate 版本,这可能会导致 ClassCastExceptions 或其他类加载问题。如果发生了这种情况,你需要使用下一个方法。
  2. 设置服务器只使用 Hibernate 3 库。

    JBoss EAP 6 允许你将 Hibernate 3.5 (或更高版本) 持久化提供者 JAR 和应用程序打包。为了让服务器只使用 Hibernate 3 库而排除 Hibernate 4 库,你需要在 persistence.xml 里设置 jboss.as.jpa.providerModulehibernate3-bundled
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
        <persistence-unit name="plannerdatasource_pu">
            <description>Hibernate 3 Persistence Unit.</description>
            <jta-data-source>java:jboss/datasources/PlannerDS</jta-data-source>
            <properties>
                <property name="hibernate.show_sql" value="false" />
                <property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
            </properties>
        </persistence-unit>
    </persistence>
    
    
    Java Persistence API (JPA) 开发人员将检测到应用程序里持久性提供者的存在并使用 Hibernate 3 库。关于 JPA 持久性属性的更多信息,请参考 第 3.2.2.3 节 “持久化单元属性”
  3. 禁用 Hibernate 的二级缓存

    Hibernate 3 的二级缓存在 JBoss EAP 6 没有展现和以前版本里一样的行为。如果你在应用程序里是了 Hibernate 二级缓存,你必须禁用它,直至你升级到 Hibernate 4。要禁用二级缓存,你需要在 persistence.xml 文件里将 <hibernate.cache.use_second_level_cache> 设置为 false

3.2.2.3. 持久化单元属性

Hibernate 4.x 配置属性

JBoss EAP 6 自动设置下列 Hibernate 4.x 配置属性:

表 3.3. Hibernate 持久化单元属性

属性名 默认值 目的
hibernate.id.new_generator_mappings true
如果你使用 @GeneratedValue(AUTO) 为新条目生成唯一的索引值,这个设置就是相关的。新的应用程序应该保持默认的值 true。现有的使用 Hibernate 3.3.x 的应用程序可能需要将其修改为 false 以继续使用序列对象或基于表的生成器并保持向后的兼容性。应用程序可以在 persistence.xml 文件里覆盖这个值。
下面提供了关于这种行为的更多信息。
hibernate.transaction.jta.platform org.hibernate.service.jta.platform.spi.JtaPlatform 接口的实例
这个类将事务管理者、用户事务、事务同步注册表传入 Hibernate。
hibernate.ejb.resource_scanner org.hibernate.ejb.packaging.Scanner 接口的实例
这个类知道如何使用 JBoss EAP 注解 indexer 来提供更快的部署。
hibernate.transaction.manager_lookup_class
如果在 persistence.xml 里找到这个属性,它将被删除,因为它和 hibernate.transaction.jta.platform 相冲突
hibernate.session_factory_name QUALIFIED_PERSISTENCE_UNIT_NAME
这将设置为应用程序名称 + 持久化单元名称。应用程序可以指定不同的值但它必须在 JBoss EAP 实例上的所有应用程序部署里都是唯一的。
hibernate.session_factory_name_is_jndi false
只有应用程序没有为 hibernate.session_factory_name 指定值时它才会被设置。
hibernate.ejb.entitymanager_factory_name QUALIFIED_PERSISTENCE_UNIT_NAME
这将设置为应用程序名称 + 持久化单元名称。应用程序可以指定不同的值但它必须在 JBoss EAP 实例上的所有应用程序部署里都是唯一的。
在 Hibernate 4.x 里,如果 new_generator_mappings 被设置为 true
  • @GeneratedValue(AUTO) 映射 org.hibernate.id.enhanced.SequenceStyleGenerator
  • @GeneratedValue(TABLE) 映射 org.hibernate.id.enhanced.TableGenerator
  • @GeneratedValue(SEQUENCE) 映射 org.hibernate.id.enhanced.SequenceStyleGenerator
在 Hibernate 4.x 里,如果 new_generator_mappings 被设置为 false
  • @GeneratedValue(AUTO) 映射 Hibernate "native".
  • @GeneratedValue(TABLE) 映射 org.hibernate.id.MultipleHiLoPerTableGenerator
  • @GeneratedValue(SEQUENCE) 映射 Hibernate "seqhilo"。
关于这些属性的更多信息,请访问 http://www.hibernate.org/docs 并查看 Hibernate 4.1 Developer Guide
JPA 持久化属性

persistence.xml 文件里的持久化单元定义支持下面的 JPA 属性:

表 3.4. JPA 持久化单元属性

属性名 默认值 目的
jboss.as.jpa.providerModule org.hibernate
持久化提供者模块的名称。
如果 Hibernate 3 JAR 位于应用程序归档里,这个值应该为 hibernate3-bundled
如果这个应用程序和持久化提供者一起打包,这个值应该为 application
jboss.as.jpa.adapterModule org.jboss.as.jpa.hibernate:4
帮助 JBoss EAP 和持久化提供者一起使用的集成类的名称。
目前的有效值是:
  • org.jboss.as.jpa.hibernate:4:用于 Hibernate 4 集成类
  • org.jboss.as.jpa.hibernate:3:用于 Hibernate 3 集成类

3.2.2.4. 更新 Hibernate 3 应用程序以使用 Hibernate 4

总结

当你更新应用程序以使用 Hibernate 4 时,某些更新是通用的,而不管你目前使用的是什么版本的 Hibernate。对于其他的更新,你必须确定你当前使用的版本。

过程 3.13. 更新应用程序以使用 Hibernate 4

  1. 自动增量序列生成器的默认行为在 JBoss EAP 6 已经进行了修改。详情请参考 第 3.2.2.5 节 “Hibernate 标识符自动生成值”
  2. 确定应用程序当前使用的 Hibernate 版本并选择下面正确的更新过程。

3.2.2.5. Hibernate 标识符自动生成值

Hibernate 3.5 引入了名为 hibernate.id.new_generator_mappings 的核心属性,它指定在使用 @GeneratedValue 时如何生成标识符或序列号。在 JBoss EAP 6 里,这个属性的默认值是:
  • 当你部署原声 Hibernate 应用程序时,这个默认值是 false
  • 当你部署 JPA 应用程序时,这个默认值是 true
新应用程序准则

使用 @GeneratedValue 的新应用程序应该设置 hibernate.id.new_generator_mappings 属性为 true。这是首选的设置,因为对于不同的数据库它更容易移植。在多数情况下,它的效率更高;在某些情况下,它解决了和 JPA 2 规格的兼容问题。

  • 对于新的 JPA 应用程序,JBoss EAP 6 默认 hibernate.id.new_generator_mappings 属性为 true 且不应该被改变。
  • 对于新的原生 Hibernate 应用程序,JBoss EAP 6 默认 hibernate.id.new_generator_mappings 属性为 false。你应该设置它为 true
现有的 JBoss EAP 5 应用程序的准则

当移植到 JBoss EAP 6 时,现有的使用 @GeneratedValue 注解的应用程序应该确保使用相同的生成器来为新条目创建主键值。

  • 对于现有的 JPA 应用程序,JBoss EAP 6 默认 hibernate.id.new_generator_mappings 属性为 true。你应该在 persistence.xml 里将这个属性设置为 false
  • 对于现有的原生 Hibernate 应用程序,JBoss EAP 6 默认 hibernate.id.new_generator_mappings 属性为 false 且不应该被改变。
关于这些属性设置的更多信息,请参考 第 3.2.2.3 节 “持久化单元属性”

3.2.2.6. 移植你的 Hibernate 3.3.x 应用程序到 Hibernate 4.x

过程 3.14. 

  1. 映射 Hibernate text 类型为 JDBC LONGVARCHAR

    在 Hibernate 3.5 以前的版本里,text 类型被映射为 JDBC CLOB。Hibernate 4 里引入了一个新的 Hibernate 类型materialized_clob,将 Java String 属性映射为 JDBC CLOB。如果你的应用程序里有属性需要将 type="text" 映射为 JDBC CLOB,你必须这样:
    1. 如果你的应用程序使用了 hbm 映射文件,请将这个属性修改为 type="materialized_clob"
    2. 如果你的应用程序使用了注解,你应该将 @Type(type = "text") 替换为 @Lob
  2. 复查代码以找出返回值类型的修改

    数字型组合标准的 Projection 现在返回的是 HQL 对应类型的值。因此,org.hibernate.criterion 里的下列 projection 的返回类型已经有了改动。
    1. CountProjectionProjections.rowCount()Projections.count(propertyName)Projections.countDistinct(propertyName) 里的修改,countcount distinct projection 现在返回的是 Long 型值。
    2. 由于 Projections.sum(propertyName) 里的修改, sum projection 现在返回依赖于属性类型的值。

      注意

      没有修改你的代码可能导致 java.lang.ClassCastException
      1. 对映射为 Long、Short、Integer 或原始类型的属性,将返回 Long 值。
      2. 对映射为 Float、Double 或原始浮点类型的属性,将返回 Double 值。

3.2.2.7. 移植你的 Hibernate 3.5.x 应用程序到 Hibernate 4.x

过程 3.15. 

  1. 合并 AnnotationConfiguration 到配置里

    AnnotationConfiguration 已废弃,它不应该影响应用程序的移植。
    如果你还在使用 hbm.xml 文件,你应该意识到 JBoss EAP 6 现在在 AnnotationConfiguration 里使用 org.hibernate.cfg.EJB3NamingStrategy,而不是以前版本里的 org.hibernate.cfg.DefaultNamingStrategy。这可能导致命名不匹配。如果你依赖于这个命名策略来设置关系(多对多和元素集合)表的默认名称,你可能会遇到这个现象。要解决这个问题,你可以调用 Configuration#setNamingStrategy 传入 org.hibernate.cfg.DefaultNamingStrategy#INSTANCE 让 Hibernate 使用旧的org.hibernate.cfg.DefaultNamingStrategy
  2. 修改这个命名空间以遵循新的 Hibernate DTD 文件名称规则。

    表 3.5. 

    以前的 DTD 命名空间 新的 DTD 命名空间
    http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd
    http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd
  3. 修改环境变量

    1. 如果你在使用 Oracle 以及 materialized_clobmaterialized_blob 属性,全局环境变量 hibernate.jdbc.use_streams_for_binary 必须设置为 true。
    2. 如果你在使用 PostgreSQL 以及 CLOBBLOB 属性,全局环境变量 hibernate.jdbc.use_streams_for_binary 必须设置为 false。

3.2.2.8. 修改运行在群集环境里的移植的 Seam 和 Hibernate 应用程序的持久化属性

如果你在移植 JPA 容器管理的应用程序,影响扩展持久化上下文序列化的属性会自动传递给容器。
然而,由于 Hibernate 的修改,如果移植的 Seam 或 Hibernate 应用程序在群集环境里运行,你可能会遇到序列化问题。你会看到类似于这样的错误日志消息:
javax.ejb.EJBTransactionRolledbackException: JBAS010361: Failed to deserialize 
....
Caused by: java.io.InvalidObjectException: could not resolve session factory during session deserialization [uuid=8aa29e74373ce3a301373ce3a44b0000, name=null]
要纠正这些错误,你需要修改配置文件里的属性。在多数情况下是 persistence.xml 文件。对于原生的 Hibernate API 应用程序来说是 hibernate.cfg.xml 文件。

过程 3.16. 设置持久化属性以运行在群集环境里

  1. 设置 hibernate.session_factory_name 为唯一的名字。这个名字必须在 JBoss EAP 实例里的所有应用程序部署里是唯一的。例如:
    <property name="hibernate.session_factory_name" value="jboss-seam-booking.ear_session_factory"/>
    
    
  2. 设置 hibernate.ejb.entitymanager_factory_name 为唯一的名字。这个名字必须在 JBoss EAP 实例里的所有应用程序部署里是唯一的。例如:
    <property name="hibernate.ejb.entitymanager_factory_name" value="seam-booking.ear_PersistenceUnitName"/>
    
    
关于 Hibernate JPA 持久化单元属性设置的更多信息,请参考 第 3.2.2.3 节 “持久化单元属性”

3.2.2.9. 更新你的应用程序以遵循 JPA 2.0 规格

总结

JPA 2.0 规格要求持久化上下文不能在 JTA 事务外部传播。如果你的应用程序只使用事务范围的持久化上下文,JBoss EAP 6 里这个行为和之前版本的一样,无需进行修改。然而,如果你的应用程序使用了扩展的持久化上下文(XPC)以允许数据修改的排队或批处理,你可能需要修改你的应用程序。

持久化上下文的传播行为

如果你的应用程序有一个 stateful session bean Bean1 ,它具有扩展的持久化上下文,并调用另一个使用事务范围的持久化上下文的 stateless session bean Bean2,你会看到下列结果:

  • 如果 Bean1 启动了一个 JTA 事务并在 JTA 事务活动时调用了 Bean2 的方法,JBoss EAP 6 里的行为将和之前版本一样,无需进行修改。
  • 如果 Bean1 没有启动 JTA 事务且调用 Bean2 的方法,JBoss EAP 6 则不会将扩展的持久化上下文传播到 Bean2。这个行为和会将持久化上下文传播至 Bean2 的以前版本不一样。如果你的应用程序期望用事务性实体管理者传播扩展持久化上下文,你需要修改应用程序以在活动的 JTA 事务里进行调用。

3.2.2.10. 用 Infinispan 替换 JPA/Hibernate 二级缓存

总结

对于二级缓存(2LC)而言,JBoss Cache 已经被 Infinispan 所替代。这要求修改 persistence.xml 文件。其语法稍有不同,这取决于你使用 JPA 还是 Hibernate 二级缓存。下面的例子假设你在使用 Hibernate.。

这是 JBoss EAP 5.x 里用 persistence.xml 文件指定二级缓存属性的例子。
<property name="hibernate.cache.region.factory_class"
     value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
<property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
<property name="hibernate.cache.region_prefix" value="services"/>

下面的步骤将使用这个例子来配置 JBoss EAP 6 里的 Infinispan。

过程 3.17. 修改 persistence.xml 文件以使用 Infinispan

  1. 为 JBoss EAP 6 里的 JPA 应用程序配置 Infinispan

    这是如何用 JBoss EAP 6 里的 Infinispan 来指定属性以实现用于 JPA 应用程序的相同配置:
    <property name="hibernate.cache.use_second_level_cache" value="true"/>
    
    
    此外,你需要指定一个值为 ENABLE_SELECTIVEALLshared-cache-mode
    • ENABLE_SELECTIVE 是默认的推荐值。它表示实体不会被缓存,除非你显性地将它标记为可缓存的。
      <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
      
      
    • ALL 表示实体总是被缓存,即使你将其标记为不可缓存的。
      <shared-cache-mode>ALL</shared-cache-mode>
      
      
  2. 为 JBoss EAP 6 里的原生 Hibernate 应用程序配置 Infinispan

    这是如何用 JBoss EAP 6 里的 Infinispan 来指定用于原生 Hibernate 应用程序的相同配置:
    <property name="hibernate.cache.region.factory_class"
         value="org.jboss.as.jpa.hibernate4.infinispan.InfinispanRegionFactory"/>
    <property name="hibernate.cache.infinispan.cachemanager"
         value="java:jboss/infinispan/container/hibernate"/>     
    <property name="hibernate.transaction.manager_lookup_class"
         value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
    <property name="hibernate.cache.use_second_level_cache" value="true"/>
    
    
    
    你也必须将下列依赖关系添加至 MANIFEST.MF 文件:
    Manifest-Version: 1.0
    Dependencies: org.infinispan, org.hibernate
    
关于 Hibernate 缓存属性的更多信息,请参考: 第 3.2.2.11 节 “Hibernate Cache 属性”

3.2.2.11. Hibernate Cache 属性

表 3.6. 属性

属性名 描述
hibernate.cache.provider_class
自定义 CacheProvider 的类名。
hibernate.cache.use_minimal_puts
布尔值。优化二级缓存操作,通过更频繁的读操作来最小化写操作。这个设置对于群集缓存更有用,且在 Hibernate3 里,群集缓存实现是默认启用它的。
hibernate.cache.use_query_cache
布尔值。启用队列缓存。单个的对列仍然需要设置为 cacheable。
hibernate.cache.use_second_level_cache
布尔值。用来完全禁用二级缓存,而它对于指定了<缓存>映射的类来说默认是启用的。
hibernate.cache.query_cache_factory
自定义 QueryCache 接口的类名。默认值是内置的 StandardQueryCache
hibernate.cache.region_prefix
用于二级缓存区名的前缀。
hibernate.cache.use_structured_entries
布尔值。强制 Hibernate 以更友好的格式在二级缓存里保存数据。
hibernate.cache.default_cache_concurrency_strategy
当使用 @Cacheable@Cache 时为使用的默认org.hibernate.annotations.CacheConcurrencyStrategy 设置的名称。而 @Cache(strategy="..") 用来覆盖这个默认值。

3.2.2.12. 移植到 Hibernate Validator 4

总结

Hibernate Validator 4.x 采用了完全的新代码,它实现了 JSR 303 - Bean Validation。从 Validator 3.x 到 4.x 的移植过程非常直观,但在移植时你必须进行一些修改。

过程 3.18. 你可能需要执行一个或多个下面的任务:

  1. 访问默认的 ValidatorFactory

    JBoss EAP 6 捆绑了一个默认的 ValidatorFactory java:comp/ValidatorFactory 到 JNDI 上下文。
  2. 理解生命周期触发的检验

    和 Hibernate Core 4 一起使用时,Hibernate Core 将自动启用基于生命周期的检验。
    1. 检验发生在实体 INSERTUPDATEDELETE 操作上。
    2. 你可以配置组通过使用下列属性的事件类型来检验:
      • javax.persistence.validation.group.pre-persist
      • javax.persistence.validation.group.pre-update,和
      • javax.persistence.validation.group.pre-remove
      这些属性的值是用逗号隔开的、要检验的组的全限定类名。
      Validation 组是 Bean Validation 规格的一个新功能。如果你不想利用这个功能,移植到 Hibernate Validator 4 时无需进行任何修改。
    3. 你可以设置 javax.persistence.validation.mode 属性为 none 来禁用基于生命周期的检验。这个属性的其他值还有 auto(默认值),callbackddl
  3. 配置你的应用程序以使用手动检验

    1. 如果你想手动控制检验,你可以用下列方法创建一个 Validator:
      • getValidator() 方法从 ValidatorFactory 创建一个 Validator 实例。
      • 注入 Validator 实例到 EJB、CDI Bean 或其他 Java EE 可注入的资源。
    2. 你可以使用 ValidatorFactory.usingContext() 返回的 ValidatorContext 来定制你的 Validator 实例。使用这个 API,你可以配置一个自定义的 MessageInterpolatorTraverableResolverConstraintValidatorFactory。Bean Validator 规格里指定了这些接口,在 Hibernate Validator 4 里它们是新的接口。
  4. 修改代码以使用新的 Bean Validation 约束

    当移植到 Hibernate Validator 4 时新的 Bean 级别的检验约束需要修改代码。
    1. 要升级到 Hibernate Validator 4,你必须使用下列包里的约束:
      • javax.validation.constraints
      • org.hibernate.validator.constraints
    2. Hibernate Validator 3 里所有的约束在 Hibernate Validator 4 里仍然可用。要使用它们,你需要导入指定的类,且在某些情况下,修改其约束参数的名称或类型。
  5. 使用自定义的约束

    在 Hibernate Validator 3 里,自定义的约束需要实现 org.hibernate.validator.Validator 接口。 在 Hibernate Validator 4 里,你需要实现 javax.validation.ConstraintValidator 接口。这个接口包含了和以前接口相同的 initialize()isValid() 方法,但方法签名有了变动。此外,Hibernate Validator 4 不再支持 DDL 修改。

3.2.3. JSF 的修改

3.2.3.1. 启用应用程序以使用更旧版本的 JSF

总结

如果你的应用程序使用了更旧版本的 JSF,你不需要升级到 JSF 2.0。相反,你可以创建一个 jboss-deployment-structure.xml 文件来请求 JBoss EAP 6 对你的应用程序部署使用 JSF 1.2 而不是 JSF 2.0。这个 JBoss 专有的部署描述符用来控制类加载并放在你的 WAR 的 META-INF/WEB-INF/ 目录里,或者 EAR 的 META-INF/ 目录里。

下面是一个 jboss-deployment-structure.xml 文件,它添加 JSF 1.2 模块的依赖关系且排斥或阻止了 JSF 2.0 模块的自动加载。
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
    <deployment>
        <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
            <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
        </dependencies>
    </deployment>
    <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
            <module name="com.sun.jsf-impl" slot="main"/>
        </exclusions>
        <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
            <module name="com.sun.jsf-impl" slot="1.2"/>
        </dependencies>
    </sub-deployment>
</jboss-deployment-structure>

3.2.4. JAX-RS 和 RESTEasy 的修改

3.2.4.1. 配置 JAX-RS 和 RESTEasy 的修改

JBoss EAP 6 会自动设置 RESTEasy,所以你不需要自己来配置。因此,你应该从 web.xml 文件里删除所有现有的 RESTEasy 配置并替换为下列三个选项之一:

  1. 子类化 javax.ws.rs.core.Application 并使用 @ApplicationPath 注解。
    这是最简单的选项且不要求任何 XML 配置。在你的应用程序里简单地将 javax.ws.rs.core.Application 作为子类并用你想将 JAX-RS 类可用的路径来进行注解。例如:
    @ApplicationPath("/mypath")
    public class MyApplication extends Application {
    }
    
    在上面的例子里,JAX-RS 资源位于路径 /MY_WEB_APP_CONTEXT/mypath/

    注意

    注意,路径应该指定为 /mypath 而不是 /mypath/*
  2. javax.ws.rs.core.Application 作为子类并使用 web.xml 文件来设立 JAX-RS 映射。
    如果你想使用 @ApplicationPath 注解,你仍需要将 javax.ws.rs.core.Application 子类化。然后你可以在 web.xml 文件里设置 JAX-RS 映射。例如:
    public class MyApplication extends Application {
    }
    
    <servlet-mapping>
       <servlet-name>com.acme.MyApplication</servlet-name>
       <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    
    在上面的例子里,JAX-RS 资源位于路径 /MY_WEB_APP_CONTEXT/hello

    注意

    你也可以使用这个方法来覆盖用 @ApplicationPath 注解设置的应用程序路径。
  3. 修改 web.xml 文件。
    如果你不想将 Application 子类化,你可以像下面这样在 web.xml 文件里设置 JAX-RS 映射:
    <servlet-mapping>
       <servlet-name>javax.ws.rs.core.Application</servlet-name>
       <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    
    在上面的例子里,JAX-RS 资源位于路径 /MY_WEB_APP_CONTEXT/hello

    注意

    当你选择这个选项时,你只需要添加映射。你不需要添加对应的 servlet。服务器将负责自动添加对应的 servlet。

3.2.5. LDAP Security Realm 的修改

3.2.5.1. 配置 LDAP Security Realm 的修改

在 JBoss EAP 5 里,LDAP 安全区是在 login-config.xml 文件里的 <application-policy> 元素中配置的。而在 JBoss EAP 6 里,LDAP 安全区是在服务器配置文件里的 <security-domain> 里进行配置。对于独立服务器,配置文件是 standalone/configuration/standalone.xml;如果是在受管域里运行服务器,这个文件是 domain/configuration/domain.xml
下面是一个 JBoss EAP 5 里 login-config.xml 文件中的 LDAP 安全区配置:
<application-policy name="mcp_ldap_domain">
  <authentication>
    <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required">
      <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
      <module-option name="java.naming.security.authentication">simple</module-option>
      ....
    </login-module>
  </authentication>
</application-policy>
这是一个 JBoss EAP 6 里服务器配置文件里的 LDAP 配置的例子:
<subsystem xmlns="urn:jboss:domain:security:1.0">
  <security-domains>
    <security-domain name="mcp_ldap_domain" type="default">
      <authentication>
        <login-module code="org.jboss.security.auth.spi.LdapLoginModule" flag="required">
          <module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
          <module-option name="java.naming.security.authentication" value="simple"/>
          ...
        </login-module>
      </authentication>
    </security-domain>
  </security-domains>
</subsystem>

注意

JBoss EAP 6 里修改了 XML parser。在 JBoss EAP 5 里,你像这样指定模块选项为元素内容:
<module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
现在,模块选项必须被指定为带有 "value=" 的元素属性:
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>

3.2.6. HornetQ 的修改

3.2.6.1. 关于 HornetQ 和 NFS

在多数情况下,如果日志类型使用 NIO,由于同步的锁机制, NFS 并不是一个存储和 HornetQ 一起使用的 JMS 数据的合适方式。然而,在某些情况下 NFS 可以用于红帽企业版 Linux 服务器。这是因为红帽企业版 Linux 使用的 NFS 实现所致。
红帽企业版 Linux NFS 实现支持直接 I/O(设置 O_DIRECT 标记并打开文件)以及基于内核的异步 I/O。使用这些功能,依据严格的配置规则,你可以将 NFS 做为一个共享存储选项使用。
  • 你必须配置 HornetQ 以使用 ASYNCIO 日志类型。
  • 红帽企业版 Linux NFS 客户缓存必须被禁用。

重要

你应该在 JBoss EAP 6 启动后检查服务器日志,以确保成功加载原生库且使用 ASYNCIO 日志类型。如果原生库加载失败,HornetQ 将使用 NIO 日志类型并在服务器日志里注明。

重要

实现异步 I/O 的原生库要求将 libaio 安装在 JBoss EAP 6 所运行的红帽企业版 Linux 系统上。

3.2.6.2. 配置 JMS 桥以移植现有的 JMS 消息到到 JBoss EAP 6

JBoss EAP 6 将 JBoss Messaging 替换为 HornetQ 以作为默认的 JMS 实现。从一个环境移植 JMS 消息到另外一个环境的最简单的方法是使用 JMS 桥。虽然 JBoss EAP 6 目前没有可用的 JMS 桥,但你可以在 JBoss EAP 5.x 里部署一个 JMS 桥来将消息移至 JBoss EAP 6。要避免版本间类的冲突,你必须使用下列过程来配置 JMS 桥。SAR 目录和桥的名称都是所写且可以根据需要更改。

过程 3.19. 配置 JMS 桥

  1. 在 JBoss EAP 6 的 deploy 目录里创建一个子目录来包含 SAR,如EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar
  2. EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar/ 里创建一个名为 META-INF 的子目录。
  3. EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar/META-INF/ 目录里创建一个包含类似于下列信息的 jboss-service.xml 文件。
    <server>
       <loader-repository>
          com.example:archive=unique-archive-name
          <loader-repository-config>java2ParentDelegation=false</loader-repository-config>
       </loader-repository> 
    
       <!-- JBoss Enterprise Application Platform 6 JMS Provider --> 
       <mbean code="org.jboss.jms.jndi.JMSProviderLoader" name="jboss.messaging:service=JMSProviderLoader,name=EnterpriseApplicationPlatform6JMSProvider">
          <attribute name="ProviderName">EnterpriseApplicationPlatform6JMSProvider</attribute>
          <attribute name="ProviderAdapterClass">org.jboss.jms.jndi.JNDIProviderAdapter</attribute>
          <attribute name="FactoryRef">jms/RemoteConnectionFactory</attribute> 
          <attribute name="QueueFactoryRef">jms/RemoteConnectionFactory</attribute>  
          <attribute name="TopicFactoryRef">jms/RemoteConnectionFactory</attribute>      
          <attribute name="Properties">
             java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
             java.naming.provider.url=remote://EnterpriseApplicationPlatform6host:4447
             java.naming.security.principal=jbossuser
             java.naming.security.credentials=jbosspass
          </attribute>
       </mbean> 
    
       <mbean code="org.jboss.jms.server.bridge.BridgeService" name="jboss.jms:service=Bridge,name=MyBridgeName" xmbean-dd="xmdesc/Bridge-xmbean.xml">      
          <depends optional-attribute-name="SourceProviderLoader">jboss.messaging:service=JMSProviderLoader,name=JMSProvider</depends>          
          <depends optional-attribute-name="TargetProviderLoader">jboss.messaging:service=JMSProviderLoader,name=EnterpriseApplicationPlatform6JMSProvider</depends>          
          <attribute name="SourceDestinationLookup">/queue/A</attribute>      
          <attribute name="TargetDestinationLookup">jms/queue/test</attribute>       
          <attribute name="QualityOfServiceMode">1</attribute>           
          <attribute name="MaxBatchSize">1</attribute>      
          <attribute name="MaxBatchTime">-1</attribute>           
          <attribute name="FailureRetryInterval">60000</attribute>      
          <attribute name="MaxRetries">-1</attribute>      
          <attribute name="AddMessageIDInHeader">false</attribute>
          <attribute name="TargetUsername">jbossuser</attribute>
          <attribute name="TargetPassword">jbosspass</attribute>
       </mbean>
    </server>
            
    
    

    注意

    <load-repository> 确保了 SAR 具有隔离的类加载器。而且请注意 JNDI 查找和桥 “目标” 都包含了用户 "jbossuser"/密码 "jbosspass" 的安全凭证。这是因为 EAP 6 默认是有设置安全性的。名为 "jbossuser" /密码为 "jbosspass" 的用户是在角色为 guestApplicationRealm 里用 EAP_HOME/bin/add_user.sh 脚本来创建的。
  4. 将下列 JAR 从 EAP_HOME/modules/system/layers/base/ 目录复制到 EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar/ 目录。用实际的版本号替换每个 VERSION_NUMBER
    • org/hornetq/main/hornetq-core-VERSION_NUMBER.jar
    • org/hornetq/main/hornetq-jms-VERSION_NUMBER.jar
    • org/jboss/ejb-client/main/jboss-ejb-client-VERSION_NUMBER.jar
    • org/jboss/logging/main/jboss-logging-VERSION_NUMBER.jar
    • org/jboss/logmanager/main/jboss-logmanager-VERSION_NUMBER.jar
    • org/jboss/marshalling/main/jboss-marshalling-VERSION_NUMBER.jar
    • org/jboss/marshalling/river/main/jboss-marshalling-river-VERSION_NUMBER.jar
    • org/jboss/remote-naming/main/jboss-remote-naming-VERSION_NUMBER.jar
    • org/jboss/remoting3/main/jboss-remoting-VERSION_NUMBER.jar
    • org/jboss/sasl/main/jboss-sasl-VERSION_NUMBER.jar
    • org/jboss/netty/main/netty-VERSION_NUMBER.jar
    • org/jboss/remoting3/remote-jmx/main/remoting-jmx-VERSION_NUMBER.jar
    • org/jboss/xnio/main/xnio-api-VERSION_NUMBER.jar
    • org/jboss/xnio/nio/main.xnio-nio-VERSION_NUMBER.jar

    注意

    不要简单地复制 EAP_HOME/bin/client/jboss-client.jar,因为 javax API 类会和 JBoss EAP 5.x 里的类相冲突。

3.2.6.3. 移植你的应用程序以将 HornetQ 用作 JMS 提供者

EAP 6 里不再包含 JBoss Messaging。如果你的应用程序将 JBoss Messaging 用作消息提供者,你需要用 HornetQ 替换 JBoss Messaging。

过程 3.20. 在开始之前

  1. 关闭客户和服务器。
  2. 备份任何 JBoss Messaging 数据。消息数据保存在数据库中前缀为 JBM_ 的表里。

过程 3.21. 修改提供者为 HornetQ

  1. 转移配置

    请转移现有的 JBoss Messaging 配置到 JBoss EAP。下面的配置可在位于 JBoss Messaging 服务器的上的部署描述符里找到:
    • 连接工厂服务的配置
      这个配置描述了和 JBoss Messaging 服务器一起部署的 JMS 连接工厂。JBoss Messaging 用一个名为 connection-factories-service.xml 的文件来配置连接工厂,这个文件位于应用服务器的 deployment 目录里。
    • 目的地配置
      这个配置描述了和 JBoss Messaging 一同部署的 JMS 队列和主题。在默认情况下,JBoss Messaging 在一个名为 destinations-service.xml 的文件里配置目的地,这个文件位于应用服务器的 deployment 目录下。
    • 消息桥服务配置
      这个配置描述了和 JBoss Messaging 服务器一起部署的桥服务。在默认情况下不会部署桥,所以部署文件的名称根据 JBoss Messaging 安装而有所不同。
  2. 修改你的程序代码

    如果应用程序代码使用了标准的 JMS,你不需要修改代码。然而,如果应用程序将连至群集,你必须小心地查看 HornetQ 文档里关于群集模式的内容。群集超出了 JMS 规格和 HornetQ 的范围,JBoss Messaging 采用了很不一样的方法来实现群集功能。
    如果应用程序使用 JBoss Messaging 专有的功能,你必须修改代码以使用 HornetQ 里相等的功能。
    关于如何用 HornetQ 配置消息系统,请参考 第 3.2.6.4 节 “用 HornetQ 配置消息系统”
  3. 移植现有的消息

    使用 JMS 桥移动 JBoss Messaging 数据库里的任何消息到 HornetQ 日志。配置 JMS 桥的说明可以在这里找到: 第 3.2.6.2 节 “配置 JMS 桥以移植现有的 JMS 消息到到 JBoss EAP 6 ”

3.2.6.4. 用 HornetQ 配置消息系统

我们推荐在 JBoss EAP 6 里配置消息系统的方法是通过管理控制台或管理 CLI。你可以用这些工具来进行持久性的修改而无需手动编辑 standalone.xmldomain.xml 文件。但熟悉默认配置文件的消息组件是有用处的,而使用管理工具的示例的文档里包含了配置文件的片段以供参考。

3.2.7. 群集的修改

3.2.7.1. 修改应用程序以用于群集环境

过程 3.22. 

  1. 启动启用了群集的 JBoss EAP 6

    要在 JBoss EAP 5.x 里启用群集,你需要使用 all 配置集或其衍生配置集来启动服务器实例,如:
    $ EAP5_HOME/bin/run.sh -c all
    在 JBoss EAP 6 里,启用群集的方法取决于服务器是独立的还是运行在受管域里。
    1. 为运行在受管域l里的服务器启用群集

      要为使用域控制器启动的服务器的群集,请更新你的 domain.xml 并指定一个服务器组来使用 ha 配置集和 ha-sockets 套接字绑定组。例如:
      <server-groups>
        <server-group name="main-server-group" profile="ha">
          <jvm name="default">
            <heap size="64m" max-size="512m"/>
          </jvm>
          <socket-binding-group ref="ha-sockets"/>
        </server-group>
      </server-group>
      
    2. 为独立服务器启用群集

      要为独立服务器启用群集,用下列配置文件启动服务器 $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME
  2. 指定绑定地址

    在 JBoss EAP 5.x 里,你通常要用 -b 命令行参数来指定用于群集的绑定地址:$ EAP_HOME/bin/run.sh -c all -b 192.168.0.2
    在 JBoss EAP 6 里,绑定地址是在 EAP 6 配置文件里的相关套接字绑定部分显性定义的。对于用域控制器启动的服务器,绑定地址是在 domain/configuration/host.xml 文件里定义的;而对于独立服务器, 绑定地址是在 standalone-ha.xml 文件里定义的:
    <interfaces>
          <interface name="management">
        <inet-address value="192.168.0.2"/>
          </interface>
          <interface name="public">
        <inet-address value="192.168.0.2"/>
      </interface>
      </interfaces>
    
    <socket-binding-groups>
          <socket-binding-group name="ha-sockets" default-interface="public">
        <!-- ... -->
          </socket-binding-group>
      </socket-binding-groups>
    
    在上面的例子里,public 接口被指定为 ha-sockets 套接字绑定组里的所有套接字的默认接口。
  3. 配置 jvmRoute 以支持 mod_jk 和 mod_proxy

    在 JBoss EAP 5 里,web 服务器 jvmRoute 是用 server.xml 文件里的一个属性进行配置的。在 JBoss EAP 6 里,jvmRoute 属性使用 instance-id 属性在服务器配置文件的 web subsystem 部分进行配置。
    <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false" instance-id="{JVM_ROUTE_SERVER}">
    
    
    上面的 {JVM_ROUTE_SERVER} 应该用 jvmRoute 服务器 ID 替换。
    instance-id 也可以用管理控制台来设置。
  4. 指定多点传送地址和端口

    在 JBoss EAP 5.x 里,你可以使用命令行参数 -u-m 分别指定用于群集间通讯的多点传送地址和端口,如:$ EAP_HOME/bin/run.sh -c all -u 228.11.11.11 -m 45688
    在 JBoss EAP 6 里,用于群集间通讯的多点传送地址和端口是用相关的 JGroups 协议栈引用的 socket-binding 定义的。
    <subsystem xmlns="urn:jboss:domain:jgroups:1.0" default-stack="udp">
        <stack name="udp">
            <transport type="UDP" socket-binding="jgroups-udp"/>
            <!-- ... -->
        </stack>
    </subsystem>
    
    <socket-binding-groups>
        <socket-binding-group name="ha-sockets" default-interface="public">
            <!-- ... -->
            <socket-binding name="jgroups-udp" port="55200" multicast-address="228.11.11.11" multicast-port="45688"/>
            <!-- ... -->
        </socket-binding-group>
    </socket-binding-groups>
    
    
    如果你想在命令行指定多点传送地址和端口,你可以将多点传送地址和端口定义为系统属性并在启动服务器时在命令行使用这些属性。在下面的例子里, jboss.mcast.addr 是多点传送地址的变量名,而 jboss.mcast.port 是端口的变量名。
    <socket-binding name="jgroups-udp" port="55200"
     multicast-address="${jboss.mcast.addr:230.0.0.4}" multicast-port="${jboss.mcast.port:45688}"/>
    
    
    你可以用下列命令行参数启动服务器:$ EAP_HOME/bin/domain.sh -Djboss.mcast.addr=228.11.11.11 -Djboss.mcast.port=45688
  5. 使用其他的协议栈

    在 JBoss EAP 5.x 里,你可以使用 jboss.default.jgroups.stack 系统属性来操纵用于群集服务的默认协议栈。$ EAP_HOME/bin/run.sh -c all -Djboss.default.jgroups.stack=tcp
    在 JBoss EAP 6 里,默认的协议栈是由 domain.xmlstandalone-ha.xml里的 JGroups 子系统定义的:
    <subsystem xmlns="urn:jboss:domain:jgroups:1.0" default-stack="udp">
        <stack name="udp">
            <!-- ... -->
        </stack>
    </subsystem>
    

3.2.7.2. 实现 HA 单点登录

总结

在 JBoss EAP 5 里,HA 单点登录独立于其他部署部署在 deploy-hasingleton/ 目录里。这是为了防止自动部署并确保 HASingletonDeployer 服务控制部署并只在群集里的主节点上部署归档。因为没有热部署功能,所以重新部署需要重起服务器。而且,如果主节点发生故障时用另外的节点作为主节点,单点登录服务必须完成整个部署过程来提供服务。

在 JBoss EAP 6 里这一点已经改变了。使用 SingletonService 时,目标服务安装在群集里的每个节点上,但在任一时刻都只能启动一个节点。这个方法简化了部署要求并最小化了重定位节点间的单点登录主服务所需的时间。

过程 3.23. 实现 HA 单点登录服务

  1. 编写 HA 单点登录服务应用程序。

    下面是用 SingletonService decorater 包裹的服务作为单点登录服务部署的一个简单例子。
    1. 创建单点登录服务。

      下面是一个 singleton 服务的例子:
      package com.mycompany.hasingleton.service.ejb;
      
      import java.util.concurrent.atomic.AtomicBoolean;
      import java.util.logging.Logger;
      
      import org.jboss.as.server.ServerEnvironment;
      import org.jboss.msc.inject.Injector;
      import org.jboss.msc.service.Service;
      import org.jboss.msc.service.ServiceName;
      import org.jboss.msc.service.StartContext;
      import org.jboss.msc.service.StartException;
      import org.jboss.msc.service.StopContext;
      import org.jboss.msc.value.InjectedValue;
      
      /**
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      public class EnvironmentService implements Service<String> {
          private static final Logger LOGGER = Logger.getLogger(EnvironmentService.class.getCanonicalName());
          public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton");
          /**
           * A flag whether the service is started.
           */
          private final AtomicBoolean started = new AtomicBoolean(false);
      
          private String nodeName;
      
          private final InjectedValue<ServerEnvironment> env = new InjectedValue<ServerEnvironment>();
      
          public Injector<ServerEnvironment> getEnvInjector() {
              return this.env;
          }
      
          /**
           * @return the name of the server node
           */
          public String getValue() throws IllegalStateException, IllegalArgumentException {
              if (!started.get()) {
                  throw new IllegalStateException("The service '" + this.getClass().getName() + "' is not ready!");
              }
              return this.nodeName;
          }
      
          public void start(StartContext arg0) throws StartException {
              if (!started.compareAndSet(false, true)) {
                  throw new StartException("The service is still started!");
              }
              LOGGER.info("Start service '" + this.getClass().getName() + "'");
              this.nodeName = this.env.getValue().getNodeName();
          }
      
          public void stop(StopContext arg0) {
              if (!started.compareAndSet(true, false)) {
                  LOGGER.warning("The service '" + this.getClass().getName() + "' is not active!");
              } else {
                  LOGGER.info("Stop service '" + this.getClass().getName() + "'");
              }
          }
      }
      
      
    2. 创建一个 Singleton EJB 在服务器启动时将这个服务作为 SingletonService 启动。

      下面是在服务器启动时启动 SingletonService 的 Singleton EJB 的例子:
      package com.mycompany.hasingleton.service.ejb;
      
      import java.util.Collection;
      import java.util.EnumSet;
      
      import javax.annotation.PostConstruct;
      import javax.annotation.PreDestroy;
      import javax.ejb.Singleton;
      import javax.ejb.Startup;
      
      import org.jboss.as.clustering.singleton.SingletonService;
      import org.jboss.as.server.CurrentServiceContainer;
      import org.jboss.as.server.ServerEnvironment;
      import org.jboss.as.server.ServerEnvironmentService;
      import org.jboss.msc.service.AbstractServiceListener;
      import org.jboss.msc.service.ServiceController;
      import org.jboss.msc.service.ServiceController.Transition;
      import org.jboss.msc.service.ServiceListener;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      
      /**
       * A Singleton EJB to create the SingletonService during startup.
       * 
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      @Singleton
      @Startup
      public class StartupSingleton {
        private static final Logger LOGGER = LoggerFactory.getLogger(StartupSingleton.class);
      
        /**
         * Create the Service and wait until it is started.<br/>
         * Will log a message if the service will not start in 10sec. 
         */
        @PostConstruct
        protected void startup() {
          LOGGER.info("StartupSingleton will be initialized!");
      
          EnvironmentService service = new EnvironmentService();
          SingletonService<String> singleton = new SingletonService<String>(service, EnvironmentService.SINGLETON_SERVICE_NAME);
          // if there is a node where the Singleton should deployed the election policy might set,
          // otherwise the JGroups coordinator will start it
          //singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new NamePreference("node2/cluster"), new SimpleSingletonElectionPolicy()));
          ServiceController<String> controller = singleton.build(CurrentServiceContainer.getServiceContainer())
              .addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvInjector())
              .install();
      
          controller.setMode(ServiceController.Mode.ACTIVE);
          try {
            wait(controller, EnumSet.of(ServiceController.State.DOWN, ServiceController.State.STARTING), ServiceController.State.UP);
            LOGGER.info("StartupSingleton has started the Service");
          } catch (IllegalStateException e) {
            LOGGER.warn("Singleton Service {} not started, are you sure to start in a cluster (HA) environment?",EnvironmentService.SINGLETON_SERVICE_NAME);
          }
        }
      
        /**
         * Remove the service during undeploy or shutdown
         */
        @PreDestroy
        protected void destroy() {
          LOGGER.info("StartupSingleton will be removed!");
          ServiceController<?> controller = CurrentServiceContainer.getServiceContainer().getRequiredService(EnvironmentService.SINGLETON_SERVICE_NAME);
          controller.setMode(ServiceController.Mode.REMOVE);
          try {
            wait(controller, EnumSet.of(ServiceController.State.UP, ServiceController.State.STOPPING, ServiceController.State.DOWN), ServiceController.State.REMOVED);
          } catch (IllegalStateException e) {
            LOGGER.warn("Singleton Service {} has not be stopped correctly!",EnvironmentService.SINGLETON_SERVICE_NAME);
          }
        }
      
        private static <T> void wait(ServiceController<T> controller, Collection<ServiceController.State> expectedStates, ServiceController.State targetState) {
          if (controller.getState() != targetState) {
            ServiceListener<T> listener = new NotifyingServiceListener<T>();
            controller.addListener(listener);
            try {
              synchronized (controller) {
                int maxRetry = 2;
                while (expectedStates.contains(controller.getState()) && maxRetry > 0) {
                  LOGGER.info("Service controller state is {}, waiting for transition to {}", new Object[] {controller.getState(), targetState});
                  controller.wait(5000);
                  maxRetry--;
                }
              }
            } catch (InterruptedException e) {
              LOGGER.warn("Wait on startup is interrupted!");
              Thread.currentThread().interrupt();
            }
            controller.removeListener(listener);
            ServiceController.State state = controller.getState();
            LOGGER.info("Service controller state is now {}",state);
            if (state != targetState) {
              throw new IllegalStateException(String.format("Failed to wait for state to transition to %s.  Current state is %s", targetState, state), controller.getStartException());
            }
          }
        }
      
        private static class NotifyingServiceListener<T> extends AbstractServiceListener<T> {
          @Override
          public void transition(ServiceController<? extends T> controller, Transition transition) {
            synchronized (controller) {
              controller.notify();
            }
          }
        }
      }
      
      
    3. 创建一个 Stateless Session Bean 从客户端访问服务。

      下面是一个通过客户端访问服务的 stateless session bean 的例子:
      package com.mycompany.hasingleton.service.ejb;
      
      import javax.ejb.Stateless;
      
      import org.jboss.as.server.CurrentServiceContainer;
      import org.jboss.msc.service.ServiceController;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      /**
       * A simple SLSB to access the internal SingletonService.
       * 
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      @Stateless
      public class ServiceAccessBean implements ServiceAccess {
          private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAccessBean.class);
      
          public String getNodeNameOfService() {
              LOGGER.info("getNodeNameOfService() is called()");
              ServiceController<?> service = CurrentServiceContainer.getServiceContainer().getService(
                      EnvironmentService.SINGLETON_SERVICE_NAME);
              LOGGER.debug("SERVICE {}", service);
              if (service != null) {
                  return (String) service.getValue();
              } else {
                  throw new IllegalStateException("Service '" + EnvironmentService.SINGLETON_SERVICE_NAME + "' not found!");
              }
          }
      }
      
      
    4. 创建单点登录服务的商业逻辑接口。

      下面是一个用于 SingletonService 的商业逻辑接口的例子:
      package com.mycompany.hasingleton.service.ejb;
      
      import javax.ejb.Remote;
      
      /**
       * Business interface to access the SingletonService via this EJB
       * 
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      @Remote
      public interface ServiceAccess {
          public abstract String getNodeNameOfService();
      } 
      
      
      
  2. 启动每个启用了群集的 JBoss EAP 6 实例。

    启用群集的方法取决于服务器是否是独立服务器还是运行在受管域里。
    1. 为运行在受管域l里的服务器启用群集。

      你可以使用管理 CLI 启用群集或者你可以手动编辑配置文件。
      • 使用管理 CLI 启用群集。

        1. 启动你的域控制器。

        2. 在你的操作系统里打开一个命令行提示窗口。

        3. 连接至管理 CLI,传入域控制器 IP 地址或 DNS 名称。

          在这个例子里,我们假设域控制器的 IP 地址是 192.168.0.14
          • 对于 Linux,输入下列命令:
            $ EAP_HOME/bin/jboss-cli.sh --connect --controller=192.168.0.14
            $ Connected to domain controller at 192.168.0.14
            
          • 对于 Windows,输入下列命令:
            C:\>EAP_HOME\bin\jboss-cli.bat --connect --controller=192.168.0.14
            C:\> Connected to domain controller at 192.168.0.14
            
        4. 添加 main-server 服务器组。

          [domain@192.168.0.14:9999 /] /server-group=main-server-group:add(profile="ha",socket-binding-group="ha-sockets") 
          {
              "outcome" => "success",
              "result" => undefined,
              "server-groups" => undefined
          }
          
        5. 创建一个名为 server-one 的服务器并将其添加至 main-server 服务器组。

          [domain@192.168.0.14:9999 /]  /host=station14Host2/server-config=server-one:add(group=main-server-group,auto-start=false)
          {
              "outcome" => "success",
              "result" => undefined
          }
          
        6. main-server 服务器组配置 JVM。

          [domain@192.168.0.14:9999 /] /server-group=main-server-group/jvm=default:add(heap-size=64m,max-heap-size=512m)
          {
              "outcome" => "success",
              "result" => undefined,
              "server-groups" => undefined
          }
          
        7. 创建一个名为 server-two 的服务器,将其放在独立的服务器组里并将端口偏移量设置为 100。

          [domain@192.168.0.14:9999 /]  /host=station14Host2/server-config=server-two:add(group=distinct2,socket-binding-port-offset=100)
          {
              "outcome" => "success",
              "result" => undefined
          }
          
      • 通过手动编辑服务器配置文件来启用群集。

        1. 停止 JBoss EAP 服务器。

          重要

          要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
        2. 打开 domain.xml 配置文件

          指定一个服务器组来使用 ha 配置集和 ha-sockets 套接字绑定组:
          <server-groups>
            <server-group name="main-server-group" profile="ha">
              <jvm name="default">
                <heap size="64m" max-size="512m"/>
              </jvm>
              <socket-binding-group ref="ha-sockets"/>
            </server-group>
          </server-groups>
          
          
        3. 打开 host.xml 配置文件

          像下面这样修改这个文件:
          <servers>
            <server name="server-one" group="main-server-group" auto-start="false"/>
            <server name="server-two" group="distinct2">
              <socket-bindings port-offset="100"/>
            </server>
          <servers>
          
          
        4. 启动服务器。

          • 对于 Linux,,输入:EAP_HOME/bin/domain.sh
          • 对于 Microsoft Windows,输入 EAP_HOME\bin\domain.bat
    2. 为独立服务器启用群集

      要为独立服务器启用群集,用节点名称和 standalone-ha.xml 配置文件启动服务器:
      • 对于 Linux,输入:EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME
      • 对于 Microsoft Windows,输入:EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME

    注意

    为了避免在同一主机上运行多个服务器时出现端口冲突, 为每个服务器实例配置standalone-ha.xml 以绑定独立的接口。或者,你可以用端口偏移启动随后的服务器实例,如使用下列命令行里的参数:-Djboss.socket.binding.port-offset=100
  3. 将应用程序部署到服务器上

    如果你使用 Maven 来部署应用程序,请使用 Maven 命令部署到在默认端口运行的服务器:
    mvn clean install jboss-as:deploy
    要部署其他服务器,在命令行传入服务器名和端口号:
    mvn clean package jboss-as:deploy -Ddeploy.hostname=localhost -Ddeploy.port=10099

3.2.8. 服务风格的部署的修改

3.2.8.1. 更新使用服务风格部署的应用程序

总结

虽然 JBoss EAP 6 不再使用服务风格的描述符,容器还是支持这些服务风格的部署而吴需修改。这意味着如果你在 JBoss EAP 5.x 应用程序里使用了 jboss-service.xmljboss-beans.xml 部署描述符,它们应该无需或几乎无需修改就可以运行在 EAP 6 里。你可以继续将这些文件打包在 EAR 或 SAR 里,或者你可以将文件直接放在部署目录里。如果你运行的是独立服务器,部署目录是 EAP_HOME/standalone/deployments/;如果服务器是在受管域里运行,部署目录是:EAP_HOME/domain/deployments/

3.2.9. 远程调用的修改

3.2.9.1. 将进行远程调用的 JBoss EAP 5 应用程序移植到 JBoss EAP 6。

总结

在 JBoss EAP 6 里,对服务器进行远程调用有两个途径:

  • 你可以使用新的 JBoss 专有的 EJB 客户 API 来进行调用。
  • 你可以使用 JNDI 来查找 Bean 的代理并在返回的代理上进行调用。
这个部分包含选项 2:使用 JNDI 的客户程序需要修改代码。

在 JBoss EAP 5 里,EJB 远程接口绑定在 JNDI 里,在默认情况下,本地接口是 "ejbName/local",而远程接口是 "ejbName/remote"。客户应用程序可以用 "ejbName/remote" 来查找 Bean。
在 JBoss EAP 6 里,你可以用下列语法通过 ejb:NAMESPACE_NAME 来远程访问 EJB:对于 stateless bean:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>

对于 stateful bean:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>?stateful

在上面的语法里要替换的值是:
  • <app-name> - 部署的 EJB 的应用程序名称。这通常是 EJB 部署的 EAR 名称,不带 .ear 后缀,但可以在 application.xml 里进行覆盖。如果应用程序没有部署为 .EAR,这个值是一个空字符串。我们假设这个例子没有部署为 EAR。
  • <module-name> - 服务器上部署的 EJB 名称。这通常是 EJB 部署的 JAR 名称,不带 .jar 后缀,但可以在 ejb-jar.xml 里进行覆盖。在这个例子里,假设 EJB 是部署在 jboss-as-ejb-remote-app.jar 里,所以其模块名是 jboss-as-ejb-remote-app。
  • <distinct-name> - EJB 的一个可选的不同名称。这个例子没有使用不同名称,所以它使用空字符串。
  • <bean-name> - 默认是 bean 实现类的简单名称。
  • <fully-qualified-classname-of-the-remote-interface> - the remote view fully qualified class name.
更新客户端代码

假设你已经将下列 stateless EJB 部署到了 JBoss EAP 6 服务器。请注意,它开放了 bean 的一个远程视图。

@Stateless
@Remote(RemoteCalculator.class)
public class CalculatorBean implements RemoteCalculator {
 
    @Override
    public int add(int a, int b) {
        return a + b;
    }
 
    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}
在 JBoss EAP 5 里,使用上面的信息,客户 EJB 查找和调用的代码类似于:
InitialContext ctx = new InitialContext();
RemoteCalculator calculator = (RemoteCalculator) ctx.lookup("CalculatorBean/remote");
int a = 204;
int b = 340;
int sum = calculator.add(a, b);
在 JBoss EAP 6 里,使用上面的信息,客户查找和调用的代码类似于:
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
final String appName = "";
final String moduleName = "jboss-as-ejb-remote-app";
final String distinctName = "";
final String beanName = CalculatorBean.class.getSimpleName();
final String viewClassName = RemoteCalculator.class.getName();
final RemoteCalculator statelessRemoteCalculator =  (RemoteCalculator) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName);
 
int a = 204;
int b = 340;
int sum = statelessRemoteCalculator.add(a, b);
如果你的客户应用程序访问 stateful EJB,你必须在上下文查找后面附加 “?stateful”:
final RemoteCalculator statefulRemoteCalculator =  (RemoteCalculator) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName + "?stateful")

你可以在 QuickStarts 里找到一个完整的、包含服务器和客户端代码的可运行的例程。相关的更多信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》里的『开发企业级应用程序起步』章节中的『回顾 Quickstart 教程』
关于使用 JNDI 进行远程调用的更多信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss 部署指南》里的『Enterprise JavaBeans』章节的『使用 JNDI 远程调用 Session Bean』

3.2.9.2. 用 JNDI 远程调用 Session Bean

这个任务描述了如何为远程客户添加支持以使用 JNDI 调用 Session Bean。这个任务假设该项目是用 Maven 构建的。
ejb-remote Quickstart 包含了演示这个功能的 Maven 项目。这个 Quickstart 包含了要部署的 Session Bean 和远程客户项目。下面的代码示例是从远程客户端程序抽取的。
这个任务假设 Session Bean 不要求进行验证。

前提条件

在开始之前,下列预备条件必须被满足:
  • 你必须有一个可用的 Maven 项目。
  • 已经添加了用于 JBoss EAP 6 Maven Repository 的配置。
  • 已经部署了你要调用的 Session Bean。
  • 部署的 Session Bean 实现了远程商业接口。
  • Session Bean 的远程商业接口作为 Maven 依赖关系可用。如果远程商业接口只作为 JAR 文件可用,那么我们推荐你将 JAR 添加到 Maven 库作为 artifact 使用。请参考 http://maven.apache.org/plugins/maven-install-plugin/usage.html 里的 install:install-file 说明。
  • 你需要知道这个 session bean 所在的服务器的主机名和 JNDI 端口号。
要从远程客户调用 session bean,你首先必须正确配置这个项目。

过程 3.24. 为 session bean 的远程商业接口添加 Maven 项目依赖关系。

  1. 添加所需的项目依赖关系

    必须更新项目的 pom.xml 以包含必要的依赖关系。
  2. 添加 jboss-ejb-client.properties 文件

    JBoss EJB 客户 API 期望在项目根目录找到名为 jboss-ejb-client.properties 的文件,它包含 JNDI 服务的连接信息。添加这个文件到项目的 src/main/resources/ 目录并包含下列内容。
    remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
    
    remote.connections=default
    
    remote.connection.default.host=localhost
    remote.connection.default.port = 4447
    remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
    
    
    根据你的服务器来修改主机名和端口号。4447 是默认的端口号码。对于安全的连接,请将 SSL_ENABLED 一行设置为 true 并取消 SSL_STARTTLS 行的注释。容器里的 Remoting 接口支持使用相同端口的安全和不安全的连接。
  3. 为远程商业接口添加依赖关系

    pom.xml 里为 session bean 的远程商业接口添加 Maven 依赖关系。
    <dependency>
       <groupId>org.jboss.as.quickstarts</groupId>
       <artifactId>jboss-as-ejb-remote-server-side</artifactId>
       <type>ejb-client</type>
       <version>${project.version}</version>
    </dependency>
    
现在已经正确配置了这个项目,你可以添加代码来访问并调用 session bean。

过程 3.25. 用 JNDI 获取 Bean 代理并调用 Bean 的方法

  1. 处理 checked 异常

    下面代码里的两个方法 (InitialContext()lookup()) 都会抛出类型为 javax.naming.NamingException 的 checked 异常。这些方法调用必须包括在捕获 NamingException 的 try/catch 块里,或者是再声明抛出 NamingException 的方法里。ejb-remote quickstart 使用了第二种方法。
  2. 创建一个 JNDI 上下文

    JNDI 上下文对象提供了从服务器请求资源的机制。请使用下列代码创建 JNDI 上下文:
    final Hashtable jndiProperties = new Hashtable();
    jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
    final Context context = new InitialContext(jndiProperties);
    
    JNDI 服务的连接属性是从 jboss-ejb-client.properties 文件里读取的。
  3. 使用 JNDI Context's lookup() 方法来获取 bean 代理

    调用 bean 代理的 lookup() 方法并将你请求的 session bean 的 JNDI 名称传入。这将返回一个对象,它必须转换为包含你要调用的方法的远程商业接口的类型。
    
    final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
        "ejb:/jboss-as-ejb-remote-server-side/CalculatorBean!" + 
        RemoteCalculator.class.getName());
    
    
    Session bean JNDI 名称是用特殊语法定义的。
  4. 调用方法

    现在你已有一个代理 bean 对象,你可以调用远程商业接口里包含的任何方法了。
    int a = 204;
    int b = 340;
    System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
    int sum = statelessRemoteCalculator.add(a, b);
    System.out.println("Remote calculator returned sum = " + sum);
    
    代理 bean 将方法调用请求传入服务器上的 session bean。其结果被返回到代理 bean,然后再返回给调用者。代理 bean 和远程 session bean 间的通讯对于调用者来说是透明的。
你现在应该可以配置一个 Maven 项目来支持调用远程服务器上的 session bean 并编写代码,使用通过 JNDI 从服务器获取的代理 bean 来调用 session bean 方法。

3.2.10. EJB 2.x 的修改

3.2.10.1. 更新使用 EJB 2.x 的应用程序

JBoss EAP 6 提供了对 EJB 2.x 的支持,然而,你需要进行一些代码的修改并用 Full 配置集启动服务器。

过程 3.26. 在 EAP 6 上运行 EJB 2.x

  1. 修改代码以使用新的 JNDI 命名空间规则

    和 EJB 3.0 不同,对于 EJB 2.x,你必须使用完整的 JNDI 前缀。关于新的 JNDI 命名空间规则和代码示例,请参考 第 3.1.8.1 节 “更新应用程序 JNDI 命名空间的名称”
    以前版本里显示如何更新 JNDI 命名空间的示例可以在这里找到: 第 3.1.8.5 节 “以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的”
  2. 替换 JBoss AOP 拦截器

    EAP 6 里不再包含 JBoss AOP (Aspect Oriented Programming,面向方面编程) 。在以前的版本里,EJB 容器使用了 JBoss AOP。然而,在 JBoss EAP 6 里,EJB 容器使用了新的机制。如果你的应用程序使用 JBoss AOP,你需要象下面这样修改你的代码。

    • ejb3-interceptors-aop.xml 里的标准 EJB3 配置现在已经在服务器配置文件里完成了。对于独立的服务器,就是 standalone/configuration/standalone-full.xml 文件。如果你在受管域里运行服务器,这个文件将是 domain/configuration/domain.xml
    • 集成 AOP 拦截器到 EJB 层的应用程序必须注册以使用 EJB3 拦截器和 CDI。服务器端的拦截器可以修改为 EJB3 拦截器。
  3. 修改 jboss-web.xml 文件描述符

    对每个 <ejb-ref><jndi-name> 进行修改以使用新的 JNDI 全限定查找格式。
  4. 替换 jboss.xml 部署描述符文件

    jboss-ejb3.xml 部署描述符替换 jboss.xml 以覆盖和添加 Java EE 定义的 ejb-jar.xml 里提供的功能。这个新文件和 jboss.xml 兼容,而目前的部署里已经忽略了jboss.xml
  5. 用 Full 配置集启动服务器

    EJB 2.x 要求 Java EE 6 的 Full 配置集。要用 Full 配置集启动 EAP 6,在启动服务器时请在命令行使用参数 -c standalone-full.xml

3.2.11. 移植 Seam 2.2 应用程序

3.2.11.1. 移植 Seam 2.2 归档到 JBoss EAP 6

概述

当你移植 Seam 2.2 应用程序时,你需要配置数据源并指定任何模块化依赖关系。你也需要确定应用程序是否依赖于 JBoss EAP 6 不附带的归档并将依赖的 JAR 复制到应用程序的 lib/ 目录。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 3.27. 移植 Seam 2.2 归档

  1. 更新数据源配置

    某些 Seam 2.2 示例使用名为 java:/ExampleDS 的默认 JDBC 数据源。JBoss EAP 6 里已经修改了这个默认的数据源为 java:jboss/datasources/ExampleDS。如果你的应用程序使用了 example 数据库,你可以:
    • 如果你想使用 JBoss EAP 6 附带的示例数据库,请修改 META-INF/persistence.xml 文件以替换现有的 jta-data-source 元素为示例数据库数据源 JNDI 名:
      <!-- <jta-data-source>java:/ExampleDS</jta-data-source> -->
      <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
      
    • 如果你像保留现有的数据库,你可以在 EAP_HOME/standalone/configuration/standalone.xml 文件里添加数据源定义。

      重要

      要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
      下面是在 JBoss EAP 6 里定义的默认 HSQL 数据源定义:
      <datasource name="ExampleDS" jndi-name="java:/ExampleDS" enabled="true" jta="true" use-java-context="true" use-ccm="true">
         <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
         <driver>h2</driver>
         <security>
            <user-name>sa</user-name>
            <password>sa</password>
         </security>
      </datasource>
      
    • 你也可以用 jbossadmin 命令行接口添加数据源定义。下面是你添加数据源所必须使用的语法。结尾的 "\" 表示下一行仍是命令行的一部分。

      例 3.1. 添加数据源定义的语法示例

      $ EAP_HOME/bin/jboss-cli --connect 
      [standalone@localhost:9999 /] data-source add --name=ExampleDS --jndi-name=java:/ExampleDS \ 
            --connection-url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 --driver-name=h2 \ 
            --user-name=sa --password=sa
      
    关于如何配置数据源的更多信息,请参考 第 3.1.6.2 节 “更新数据源配置”
  2. 添加所需的依赖关系

    既然 Seam 2.2 应用程序使用了 JSF 1.2,你需要添加 JSF 1.2 模块的依赖关系并排除 JSF 2.0 模块。为了实现这一点,你需要在包含下列数据的 EAR 的 META-INF/ 目录下创建一个 jboss-deployment-structure.xml 文件:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
                  <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
          </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
                  <module name="com.sun.jsf-impl" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
                  <module name="com.sun.jsf-impl" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    
    如果你的应用程序使用了第三方的日志框架,你需要添加如下这些依赖关系:第 3.1.4.1 节 “修改日志依赖关系”.
  3. 如果你的应用程序使用了 Hibernate 3.x,请首先用 Hibernate 4 库来运行应用程序。

    如果你的应用程序没有使用 Seam Managed Persistence Context、Hibernate search、validation 或其他 Hibernate 4 里已修改的功能,你应该可以用 Hibernate 4 库来运行它。然而,如果你看到指向 Hiberante 类的 ClassNotFoundExceptionsClassCastExceptions,或者看到类似于下面的错误信息,你就可能需要遵循下一步的说明并修改应用程序以使用 Hibernate 3.x 库。
            Caused by: java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.jboss.seam.persistence.HibernateSessionProxy.getSession(Lorg/hibernate/EntityMode;)Lorg/hibernate/Session;" the class loader (instance of org/jboss/modules/ModuleClassLoader) of the current class, org/jboss/seam/persistence/HibernateSessionProxy, and the class loader (instance of org/jboss/modules/ModuleClassLoader) for interface org/hibernate/Session have different Class objects for the type org/hibernate/Session used in the signature
    
  4. 从外部框架或其他位置复制依赖的归档

    如果你的应用程序使用了 Hibernate 3.x 且你无法成功使用 Hibernate 4,你需要像下面这样复制 Hibernate 3.x JAR 到 /lib 目录并在 META-INF/jboss-deployment-structure.xml 里的 deployment 部分排除 Hibernate 库:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
     <deployment>
       <exclusions>
         <module name="org.hibernate"/>
       </exclusions>
     <deployment>
    </jboss-deployment-structure>
    
    当你的应用程序捆绑 Hibernate 3.x 时你还必须采取一些其他的步骤。相关的信息,请参考 第 3.2.2.2 节 “修改使用 Hibernate 和 JPA 的应用程序的配置”
  5. 调试和解决 Seam 2.2 JNDI 错误

    当你移植 Seam 2.2 应用程序时,你可能会在日志里使用 javax.naming.NameNotFoundException错误:
    javax.naming.NameNotFoundException: Name 'jboss-seam-booking' not found in context ''
    如果你不想修改代码里的 JNDI 查找,你可以修改应用程序的 components.xml 文件:
    1. 替换现有的 core-init 元素

      首先,你需要替换现有的 core-init 元素:
      <!-- <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/>   -->
      <core:init debug="true" distributable="false"/>
      
      
    2. 找到服务器日志里的 JNDI 绑定 INFO 消息

      然后,在服务器日志里找到应用程序部署时的 JNDI 绑定 INFO 消息。JNDI 绑定消息应该类似于:
      INFO org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor (MSC service thread 1-1) JNDI bindings for session bean
      named AuthenticatorAction in deployment unit subdeployment "jboss-seam-booking.jar" of deployment "jboss-seam-booking.ear" are as follows:
          java:global/jboss-seam-booking/jboss-seam-booking.jar/AuthenticatorAction!org.jboss.seam.example.booking.Authenticator
          java:app/jboss-seam-booking.jar/AuthenticatorAction!org.jboss.seam.example.booking.Authenticator
          java:module/AuthenticatorAction!org.jboss.seam.example.booking.Authenticator
          java:global/jboss-seam-booking/jboss-seam-booking.jar/AuthenticatorAction
          java:app/jboss-seam-booking.jar/AuthenticatorAction
          java:module/AuthenticatorAction
      
    3. 添加组件元素

      对于日志里的每个 JNDI 绑定 INFO 消息,添加匹配的 component 元素 components.xml 文件:
      <component class="org.jboss.seam.example.booking.AuthenticatorAction" jndi-name="java:app/jboss-seam-booking.jar/AuthenticatorAction" />
      
    关于如何调试和解决移植问题的更多信息,请参考 第 4.2.1 节 “调试和解决移植问题”
    关于 Seam 2 归档的已知移植问题列表,请参考 第 3.2.11.2 节 “Seam 2.2 归档移植的问题”
结果

Seam 2.2 归档成功地在 JBoss EAP 6 里运行。

3.2.11.2. Seam 2.2 归档移植的问题

Seam 2.2 Drools 和 Java 7 是不兼容的
Seam 2.2 Drools 和 Java 7 是不兼容的且会抛出异常 org.drools.RuntimeDroolsException: value '1.7' is not a valid language level error。
签注了 cglib.jar 的 Seam 2.2.5 阻止了 Spring 例程的运行
当 Spring 例程使用 JBoss EAP 5 里 Seam 2.2.5 附带的签注的 cglib.jar 时,它会运行失败并带有下列错误:
java.lang.SecurityException: class "org.jboss.seam.example.spring.UserService$$EnhancerByCGLIB$$7d6c3d12"'s signer information does not match signer information of other classes in the same package
解决办法是取消 cglib.jar 的签注:
zip -d $SEAM_DIR/lib/cglib.jar META-INF/JBOSSCOD\*
Seambay 例程会抛出异常 NotLoggedInException
造成这个结果的原因是当处理 SOAPRequestHandler 里的消息时,SOAP 消息头部为空,从而未设置会话 ID。
解决办法是像 https://issues.jboss.org/browse/JBPAPP-8376 里描述的那样,覆盖 org.jboss.seam.webservice.SOAPRequestHandler.handleOutbound
Seambay 例程抛出异常 UnsupportedOperationException:没有事务。
这个程序错误是由于修改 JBoss EAP 6 里的 UserTransaction 的 JNDI 名称而引起的。
解决办法是如 https://issues.jboss.org/browse/JBPAPP-8322 里所描述的,覆盖 org.jboss.seam.transaction.Transaction.getUserTransaction
Seambay 例程会抛出异常 NotLoggedInException
造成这个结果的原因是当处理 SOAPRequestHandler 里的消息时,SOAP 消息头部为空,从而未设置会话 ID。
解决办法是像 https://issues.jboss.org/browse/JBPAPP-8376 里描述的那样,覆盖 org.jboss.seam.webservice.SOAPRequestHandler.handleOutbound
Tasks 例程抛出 org.jboss.resteasy.spi.UnhandledException:无法解码请求的主体。
这个程序错误是由于 JBoss EAP 5.1.2 里包含的 seam-resteasy-2.2.5 和 JBoss EAP 6 里的 RESTEasy 2.3.1.GA 不兼容而引起的。
解决办法是按照 https://issues.jboss.org/browse/JBPAPP-8315 里所描述的,使用 jboss-deployment-structure.xml 从主部署里排除 resteasy-jaxrs、resteasy-jettison-provider 和 resteasy-jaxb-provider,以及从 jboss-seam-tasks.war 里排除 resteasy-jaxrs、resteasy-jettison-provider、resteasy-jaxb-provider 和 resteasy-yaml-provider。然后你需要在 EAR 里包含 Seam 2.2 附带的 RESTEasy 库。
在 AJAX 请求过程中 org.jboss.seam.core.SynchronizationInterceptor 和 stateful 组件实例 EJB 锁之间发生死锁。
显示错误页面 "Caused by javax.servlet.ServletException with message: "javax.el.ELException: /main.xhtml @36,71 value="#{hotelSearch.pageSize}": org.jboss.seam.core.LockTimeoutException: could not acquire lock on @Synchronized component: hotelSearch" 或类似的错误信息。
问题是 Seam 2 在 stateful session bean(SFSB)锁外部进行锁定且使用不同的作用域。这意味着如果某个线程在相同的事务里两次访问了同一个 EJB,在第一次调用后,它将具有 SFSB 锁,而不是 Seam 的锁。然后另外一个线程可能获得 Seam 锁,这样将和 EJB 锁冲突并阻塞。当第一个线程开始第二次调用时,它将阻塞在 Seam 2 拦截器上并形成死锁。在 Java EE 5 里,EJB 遇到并发访问时将立即抛出异常。而在 JBoss EAP 6 里已经修改了这个行为。
解决办法是在 EJB 里添加 @AccessTimeout(0)。这会使它在发生这种情况时立即抛出 ConcurrentAccessException
Dvdstore 例程创建订单失败并抛出 javax.ejb.EJBTransactionRolledbackException
dvdstore 例程显示了下列错误:
JBAS011437: Found extended persistence context in SFSB invocation call stack but that cannot be used because the transaction already has a transactional context associated with it. This can be avoided by changing application code, either eliminate the extended persistence context or the transactional context. See JPA spec 2.0 section 7.6.3.1.
这是由于 JPA 规格的改变而引起的。
解决办法时修改 CheckoutActionShowOrdersAction 类里的持久化上下文为 transactional 并在 cancelOrderdetailOrder 方法里使用实体管理者的合并操作。
在 JBoss EAP 6 里无法使用 Seam Cache 提供者 JBoss Cache
在 JBoss EAP 6 里不支持 JBoss Cache。这导致应用服务器上的一个 Seam 程序里使用 JBoss Cache 时抛出异常:
java.lang.NoClassDefFoundError: org/jboss/util/xml/JBossEntityResolver
.
Hibernate 3.3.x 自动扫描 JBoss EAP 6 里 JPA 实体的问题。
解决办法时在 persistence.xml 文件里手动列出所有的实体类。例如:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
    <persistence-unit name="example_pu">
        <description>Hibernate 3 Persistence Unit.</description>
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <properties>
            <property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
        </properties>
        <class>com.acme.Foo</class>
        <class>com.acme.Bar</class>
    </persistence-unit>
</persistence>

3.2.12. 移植 Spring 应用程序

3.2.12.1. 移植 Spring 应用程序

关于移植 Spring 应用程序的信息可以在 JBoss Web Framework Kit 文档里找到。你可以从 Customer Portal https://access.redhat.com 下载这个文档。点击KnowledgeProduct Documentation,找到 JBoss Enterprise Middleware,然后点击 JBoss Web Framework Kit 链接。

3.2.13. 其他影响移植的修改

3.2.13.1. 熟悉其他可能影响到移植的修改

下面是 JBoss EAP 6 里可能会影响移植的修改的列表。

3.2.13.2. 修改 Maven 插件名称

jboss-maven-plugin 还没有更新,无法在 JBoss EAP 6 里使用。你必须使用 org.jboss.as.plugins:jboss-as-maven-plugin 来部署到正确的目录里。

3.2.13.3. 修改客户端应用程序

如果你计划移植连接至 JBoss EAP 6 的客户端程序,你应该注意捆绑了客户库的 JAR 的位置和名称。这个 JAR 现在的名称为 jboss-client.jar 且位于 JBOSS_HOME/bin/client/ 目录。它代替了 JBOSS_HOME/client/jbossall-client.jar 并包含所有从远程客户连接 JBoss EAP 6 所需的依赖关系。

第 4 章 工具和提示

4.1. 协助移植的资源

4.1.1. 可协助移植的资源

下面是在移植应用程序到 JBoss EAP 6 时可能会由帮助的资源列表。
工具
这是几个自动化某些配置修改的工具。详情请参考: 第 4.1.2 节 “熟悉可以协助移植的工具”
调试提示
关于你可能遇到的问题和错误的常见原因和解决办法,请参考: 第 4.2.1 节 “调试和解决移植问题”
例程移植
关于已经移植到 JBoss EAP 6 里的例程,请参考: 第 4.3.1 节 “复查例程的移植”

4.1.2. 熟悉可以协助移植的工具

总结

有些工具可以协助你移植应用程序。下面是这些工具以及相关的描述。

Tattletale
由于采用模块化类加载,你需要找到并修订应用程序的依赖关系。Tattletale 可以帮助你确定依赖的模块名称并为应用程序生成配置 XML 内容。
IronJacamar 移植工具
在 JBoss EAP 6 里,数据源和资源适配器都不再在单独的文件里配置了。它们现在在服务器配置文件里进行配置且使用新的 schema。IronJacamar 移植工具可以帮助转换旧的配置为 JBoss EAP 6 需要的格式。

4.1.3. 使用 Tattletale 来查找应用程序的依赖关系

总结

由于 JBoss EAP 6 里的模块化类加载的改动,当你移植应用程序时,你可能会在 JBoss 日志里看到 ClassNotFoundExceptionClassCastException 跟踪信息。要解决这些错误,你需要找到包含这些异常指定的类的 JAR。

Tattletale 是一个优秀的第三方工具,它递归地扫描你的应用程序并提供其内容的详细报告。Tattletale 1.2.0.Beta2 或更新版本包含额外的支持以帮助新的 JBoss 模块化类加载。Tattletale 的 "JBoss EAP 6" 报告可以用于自动确定和生成依赖模块的名称来包含应用程序的 jboss-deployment-structure.xml 文件。

过程 4.1. 安装并运行 Tattletale 来查找应用程序的依赖关系

注意

Tattletale 是一个第三方的工具,它并不是 JBoss EAP 6 的一部分。关于如何安装和使用 Tattletale 的最新文档,请访问其网站:http://www.jboss.org/tattletale

4.1.4. 下载和安装 Tattletale

过程 4.2. 

  1. http://sourceforge.net/projects/jboss/files/JBoss%20Tattletale 下载 Tattletale 1.2.0.Beta2 或更新版本。
  2. 解压文件到你指定的目录。
  3. 修改 TATTLETALE_HOME/jboss-tattletale.properties 文件:
    1. 添加 ee6as7profiles 属性。
      profiles=java5, java6, ee6, as7
    2. 取消 scanreports 属性的注释。

注意

Tattletale 是一个第三方的工具,它并不是 JBoss EAP 6 的一部分。关于如何安装和使用 Tattletale 的最新文档,请访问其网站:http://www.jboss.org/tattletale

4.1.5. 创建和复查 Tattletale 报告

过程 4.3. 

  1. 用这个命令创建 Tattletale 报告: java -jar TATTLETALE_HOME/tattletale.jarAPPLICATION_ARCHIVEOUTPUT_DIRECTORY
    例如: java -jar tattletale-1.2.0.Beta2/tattletale.jar applications/jboss-seam-booking.ear output-results/
  2. 在浏览器里打开 OUTPUT_DIRECTORY/index.html 文件并点击 "Reports" 下的 "JBoss EAP 6"。
    1. 左侧的列列出了应用程序使用的归档。点击 ARCHIVE_NAME 链接来查看归档的细节,如位置、Manifest 信息和它包含的类。
    2. 右侧列上的 jboss-deployment-structure.xml 链接显示了如何为左侧列命名的归档指定模块依赖关系。点击这个链接来查看如何为这个归档定义部署依赖关系模块信息。

注意

Tattletale 是一个第三方的工具,它并不是 JBoss EAP 6 的一部分。关于如何安装和使用 Tattletale 的最新文档,请访问其网站:http://www.jboss.org/tattletale

4.1.6. 使用 IronJacamar 工具来移植数据源和资源适配器配置

总结

在以前的应用服务器版本里,数据源和资源适配器都是使用后缀为 *-ds.xml 的文件来配置和部署的。IronJacamar 1.1 版本包含了一个移植工具,它可以用来将这些文件转换为 JBoss EAP 6 所要求的格式。这个工具解析了之前版本的源配置文件,然后创建 XML 配置并写入新格式的输出文件。这个 XML 文件可以在 JBoss EAP 6 服务器配置文件里的正确的子系统下复制和粘贴。这个工具尽量将旧的属性和元素转换为新的格式,然而,你还是有必要对生成的文件进行额外的修改。

注意

IronJacamar 移植工具是一个第三方的工具,它不会以 JBoss EAP 6 的一部分而被支持。关于 IronJacamar 的更多信息,请访问 http://www.jboss.org/ironjacamar。关于如何安装和使用这个工具的最新文档,请访问:http://docs.jboss.org/ironjacamar/userguide/1.1/en-US/html/tools.html#tools_migration

4.1.7. 下载和安装 IronJacamar 移植工具

注意

只有 IronJacamar 1.1 或更高版本里才有可用的移植工具。

过程 4.5. 

  1. 请从这个链接下载 IronJacamar 1.1 或更高的版本:http://www.jboss.org/ironjacamar/downloads/
  2. 解压下载的文件到你指定的目录。
  3. 在 IronJacamar 里找到转换脚本。
    • Linux 脚本在这里:IRONJACAMAR_HOME/doc/as/converter.sh
    • Windows 批处理文件在这里:IRONJACAMAR_HOME/doc/as/converter.bat

注意

IronJacamar 移植工具是一个第三方的工具,它不会以 JBoss EAP 6 的一部分而被支持。关于 IronJacamar 的更多信息,请访问 http://www.jboss.org/ironjacamar。关于如何安装和使用这个工具的最新文档,请访问:http://docs.jboss.org/ironjacamar/userguide/1.1/en-US/html/tools.html#tools_migration

4.1.8. 使用 IronJacamar 移植工具来转换数据源配置文件

过程 4.6. 

  1. 打开命令行并进入 IRONJACAMAR_HOME/docs/as/ 目录。
  2. 请输入以下命令来运行转换脚本:
    • 对于 Linux:./converter.sh -ds SOURCE_FILE TARGET_FILE
    • 对于 Microsoft Windows:./converter.bat -ds SOURCE_FILE TARGET_FILE
    SOURCE_FILE 是以前版本的 -ds.xml 文件。TARGET_FILE 包含新的配置。
    例如,要转换位于当前目录里的 jboss-seam-booking-ds.xml 数据源配置文件,你可以输入:
    • 对于 Linux:./converter.sh -ds jboss-seam-booking-ds.xml new-datasource-config.xml
    • 对于 Microsoft Windows:./converter.bat -ds jboss-seam-booking-ds.xml new-datasource-config.xml
    请注意数据源转换的参数是 -ds
  3. 从目标文件复制 <datasource> 元素并粘贴到 <subsystem xmlns="urn:jboss:domain:datasources:1.0"><datasources> 元素下的服务器配置文件。

    重要

    要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
    • 如果服务器运行在受管域里,请将 XML 复制到 EAP_HOME/domain/configuration/domain.xml 文件里。
    • 如果运行的是独立服务器,请将 XML 复制到 EAP_HOME/standalone/configuration/standalone.xml 文件里。
  4. 修改新配置文件里生成的 XML。
    下面是 JBoss EAP 5.x 里附带的 Seam 2.2 Booking 例程里的 jboss-seam-booking-ds.xml 数据源配置文件:
    <?xml version="1.0" encoding="UTF-8"?>
    <datasources>
      <local-tx-datasource>
        <jndi-name>bookingDatasource</jndi-name>
        <connection-url>jdbc:hsqldb:.</connection-url>
        <driver-class>org.hsqldb.jdbcDriver</driver-class>
        <user-name>sa</user-name>
        <password></password>
      </local-tx-datasource>
    </datasources>
    
    下面是运行转换器脚本生成的配置文件。生成的文件包含 <driver-class> 元素。首选的方式是定义 JBoss EAP 6 里的驱动类使用 <driver> 元素。下面是注释 <driver-class>元素并添加对应的 <driver> 元素的 JBoss EAP 6 配置文件里的 XML 内容:
    <subsystem xmlns="urn:jboss:domain:datasources:1.0">
      <datasources>
        <datasource enabled="true" jndi-name="java:jboss/datasources/bookingDatasource" jta="true"
                pool-name="bookingDatasource" use-ccm="true" use-java-context="true">
          <connection-url>jdbc:hsqldb:.</connection-url>
          <!-- Comment out the following driver-class element 
               since it is not the preferred way to define this.
               <driver-class>org.hsqldb.jdbcDriver</driver-class>     -->
          <transaction-isolation>TRANSACTION_NONE</transaction-isolation>
          <pool>
            <prefill>false</prefill>
            <use-strict-min>false</use-strict-min>
            <flush-strategy>FailingConnectionOnly</flush-strategy>
          </pool>
          <security>
            <user-name>sa</user-name>
            <password/>
          </security>
          <validation>
            <validate-on-match>false</validate-on-match>
            <background-validation>false</background-validation>
            <use-fast-fail>false</use-fast-fail>
          </validation>
          <timeout/>
          <statement>
            <track-statements>false</track-statements>
          </statement>
        </datasource>
        <drivers>
          <!-- The following driver element was not in the 
         XML target file. It was created manually. -->
          <driver name="h2" module="com.h2database.h2">
            <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
            </driver>
          </drivers>
      </datasources>
    </subsystem>
    
    

注意

IronJacamar 移植工具是一个第三方的工具,它不会以 JBoss EAP 6 的一部分而被支持。关于 IronJacamar 的更多信息,请访问 http://www.jboss.org/ironjacamar。关于如何安装和使用这个工具的最新文档,请访问:http://docs.jboss.org/ironjacamar/userguide/1.1/en-US/html/tools.html#tools_migration

4.1.9. 使用 IronJacamar 移植工具来转换资源适配器配置文件

过程 4.7. 

  1. 打开命令行并进入 IRONJACAMAR_HOME/docs/as/ 目录。
  2. 请输入以下命令来运行转换脚本:
    • 对于 Linux:./converter.sh -ra SOURCE_FILE TARGET_FILE
    • 对于 Microsoft Windows: ./converter.bat -ra SOURCE_FILE TARGET_FILE
    SOURCE_FILE 是以前版本的资源适配器 -ds.xml 文件。TARGET_FILE 包含新的配置。
    例如,要转换位于当前目录里的 mttestadapter-ds.xml 资源适配器配置文件,你可以输入:
    • 对于 Linux: ./converter.sh -ra mttestadapter-ds.xml new-adapter-config.xml
    • 对于 Microsoft Windows: ./converter.bat -ra mttestadapter-ds.xml new-adapter-config.xml
    请注意资源适配器转换的参数是 -ra
  3. 从目标文件将整个 <resource-adapters> 元素复制并粘贴到 <subsystem xmlns="urn:jboss:domain:resource-adapters:1.0"> 元素下的服务器配置文件里。

    重要

    要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
    • 如果服务器运行在受管域里,请将 XML 复制到 EAP_HOME/domain/configuration/domain.xml 文件里。
    • 如果运行的是独立服务器,请将 XML 复制到 EAP_HOME/standalone/configuration/standalone.xml 文件里。
  4. 修改新配置文件里生成的 XML。
    下面是 JBoss EAP 5.x TestSuite 里的 mttestadapter-ds.xml 资源适配器配置文件的一个例子:
    <?xml version="1.0" encoding="UTF-8"?>
      <!-- ==================================================================== -->
      <!-- ConnectionManager setup for jboss test adapter                       -->
      <!-- Build jmx-api (build/build.sh all) and view for config documentation -->
      <!-- ==================================================================== -->
    <connection-factories>
      <tx-connection-factory>
        <jndi-name>JBossTestCF</jndi-name>
        <xa-transaction/>
        <rar-name>jbosstestadapter.rar</rar-name>
        <connection-definition>javax.resource.cci.ConnectionFactory</connection-definition>
        <config-property name="IntegerProperty" type="java.lang.Integer">2</config-property>
        <config-property name="BooleanProperty" type="java.lang.Boolean">false</config-property>
        <config-property name="DoubleProperty" type="java.lang.Double">5.5</config-property>
        <config-property name="UrlProperty" type="java.net.URL">http://www.jboss.org</config-property>
        <config-property name="sleepInStart" type="long">200</config-property>
        <config-property name="sleepInStop" type="long">200</config-property>
      </tx-connection-factory>
      <tx-connection-factory>
        <jndi-name>JBossTestCF2</jndi-name>
        <xa-transaction/>
        <rar-name>jbosstestadapter.rar</rar-name>
        <connection-definition>javax.resource.cci.ConnectionFactory</connection-definition>
        <config-property name="IntegerProperty" type="java.lang.Integer">2</config-property>
        <config-property name="BooleanProperty" type="java.lang.Boolean">false</config-property>
        <config-property name="DoubleProperty" type="java.lang.Double">5.5</config-property>
        <config-property name="UrlProperty" type="java.net.URL">http://www.jboss.org</config-property>
        <config-property name="sleepInStart" type="long">200</config-property>
        <config-property name="sleepInStop" type="long">200</config-property>
      </tx-connection-factory>
      <tx-connection-factory>
        <jndi-name>JBossTestCFByTx</jndi-name>
        <xa-transaction/>
        <track-connection-by-tx>true</track-connection-by-tx>
        <rar-name>jbosstestadapter.rar</rar-name>
        <connection-definition>javax.resource.cci.ConnectionFactory</connection-definition>
        <config-property name="IntegerProperty" type="java.lang.Integer">2</config-property>
        <config-property name="BooleanProperty" type="java.lang.Boolean">false</config-property>
        <config-property name="DoubleProperty" type="java.lang.Double">5.5</config-property>
        <config-property name="UrlProperty" type="java.net.URL">http://www.jboss.org</config-property>
        <config-property name="sleepInStart" type="long">200</config-property>
        <config-property name="sleepInStop" type="long">200</config-property>
      </tx-connection-factory>
    </connection-factories>
    
    下面是运行转换器脚本生成的配置文件。用正确的受管连接工厂的类名替换生成的 XML 里的 class-name 属性值 "FIXME_MCF_CLASS_NAME",在这个例子是 "org.jboss.test.jca.adapter.TestManagedConnectionFactory"。下面是已修改了 <class-name> 元素值的 JBoss EAP 6 配置文件的 XML 内容:
    <subsystem xmlns="urn:jboss:domain:resource-adapters:1.0">
      <resource-adapters>
        <resource-adapter>
          <archive>jbosstestadapter.rar</archive>
          <transaction-support>XATransaction</transaction-support>
          <connection-definitions>
      <!-- Replace the "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
      <connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
        jndi-name="java:jboss/JBossTestCF" pool-name="JBossTestCF" 
        use-ccm="true" use-java-context="true"> -->
      <connection-definition 
        class-name="org.jboss.test.jca.adapter.TestManagedConnectionFactory" 
        enabled="true"
        jndi-name="java:jboss/JBossTestCF" pool-name="JBossTestCF" 
        use-ccm="true" use-java-context="true">
        <config-property name="IntegerProperty">2</config-property>
        <config-property name="sleepInStart">200</config-property>
        <config-property name="sleepInStop">200</config-property>
        <config-property name="BooleanProperty">false</config-property>
        <config-property name="UrlProperty">http://www.jboss.org</config-property>
        <config-property name="DoubleProperty">5.5</config-property>
        <pool>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
        </pool>
        <security>
          <application/>
        </security>
        <timeout/>
        <validation>
          <background-validation>false</background-validation>
          <use-fast-fail>false</use-fast-fail>
        </validation>
      </connection-definition>
          </connection-definitions>
        </resource-adapter>
        <resource-adapter>
          <archive>jbosstestadapter.rar</archive>
          <transaction-support>XATransaction</transaction-support>
          <connection-definitions>
      <!-- Replace the "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
       <connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
        jndi-name="java:jboss/JBossTestCF2" pool-name="JBossTestCF2" 
        use-ccm="true" use-java-context="true"> -->
      <connection-definition 
        class-name="org.jboss.test.jca.adapter.TestManagedConnectionFactory" 
        enabled="true"
        jndi-name="java:jboss/JBossTestCF2" pool-name="JBossTestCF2" 
        use-ccm="true" use-java-context="true">
        <config-property name="IntegerProperty">2</config-property>
        <config-property name="sleepInStart">200</config-property>
        <config-property name="sleepInStop">200</config-property>
        <config-property name="BooleanProperty">false</config-property>
        <config-property name="UrlProperty">http://www.jboss.org</config-property>
        <config-property name="DoubleProperty">5.5</config-property>
        <pool>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
        </pool>
        <security>
          <application/>
        </security>
        <timeout/>
        <validation>
          <background-validation>false</background-validation>
          <use-fast-fail>false</use-fast-fail>
        </validation>
      </connection-definition>
          </connection-definitions>
        </resource-adapter>
        <resource-adapter>
          <archive>jbosstestadapter.rar</archive>
          <transaction-support>XATransaction</transaction-support>
          <connection-definitions>
      <!-- Replace the "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
      <connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
         jndi-name="java:jboss/JBossTestCFByTx" pool-name="JBossTestCFByTx" 
         use-ccm="true" use-java-context="true"> -->
      <connection-definition 
        class-name="org.jboss.test.jca.adapter.TestManagedConnectionFactory" 
        enabled="true"
        jndi-name="java:jboss/JBossTestCFByTx" pool-name="JBossTestCFByTx" 
        use-ccm="true" use-java-context="true">
        <config-property name="IntegerProperty">2</config-property>
        <config-property name="sleepInStart">200</config-property>
        <config-property name="sleepInStop">200</config-property>
        <config-property name="BooleanProperty">false</config-property>
        <config-property name="UrlProperty">http://www.jboss.org</config-property>
        <config-property name="DoubleProperty">5.5</config-property>
        <pool>
          <prefill>false</prefill>
          <use-strict-min>false</use-strict-min>
          <flush-strategy>FailingConnectionOnly</flush-strategy>
        </pool>
        <security>
          <application/>
        </security>
        <timeout/>
        <validation>
          <background-validation>false</background-validation>
          <use-fast-fail>false</use-fast-fail>
        </validation>
      </connection-definition>
          </connection-definitions>
        </resource-adapter>
      </resource-adapters>
    </subsystem>
    
    

注意

IronJacamar 移植工具是一个第三方的工具,它不会以 JBoss EAP 6 的一部分而被支持。关于 IronJacamar 的更多信息,请访问 http://www.jboss.org/ironjacamar。关于如何安装和使用这个工具的最新文档,请访问:http://docs.jboss.org/ironjacamar/userguide/1.1/en-US/html/tools.html#tools_migration

4.2. 调试移植的问题

4.2.1. 调试和解决移植问题

由于应用服务器里的类加载、JNDI 命名规则以及其他修改,如果你试图就地部署应用程序,你可能会遇到异常抛出或其他错误。下面的内容描述了如何解决一些常见的异常和错误。

4.2.2. 调试和解决 ClassNotFoundExceptions 和 NoClassDefFoundErrors

总结

ClassNotFoundExceptions 通常是由未解决的依赖关系引起的。这意味着你必须显性地定义对其他模块的依赖关系或从外部源复制 JAR。

过程 4.8. 

  1. 如果缺失的类没有模块,请访问 第 4.2.4 节 “在以前的安装里查找 JAR”

4.2.3. 查找 JBoss 模块依赖关系

要解决依赖关系,首先,通过查找 EAP_HOME/modules/system/layers/base/ 目录找到包含 ClassNotFoundException 指定的类的模块。如果你找到这个类的模块,你必须在 manifest 条目里添加一个依赖关系。
例如,如果你在日志里看到 ClassNotFoundException 跟踪信息:
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.Log 
    from [Module "deployment.TopicIndex.war:main" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:188)
通过下列步骤来查找包含这个类的 JBoss 模块:

过程 4.9. 

  1. 首先确定这个类是否有明显的模块。
    1. 进入 EAP_HOME/modules/system/layers/base/ 目录并查找对应 ClassNotFoundException 里命名的类的模块路径。
      找到模块路径 org/apache/commons/logging/
    2. 打开 EAP_HOME/modules/system/layers/base/org/apache/commons/logging/main/module.xml 文件并找到模块名称。在这个例子里是 "org.apache.commons.logging"。
    3. 添加模块名到 MANIFEST.MF 文件里的依赖关系:
      Manifest-Version: 1.0
      Dependencies: org.apache.commons.logging
      
  2. 如果这个类没有明显的模块路径,你可能需要在另一个位置寻找依赖关系。
    1. 在 Tattletale 报告里找到名为 ClassNotFoundException 的类。
    2. EAP_HOME/modules 目录里找到包含 JAR 的模块以及和前一步骤里相同的模块名。

4.2.4. 在以前的安装里查找 JAR

如果在服务器定义的模块里的 JAR 中没有找到这个类,请到 EAP5_HOME 版本或之前服务器的 lib/ 目录里查找这个 JAR。
例如,如果你在日志里看到 ClassNotFoundException跟踪信息:
Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/ClassValidator at java.lang.Class.getDeclaredMethods0(Native Method)
通过下列步骤来查找包含这个类的 JAR:
  1. 打开一个终端窗口并进入 EAP5_HOME/ 目录。
  2. 执行这个命令:
    grep 'org.hibernate.validator.ClassValidator' `find . \-name '*.jar'`
  3. 你可能会看到多个结果。在这个例子里,下面的结果就是我们需要的 JAR:
    Binary file ./jboss-eap-5.1/seam/lib/hibernate-validator.jar matches
  4. 将这个 JAR 复制到 lib/ 目录。
    如果你发现你需要大量的 JAR,为这些类定义一个模块可能更加便利。关于更多的信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》里的『开发企业级应用程序起步』章节中的『模块』
  5. 重建和重部署这个应用程序。

4.2.5. 调试和解决 ClassCastExceptions

ClassCastException 通常是类被不同于它扩展的类加载器加载而引起的,它也可能是因为在多个 JAR 里存在相同的类而引起的。

过程 4.10. 

  1. 搜索应用程序来查找包含 ClassNotFoundException 里命名的类的所有 JAR。如果为这个类定义了模块,请从应用程序的 WAR 或 EAR 里查找并删除重复的 JAR。
  2. 找到包含这个类的 JBoss 模块并显性地在 MANIFEST.MF 文件或 jboss-deployment-structure.xml 文件里定义依赖关系。关于更多的信息,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》里『类加载和模块』章节中的『类加载和子部署』
  3. 如果你无法使用上面的步骤解决这个问题,你可以将类加载器的信息输出到日志里确定原因。例如,你可以在日志里看下列 ClassCastException
    java.lang.ClassCastException: com.example1.CustomClass1 cannot be cast to com.example2.CustomClass2
    1. 在你的代码里,将 ClassCastException 里出现的类的类加载信息输出到日志。例如:
      logger.info("Class loader for CustomClass1: " + 
            com.example1.CustomClass1.getClass().getClassLoader().toString());
      logger.info("Class loader for CustomClass2: " + 
            com.example2.CustomClass2.getClass().getClassLoader().toString());
      
    2. 日志里的信息显示了哪些模块在加载类,且你需要根据你的应用程序删除或移走冲突的 JAR。

4.2.6. 调试和解决 DuplicateServiceException

当你在 JBoss EAP 6 里部署 EAR 时,得到一个 JAR 子部署的 DuplicateServiceException 或显示 WAR 应用程序已经安装的消息,这可能是因为 JBossWS 处理部署的方式的改变而引起的。
JBossWS 3.3.0 对于基于端点的 servlet 引入了一个新的 Context Root 映射算法以允许它和 TCK6 无缝兼容。如果应用程序 EAR 归档包含具有相同名称的 WAR 和 JAR,JBossWS 可能会创建一个具有相同名字的 WAR 上下文和 web 上下文。Web 上下文和 WAR 上下文相冲突,这会导致部署错误。请用下列方法来解决部署问题:
  • 将 JAR 文件重命名为与 WAR 不同的名称,这样生成的 Web 和 WAR 上下文就是唯一的。
  • jboss-web.xml 文件里提供一个 <context-root> 元素。
  • jboss-webservices.xml 文件里提供一个 <context-root> 元素。
  • application.xml 文件为 WAR 自定义 <context-root> 元素。

4.2.7. 调试和解决 JBoss Seam 调试页面的错误

在你移植和成功部署应用程序后,你可能遇到一个运行时错误,并重定向到 “JBoss Seam 调试页面”。这个页面的 URL 是 "http://localhost:8080/APPLICATION_CONTEXT/debug.seam"。这个页面允许你查看和检查和当前登陆会话关联的任何 Seam 上下文里的 Seam 组件。
JBoss Seam 调试页面

图 4.1. JBoss Seam 调试页面

重定向到这个页面的最可能的原因是 Seam 已经捕获了应用程序里没有处理的异常。异常的根本原因常常可以在 "JBoss Seam 调试页面" 上的链接里找到。

过程 4.11. 

  1. 展开页面上的 Component 部分并查找 org.jboss.seam.caughtException 组件。
  2. 其原因和跟踪信息应该指向缺失的依赖关系。
    组件 org.jboss.seam.caughtException 的信息

    图 4.2. 组件 org.jboss.seam.caughtException 的信息

  3. 使用 第 4.2.2 节 “调试和解决 ClassNotFoundExceptions 和 NoClassDefFoundErrors” 里描述的技术来解决模块依赖关系。
    在上面的例子里,最简单的办法是添加 org.slf4jMANIFEST.MF 里。
    Manifest-Version: 1.0
    Dependencies: org.slf4j
    
    另一个选择是添加模块依赖关系到 jboss-deployment-structure.xml 文件:
    <jboss-deployment-structure>
       <deployment>
            <dependencies>
              <module name="org.slf4j" />
            </dependencies>
        </deployment>
    </jboss-deployment-structure>
    

4.3. 复查例程的移植

4.3.1. 复查例程的移植

概述

下面是一个已经从 JBoss EAP 5.x 移植到 JBoss EAP 6 的例程列表。要查看特定程序的修改细节,请点击下列链接。

4.3.2. 移植 Seam 2.2 JPA 例程到 JBoss EAP 6

总结

下面的任务列表总结了移植 Seam 2.2 JPA 例程到 JBoss EAP 6 所需的修改。这个例程可以在 JBoss EAP 5.1 的 EAP5.1_HOME/jboss-eap-5.1/seam/examples/jpa/ 目录下找到。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 4.12. 移植 Seam 2.2 JPA 例程

  1. 删除 jboss-web.xml 文件

    jboss-seam-jpa.war/WEB-INF/ 目录删除 jboss-web.xml 文件。jboss-web.xml 里定义的类加载现在是默认的行为。
  2. 从 persistence.xml 文件里删除废弃的属性

    删除或注释 jboss-seam-jpa.war/WEB-INF/classes/META-INF/persistence.xml 文件里的 hibernate.cache.provider_class 属性:
    <!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
    
  3. 添加 Seam 2.2 依赖关系

    从 Seam 2.2 的 SEAM_HOME/lib/ 目录复制下列 JAR 到 jboss-seam-jpa.war/WEB-INF/lib/ 目录:

    • slf4j-api.jar
    • slf4j-log4j12.jar
    • hibernate-entitymanager.jar
    • hibernate-core.jar
    • hibernate-annotations.jar
    • hibernate-commons-annotations.jar
    • hibernate-validator.jar
  4. 创建一个 jboss-deployment-structure 文件来添加剩下的依赖关系

    jboss-seam-jpa.war/WEB-INF/ 目录里创建一个包含下列数据的 jboss-deployment-structure.xml 文件:
    <jboss-deployment-structure>
       <deployment>
            <exclusions>
              <module name="javax.faces.api" slot="main"/>
              <module name="com.sun.jsf-impl" slot="main"/>
            </exclusions>
            <dependencies>
              <module name="org.apache.log4j" />
              <module name="org.dom4j" />
              <module name="org.apache.commons.logging" />
              <module name="org.apache.commons.collections" />
              <module name="javax.faces.api" slot="1.2"/>
              <module name="com.sun.jsf-impl" slot="1.2"/>
            </dependencies>
        </deployment>
    </jboss-deployment-structure>
    
结果:

Seam 2.2 JPA 例程在 JBoss EAP 6 上成功部署和运行。

4.3.3. 移植 Seam 2.2 Booking 例程到 JBoss EAP 6

总结

Seam 2.2 Booking EAR 移植比 Seam 2.2 JPA WAR 更为复杂。在 第 4.3.2 节 “移植 Seam 2.2 JPA 例程到 JBoss EAP 6” 里可以找到 Seam 2.2 JPA WAR 例程移植的文档。要移植这个程序,你必须:

  1. 初始化 JSF 1.2 而不是默认的 JSF 2。
  2. 捆绑旧版的 Hibernate JAR 而不是使用 JBoss EAP 6 附带的版本。
  3. 修改 JNDI 绑定以使用新的 Java EE 6 JNDI 可移植的语法。
上面的头两个步骤是在 Seam 2.2 JPA WAR 例程移植里完成的。第三个步骤是新的,因为 EAR 包含了 EJB,它也是必需的。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 4.13. 移植 Seam 2.2 Booking 例程

  1. 创建 jboss-deployment-structure.xml 文件

    jboss-seam-booking.ear/META-INF/ 里创建一个名为 jboss-deployment-structure.xml 的文件并添加下列内容:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
            <dependencies>
              <module name="org.apache.log4j" export="true"/>
              <module name="org.dom4j" export="true"/>
              <module name="org.apache.commons.logging" export="true"/>
              <module name="org.apache.commons.collections" export="true"/>
              <module name="javax.faces.api" slot="1.2" export="true"/>
              <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
            </dependencies>
      </deployment>
      <sub-deployment name="booking-web.war"> 
          <exclusions>
              <module name="javax.faces.api" slot="main"/>
              <module name="com.sun.jsf-impl" slot="main"/>
            </exclusions>
            <dependencies>
              <module name="javax.faces.api" slot="1.2"/>
              <module name="com.sun.jsf-impl" slot="1.2"/>
            </dependencies>
      </sub-deployment> 
     </jboss-deployment-structure>
    
  2. 删除每个 cache provider 类的 hibernate 属性

    删除或注释 jboss-seam-booking.jar/META-INF/persistence.xml 文件里的 cache provider 类的 hibernate 属性:
    <!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
    
  3. 从 Seam 2.2 复制 JAR 文件

    从 Seam 2.2 的 EAP5.x_HOME/jboss-eap5.x/seam/lib/ 目录复制下列 JAR 到 jboss-seam-booking.ear/lib 目录:
    slf4j-api.jar 
    slf4j-log4j12.jar 
    hibernate-core.jar 
    hibernate-entitymanager.jar 
    hibernate-validator.jar 
    hibernate-annotations.jar 
    hibernate-commons-annotations.jar
    
  4. 修改 JNDI 查找名称

    修改 jboss-seam-booking.war/WEB-INF/components.xml 文件里的 JNDI 查找字符串。由于新的 JNDI 可移植规则,JBoss EAP 6 现在使用 JNDI 可移植语法规则绑定 EJB,你无法使用 JBoss EAP 5 里使用的单个的 jndiPattern。应用程序的 EJB JNDI 查找字符串必须修改成:
    java:global/seam-booking/booking-ejb/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching
    java:app/booking-ejb/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching
    java:module/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching
    java:global/seam-booking/booking-ejb/HotelSearchingAction
    java:app/booking-ejb/HotelSearchingAction
    java:module/HotelSearchingAction
    
    Seam 2.2 框架 EJB 的 NDI 查找字符串必须修改成:
    java:global/seam-booking/jboss-seam/EjbSynchronizations!org.jboss.seam.transaction.LocalEjbSynchronizations
    java:app/jboss-seam/EjbSynchronizations!org.jboss.seam.transaction.LocalEjbSynchronizations
    java:module/EjbSynchronizations!org.jboss.seam.transaction.LocalEjbSynchronizations
    java:global/seam-booking/jboss-seam/EjbSynchronizations
    java:app/jboss-seam/EjbSynchronizations
    java:module/EjbSynchronizations
    
    你可以使用下列方式中的一个:
    1. 添加组件元素

      你可以为每个 EJB 添加一个 jndi-nameWEB-INF/components.xml
      <component class="org.jboss.seam.transaction.EjbSynchronizations" jndi-name="java:app/jboss-seam/EjbSynchronizations"/>
      <component class="org.jboss.seam.async.TimerServiceDispatcher" jndi-name="java:app/jboss-seam/TimerServiceDispatcher"/>
      <component class="org.jboss.seam.example.booking.AuthenticatorAction" jndi-name="java:app/booking-ejb/AuthenticatorAction" />
      <component class="org.jboss.seam.example.booking.BookingListAction"  jndi-name="java:app/booking-ejb/BookingListAction" />
      <component class="org.jboss.seam.example.booking.RegisterAction" jndi-name="java:app/booking-ejb/RegisterAction" />
      <component class="org.jboss.seam.example.booking.HotelSearchingAction" jndi-name="java:app/booking-ejb/HotelSearchingAction" />
      <component class="org.jboss.seam.example.booking.HotelBookingAction" jndi-name="java:app/booking-ejb/HotelBookingAction" />
      <component class="org.jboss.seam.example.booking.ChangePasswordAction" jndi-name="java:app/booking-ejb/ChangePasswordAction" />
      
      
    2. 你可以修改代码,添加 @JNDIName(value="") 注解指定 JNDI 路径。下面时一个已修改的 stateless session bean 代码示例。关于这个产品的详细说明可以在 Seam 2.2 的参考文档里找到。
      @Stateless
      @Name("authenticator")
      @JndiName(value="java:app/booking-ejb/AuthenticatorAction")
      public class AuthenticatorAction 
          implements Authenticator
      {
      ...
      }
      
结果:

Seam 2.2 Booking 应用程序在 JBoss EAP 6 上成功部署和运行。

4.3.4. 移植 Seam 2.2 Booking 例程到 JBoss EAP 6:逐步说明

这是一个将 Seam 2.2 Booking 从 JBoss EAP 5.1 移植到 JBoss EAP 6 的逐步指南。虽然移植应用程序有更好的方法,许多开发人员可能会冒险直接将归档部署到 JBoss EAP 6,并看看会有什么发生。这个文档的目的是展示你可能遇到的各种问题,并如何调试和解决这些问题。
对于这个例子,应用程序 EAR 部署在 EAP6_HOME/standalone/deployments 目录,对现有归档无需进行修改。这允许你在遇到问题时可以轻易地修改归档里的 XML 文件从而解决问题。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。
此时你应该可以通过 http://localhost:8080/seam-booking/ 成功访问应用程序了。用 demo/demo 登陆你就会看到 Booking 的欢迎页面。

4.3.5. 构建和部署 Seam 2.2 Booking 例程的 JBoss EAP 5.1 版本。

在移植这个应用程序前,你应该构建 JBoss EAP 5.1 下的 Seam 2.2 Booking 例程,展开归档文件,并复制到 JBoss EAP 6 的部署目录下。

过程 4.15. 构建和部署 EAR:

  1. 构建 EAR:
    $ cd /EAP5_HOME/jboss-eap5.1/seam/examples/booking
    $ ANT_HOME/ant explode
    
  2. 复制 EAR 到 EAP6_HOME 部署目录:
    $ cp -r EAP5_HOME/seam/examples/booking/exploded-archives/jboss-seam-booking.ear EAP6_HOME/standalone/deployments/
    $ cp -r EAP5_HOME/seam/examples/booking/exploded-archives/jboss-seam-booking.war EAP6_HOME/standalone/deployments/jboss-seam.ear
    $ cp -r EAP5_HOME/seam/examples/booking/exploded-archives/jboss-seam-booking.jar EAP6_HOME/standalone/deployments/jboss-seam.ear
    
  3. 启动 JBoss EAP 6 服务器并检查日志。你会看到:
    INFO [org.jboss.as.deployment] (DeploymentScanner-threads - 1) Found jboss-seam-booking.ear in deployment directory. 
        To trigger deployment create a file called jboss-seam-booking.ear.dodeploy
    
  4. 创建一个名为 jboss-seam-booking.ear.dodeploy 的空文件并将其复制到 EAP6_HOME/standalone/deployments 目录。在移植应用程序时,你需要多次将这个文件复制到部署目录,所以请将它放在一个容易找到的目录里。在日志里,你现在应该看到下列信息,表示它正在进行部署:
    INFO [org.jboss.as.server.deployment] (MSC service thread 1-1) Starting deployment of "jboss-seam-booking.ear"
    INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) Starting deployment of "jboss-seam-booking.jar"
    INFO [org.jboss.as.server.deployment] (MSC service thread 1-6) Starting deployment of "jboss-seam.jar"
    INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) Starting deployment of "jboss-seam-booking.war"
    
    此时,你会遇到第一个部署错误。在下一步骤里,你将遇到每个问题并学习如何进行调试和解决。
    要学习如何调试和解决部署问题,点击这里:第 4.3.6 节 “调试和解决 Seam 2.2 Booking 例程的部署错误和异常”

4.3.6. 调试和解决 Seam 2.2 Booking 例程的部署错误和异常

在上面的步骤 第 4.3.5 节 “构建和部署 Seam 2.2 Booking 例程的 JBoss EAP 5.1 版本。” 里,你构建了 JBoss EAP 5.1 的 Seam 2.2 Booking 例程且将其部署到了 JBoss EAP 6 的部署目录里。在这个步骤,你可以调试和解决遇到的每个部署错误。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 4.16. 调试和解决部署错误和异常

  1. 问题 - java.lang.ClassNotFoundException: javax.faces.FacesException
    当你部署应用程序时,日志会包含下列错误:
    ERROR \[org.jboss.msc.service.fail\] (MSC service thread 1-1) MSC00001: Failed to start service jboss.deployment.subunit."jboss-seam-booking.ear"."jboss-seam-booking.war".POST_MODULE:
    org.jboss.msc.service.StartException in service jboss.deployment.subunit."jboss-seam-booking.ear"."jboss-seam-booking.war".POST_MODULE:
    Failed to process phase POST_MODULE of subdeployment "jboss-seam-booking.war" of deployment "jboss-seam-booking.ear"
        (.. additional logs removed ...)
    Caused by: java.lang.ClassNotFoundException: javax.faces.FacesException from \[Module "deployment.jboss-seam-booking.ear:main" from Service Module Loader\]
        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:191)
    
    它表示:

    ClassNotFoundException 表示缺失的依赖关系。在这个例子里是无法找到 javax.faces.FacesException,你需要显性地添加这个依赖关系。

    如何解决这个问题:

    EAP6_HOME/modules/system/layers/base/ 目录里通过查找匹配缺失的类的路径找到这个类的模块名。在这个例子里,你会找到两个匹配的模块:

    javax/faces/api/main
    javax/faces/api/1.2
    
    这两个模块都有相同的模块名:javax.faces.api,但位于 main 目录里的是用于 JSF 2.0 的,而位于 1.2 目录里的另外一个是用于 JSF 1.2 的。如果只有一个可用的模块,你可以简单地创建一个 MANIFEST.MF 并添加模块依赖关系。在这个例子里,你像使用 JSF 1.2 版本而不是 main 里的 JSF 2.0,所以你需要指定一个并排斥另外一个。为此,你可以在 EAR 的 META-INF/ 目录里创建一个包含下列数据的 jboss-deployment-structure.xml
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
          </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    
    
    deployment 部分,你可以添加用于 JSF 1.2 模块的 javax.faces.api 的依赖关系。你也可以在 WAR 的 subdeployment 部分添加用于 JSF 1.2 模块的依赖关系并排除用于 JSF 2.0 的模块。

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  2. 问题 - java.lang.ClassNotFoundException: org.apache.commons.logging.Log
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC00001: Failed to start service jboss.deployment.unit."jboss-seam-booking.ear".INSTALL:
    org.jboss.msc.service.StartException in service jboss.deployment.unit."jboss-seam-booking.ear".INSTALL:
    Failed to process phase INSTALL of deployment "jboss-seam-booking.ear"
        (.. additional logs removed ...)
    Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.Log from [Module "deployment.jboss-seam-booking.ear.jboss-seam-booking.war:main" from Service Module Loader]
    
    它表示:

    ClassNotFoundException 指示了一个缺失的依赖关系。在这个例子里是无法找到 org.apache.commons.logging.Log 且你需要显性地添加依赖关系。

    如何解决这个问题:

    通过查找匹配缺失类的路径在 EAP6_HOME/modules/system/layers/base/ 里找到这个类的模块名。在这个例子里,你要找到匹配路径 org/apache/commons/logging/ 的模块。这个模块名是 “org.apache.commons.logging”。

    修改 jboss-deployment-structure.xml 文件,添加模块依赖关系至部署部分。
    <module name="org.apache.commons.logging" export="true"/>
    
    
    jboss-deployment-structure.xml 应该类似于:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
            <module name="org.apache.commons.logging" export="true"/>
          </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    
    
    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  3. 问题 - java.lang.ClassNotFoundException: org.dom4j.DocumentException
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/seam-booking]] (MSC service thread 1-3) Exception sending context initialized event to listener instance of class org.jboss.seam.servlet.SeamListener: java.lang.NoClassDefFoundError: org/dom4j/DocumentException
        (... additional logs removed ...)
    Caused by: java.lang.ClassNotFoundException: org.dom4j.DocumentException from [Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader]
    
    它表示:

    ClassNotFoundException 表示缺失的依赖关系。在这个例子里是无法找到 org.dom4j.DocumentException 类。

    如何解决这个问题:

    EAP6_HOME/modules/system/layers/base/ 目录里通过查找 org/dom4j/DocumentException 来查找模块名。这个模块名是 “org.dom4j”。修改 jboss-deployment-structure.xml 文件,添加模块依赖关系至文件的部署部分。

    <module name="org.dom4j" export="true"/>
    
    
    jboss-deployment-structure.xml 文件应该类似于:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
            <module name="org.apache.commons.logging" export="true"/>
                <module name="org.dom4j" export="true"/>
              </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  4. 问题 - java.lang.ClassNotFoundException: org.hibernate.validator.InvalidValue
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/seam-booking]] (MSC service thread 1-6) Exception sending context initialized event to listener instance of class org.jboss.seam.servlet.SeamListener: java.lang.RuntimeException: Could not create Component: org.jboss.seam.international.statusMessages
        (... additional logs removed ...)
    Caused by: java.lang.ClassNotFoundException: org.hibernate.validator.InvalidValue from [Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader]
    
    它表示:

    ClassNotFoundException 表示缺失的依赖关系。在这个例子里是无法找到 org.hibernate.validator.InvalidValue 类。

    如何解决这个问题:

    存在名为 “org.hibernate.validator” 的模块,但 JAR 不包含 org.hibernate.validator.InvalidValue 类,所以添加模块依赖关系不会解决这个问题。在这个例子里,JAR 包含这个类,将其作为 JBoss EAP 5.1 部署的一部分。在 EAP5_HOME/seam/lib/ 目录里查找包含缺失类的 JAR。为此,打开控制台并输入:

    $ cd EAP5_HOME/seam/lib
    $ grep 'org.hibernate.validator.InvalidValue' `find . -name '*.jar'
    
    其结果显示:
    $ Binary file ./hibernate-validator.jar matches
    $ Binary file ./test/hibernate-all.jar matches
    
    在这个例子里,复制 hibernate-validator.jarjboss-seam-booking.ear/lib/ 目录:
    $ cp EAP5_HOME/seam/lib/hibernate-validator.jar jboss-seam-booking.ear/lib
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  5. 问题 - java.lang.InstantiationException: org.jboss.seam.jsf.SeamApplicationFactory
    当你部署应用程序时,日志会包含下列错误:
    INFO  [javax.enterprise.resource.webcontainer.jsf.config] (MSC service thread 1-7) Unsanitized stacktrace from failed start...: com.sun.faces.config.ConfigurationException: Factory 'javax.faces.application.ApplicationFactory' was not configured properly.
      at com.sun.faces.config.processor.FactoryConfigProcessor.verifyFactoriesExist(FactoryConfigProcessor.java:296) [jsf-impl-2.0.4-b09-jbossorg-4.jar:2.0.4-b09-jbossorg-4]
      (... additional logs removed ...)
    Caused by: javax.faces.FacesException: org.jboss.seam.jsf.SeamApplicationFactory
      at javax.faces.FactoryFinder.getImplGivenPreviousImpl(FactoryFinder.java:606) [jsf-api-1.2_13.jar:1.2_13-b01-FCS]
      (... additional logs removed ...)
      at com.sun.faces.config.processor.FactoryConfigProcessor.verifyFactoriesExist(FactoryConfigProcessor.java:294) [jsf-impl-2.0.4-b09-jbossorg-4.jar:2.0.4-b09-jbossorg-4]
      ... 11 more
    Caused by: java.lang.InstantiationException: org.jboss.seam.jsf.SeamApplicationFactory
      at java.lang.Class.newInstance0(Class.java:340) [:1.6.0_25]
      at java.lang.Class.newInstance(Class.java:308) [:1.6.0_25]
      at javax.faces.FactoryFinder.getImplGivenPreviousImpl(FactoryFinder.java:604) [jsf-api-1.2_13.jar:1.2_13-b01-FCS]
      ... 16 more
    
    它表示:

    com.sun.faces.config.ConfigurationExceptionjava.lang.InstantiationException 表示存在依赖关系的问题。在这个例子里,原因并不明显。

    如何解决这个问题:

    你需要找到包含 com.sun.faces 类的模块。虽然这里没有 com.sun.faces 模块,但却有两个 com.sun.jsf-impl 模块。在 1.2 目录里快速检查 jsf-impl-1.2_13.jar,你会发现它包含 com.sun.faces 类。如你对 javax.faces.FacesExceptionClassNotFoundException 所做的一样,你希望在 main 里使用 JSF 1.2 版本而不是 JSF 2.0,所以你需要指定一个且排除另外一个。你需要修改 jboss-deployment-structure.xml 以添加模块依赖关系到这个文件的 deployment 部分。你也需要添加它到 WAR 子部署并排除 JSF 2.0 模块。这个文件现在应该类似于:

    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
                  <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
            <module name="org.apache.commons.logging" export="true"/>
            <module name="org.dom4j" export="true"/>
          </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
            <module name="com.sun.jsf-impl" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
                  <module name="com.sun.jsf-impl" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  6. 问题 - java.lang.ClassNotFoundException: org.apache.commons.collections.ArrayStack
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/seam-booking]] (MSC service thread 1-1) Exception sending context initialized event to listener instance of class com.sun.faces.config.ConfigureListener: java.lang.RuntimeException: com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED! org.apache.commons.collections.ArrayStack from [Module "deployment.jboss-seam-booking.ear:main" from Service Module Loader]
        (... additional logs removed ...)
    Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.ArrayStack from [Module "deployment.jboss-seam-booking.ear:main" from Service Module Loader]
    
    它表示:

    ClassNotFoundException 表示缺失的依赖关系。在这个例子里是无法找到 org.apache.commons.collections.ArrayStack

    如何解决这个问题:

    EAP6_HOME/modules/system/layers/base/ 目录里通过查找 org/apache/commons/collections 路径来查找模块名。这个模块名是 “org.apache.commons.collections”。修改 jboss-deployment-structure.xml 文件,添加模块依赖关系至文件的部署部分。

    <module name="org.apache.commons.collections" export="true"/>
    
    jboss-deployment-structure.xml 文件应该类似于:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
          <dependencies>
            <module name="javax.faces.api" slot="1.2" export="true"/>
                  <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
            <module name="org.apache.commons.logging" export="true"/>
            <module name="org.dom4j" export="true"/>
            <module name="org.apache.commons.collections" export="true"/>
        </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
        <exclusions>
            <module name="javax.faces.api" slot="main"/>
            <module name="com.sun.jsf-impl" slot="main"/>
          </exclusions>
          <dependencies>
            <module name="javax.faces.api" slot="1.2"/>
                  <module name="com.sun.jsf-impl" slot="1.2"/>
          </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  7. 问题 - Services with missing/unavailable dependencies
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.jboss.as.deployment] (DeploymentScanner-threads - 2) {"Composite operation failed and was rolled back. Steps that failed:" => {"Operation step-2" => {"Services with missing/unavailable dependencies" => ["jboss.deployment.subunit.\"jboss-seam-booking.ear\".\"jboss-seam-booking.jar\".component.AuthenticatorAction.START missing [ jboss.naming.context.java.comp.jboss-seam-booking.\"jboss-seam-booking.jar\".AuthenticatorAction.\"env/org.jboss.seam.example.booking.AuthenticatorAction/em\" ]","jboss.deployment.subunit.\"jboss-seam-booking.ear\".\"jboss-seam-booking.jar\".component.HotelSearchingAction.START missing [ jboss.naming.context.java.comp.jboss-seam-booking.\"jboss-seam-booking.jar\".HotelSearchingAction.\"env/org.jboss.seam.example.booking.HotelSearchingAction/em\" ]","
      (... additional logs removed ...)
    "jboss.deployment.subunit.\"jboss-seam-booking.ear\".\"jboss-seam-booking.jar\".component.BookingListAction.START missing [ jboss.naming.context.java.comp.jboss-seam-booking.\"jboss-seam-booking.jar\".BookingListAction.\"env/org.jboss.seam.example.booking.BookingListAction/em\" ]","jboss.persistenceunit.\"jboss-seam-booking.ear/jboss-seam-booking.jar#bookingDatabase\" missing [ jboss.naming.context.java.bookingDatasource ]"]}}}
    
    它表示:

    当你遇到 “Services with missing/unavailable dependencies” 错误,请查看 “missing” 后面括号里的文本。在这里例子里是:

    missing [ jboss.naming.context.java.comp.jboss-seam-booking.\"jboss-seam-booking.jar\".AuthenticatorAction.\"env/org.jboss.seam.example.booking.AuthenticatorAction/em\" ]
    
    “/em” 表示这是一个 Entity Manager 和数据源的问题。

    如何解决这个问题:

    在 JBoss EAP 6 里,数据源配置已经进行了修改,它需要在 EAP6_HOME/standalone/configuration/standalone.xml 文件里进行定义。既然 JBoss EAP 6 附带的示例数据库已经在 standalone.xml 文件里进行了定义,请修改 persistence.xml 文件来使用这个示例数据库。请看 standalone.xml 文件,你可以看到示例数据库的 jndi-namejava:jboss/datasources/ExampleDS。修改 jboss-seam-booking.jar/META-INF/persistence.xml 文件,注释现有的 jta-data-source 元素并用下列内容来替代:

    <!-- <jta-data-source>java:/bookingDatasource</jta-data-source> -->
    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  8. 问题 - java.lang.ClassNotFoundException: org.hibernate.cache.HashtableCacheProvider
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.jboss.msc.service.fail] (MSC service thread 1-4) MSC00001: Failed to start service jboss.persistenceunit."jboss-seam-booking.ear/jboss-seam-booking.jar#bookingDatabase": org.jboss.msc.service.StartException in service jboss.persistenceunit."jboss-seam-booking.ear/jboss-seam-booking.jar#bookingDatabase": Failed to start service
      at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1786)
      (... log messages removed ...)
    Caused by: java.lang.ClassNotFoundException: org.hibernate.cache.HashtableCacheProvider from [Module "org.hibernate:main" from local module loader @12a3793 (roots: /home/sgilda/tools/jboss7/modules)]
      at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:191)
      (... log messages removed ...)
    
    它表示:

    ClassNotFoundException 表示一个缺失的依赖关系。在这里例子里,它无法找到 org.hibernate.cache.HashtableCacheProvider

    如何解决这个问题:

    没有模块 “org.hibernate.cache”。在这个例子里,包含这个类的 JAR 是 JBoss EAP 5.1 部署的一部分。请在 EAP5_HOME/jboss-eap-5.1/seam/lib/ 目录里查找包含缺失类的 JAR。为此,请打开控制台并输入:

    $ cd EAP5_HOME/seam/lib
    $ grep 'org.hibernate.validator.InvalidValue' `find . -name '*.jar'`
    
    其结果显示:
    Binary file ./hibernate-core.jar matches
    Binary file ./test/hibernate-all.jar matches
    
    在这个例子里,复制 hibernate-core.jarjboss-seam-booking.ear/lib/ 目录:
    cp EAP5_HOME/seam/lib/hibernate-core.jar jboss-seam-booking.ear/lib
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  9. 问题 - java.lang.ClassCastException: org.hibernate.cache.HashtableCacheProvider
    当你部署应用程序时,日志会包含下列错误:
    ERROR [org.jboss.msc.service.fail] (MSC service thread 1-2) MSC00001: Failed to start service jboss.persistenceunit."jboss-seam-booking.ear/jboss-seam-booking.jar#bookingDatabase": org.jboss.msc.service.StartException in service jboss.persistenceunit."jboss-seam-booking.ear/jboss-seam-booking.jar#bookingDatabase": Failed to start service
      at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1786)
      (... log messages removed ...)
    Caused by: java.lang.ClassCastException: org.hibernate.cache.HashtableCacheProvider cannot be cast to org.hibernate.cache.spi.CacheProvider
      at org.hibernate.cache.internal.bridge.RegionFactoryCacheProviderBridge.init(RegionFactoryCacheProviderBridge.java:65)
      ... 20 more
    
    它表示:

    很多情况下都会遇到 ClassCastException。如果你在日志里看到了这个异常,就表示 org.hibernate.cache.HashtableCacheProvider 扩展了 org.hibernate.cache.spi.CacheProvider 且正由和它扩展的类不同的类加载器加载。org.hibernate.cache.HashtableCacheProvider 类位于 hibernate-core.jar 且由应用程序类加载器加载。它扩展的类, org.hibernate.cache.spi.CacheProvider,位于 org/hibernate/main/hibernate-core-4.0.0.Beta1.jar 且由该模块隐性地加载。这并不明显,但是由于 Hibernate 4 里的改动,由于移动 HashtableCacheProvider 类到其他软件包会导致向后的兼容性问题。这个类将从 org.hibernate.cache 软件包移动到 org.hibernate.cache.internal 软件包。如果你没有从 persistence.xml 删除 hibernate.cache.provider_class 属性,它将强制 Seam 应用程序捆绑旧的 Hibernate 库,导致 ClassCastExceptions。在 JBoss EAP 6 里,你应该使用 Infinispan 而避免使用 HashtableCacheProvider。

    如何解决这个问题:

    在 JBoss EAP 6 里,注释 jboss-seam-booking.jar/META-INF/persistence.xml 文件里的 hibernate.cache.provider_class 属性:

    <!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
    

    通过删除 EAP6_HOME/standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  10. 此时,应用程序应该已部署无误了,但当你访问 http://localhost:8080/seam-booking/ 并尝试 "Account Login" 时,你会遇到一个运行时错误 “The page isn't redirecting properly”。在下一步里,你会学习如何调试和解决运行时错误。
    要学习如何调试和解决运行时的问题,点击这里:第 4.3.7 节 “调试和解决 Seam 2.2 Booking 例程的运行时错误和异常”

4.3.7. 调试和解决 Seam 2.2 Booking 例程的运行时错误和异常

在前面的步骤 第 4.3.6 节 “调试和解决 Seam 2.2 Booking 例程的部署错误和异常” 里,你学习了如何调试部署错误,在这个步骤里,你会调试和解决你遇到的运行时错误。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。

过程 4.17. 调试和解决运行时错误和异常

目前,当你部署这个应用程序时,你没有在日志里看到任何错误。但是,当你访问这个程序的 URL 是,日志里就出现了错误。
  1. 问题 - javax.naming.NameNotFoundException: Name 'jboss-seam-booking' not found in context ''
    当你通过浏览器访问 URL http://localhost:8080/seam-booking/ 时,你会看到 "The page isn't redirecting properly" 且日志会包含下列错误:
    SEVERE [org.jboss.seam.jsf.SeamPhaseListener] (http--127.0.0.1-8080-1) swallowing exception: java.lang.IllegalStateException: Could not start transaction
      at org.jboss.seam.jsf.SeamPhaseListener.begin(SeamPhaseListener.java:598) [jboss-seam.jar:]
      (... log messages removed ...)
    Caused by: org.jboss.seam.InstantiationException: Could not instantiate Seam component: org.jboss.seam.transaction.synchronizations
      at org.jboss.seam.Component.newInstance(Component.java:2170) [jboss-seam.jar:]
      (... log messages removed ...)
    Caused by: javax.naming.NameNotFoundException: Name 'jboss-seam-booking' not found in context ''
      at org.jboss.as.naming.util.NamingUtils.nameNotFoundException(NamingUtils.java:109)
      (... log messages removed ...)
    
    它表示:

    NameNotFoundException 表示了 JNDI 命名问题。JNDI 命名规则在 JBoss EAP 6 里已经进行了修改,所以你需要修改查找名称以遵循新的规则。

    如何解决这个问题:

    为了调集试,

    15:01:16,138 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named RegisterAction in deployment unit subdeployment "jboss-seam-booking.jar" of deployment "jboss-seam-booking.ear" are as follows:
      java:global/jboss-seam-booking/jboss-seam-booking.jar/RegisterAction!org.jboss.seam.example.booking.Register
      java:app/jboss-seam-booking.jar/RegisterAction!org.jboss.seam.example.booking.Register
      java:module/RegisterAction!org.jboss.seam.example.booking.Register
      java:global/jboss-seam-booking/jboss-seam-booking.jar/RegisterAction
      java:app/jboss-seam-booking.jar/RegisterAction
      java:module/RegisterAction
      [JNDI bindings continue ...]
    
    日志里一共列出了 8 个 INFO JNDI 绑定,每个都对应一个会话 bean:RegisterAction、BookingListAction、HotelBookingAction、AuthenticatorAction、ChangePasswordAction、HotelSearchingAction、EjbSynchronizations 和 TimerServiceDispatcher。你需要修改 WAR 的 lib/components.xml 文件来使用新的 JNDI 绑定。在日志里,注意 EJB JNDI 绑定都以 "java:app/jboss-seam-booking.jar" 开始。请替换 core:init 元素:
    <!--     <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/> -->
    <core:init jndi-pattern="java:app/jboss-seam-booking.jar/#{ejbName}" debug="true" distributable="false"/>
    
    
    然后,你需要添加 EjbSynchronizations 和 TimerServiceDispatcher JNDI 绑定。请添加下列 component 元素到这个文件里:
    <component class="org.jboss.seam.transaction.EjbSynchronizations" jndi-name="java:app/jboss-seam/EjbSynchronizations"/>
    <component class="org.jboss.seam.async.TimerServiceDispatcher" jndi-name="java:app/jboss-seam/TimerServiceDispatcher"/>
    
    
    components.xml 文件应该类似于:
    <?xml version="1.0" encoding="UTF-8"?>
    <components xmlns="http://jboss.com/products/seam/components"
      xmlns:core="http://jboss.com/products/seam/core"
      xmlns:security="http://jboss.com/products/seam/security"
      xmlns:transaction="http://jboss.com/products/seam/transaction"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation=
        "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd
         http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.2.xsd
         http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd
         http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">
    
        <!-- <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/> -->
        <core:init jndi-pattern="java:app/jboss-seam-booking.jar/#{ejbName}" debug="true" distributable="false"/>
        <core:manager conversation-timeout="120000"
                      concurrent-request-timeout="500"
                      conversation-id-parameter="cid"/>
        <transaction:ejb-transaction/>
        <security:identity authenticate-method="#{authenticator.authenticate}"/>
        <component class="org.jboss.seam.transaction.EjbSynchronizations"
                jndi-name="java:app/jboss-seam/EjbSynchronizations"/>
        <component class="org.jboss.seam.async.TimerServiceDispatcher"
                jndi-name="java:app/jboss-seam/TimerServiceDispatcher"/>
    </components>
    
    

    通过删除 standalone/deployments/jboss-seam-booking.ear.failed 文件并在相同目录里创建一个空白的 jboss-seam-booking.ear.dodeploy 文件来重新部署应用程序。
  2. 运行时错误应该已被解决
    此时,应用程序已部署且运行无误。当你访问 http://localhost:8080/seam-booking/,你可以成功地登录。

4.3.8. 当移植 Seam 2.2 Booking 例程时所作修改的总结

尽管提前决定依赖关系并在一个步骤里添加隐性的依赖关系具有更高的效率,这种方式还是展示了问题是如何出现在日志里并提供如何调试和解决的信息。下面是在移植到 JBoss EAP 6 时所作修改的总结。

重要

直接使用 Hibernate 的 Seam 2.2 应用程序可以使用包裹在应用程序里的一个 Hibernate 3 版本。而通过 JBoss EAP 6 的 org.hibernate 模块提供的 Hibernate 4,不被 Seam 2.2 支持。这个例子将帮助你在 JBoss EAp 6 运行应用程序。请注意,将 Hibernate 3 包裹在 Seam 2.2 应用程序不是被支持的配置。
  1. 你在 EAR 的 META-INF/ 目录里创建了一个 jboss-deployment-structure.xml 文件。你添加了 <dependencies><exclusions> 来解析 ClassNotFoundExceptions。这个文件包含下列数据:
    <jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
      <deployment>
      	  <dependencies>
    	      <module name="javax.faces.api" slot="1.2" export="true"/>
    	      <module name="com.sun.jsf-impl" slot="1.2" export="true"/>
    	      <module name="org.apache.commons.logging" export="true"/>
        	      <module name="org.dom4j" export="true"/>
    	      <module name="org.apache.commons.collections" export="true"/>
    	    </dependencies>
      </deployment>
      <sub-deployment name="jboss-seam-booking.war">
      	<exclusions>
    	      <module name="javax.faces.api" slot="main"/>
    	      <module name="com.sun.jsf-impl" slot="main"/>
    	    </exclusions>
    	    <dependencies>
    	      <module name="javax.faces.api" slot="1.2"/>
    	      <module name="com.sun.jsf-impl" slot="1.2"/>
    	    </dependencies>
      </sub-deployment>
    </jboss-deployment-structure>
    
    
  2. 你将下列 JAR 从 EAP5_HOME/jboss-eap-5.1/seam/lib/ 目录复制到 jboss-seam-booking.ear/lib/ 目录以解析 ClassNotFoundExceptions

    • hibernate-core.jar
    • hibernate-validator.jar
  3. 你修改了 jboss-seam-booking.jar/META-INF/persistence.xml 文件。
    1. 你修改了 jta-data-source 元素以使用 JBoss EAP 6 附带的示例数据库。
      <!-- <jta-data-source>java:/bookingDatasource</jta-data-source> -->
      <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
      
      
    2. 你注释了 hibernate.cache.provider_class 属性:
      <!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
      
      
  4. 你修改了 WAR 的 lib/components.xml 文件来使用新的 JNDI 绑定。
    1. 你替换了现有的 core:init 元素:
      <!-- <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/> -->
      <core:init jndi-pattern="java:app/jboss-seam-booking.jar/#{ejbName}" debug="true" distributable="false"/>
      
      
    2. 你为 "EjbSynchronizations" 和 "TimerServiceDispatcher" JNDI 绑定添加了 component 元素:
      <component class="org.jboss.seam.transaction.EjbSynchronizations" jndi-name="java:app/jboss-seam/EjbSynchronizations"/>
       <component class="org.jboss.seam.async.TimerServiceDispatcher" jndi-name="java:app/jboss-seam/TimerServiceDispatcher"/>
      
      

附录 A. Revision History

修订历史
修订 3.0.0-1Mon Jan 06 2014CS Builder Robot
Built from Content Specification: 11863, Revision: 507564

法律通告

Copyright © 2014 Red Hat, Inc..
This document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.