迁移指南
适用于红帽 JBoss 企业版应用程序平台 6
Red Hat 客户内容服务 JBoss EAP 团队
摘要
第 1 章 介绍
1.1. 关于 Red Hat JBoss 企业版应用程序平台 6
1.2. 关于移植指南
第 2 章 准备移植
2.1. 准备移植
查看 JBoss EAP 6 里的新功能以及不同之处
这个版本里已经修改了大量的内容,可能会影响到 JBoss EAP 5 应用程序的开发。这包括对文件目录结构、脚本、部署配置、类加载和 JNDI 查找的修改。详情请参考 第 2.2 节 “查看 JBoss EAP 6 里的新功能以及不同之处”。阅读关于开发应用程序起步的文档
请务必阅读《JBoss EAP 6 开发指南》里的『开发应用程序起步』章节:https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/。它包含下列重要的信息:- Java EE 6
- 新的模块化类加载系统
- 文件结构的改变
- 如何下载和安装 JBoss EAP 6
- 如何下载和安装 JBoss Developer Studio
- 如何为您的开发环境配置 Maven
- 如何下载和运行产品附带的 Quickstart 例程。
学习在 Maven 项目里如何使用 JBoss EAP 6 依赖关系
请务必阅读《JBoss EAP 6 开发指南》里的『Maven 指南』章节:https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/。『管理项目依赖关系』部分包含配置项目使用 JBoss EAP Bill of Material(BOM)artifacts 的重要信息。分析和理解您的应用程序
每个应用程序都是独特的,在移植前您必须彻底了解现有应用程序的相关组件和架构。
重要
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 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/
目录。在 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 节 “可移植的 EJB JNDI 名称”。
2.3. 复查已丢弃和不被支持的功能的列表
第 3 章 移植您的应用程序
3.1. 多数应用程序要求的修改
3.1.1. 复查多数程序所要求的修改
3.1.2. 类加载的修改
3.1.2.1. 根据类加载的变化更新应用程序
- 首先,查看您的应用程序的软件包结构及其依赖关系。详情请参考 第 3.1.2.3 节 “根据类加载的修改更新应用程序的依赖关系”。
- 如果您的应用程序使用了日志,您需要制定正确的模块依赖关系。请查看 第 3.1.4.1 节 “修改日志依赖关系” 里的相关细节。
- 由于模块化类加载的修改,您可能需要修改 EAR 或 WAR 的软件包结构。详情请参考 第 3.1.5.1 节 “修改 EAR 和 WAR 的打包”。
3.1.2.2. 了解模块依赖关系
模块只能访问自己的类,以及它具有显性或隐性依赖关系的任何模块上的类。
过程 3.1. 了解模块依赖关系
了解隐性依赖关系
服务器里的部署者隐性地自动添加一些常用的模块依赖关系,如javax.api
和sun.jdk
。这使得类在运行时对于部署可见,并让开发人员不用显性地添加依赖关系。关于如何和何时添加这些隐性的依赖关系,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的『类记载和模块』章节的『隐性模块依赖关系』。了解显性依赖关系
对于其他类,模块必须显性地指定,否则缺失的依赖关系会导致部署或运行时错误。如果缺失了依赖关系,您可以在服务器日志里看到ClassNotFoundExceptions
或NoClassDefFoundErrors
跟踪信息。如果多于一个模块加载了相同的 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 里的单一的、层级的类加载器,而是每个库都成为一个模块,链接它所依赖的模块。JBoss EAP 6 里的部署也是模块,它们不能访问应用服务器里 JAR 中定义的类,除非在这些类上显性地定义了依赖关系。应用服务器里定义的一些模块依赖关系是自动设置的。例如,如果您在部署一个 Java EE 应用程序,Java EE API 的依赖关系将被自动或隐性地添加到您的模块里。关于自动添加的依赖关系的完整列表,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》的『类加载和模块』章节里的『隐性模块依赖关系』。
当您移植程序到 JBoss 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-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/resources/META-INF/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.jar
且myApp.war
打包在myApp.ear
里的应用程序。myApp.war
使用@EJB
注解从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.jar
和myApp.war
的顺序。下面是一个使用<initialize-in-order>
元素来控制部署顺序的例子。myBeans.jar
在myApp.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 定义的ejb-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 的一个新的可选的部署描述符。这个部署描述符提供了对部署里的类加载的控制。
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/classes
和 WEB-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 里且可为应用程序所用。要使属性为 JBoss EAP 6 里的应用程序的 classpath 所用,您必须将其打包至您的应用程序里。
过程 3.2. 修改 ResourceBundle 属性的位置
- 如果您在开发 WAR 归档,您必须将这些属性包裹至 WAR 的
WEB-INF/classes/
目录里。 - 如果您想要 EAP 里所有组件都可以访问这些属性,那您必须将其打包到 JAR 的根目录并将 JAR 放入 EAR 的
lib/
目录下。
3.1.3.5. 创建自定义模块
过程 3.3. 创建自定义模块
- 创建和填充
module/
目录结构。- 在
EAP_HOME/module
目录下创建一个目录结构来包含文件和 JAR。例如:$ cd EAP_HOME/modules/
$ mkdir -p myorg-conf/main/properties
- 将属性文件移到您在前一步骤里创建的
EAP_HOME/modules/myorg-conf/main/properties/
目录里。 - 在
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>
- 修改服务器配置文件里的
ee
子系统。您可以使用 JBoss CLI 或手动编辑这个文件。- 按照下列步骤使用 JBoss CLI 来修改服务器配置文件。
- 启动服务器并连接至管理 CLI。
- 对于 Linux,输入下列命令:
$ EAP_HOME/bin/jboss-cli.sh --connect
- 对于 Windows,输入下列命令:
C:\>EAP_HOME\bin\jboss-cli.bat --connect
您应该看到下面的结果:Connected to standalone controller at localhost:9999
- 要在
ee
子系统里创建myorg-conf
<global-modules> 元素,输入下列命令:/subsystem=ee:write-attribute(name=global-modules, value=[{"name"=>"myorg-conf","slot"=>"main"}])
您应该看到下面的结果:{"outcome" => "success"}
- 如果您向手动编辑服务器配置文件,请按照下列步骤进行。
- 停止服务器并打开服务器配置文件。如果您使用的是独立服务器,这个文件是
EAP_HOME/standalone/configuration/standalone.xml
,如果是受管域,这个文件是EAP_HOME/domain/configuration/domain.xml
。 - 找到
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>
- 假设您复制了一个名为
my.properties
的文件到正确的模块位置,您现在可以使用下面的代码来加载属性文件了:Thread.currentThread().getContextClassLoader().getResource("my.properties");
3.1.4. 日志的修改
3.1.4.1. 修改日志依赖关系
JBoss LogManager 支持所有日志框架的前端,所以您可以保持现有的日志代码或移至新的 JBoss 日志框架。不管用哪种方法,因为模块化类加载有改变,您可能需要修改您的应用程序来添加所需的依赖关系。
过程 3.4. 更新程序日志代码
3.1.4.2. 为第三方日志框架更新应用程序代码
在 JBoss EAP 6 里,在默认情况下都已经添加了常见的第三方框架如 Apache Commons Loggin、Apache log4j、SLF4J 和 Java Logging 的日志依赖关系。在多数情况下,使用 JBoss EAP 容器提供的日志框架是更可取的。但是,如果您需要第三方框架提供的专有功能,您必须在部署里排除对应的 JBoss EAP 模块。请注意,虽然您的部署使用第三方的框架,服务器日志仍会继续使用 JBoss EAP 日志子系统配置。
org.apache.log4j
模块。第一个过程适用于所有的 JBoss EAP 6 版本,而第二个过程只适用于 JBoss EAP 6.3 及以后的版本。
过程 3.5. 配置 JBoss EAP 6 以使用 log4j.properties 或 log4j.xml 文件
注意
- 用下列内容创建一个
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>
- 如果您在部署 WAR,请将
jboss-deployment-structure.xml
文件放入META-INF/
或WEB-INF/
目录;如果是部署 EAR,则请放入META-INF/
目录。如果您的部署包含了依赖的子部署,您必须在每个子部署里排除这些模块。 - 在 EAR 的
lib/
或 WAR 部署的WEB-INF/classes/
里包含log4j.properties
或log4j.xml
文件。如果您想将文件放到lib/
目录里,您必须在jboss-deployment-structure.xml
文件里指定<resource-root>
路径。<jboss-deployment-structure> <deployment> <!-- Exclusions allow you to prevent the server from automatically adding some dependencies --> <exclusions> <module name="org.apache.log4j" /> </exclusions> <resources> <resource-root path="lib" /> </resources> </deployment> </jboss-deployment-structure>
- 用下列 runtime 参数启动 JBoss EAP 6 服务器以防止在部署应用程序时控制台出现
ClassCastException
。-Dorg.jboss.as.logging.per-deployment=false
- 部署您的应用程序
过程 3.6. 配置 JBoss EAP 6.3 或更新版本的日志依赖关系
add-logging-api-dependencies
logging 系统属性来排除第三方的日志框架依赖关系。下面的步骤演示了在 JBoss EAP 独立服务器上如何修改这个 logging 属性。
- 用下列 runtime 参数启动 JBoss EAP 6 服务器以防止在部署应用程序时控制台出现
ClassCastException
。-Dorg.jboss.as.logging.per-deployment=false
- 打开终端窗口并连接至管理 CLI。
- 对于 Linux,输入下列命令:
$ EAP_HOME/bin/jboss-cli.sh --connect
- 对于 Windows,输入下列命令:
C:\>EAP_HOME\bin\jboss-cli.bat --connect
- 修改日志子系统里的
add-logging-api-dependencies
属性。这个属性控制容器是否添加隐性的 logging API 依赖关系到部署里。- 如果为
true
(默认值),将添加所有隐性的 logging API 依赖关系。 - 如果设置为
false
,依赖关系不会添加到部署里。
要排除第三方的日志框架依赖关系,您必须用下列命令设置这个属性为false
。/subsystem=logging:write-attribute(name=add-logging-api-dependencies, value=false)
这个命令添加了<add-logging-api-dependencies>
元素到standalone.xml
配置文件的logging
子系统里。<subsystem xmlns="urn:jboss:domain:logging:1.4"> <add-logging-api-dependencies value="false"/> .... </subsystem>
- 部署您的应用程序
3.1.4.3. 修改代码以使用新的 JBoss Logging 框架
要使用新的框架,请修改您的导入和代码:
过程 3.7. 修改代码和依赖关系以使用新的 JBoss Logging 框架
修改您的导入和日志代码
下面是一个使用新的 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); }
添加日志依赖关系
包含 JBoss Logging 类的 JAR 位于名为org.jboss.logging
的模块。您的MANIFEST-MF
文件应该类似要:Manifest-Version: 1.0 Dependencies: org.jboss.logging
关于如何查找模块依赖关系的更多信息,请参考 第 3.1.2.3 节 “根据类加载的修改更新应用程序的依赖关系” and 第 4.2.1 节 “调试和解决移植问题”。
3.1.5. 应用程序包的修改
3.1.5.1. 修改 EAR 和 WAR 的打包
当您移植应用程序时,由于模块化类加载的修改,您可能需要修改 EAR 或 WAR 的打包结构。模块依赖关系是以特定的顺序加载的:
- 系统依赖关系
- 用户依赖关系
- 本地资源
- 部署间的依赖关系
过程 3.8. 修改归档的打包
打包 WAR
WAR 是一个模块,它里面的所有类都用相同的类加载器加载。这意味着打包在WEB-INF/lib/
目录里的类将和WEB-INF/classes
里的一样被对待。打包 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. 根据配置的变化更新应用程序
- 如果您的应用程序使用了数据源,请参考: 第 3.1.6.2 节 “更新数据源配置”。
- 如果您的应用程序使用了 JPA 且捆绑了 Hibernate JAR,请参考下列移植选项: 第 3.1.6.4 节 “为 Hibernate 或 JPA 配置数据源”。
- 如果您的应用程序使用了资源适配器,请参考: 第 3.1.6.5 节 “更新资源适配器配置”。
- 关于如何配置基本安全性,请参考: 第 3.1.7.1 节 “应用程序安全性的修改”。
3.1.6.2. 更新数据源配置
在以前的 JBoss EAP 版本里,JCA 数据源配置在一个后缀为 *-ds.xml
的文件里定义。这个文件然后部署在服务器的 deploy/
下或和应用程序一起打包。JDBC 驱动会被复制到 server/lib/
目录或打包在应用程序的 WEB-INF/lib/
目录里。虽然这种配置数据源的方法仍被支持,我们不推荐在产品环境里使用它,因为 JBoss 的管理根据已不支持。
domain/configuration/domain.xml
文件里配置。如果 JBoss EAP 实例以独立服务器运行,那么数据源是在 standalone/configuration/standalone.xml file
里配置。以这种方式配置的数据源可以用 JBoss 管理接口(包括 Web 管理控制台和命令行接口)进行管理和控制。这些工具使得在受管域上管理部署和配置多个服务器更为容易。
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 驱动”。
您可以使用 IronJacamar 工具来移植数据源和资源适配器配置。这个工具将 *-ds.xml
风格的配置文件转换为 JBoss EAP 6 所期望的格式。更多信息请参考 第 4.1.6 节 “使用 IronJacamar 工具来移植数据源和资源适配器配置”。
3.1.6.3. 安装和配置 JDBC 驱动
JDBC 驱动可以下列两种方式之一安装到容器里。
- 作为部署
- 作为核心模块
domain/configuration/domain.xml
文件里配置。如果 JBoss EAP 实例以独立服务器运行,那么数据源是在 standalone/configuration/standalone.xml file
里配置。Schema 引用信息两种模式都是一样的,您可以在 JBoss EAP 6 的 docs/schema/
里找到。我们在这里假设服务器是作为独立服务器运行的,而数据源是在 standalone.xml
文件里进行配置的。
过程 3.9. 安装和配置 JDBC 驱动
安装 JDBC 驱动。
将 JDBC 驱动作为部署安装。
这是我们推荐的安装驱动的方法。当 JDBC 驱动作为部署安装时,它会被部署为常规的 JAR。如果 JBoss EAP 实例作为独立服务器运行,请将兼容 JDBC 4.0 的 JAR 复制到EAP_HOME/standalone/deployments/
目录。对于受管域,您必须使用管理控制台或管理 CLI 将 JAR 部署到服务器组里。下面是一个安装为独立服务器的部署的 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
- 在 deployment 目录里创建一个
java.sql.Driver
文件。对于作为独立服务器运行的 JBoss EAP 6 实例,这个文件应该位于:EAP_HOME/standalone/deployments/META-INF/services/java.sql.Driver
。如果服务器位于受管服务器,您必须使用管理控制台或管理 CLI 来部署这个文件。
这种方法的优点是:这种方法的缺点是:- 因为不需要定义模块,所以这是最简单的方法。
- 当服务器运行在受管域里时,使用这个方法的部署将自动传播到域里的所有服务器。这意味着管理员不需要手动分发这个驱动 JAR。
- 如果 JDBC 驱动由多个 JAR 组成,如驱动 JAR 以及依赖的许可证 JAR 或本地化 JAR,您不能将驱动安装为部署。您必须将其安装为核心模块。
- 如果这个驱动不兼容 JDBC 4.0,您必须创建一个包含驱动类名的文件并导入至 JAR 或覆盖到
deployments/
目录。
将 JDBC 驱动安装为核心模块。
要将 JDBC 驱动安装为核心模块,您必须在EAP_HOME/modules/
下创建一个文件路径结构。这个结构包含 JDBC 驱动 JAR,任何其他供应商许可证或本地化 JAR,以及一个定义模块的module.xml
文件。将 MySQL JDBC 驱动安装为核心模块
- 创建一个目录结构
EAP_HOME/modules/com/mysql/main/
- 在
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" 错误。 - 复制 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 的驱动。- 创建目录结构
EAP_HOME/modules/com/ibm/db2/main/
。 - 在
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" 错误。 - 复制 JDBC 驱动和 license JAR 到
EAP_HOME/modules/com/ibm/db2/main/
目录里。$ cp db2jcc.jar
EAP_HOME/modules/com/ibm/db2/main/
$ cp db2jcc_license_cisuz.jarEAP_HOME/modules/com/ibm/db2/main/
这种方法的优点是:这种方法的缺点是:- 当 JDBC 驱动由多个 JAR 组成时,这是唯一的途径。
- 用这个方法,不兼容 JDBC 4.0 的驱动可以无需修改驱动 JAR 或创建覆盖文件就进行安装。
- 通过设置模块要更为困难。
- 您必须手动将模块复制到运行在受管域里的每个服务器里。
配置数据源。
添加数据库驱动。
在相同的文件的<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>
创建数据源。
在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>
更新程序代码里的 JNDI 引用。
您必须在程序源代码里替换过时的 JNDI 查找名称以使用新的 JNDI 标准化数据源名称。详情请参考: 第 3.1.8.4 节 “修改应用程序以遵循新的 JNDI 命名规则”。您也必须让现有的访问数据源的@Resource
注解使用新的 JNDI 名称。例如:@Resource(name = "java:/YourDatasourceName").
3.1.6.4. 为 Hibernate 或 JPA 配置数据源
过程 3.10. 删除 Hibernate 捆绑
- 从您的应用程序的库目录里将 Hibernate JAR 删除。
- 当不再需要
<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 引用信息两种模式都是一样的,您可以在 Iron Jacamar 网站 http://www.ironjacamar.org/documentation.html 的 Schemas 里找到。
重要
资源适配器描述符信息是在服务器配置文件中的下列 subsystem 元素里定义的:
<subsystem xmlns="urn:jboss:domain:resource-adapters:1.1"/>您将使用资源适配器
*-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. 应用程序安全性的修改
在以前的 JBoss EAP 版本里,放在 EAP_HOME/server/SERVER_NAME/conf/
下的属性文件位于 classpath 上且可以轻易地被 UsersRolesLoginModule
找到。在 JBoss EAP 6 里,目录结构已经发生了变化。属性文件必须打包在应用程序里,使其在 classpath 上可用。
重要
standalone/configuration/standalone.xml
或 domain/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.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.7.2. 更新使用 PicketLink STS 和 Web Services 的应用程序
如果您的 JBoss EAP 6.1 应用程序使用了 PicketLink STS 和 Web service,在移植到 JBoss EAP 6.2 或更新版本时您需要进行一些修改。我们修复了 JBoss EAP 以解决 CVE-2013-2133,在运行任何附加到基于 EJB3 的 WS 端点的 JAXWS 处理程序前它强制执行容器的授权检查。因此,一些 PicketLink STS 功能会受到影响,因为 PicketLink SAML2Handler
会建立一个安全性主体在以后使用。您可能在服务器日志里看到 NullPointerException
,因为当HandlerAuthInterceptor
访问 SAML2Handler
时这个主体是 NULL
。要修复这个问题,您必须要禁用安全检查。
过程 3.11. 禁用额外的授权检查
- 您可以禁用额外的授权检查并通过下列方法之一坚持使用现有的 PicketLink 部署。
设置系统属性
您可以通过设置org.jboss.ws.cxf.disableHandlerAuthChecks
属性为true
来禁用服务器级别的额外授权检查。这个方法会影响应用服务器上的所有部署。关于设置系统属性的信息,请参考《管理和配置指南》里的 『用管理 CLI 配置系统属性』。在部署的 Web 服务描述文件里创建一个属性
您可以通过设置jboss-webservices.xml
文件里的org.jboss.ws.cxf.disableHandlerAuthChecks
属性为true
来禁用部署级别的额外授权检查。这个方法只会影响专有的部署。- 在您要禁用额外授权检查的部署的
META-INF/
目录里创建一个jboss-webservices.xml
文件。 - 添加下列内容:
<?xml version="1.1" encoding="UTF-8"?> <webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee"> <property> <name>org.jboss.ws.cxf.disableHandlerAuthChecks</name> <value>true</value> </property> </webservices>
注意
org.jboss.ws.cxf.disableHandlerAuthChecks
属性会让系统易受 CVE-2013-2133 攻击。如果应用程序期望应用 EJB 方法上声明的安全限制且不独立于 JAX-WS 处理程序进行应用,那这个属性不应该启用。这个属性只能用于向后兼容的目的来避免破坏应用程序。
3.1.8. JNDI 的修改
3.1.8.1. 更新应用程序 JNDI 命名空间的名称
EJB 3.1 引入了一个标准化的全局 JNDI 命名空间和一系列相关的映射到不同 Java EE 应用程序作用域的命名空间。可移植 EJB 名称只能绑定其中三种:java:global
、java:module
和 java:app
。使用 JNDI 查找的应用程序必须进行修改以遵循新的标准化 JNDI 命名空间规则。
过程 3.12. 修改 JNDI 查找
前一版本里的 JNDI 命名空间示例以及如何在 JBoss EAP 6 里指定可以在这里找到: 第 3.1.8.5 节 “以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的”
3.1.8.2. 可移植的 EJB JNDI 名称
Java EE 6 规格定义了 4 种逻辑命名空间,每种都有自己的作用域,但可移植的 EJB 名称只能绑定到其中三种。下表详述了何时和如何使用每种命名空间。
表 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
|
3.1.8.3. 复核 JNDI 命名空间规则
JBoss EAP 6 已经改进了 JNDI 命名空间,不只是为应用服务器里绑定的每个名称提供可预测和一致的规则,也防止了将来的兼容性问题。这意味着如果名称不遵循新的规则,应用程序里的当前命名空间也会出现问题。
- 未限定的相对名称如
DefaultDS
或jdbc/DefaultDS
应该根据上下文限定于java:comp/env
、java:module/env
或java:jboss/env
。 - 未限定的
绝对
名称如/jdbc/DefaultDS
应该限定于java:jboss/root
。 - 限定的
绝对
名称如java:/jdbc/DefaultDS
应该和上面未限定的绝对
名称采用相同的方式进行限定。 java:jboss
命名空间在整个 AS 服务器实例间进行共享。- 带有
java:
前缀的相对
名称必须位于下列 5 个命名空间中的一个:comp
、module
、app
、global
或私有的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
。 - 如果您不想使用依赖关系注入,您仍可以像上面那样创建新的 InitialContext 并修改查找以使用新的 JNDI 命名空间名称。
private ProductManager productManager; try { context = new InitialContext(); productManager = (ProductManager) context.lookup("java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager"); } catch(Exception lookupError) { throw new ServletException("Unable to find the ProductManager bean", lookupError); }
3.1.8.5. 以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的
表 3.2. JNDI 命名空间映射表
JBoss EAP 5.x 里的命名空间 | JBoss EAP 6 里的命名空间 | 其他注释 |
---|---|---|
OrderManagerApp/ProductManagerBean/local | java:module/ProductManagerBean!services.ejb.ProductManager | Java EE6 标准绑定。作用域为当前模块,只可以在相同模块里访问。 |
OrderManagerApp/ProductManagerBean/local | java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager | Java EE6 标准绑定。作用域为当前应用程序,只可以在相同应用程序里访问。 |
OrderManagerApp/ProductManagerBean/local | java:global/OrderManagerApp/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager | Java EE6 标准绑定。作用域为应用服务器,可全局访问。 |
java:comp/UserTransaction | java:comp/UserTransaction | 命名空间作用域为当前组件。非 Java EE 6 的线程不可访问,例如您的应用程序直接创建的线程。 |
java:comp/UserTransaction | java:jboss/UserTransaction | 可全局访问,如果 java:comp/UserTransaction 不可用则使用它。 |
java:/TransactionManager | java:jboss/TransactionManager | |
java:/TransactionSynchronizationRegistry | java:jboss/TransactionSynchronizationRegistry |
3.2. 依赖于应用程序架构和组件的修改
3.2.1. 复查依赖于应用程序架构和组件的修改
- Hibernate 和 JPA
- 如果您的应用程序使用了 Hibernate 或 JPA,您的应用程序可能需要进行修改。详情请参考: 第 3.2.2.1 节 “更新使用 Hibernate 和/或 JPA 的应用程序”。
- REST
- 如果您的应用程序使用了 JAX-RS,您应该意识到 JBoss EAP 6 会自动设置 RESTEasy,所以您不需要再进行配置。详情请参考 第 3.2.5.1 节 “配置 JAX-RS 和 RESTEasy 的修改”。
- LDAP
- 在 JBoss EAP 6 里,LDAP 安全区的配置是不一样的。如果您的应用程序使用了 LDAP,请参考: 第 3.2.6.1 节 “配置 LDAP Security Realm 的修改”。
- Messaging
- JBoss EAP 6 里不再包含 JBoss Messaging。如果您的应用程序使用了 JBoss Messaging,您需要用 HornetQ 替换 JBoss Messaging 代码。下面是相关的信息: 第 3.2.7.4 节 “移植您的应用程序以将 HornetQ 用作 JMS 提供者”。
- 群集
- 在 JBoss EAP 6 里启用群集的方式已经修改了,详情请参考: 第 3.2.8.1 节 “修改应用程序以用于群集环境”。
- 服务风格的部署
- 虽然 JBoss EAP 6 不再使用服务风格的描述符,容器还是这种风格的部署而无需进行修改。关于部署的信息,请参考: 第 3.2.9.1 节 “更新使用服务风格部署的应用程序”。
- 远程调用
- 如果您的应用程序进行了远程调用,您仍可以使用 JNDI 为您的 Bean 查找代理并在返回的代理上进行调用。关于语法和命名空间的修改,请参考: 第 3.2.10.1 节 “将进行远程调用的 JBoss EAP 5 应用程序移植到 JBoss EAP 6”。
- Seam 2.2
- 如果您的应用程序使用了 Seam 2.2,对于可能需要的修改,请参考: 第 3.2.13.1 节 “移植 Seam 2.2 归档到 JBoss EAP 6”。
- Spring
- 如果您的用程序使用了 Spring,请参考: 第 3.2.14.1 节 “移植 Spring 应用程序”。
- 其他可能影响应用程序移植的修改
- 对于 JBoss EAP 6 里可能影响您的应用程序的其他修改,请参考: 第 3.2.15.1 节 “熟悉其他可能影响到移植的修改”。
3.2.2. Hibernate 和 JPA 的修改
3.2.2.1. 更新使用 Hibernate 和/或 JPA 的应用程序
如果您的应用程序使用了 Hibernate 或 JPA,请阅读下列章节并进行修改以移植到 JBoss EAP 6。
3.2.2.2. 修改使用 Hibernate 和 JPA 的应用程序的配置
如果您的应用程序包含一个 persistence.xml
文件或代码使用了注解 @PersistenceContext
或 @PersistenceUnit
,JBoss EAP 6 会在部署时进行检测并假设应用程序使用了 JPA。这会隐性地添加 Hibernate 4 以及一些其他的依赖关系到应用程序的 classpath。
ClassNotFoundExceptions
,您可以用下列方法来解决:
重要
过程 3.13. 配置应用程序
复制所需的 Hibernate 3 JAR 到您的应用程序库。
您可以复制包含缺失类的特定 Hibernate 4 JAR 到应用程序的lib/
目录,或者使用其他方法添加它们到 classpath 来解决这个问题。在某些情况下,由于混合使用 Hibernate 版本,这可能会导致ClassCastExceptions
或其他类加载问题。如果发生了这种情况,您需要使用下一个方法。设置服务器只使用 Hibernate 3 库。
JBoss EAP 6 允许您将 Hibernate 3.5 (或更高版本) 持久化提供者 JAR 和应用程序打包。为了让服务器只使用 Hibernate 3 库而排除 Hibernate 4 库,您需要在persistence.xml
里设置jboss.as.jpa.providerModule
为hibernate3-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 节 “持久化单元属性”。禁用 Hibernate 的二级缓存
Hibernate 3 的二级缓存在 JBoss EAP 6 没有展现和以前版本里一样的行为。如果您在应用程序里是了 Hibernate 二级缓存,您必须禁用它,直至您升级到 Hibernate 4。要禁用二级缓存,您需要在persistence.xml
文件里将<hibernate.cache.use_second_level_cache>
设置为false
。
3.2.2.3. 持久化单元属性
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 实例上的所有应用程序部署里都是唯一的。
|
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
。
new_generator_mappings
被设置为 false
:
@GeneratedValue(AUTO)
映射 Hibernate "native".@GeneratedValue(TABLE)
映射org.hibernate.id.MultipleHiLoPerTableGenerator
。@GeneratedValue(SEQUENCE)
映射 Hibernate "seqhilo"。
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 和持久化提供者一起使用的集成类的名称。
目前的有效值是:
|
3.2.2.4. 更新 Hibernate 3 应用程序以使用 Hibernate 4
当您更新应用程序以使用 Hibernate 4 时,某些更新是通用的,而不管您目前使用的是什么版本的 Hibernate。对于其他的更新,您必须确定您当前使用的版本。
过程 3.14. 更新应用程序以使用 Hibernate 4
- 自动增量序列生成器的默认行为在 JBoss EAP 6 已经进行了修改。详情请参考 第 3.2.2.5 节 “Hibernate 标识符自动生成值”。
- 确定应用程序当前使用的 Hibernate 版本并选择下面正确的更新过程。
- See 第 3.2.2.8 节 “修改运行在群集环境里的移植的 Seam 和 Hibernate 应用程序的持久化属性” 如果您准备在群集环境里运行您的应用程序。
3.2.2.5. Hibernate 标识符自动生成值
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 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.6. 移植您的 Hibernate 3.3.x 应用程序到 Hibernate 4.x
映射 Hibernate
text
类型为JDBC LONGVARCHAR
在 Hibernate 3.5 以前的版本里,text
类型被映射为JDBC CLOB
。Hibernate 4 里引入了一个新的 Hibernate 类型materialized_clob
,将 JavaString
属性映射为JDBC CLOB
。如果您的应用程序里有属性需要将type="text"
映射为JDBC CLOB
,您必须这样:- 如果您的应用程序使用了 hbm 映射文件,请将这个属性修改为
type="materialized_clob"
。 - 如果您的应用程序使用了注解,您应该将
@Type(type = "text")
替换为@Lob
。
复查代码以找出返回值类型的修改
数字型组合标准的 Projection 现在返回的是 HQL 对应类型的值。因此,org.hibernate.criterion
里的下列 projection 的返回类型已经有了改动。CountProjection
、Projections.rowCount()
、Projections.count(propertyName)
和Projections.countDistinct(propertyName)
里的修改,count
和count distinct
projection 现在返回的是Long
型值。- 由于
Projections.sum(propertyName)
里的修改,sum
projection 现在返回依赖于属性类型的值。注意
修改程序代码失败可能导致java.lang.ClassCastException
。- 对映射为 Long、Short、Integer 或原始类型的属性,将返回 Long 值。
- 对映射为 Float、Double 或原始浮点类型的属性,将返回 Double 值。
3.2.2.7. 移植您的 Hibernate 3.5.x 应用程序到 Hibernate 4.x
- 合并 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
。 - 修改这个命名空间以遵循下表里注明的新的 Hibernate DTD 文件名称规则。
表 3.5. DTS 命名空间映射表
以前的 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 - 修改环境变量。
- 如果您在使用 Oracle 以及
materialized_clob
或materialized_blob
属性,全局环境变量hibernate.jdbc.use_streams_for_binary
必须设置为 true。 - 如果您在使用 PostgreSQL 以及
CLOB
或BLOB
属性,全局环境变量hibernate.jdbc.use_streams_for_binary
必须设置为 false。
3.2.2.8. 修改运行在群集环境里的移植的 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.15. 设置持久化属性以运行在群集环境里
- 设置
hibernate.session_factory_name
为唯一的名字。这个名字必须在 JBoss EAP 实例里的所有应用程序部署里是唯一的。例如:<property name="hibernate.session_factory_name" value="jboss-seam-booking.ear_session_factory"/>
- 设置
hibernate.ejb.entitymanager_factory_name
为唯一的名字。这个名字必须在 JBoss EAP 实例里的所有应用程序部署里是唯一的。例如:<property name="hibernate.ejb.entitymanager_factory_name" value="seam-booking.ear_PersistenceUnitName"/>
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.。
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.16. 修改 persistence.xml
文件以使用 Infinispan
为 JBoss EAP 6 里的 JPA 应用程序配置 Infinispan
这是如何用 JBoss EAP 6 里的 Infinispan 来指定属性以实现用于 JPA 应用程序的相同配置:<property name="hibernate.cache.use_second_level_cache" value="true"/>
此外,您需要指定一个值为ENABLE_SELECTIVE
或ALL
的shared-cache-mode
:ENABLE_SELECTIVE
是默认的推荐值。它表示实体不会被缓存,除非您显性地将它标记为可缓存的。<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
ALL
表示实体总是被缓存,即使您将其标记为不可缓存的。<shared-cache-mode>ALL</shared-cache-mode>
为 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
3.2.2.11. Hibernate Cache 属性
表 3.6. 属性
属性名 | 描述 |
---|---|
hibernate.cache.region.factory_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.17. 您可能需要执行一个或多个下面的任务:
访问默认的 ValidatorFactory
JBoss EAP 6 捆绑了一个默认的 ValidatorFactory 到java:comp/ValidatorFactory
下的 JNDI 上下文。理解生命周期触发的检验
和 Hibernate Core 4 一起使用时,Hibernate Core 将自动启用基于生命周期的检验。- 检验发生在实体
INSERT
、UPDATE
和DELETE
操作上。 - 您可以配置组通过使用下列属性的事件类型来检验:这些属性的值是用逗号隔开的、要检验的组的全限定类名。
javax.persistence.validation.group.pre-persist
,javax.persistence.validation.group.pre-update
,和javax.persistence.validation.group.pre-remove
。
Validation 组是 Bean Validation 规格的一个新功能。如果您不想利用这个功能,移植到 Hibernate Validator 4 时无需进行任何修改。 - 您可以设置
javax.persistence.validation.mode
属性为none
来禁用基于生命周期的检验。这个属性的其他值还有auto
(默认值),callback
和ddl
。
配置您的应用程序以使用手动检验
- 如果您想手动控制检验,您可以用下列方法创建一个 Validator:
- 用
getValidator()
方法从ValidatorFactory
创建一个Validator
实例。 - 注入 Validator 实例到 EJB、CDI Bean 或其他 Java EE 可注入的资源。
- 您可以使用
ValidatorFactory.usingContext()
返回的ValidatorContext
来定制您的 Validator 实例。使用这个 API,您可以配置一个自定义的MessageInterpolator
、TraverableResolver
和ConstraintValidatorFactory
。Bean Validator 规格里指定了这些接口,在 Hibernate Validator 4 里它们是新的接口。
修改代码以使用新的 Bean Validation 约束
当移植到 Hibernate Validator 4 时新的 Bean 级别的检验约束需要修改代码。- 要升级到 Hibernate Validator 4,您必须使用下列包里的约束:
javax.validation.constraints
org.hibernate.validator.constraints
- Hibernate Validator 3 里所有的约束在 Hibernate Validator 4 里仍然可用。要使用它们,您需要导入指定的类,且在某些情况下,修改其约束参数的名称或类型。
使用自定义的约束
在 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. Web Services 的修改
3.2.4.1. Web Services 的修改
- JBossWS API 项目的修改
- JBossWS 4 里引用了 SPI 和公用组件。下表列出了可能影响应用程序移植的 API 和软件包修改。
表 3.7. Size Log Handler 属性
旧的 JAR 旧的软件包 新的 JAR 新的软件包 JBossWS SPI org.jboss.wsf.spi.annotation.* JBossWS API org.jboss.ws.api.annotation.* JBossWS SPI org.jboss.wsf.spi.binding.* JBossWS API org.jboss.ws.api.binding.* JBossWS SPI org.jboss.wsf.spi.management.recording.* JBossWS API org.jboss.ws.api.monitoring.* JBossWS SPI org.jboss.wsf.spi.tools.* JBossWS API org.jboss.ws.api.tools.* JBossWS SPI org.jboss.wsf.spi.tools.ant.* JBossWS API org.jboss.ws.tools.ant.* JBossWS SPI org.jboss.wsf.spi.tools.cmd.* JBossWS API org.jboss.ws.tools.cmd.* JBossWS SPI org.jboss.wsf.spi.util.ServiceLoader JBossWS API org.jboss.ws.api.util.ServiceLoader JBossWS Common org.jboss.wsf.common.* JBossWS API org.jboss.ws.common.* JBossWS Common org.jboss.wsf.common.handler.* JBossWS API org.jboss.ws.api.handler.* JBossWS Common org.jboss.wsf.common.addressing.* JBossWS API org.jboss.ws.api.addressing.* JBossWS Common org.jboss.wsf.common.DOMUtils JBossWS API org.jboss.ws.api.util.DOMUtils JBossWS Native org.jboss.ws.annotation.EndpointConfig JBossWS API org.jboss.ws.api.annotation.EndpointConfig - @WebContext Annotation
- 在 JBossWS 3.4.x 里,这个注解在 JBossWS SPI 项目被打包为
org.jboss.wsf.spi.annotation.WebContext
。在 JBossWS 4.0 里,这个注解被移至 JBossWS API 项目里的org.jboss.ws.api.annotation.WebContext
。如果您的应用程序包含了过时的依赖关系,您必须替换源代码里的导入和依赖关系并根据新的 JBossWS API JAR 重新编译。本版里对于不向后兼容的属性也有改动。String[] virtualHosts
属性已经改为String virtualHost
。在 JBoss EAP 6 里,对于每个部署您可以只指定一个虚拟主机。如果多个 web service 使用了@WebContext
注解,对于部署归档里定义的所有端点,virtualHost 值都必须是相等的。 - 端点配置
- JBossWS 4.0 提供了 JBoss Web Service 栈和多数 Apache CXF 项目模块的集成。集成层允许使用标准 webservice API,包括 JAX-WS。它也允许在 JBoss EAP 6 容器上使用 Apache CXF 的高级功能而无需复杂的配置或设置。JBoss EAP 6 的域配置里的
webservice
子系统包含了预定义的端点配置。您也可以定义自己的其他端点配置。@org.jboss.ws.api.annotation.EndpointConfig
注解被用来引用给定的端点配置。关于配置 JBoss 服务器里 webservice 端点的更多信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的『JAX-WS Web Services』章节。 - jboss-webservices.xml 部署描述符
- JBossWS 4.0 引入了新的部署描述符来配置 web service。
jboss-webservices.xml
文件为给定的部署提供了其他信息并部分地替换了过时的jboss.xml
文件。对于 EJB webservice 部署,jboss-webservices.xml
描述符文件期望的位置是META-INF/
目录。对于 POJO 和 WAR 里捆绑的EJB webservice 端点,jboss-webservices.xml
文件期望的位置是META-INF/
目录。下面是jboss-webservices.xml
描述符文件和描述元素的表的例子。<webservices> <context-root>foo<context-root> <config-name>Standard WSSecurity Endpoint</config-name> <config-file>META-INF/custom.xml</config-file> <property> <name>prop.name</name> <value>prop.value</value> </property> <port-component> <ejb-name>TestService</ejb-name> <port-component-name>TestServicePort</port-component-name> <port-component-uri>/*</port-component-uri> <auth-method>BASIC</auth-method> <transport-guarantee>NONE</transport-guarantee> <secure-wsdl-access>true</secure-wsdl-access> </port-component> <webservice-description> <webservice-description-name>TestService</webservice-description-name> <wsdl-publish-location>file:///bar/foo.wsdl</wsdl-publish-location> </webservice-description> </webservices>
表 3.8. jboss-webservice.xml 文件元素描述
元素名称 描述 context-root用来自定义 webservices 部署的根上下文。config-nameconfig-file用来关联端点部署和给定的端点配置。端点配置是在引用的配置文件或域配置的webservices
子系统里指定的。property用来设置简单属性的名称/值对以配置 webservice 的栈行为。port-component用来自定义 EJB 端点目标 URI 以配置和安全性相关的属性。webservice-description用来自定义或覆盖 webservice WSDL 的发布位置。
3.2.5. JAX-RS 和 RESTEasy 的修改
3.2.5.1. 配置 JAX-RS 和 RESTEasy 的修改
web.xml
文件里删除所有现有的 RESTEasy 配置并替换为下列三个选项之一:
- 子类化
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/*
。 - 将
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
注解设置的应用程序路径。 - 修改
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.6. LDAP Security Realm 的修改
3.2.6.1. 配置 LDAP Security Realm 的修改
login-config.xml
文件里的 <application-policy>
元素中配置的。而在 JBoss EAP 6 里,LDAP 安全区是在服务器配置文件里的 <security-domain>
里进行配置。对于独立服务器,配置文件是 standalone/configuration/standalone.xml
;如果是在受管域里运行服务器,这个文件是 domain/configuration/domain.xml
。
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>
<subsystem xmlns="urn:jboss:domain:security:1.2"> <security-domains> <security-domain name="mcp_ldap_domain" cache-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>
注意
<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.7. HornetQ 的修改
3.2.7.1. 关于 HornetQ 和 NFS
- 红帽企业版 Linux NFS 客户缓存必须被禁用。
重要
重要
libaio
安装在 JBoss EAP 6 所运行的红帽企业版 Linux 系统上。
3.2.7.2. 配置 JMS 桥以移植现有的 JMS 消息到到 JBoss EAP 6
3.2.7.3. 创建 JMS 桥
JMS 桥消费源 JMS 队列或主题里的消息并发送到目标 JMS 队列或主题(通常位于不同的服务器上)。它可以用于桥接 任何 JMS 服务器间的消息,只要这些消息是兼容 JMS 1.1 的。源和目的 JMS 资源通过 JNDI 查找,用于 JNDI 查找的客户类必须捆绑在模块里。然后在 JMS 桥配置里声明模块名。
过程 3.18. 创建 JMS 桥
配置源 JBoss EAP 5.x 服务器上的 JMS 桥
为了避免版本间类的冲突,您必须使用下列过程配置 JBoss EAP 5.x 上的 JMS 桥。SAR 目录和桥的名称可以是任意的,也可以进行修改。- 在 JBoss EAP 5 的 deployment 目录里创建一个子目录来包含 SAR,如
EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar
。 - 在
EAP5_HOME/server/PROFILE_NAME/deploy/myBridge.sar/
里创建一个名为META-INF
的子目录。 - 在
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 EAP 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 查找和桥 "target" 都包括用户 "jbossuser"(密码 "jbosspass")的安全性凭证。这是因为 JBoss EAP 6 默认是设置了安全性的。用户 "jbossuser" 和密码 "jbosspass" 都是在具有guest
角色的ApplicationRealm
里使用EAP_HOME/bin/add_user.sh
脚本创建的。 - 将下列 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 里的类相冲突。
配置目的 JBoss EAP 6 服务器上的 JMS 桥
在 JBoss EAP 6.1 以后的版本里,JMS 桥可以用于从任何兼容 JMS 1.1 的服务器上桥接消息。因为源和目标 JMS 资源是用 JNDI 进行查找的,源消息供应商或消息中介的 JNDI 查找类必须捆绑在 JBoss 模块里。下面的过程使用了虚拟的 'MyCustomMQ' 消息中介作为例子。- 为消息供应商创建 JBoss 模块。
- 在
EAP_HOME/modules/system/layers/base/
下为新的模块创建一个目录结构。main/
子目录将包含客户 JAR 和module.xml
文件。下面是为消息供应商 MyCustomMQ 创建的一个目录结构示例:EAP_HOME/modules/system/layers/base/org/mycustommq/main/
。 - 在
main/
子目录里,创建一个包含消息供应商的模块定义的module.xml
文件。下面是为消息供应商 MyCustomMQ 创建的module.xml
示例。<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.1" name="org.mycustommq"> <properties> <property name="jboss.api" value="private"/> </properties> <resources> <!-- Insert resources required to connect to the source or target --> <resource-root path="mycustommq-1.2.3.jar" /> <resource-root path="mylogapi-0.0.1.jar" /> </resources> <dependencies> <!-- Add the dependencies required by JMS Bridge code --> <module name="javax.api" /> <module name="javax.jms.api" /> <module name="javax.transaction.api"/> <!-- Add a dependency on the org.hornetq module since we send --> <!-- messages tothe HornetQ server embedded in the local EAP instance --> <module name="org.hornetq" /> </dependencies> </module>
- 从源资源复制消息供应商用于 JNDI 查找的的 JAR 到模块的
main/
子目录。MyCustomMQ 模块的目录结构应该类似于:modules/ `-- system `-- layers `-- base `-- org `-- mycustommq `-- main |-- mycustommq-1.2.3.jar |-- mylogapi-0.0.1.jar |-- module.xml
- 配置部署到 JBoss EAP 6 服务器的
messaging
子系统的 JMS 桥。- 在开始之前,请先停止服务器并备份当前的服务器配置文件。如果是作为独立服务器运行,它是
EAP_HOME/standalone/configuration/standalone-full-ha.xml
。如果是运行的受管域,请备份EAP_HOME/domain/configuration/domain.xml
以及EAP_HOME/domain/configuration/host.xml
。 - 在服务器配置文件里的
messaging
子系统里添加jms-bridge
元素。source
和target
元素提供的用于 JNDI 查找的 JMS 资源的名称。如果指定了user
和password
凭证,当创建 JMS 连接时它们会作为参数传入。下面是一个为消息供应商 MyCustomMQ 配置的jms-bridge
元素例子:<subsystem xmlns="urn:jboss:domain:messaging:1.3"> ... <jms-bridge name="myBridge" module="org.mycustommq"> <source> <connection-factory name="ConnectionFactory"/> <destination name="sourceQ"/> <user>user1</user> <password>pwd1</password> <context> <property key="java.naming.factory.initial" value="org.mycustommq.jndi.MyCustomMQInitialContextFactory"/> <property key="java.naming.provider.url" value="tcp://127.0.0.1:9292"/> </context> </source> <target> <connection-factory name="java:/ConnectionFactory"/> <destination name="/jms/targetQ"/> </target> <quality-of-service>DUPLICATES_OK</quality-of-service> <failure-retry-interval>500</failure-retry-interval> <max-retries>1</max-retries> <max-batch-size>500</max-batch-size> <max-batch-time>500</max-batch-time> <add-messageID-in-header>true</add-messageID-in-header> </jms-bridge> </subsystem>
在上面的例子里,JNDI 属性是在source
的context
元素里定义的。如上面的target
例子,如果忽略了context
元素,JMS 资源将在本地实例里进行查找。
3.2.7.4. 移植您的应用程序以将 HornetQ 用作 JMS 提供者
过程 3.19. 在开始之前
- 关闭客户和服务器。
- 备份任何 JBoss Messaging 数据。消息数据保存在数据库中前缀为
JBM_
的表里。
过程 3.20. 修改提供者为 HornetQ
转移配置
请转移现有的 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 安装而有所不同。
修改您的程序代码
如果应用程序代码使用了标准的 JMS,您不需要修改代码。然而,如果应用程序将连至群集,您必须小心地查看 HornetQ 文档里关于群集模式的内容。群集超出了 JMS 规格和 HornetQ 的范围,JBoss Messaging 采用了很不一样的方法来实现群集功能。如果应用程序使用 JBoss Messaging 专有的功能,您必须修改代码以使用 HornetQ 里相等的功能。关于如何用 HornetQ 配置消息系统,请参考 第 3.2.7.5 节 “用 HornetQ 配置消息系统”。移植现有的消息
使用 JMS 桥移动 JBoss Messaging 数据库里的任何消息到 HornetQ 日志。配置 JMS 桥的说明可以在这里找到: 第 3.2.7.2 节 “配置 JMS 桥以移植现有的 JMS 消息到到 JBoss EAP 6 ”。
3.2.7.5. 用 HornetQ 配置消息系统
standalone.xml
或domain.xml
文件。但熟悉默认配置文件的消息组件是有用处的,而使用管理工具的示例的文档里包含了配置文件的片段以供参考。
3.2.8. 群集的修改
3.2.8.1. 修改应用程序以用于群集环境
启动启用了群集的 JBoss EAP 6
要在 JBoss EAP 5.x 里启用群集,您需要使用all
配置集或其衍生配置集来启动服务器实例,如:$ EAP5_HOME/bin/run.sh -c all
在 JBoss EAP 6 里,启用群集的方法取决于服务器是独立的还是运行在受管域里。为运行在受管域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>
为独立服务器启用群集
要为独立服务器启用群集,用下列配置文件启动服务器:$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME
指定绑定地址
在 JBoss EAP 5.x 里,您通常要用-b
命令行参数来指定用于群集的绑定地址:$ EAP5_HOME/bin/run.sh -c all -b 192.168.0.2
JBoss EAP 6 绑定套接字到standalone.xml
、domain.xml
和host.xml
文件里<interfaces>
元素的 IP 地址和接口。JBoss EAP 6 附带的标准配置包含两个接口配置:<interfaces> <interface name="management"> <inet-address value="${jboss.bind.address.management:127.0.0.1}"/> </interface> <interface name="public"> <inet-address value="${jboss.bind.address:127.0.0.1}"/> </interface> </interfaces>
这些接口配置使用了系统属性jboss.bind.address.management
和jboss.bind.address
的值。如果没有设置这些系统属性,默认的127.0.0.1
将被使用。您也可以在启动服务器时指定绑定地址为命令行参数,或者您可以在 JBoss EAP 6 服务器配置文件里显性地定义它。- 在启动 JBoss EAP 独立服务器时在命令行指定绑定参数。下面是一个如何在命令行为独立服务器指定绑定地址的例子:
EAP_HOME/bin/standalone.sh -Djboss.bind.address=127.0.0.1
注意
您也可以使用-b
参数,它是-Djboss.bind.address=127.0.0.1
的快捷方式。EAP_HOME/bin/standalone.sh -b=127.0.0.1
JBoss EAP 5 的语法格式仍被支持:
请注意EAP_HOME/bin/standalone.sh -b 127.0.0.1
-b
参数只会修改public
接口。它不会影响management
接口。 - 在服务器配置文件里指定绑定地址。对于运行在受管域里的服务器,请在
domain/configuration/host.xml
文件里指定绑定地址。对于独立服务器,请在standalone-ha.xml
文件里指定绑定地址。在下面的例子里,public
接口被指定为ha-sockets
套接字绑定组里的所有套接字的默认接口。<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>
注意
如果您用硬编码值而不是配置文件里的系统属性来指定绑定地址,那您无法用命令行参数来覆盖它。
配置
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
也可以用管理控制台来设置。指定多点传送地址和端口
在 JBoss EAP 5.x 里,您可以使用命令行参数-u
和-m
分别指定用于群集间通讯的多点传送地址和端口,如:$ EAP5_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
使用其他的协议栈
在 JBoss EAP 5.x 里,您可以使用jboss.default.jgroups.stack
系统属性来操纵用于群集服务的默认协议栈。$ EAP5_HOME/bin/run.sh -c all -Djboss.default.jgroups.stack=tcp
在 JBoss EAP 6 里,默认的协议栈是由domain.xml
或standalone-ha.xml
里的 JGroups 子系统定义的:<subsystem xmlns="urn:jboss:domain:jgroups:1.0" default-stack="udp"> <stack name="udp"> <!-- ... --> </stack> </subsystem>
替换 Buddy Replication
JBoss EAP 5.x 使用 JBoss Cache Buddy Replication 来抑制将数据复制到群集里的所有实例。在 JBoss EAP 6 里,Buddy Replication 已被 Infinispan 的分布式缓存(也称为DIST
模式)所替代。Distribution 是一个强大的群集模式,它允许 Infinispan 在添加更多服务器到群集里时进行线性扩充。下面是配置服务器使用 DIST 缓存模式的例子。- 打开命令行窗口并用 HA 或 Full 配置集启动服务器,例如:
EAP_HOME/bin/standalone.sh -c standalone-ha.xml
- 打开另外一个命令行窗口并连接至管理 CLI。
- 对于 Linux,输入下列命令:
$ EAP_HOME/bin/jboss-cli.sh --connect
- 对于 Windows,输入下列命令:
C:\>EAP_HOME\bin\jboss-cli.bat --connect
您应该看到下面的结果:Connected to standalone controller at localhost:9999
- 运行下列命令:
/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=dist) /subsystem=infinispan/cache-container=web/distributed-cache=dist/:write-attribute(name=owners,value=3) :reload
您应该在每条命令后看到下面的结果:"outcome" => "success"
这些命令修改了standalone-ha.xml
文件的infinispan
子系统的web
<cache-container>
配置里的dist
<distributed-cache>
元素:<cache-container name="web" aliases="standard-session-cache" default-cache="dist" module="org.jboss.as.clustering.web.infinispan"> <transport lock-timeout="60000"/> <replicated-cache name="repl" mode="ASYNC" batching="true"> <file-store/> </replicated-cache> <replicated-cache name="sso" mode="SYNC" batching="true"/> <distributed-cache name="dist" owners="3" l1-lifespan="0" mode="ASYNC" batching="true"> <file-store/> </distributed-cache> </cache-container>
更多的信息请参考客户门户 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的『Web 应用程序里的群集』章节。
3.2.8.2. 实现 HA 单点登录
下面的过程演示了如何部署用 SingletonService decorator 包裹并用作群集内的单点登录服务的 Service。这个服务激活了群集里只启动一次的定时器。
过程 3.21. 实现 HA 单点登录服务
编写 HA 单点登录服务应用程序。
下面是一个将部署为单点登录服务的用SingletonService
decorator 包裹的Service
的简单例子。完整的例子请参考 JBoss EAP 6 附带的cluster-ha-singleton
quickstart。这个 Quickstart 包含构建和部署应用程序的所有说明。创建服务。
下面是一个服务的示例:package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.logging.Logger; 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; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public class HATimerService implements Service<String> { private static final Logger LOGGER = Logger.getLogger(HATimerService.class); public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer"); /** * A flag whether the service is started. */ private final AtomicBoolean started = new AtomicBoolean(false); /** * @return the name of the server node */ public String getValue() throws IllegalStateException, IllegalArgumentException { LOGGER.infof("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name")); return ""; } public void start(StartContext arg0) throws StartException { if (!started.compareAndSet(false, true)) { throw new StartException("The service is still started!"); } LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'"); final String node = System.getProperty("jboss.node.name"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).initialize("HASingleton timer @" + node + " " + new Date()); } catch (NamingException e) { throw new StartException("Could not initialize timer", e); } } public void stop(StopContext arg0) { if (!started.compareAndSet(true, false)) { LOGGER.warn("The service '" + this.getClass().getName() + "' is not active!"); } else { LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop(); } catch (NamingException e) { LOGGER.error("Could not stop timer", e); } } } }
创建一个将
Service
安装为群集单点登录服务的激活器。下面的列表是一个服务激活器的例子,它将HATimerService
安装为群集单点登录服务:package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import org.jboss.as.clustering.singleton.SingletonService; import org.jboss.logging.Logger; import org.jboss.msc.service.DelegatingServiceContainer; import org.jboss.msc.service.ServiceActivator; import org.jboss.msc.service.ServiceActivatorContext; import org.jboss.msc.service.ServiceController; /** * Service activator that installs the HATimerService as a clustered singleton service * during deployment. * * @author Paul Ferraro */ public class HATimerServiceActivator implements ServiceActivator { private final Logger log = Logger.getLogger(this.getClass()); @Override public void activate(ServiceActivatorContext context) { log.info("HATimerService will be installed!"); HATimerService service = new HATimerService(); SingletonService<String> singleton = new SingletonService<String>(service, HATimerService.SINGLETON_SERVICE_NAME); /* * To pass a chain of election policies to the singleton, for example, * to tell JGroups to prefer running the singleton on a node with a * particular name, uncomment the following line: */ // singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node2/cluster"))); singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry())) .setInitialMode(ServiceController.Mode.ACTIVE) .install() ; } }
注意
上面的代码示例使用了org.jboss.as.clustering.singleton.SingletonService
类,它是 JBoss EAP 私有 API 的一部分。JBoss EAP 7 里将有可用的公共 API,私有 API 会被废弃,但在 EAP 6.x 发行周期里我们仍将维护这个类。创建 ServiceActivator 文件
在应用程序的resources/META-INF/services/
目录里创建一个名为org.jboss.msc.service.ServiceActivator
的文件。请添加包含之前步骤里创建的 ServiceActivator 类的全限定名称的一行内容。org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator
创建一个 Singleton bean,它实现了用作群集内 Singleton Timer 的 Timer。
这个 Singleton bean 不能有 remote 接口,而且您不能从任何应用程序里的其他 EJB 引用其 local 接口。这阻止了客户或其他组件的查找,从而确保 SingletonService 可以完全控制单点登录。创建 Scheduler 接口
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public interface Scheduler { void initialize(String info); void stop(); }
创建实现群集内 Singleton Timer 的 Singleton bean。
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import javax.annotation.Resource; import javax.ejb.ScheduleExpression; import javax.ejb.Singleton; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import org.jboss.logging.Logger; /** * A simple example to demonstrate a implementation of a cluster-wide singleton timer. * * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ @Singleton public class SchedulerBean implements Scheduler { private static Logger LOGGER = Logger.getLogger(SchedulerBean.class); @Resource private TimerService timerService; @Timeout public void scheduler(Timer timer) { LOGGER.info("HASingletonTimer: Info=" + timer.getInfo()); } @Override public void initialize(String info) { ScheduleExpression sexpr = new ScheduleExpression(); // set schedule to every 10 seconds for demonstration sexpr.hour("*").minute("*").second("0/10"); // persistent must be false because the timer is started by the HASingleton service timerService.createCalendarTimer(sexpr, new TimerConfig(info, false)); } @Override public void stop() { LOGGER.info("Stop all existing HASingleton timers"); for (Timer timer : timerService.getTimers()) { LOGGER.trace("Stop HASingleton timer: " + timer.getInfo()); timer.cancel(); } } }
启动每个启用了群集的 JBoss EAP 6 实例。
要为独立服务器启用群集,您必须用HA
配置集启动每个服务器并对每个实例使用唯一的节点名称和端口偏移量。- 对于 Linux,请使用下列命令行语法启动服务器:
EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
例 3.1. 在 Linux 上启动多个独立服务器实例
$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node1
$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
- 对于 Microsoft Windows,请使用下列命令行语法启动服务器:
EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
例 3.2. 在 Microsoft Windows 上启动多个独立服务器实例
C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node1
C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
注意
如果您不想使用命令行参数,您可以为每个服务器实例配置standalone-ha.xml
文件以绑定到单独的接口上。将应用程序部署到服务器上
下面的 Maven 命令部署应用程序到运行在默认端口的独立服务器上。mvn clean install jboss-as:deploy
要部署到其他的服务器,请传入服务器名称。如果位于不同的主机,在命令行上要指定端口号码:mvn clean package jboss-as:deploy -Djboss-as.hostname=localhost -Djboss-as.port=10099
设置 JBoss EAP 6 附带的cluster-ha-singleton
quickstart 的 Maven 配置和部署细节。
3.2.9. 服务风格的部署的修改
3.2.9.1. 更新使用服务风格部署的应用程序
虽然 JBoss EAP 6 不再使用服务风格的描述符,容器还是支持这些服务风格的部署而吴需修改。这意味着如果您在 JBoss EAP 5.x 应用程序里使用了 jboss-service.xml
或 jboss-beans.xml
部署描述符,它们应该无需或几乎无需修改就可以运行在 EAP 6 里。您可以继续将这些文件打包在 EAR 或 SAR 里,或者您可以将文件直接放在部署目录里。如果您运行的是独立服务器,部署目录是 EAP_HOME/standalone/deployments/
;如果服务器是在受管域里运行,您必须使用控制台或 CLI 来部署应用程序。
3.2.10. 远程调用的修改
3.2.10.1. 将进行远程调用的 JBoss EAP 5 应用程序移植到 JBoss EAP 6
在 JBoss EAP 5 里,EJB 远程接口绑定在 JNDI 里,在默认情况下,本地接口是 "ejbName/local",而远程接口是 "ejbName/remote"。客户应用程序可以用 "ejbName/remote" 来查找 Bean。
ejb:
BEAN_REFERENCE 语法来远程访问 EJB。
ejb:
BEAN_REFERENCE 语法是:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>对于 stateful bean,
ejb:
BEAN_REFERENCE 语法是:
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-ejb-remote-app.jar 里,所以其模块名是 jboss-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-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")
3.2.10.2. 用 JNDI 远程调用 Session Bean
ejb-remote
Quickstart 包含了演示这个功能的 Maven 项目。这个 Quickstart 包含了要部署的 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 端口号。
过程 3.22. 为 session bean 的远程商业接口添加 Maven 项目依赖关系。
添加所需的项目依赖关系
必须更新项目的pom.xml
以包含必要的依赖关系。添加
jboss-ejb-client.properties
文件JBoss EJB 客户 API 期望在项目根目录找到名为jboss-ejb-client.properties
的文件,它包含 JNDI 服务的连接信息。添加这个文件到项目的src/main/resources/
目录并包含下列内容。# In the following line, set SSL_ENABLED to true for SSL remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=default # Uncomment the following line to set SSL_STARTTLS to true for SSL # remote.connection.default.connect.options.org.xnio.Options.SSL_STARTTLS=true remote.connection.default.host=localhost remote.connection.default.port = 4447 remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false # Add any of the following SASL options if required # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT=false # remote.connection.default.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS=JBOSS-LOCAL-USER
根据您的服务器来修改主机名和端口号。4447
是默认的端口号码。对于安全的连接,请将SSL_ENABLED
一行设置为true
并取消SSL_STARTTLS
行的注释。容器里的 Remoting 接口支持使用相同端口的安全和不安全的连接。为远程商业接口添加依赖关系
在pom.xml
里为 session bean 的远程商业接口添加 Maven 依赖关系。<dependency> <groupId>org.jboss.as.quickstarts</groupId> <artifactId>jboss-ejb-remote-server-side</artifactId> <type>ejb-client</type> <version>${project.version}</version> </dependency>
过程 3.23. 用 JNDI 获取 Bean 代理并调用 Bean 的方法
处理 checked 异常
下面代码里的两个方法 (InitialContext()
和lookup()
) 都会抛出类型为javax.naming.NamingException
的 checked 异常。这些方法调用必须包括在捕获NamingException
的 try/catch 块里,或者是再声明抛出NamingException
的方法里。ejb-remote
quickstart 使用了第二种方法。创建一个 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
文件里读取的。使用 JNDI Context's lookup() 方法来获取 bean 代理
调用 bean 代理的lookup()
方法并将您请求的 session bean 的 JNDI 名称传入。这将返回一个对象,它必须转换为包含您要调用的方法的远程商业接口的类型。final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup( "ejb:/jboss-ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
Session bean 的 JNDI 名称是用特殊语法定义的。详情请参考 第 3.2.10.3 节 “EJB JNDI 命名引用” 。调用方法
现在您已有一个代理 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 间的通讯对于调用者来说是透明的。
3.2.10.3. EJB JNDI 命名引用
ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful
<appName>
- 如果 Session Bean 的 JAR 文件已经部署在 EAR 里,那 JNDI 名称就是 EAR 的名称。在默认情况下,EAR 的名称是不带
.ear
后缀的文件名。应用程序名称也可以在application.xml
文件里进行覆盖。如果 Session Bean 没有部署在 EAR 里,那应将它留空。 <moduleName>
- 这个模块名是 Session Bean 部署所在的 JAR 文件的名称。在默认情况下,JAR 文件的名称是其带
.jar
后缀的文件名。模块名也可以在 JAR 的ejb-jar.xml
文件里进行覆盖。 <distinctName>
- JBoss EAP 6 允许每个部署指定可选的标识名。如果部署没有标识名,则请留空。
<beanName>
- 这是要调用的 Session Bean 的类名。
<viewClassName>
- 这是远程接口的权限定类名。它包括了接口的软件包名。
?stateful
- 当 JNDI 名称引用 stateful session bean 时,
?stateful
后缀是必需的。其他 bean 类型没有包含这个后缀。
3.2.11. EJB 2.x 的修改
3.2.11.1. 更新使用 EJB 2.x 的应用程序
- 用 Full 配置集启动服务器
- EJB 2.x Container Managed Persistence (CMP) bean 要求 Java EE 6 Full 配置集。这个配置集包含运行 CMP EJB 所需的配置元素。这个配置集包含了
org.jboss.as.cmp
扩展模块:<extensions> ... <extension module="org.jboss.as.cmp"/> ... </extensions>
它也包含cmp
子系统:<profiles> ... <subsystem xmlns="urn:jboss:domain:cmp:1.1"/> ... </profiles>
.要用 Full 配置集启动 JBoss EAP 6 独立服务器,在用命令行启动服务器时请使用-c standalone-full.xml
or-c standalone-full-ha.xml
参数。 - 容器配置不再被支持了
- 在以前的 JBoss EAP 版本里,我们可以为 CMP 实体和其他 bean 配置不同的容器并通过在
jboss.xml
部署描述符文件里设置引用来使用它。例如,对于 SLSB 对 Session Bean 的引用通常有不同的配置。在 JBoss EAP 6.x 里,我们可以将 EJB 2 Entity Bean 和标准容器一起使用。JBoss EAP 6 里的默认容器配置包含几个对 EJB2 CMP Bean 的修改:- 默认情况下悲观锁是活动的。这可能导致死锁。
- JBoss EAP 6 里不再有 JBoss EAP 5.x 里的 CMP 层的死锁检测代码。
在 JBoss EAP 5.x 里,我们也可以在定义缓存、池、commit-options
和拦截器栈。而在 JBoss EAP 6 里这是不可能的,它只有一个类似于带有commit-option
C
的Instance Per Transaction
策略的实现。如果您移植使用了cmp2.x jdbc2 pm
entity bean 容器配置的应用程序(使用了兼容 CMP2.x 的基于 JDBC 持久化管理者 ),这会对性能有影响。这个容器为性能作了优化。我们推荐您在移植应用程序之前将这些实体移植到 EJB3。 - 服务器端连接器的配置
- JBoss EAP 6 支持使用
@Interceptors
和@AroundInvoke
注解的标准 Java EEInterceptor
。然而,这并不会允许安全性或事务之外的操作。在以前的 JBoss EAP 版本里,我们可以修改拦截器栈为每个 EJB 调用定制拦截器。这通常用来实现自定义的安全性或安全检查、事务检查或创建前的重试机制。JBoss EAP 6.1 引入了容器拦截器来提供类似的功能。关于容器拦截器的更多信息,请参考《JBoss EAP 开发指南》里的『容器拦截器』章节。在事务的提交阶段之前、期间、之后提供更多控制且遵循 Java EE 规格的方法是使用事务同步注册表(Transaction Synchronization Registry)来添加 listener。资源可以用下列方法之一来获取:回调 Routine 必需实现- 使用
InitialContext
TransactionSynchronizationRegistry tsr = (TransactionSynchronizationRegistry) new InitialContext().lookup("java:jboss/TransactionSynchronizationRegistry"); tsr.registerInterposedSynchronization(new MyTxCallback());
- 使用注入
@Resource(mappedName = "java:comp/TransactionSynchronizationRegistry") TransactionSynchronizationRegistry tsr; ... tsr.registerInterposedSynchronization(new MyTxCallback());
javax.transaction.Synchronization
接口。请在事务提交或回滚前用beforeCompletion{}
方法来执行任何检查。如果这个方法抛出RuntimeException
,事务会被回滚且用EJBTransactionRolledbackException
通知客户。如果是 XA-Transaction,所有的资源将按照 XA 合约回滚。我们也可以根据事务状态用afterCompletion(int txStatus)
方法启用商业逻辑。如果这个方法抛出RuntimeException
,事务将保持之前的状态,提交或回滚,且客户不会得到通知。只有事务管理者会在服务器日志文件里显示一个警告信息。 - 客户端拦截器的服务器端配置
- 在以前的 JBoss EAP 版本里,我们可以在服务器配置文件里配置客户拦截器并只提供带有客户 API 的类。在 JBoss EAP 6 里这已不可能了,因为服务器端不会再创建客户代理并再查找后传输到客户端。这个代理现在是在客户端生成。这次优化避免了查找和类上传时对服务器的调用。
- Entity Bean 池配置
- 我们不推荐在 JBoss EAP 6 里进行 Entity bean 的池配置。因为它受限于
<strict-max-pool>
元素的配置,如果池过小而无法加载结果集里的所有实体,死锁和其他问题就可能发生。Entity Bean 在初始化时没有大型的生命周期方法,所以创建实例和使用容器并不会比池化的 Entity Bean 实例慢。 - 替换 jboss.xml 部署描述符文件
jboss-ejb3.xml
部署描述符文件替换jboss.xml
以覆盖和添加 Java EE 定义的ejb-jar.xml
里提供的功能。这个新文件和jboss.xml
兼容,而目前的部署里已经忽略了jboss.xml
。例如,在以前的 JBoss EAP 版本里,如果您在ejb-jar.xml
文件里定义了<resource-ref>
,jboss.xml
里的 JNDI 名称需要有对应的资源定义。XDoclet 自动生成这两个部署描述符文件。在 JBoss EAP 6,jboss-ejb3.xml
文件里现在已定义了 JNDI 映射信息。我们假定 Java 源代码里的数据源是如下这样定义的。DataSource ds1 = (DataSource) new InitialContext().lookup("java:comp/env/jdbc/Resource1"); DataSource ds2 = (DataSource) new InitialContext().lookup("java:comp/env/jdbc/Resource2");
ejb-jar.xml
定义了下列资源引用。<resource-ref > <res-ref-name>jdbc/Resource1</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-ref > <res-ref-name>java:comp/env/jdbc/Resource2</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
jboss-ejb3.jxml
文件用下列 XML 语法映射 JNDI 名称和引用<resource-ref> <res-ref-name>jdbc/Resource1</res-ref-name> <jndi-name>java:jboss/datasources/ExampleDS</jndi-name> </resource-ref> <resource-ref> <res-ref-name>java:comp/env/jdbc/Resource2</res-ref-name> <jndi-name>java:jboss/datasources/ExampleDS</jndi-name> </resource-ref>
JBoss EAP 6 没有实现 JBoss EAP 5.xjboss.xml
文件里可用的一些配置选项。下表描述了jboss.xml
文件里常用的属性以及在 JBoss EAP 6 是否有替代的方法。method-attribute
元素用来配置单独的 Entity 和 Session Bean 方法。read-only
和idempotent
配置选项没有移植到 JBoss EAP 6 里。transaction-timeout
选项现在在jboss-ejb3.xml
文件里进行配置。
missing-method-permission-exclude-mode
属性修改了方法的行为,而不用实现 secured bean 上的显性安全元数据。在 JBoss EAP 6 里,@PermitAll
注解也会依据和@RolesAllowed
注解类似的方式处理。
- 数据源类型映射配置
- 在以前的 JBoss EAP 版本里,我们可以在
*-ds.xml
数据源部署配置文件里配置数据源的类型映射。在 JBoss EAP 6 里,这必须在jbosscmp-jdbc.xml
部署描述符文件里进行。<defaults> <datasource-mapping>mySQL</datasource-mapping> <create-table>true</create-table> .... </defaults>
在以前的 JBoss EAP 版本里,自定义的映射是在standardjbosscmp-jdbc.xml
文件里完成的。这个文件已取消,现在映射是通过jbosscmp-jdbc.xml
部署描述符文件进行的。
- Container Managed Relationship (CMR) Iterator 和 Collection 的修改
- 在以前的 JBoss EAP 版本里,一些容器如
cmp2.x jdbc2 pm
可以迭代 CMR 容器并删除或添加关系。JBoss EAP 6 不支持这种容器配置,所以无法再这样做了。关于如何在程序代码实现相同功能的信息,请参考客户门户的『Support Knowledgebase Solutions』部分的 EJB2.1 Finder for CMP entities with relations (CMR) returns duplicates in EAP6。 - 用于 Finder 的 Container Managed Relationship (CMR) 重复条目
- 在以前的 JBoss EAP 版本里,我们可以选择使用了不同持久化策略的 CMP 容器。JBoss EAP 5.x 里的
cmp2.x jdbc2 pm
容器使用优化的SQL-92
来为 Finder 生成优化的 LEFT OUTER JOIN 语法。因为 JBoss EAP 6.x 只支持标准的 CMP 和 CMR 容器,其实现没有包含这些优化。Finder 应该在SELECT
语句里包含关键字DISTINCT
来避免结果集出现笛卡尔集合。更多的信息请参考客户门户的『Support Knowledgebase Solutions』部分的 EJB2.1 Finder for CMP entities with relations (CMR) returns duplicates in EAP6。 - 对 CMP Entity Beans 的 Cascade Delete 默认值的修改
- Cascade Delete 的默认值已改成
false
。这可能导致 JBoss EAP 6 里出现删除失败。如果实体关系被标记为cascade-delete
,您必须在jbosscmp-jdbc.xml
文件里显性地设置batch-cascade-delete
为true
。更多的信息请参考客户门户的『Support Knowledgebase Solutions』部分的 cascade delete fail for EJB2 CMP Entities after migration to EAP6。 - 用于自定义字段的 CMP 自定义映射器
- 如果您的 JBoss EAP 5.x 应用程序里使用了自定义的映射器类,如
JDBCParameterSetter
、JDBCResultSetReader
和Mapper
,在部署到 JBoss EAP 6 里时,您可能会看到java.lang.ClassNotFoundException
。这是因为这些接口的软件包名从org.jboss.ejb.plugins.cmp.jdbc.Mapper
改成了org.jboss.as.cmp.jdbc.Mapper
。更多的信息请参考客户门户的『Support Knowledgebase Solutions』部分的 How to use Field mapping for custom classes in an EJB2 CMP application in EAP6。 - 使用 entity-commands 生成主键
- 如果您的 JBoss EAP 5 应用程序使用了
entity-commands
来生成主键,如Sequence
或Auto-increment
,当移植应用程序到 JBoss EAP 6 时,您可以看到JDBCOracleSequenceCreateCommand
抛出ClassNotFoundException
。这是因为类软件包从org.jboss.ejb.plugins.cmp.jdbc
改成了org.jboss.as.cmp.jdbc.keygen
。如果您在 JBoss EAP 6 应用程序里使用了这个类,您必须添加对EAP_HOME/modules/system/layers/base/org/jboss/as/cmp
模块的依赖关系。
- 修改代码以使用新的 JNDI 命名空间规则。
- 和 EJB 3.0 一样,对 EJB 2.x 您必须使用完整的 JNDI 前缀。关于新的 JNDI 命名空间规则和代码示例,请参考 第 3.1.8.1 节 “更新应用程序 JNDI 命名空间的名称”。关于如何更新以前版本的 JNDI 命名空间的例子,您可以参考 第 3.1.8.5 节 “以前版本的 JNDI 命名空间示例和它们在 JBoss EAP 6 里是如何指定的”。
- 修改
jboss-web.xml
文件描述符 - 对每个
<ejb-ref>
的<jndi-name>
进行修改以使用新的 JNDI 全限定查找格式。 - 使用 XDoclet 来映射内部 Local 接口的 JNDI 名称
- 对于 EJB2,使用
Locator
模式来查找 Bean 是非常常见的。如果您在程序里使用了这个模式,不需要修改代码,您可以使用 XDoclet 来生成新 JNDI 名称的映射。典型的 XDoclet 注解类似于:@ejb.bean name="UserAttribute" display-name="UserAttribute" local-jndi-name="ejb21/UserAttributeEntity" view-type="local" type="CMP" cmp-version="2.x" primkey-field="id"
上述例子里的 JNDI 名称ejb21/UserAttributeEntity
在 JBoss EAP 6 里不再有效。您可以用服务器配置和 XDoclet 补丁里的naming
子系统将这个名称映射到有效的 JNDI 名称。如上面的『用于自定义字段的 CMP 自定义映射器』里说提及的,您可以创建自定义的映射器,或者您可以按照下面过程描述的那样修改代码。过程 3.24. 修改 XDoclet Generated Code 并使用 Naming 子系统
- 解压
ejb-module.jar
里的 XDocletlookup.xdt
末班并修改lookupHome
里的lookup()
:private static Object lookupHome(java.util.Hashtable environment, String jndiName, Class narrowTo) throws javax.naming.NamingException { // Obtain initial context javax.naming.InitialContext initialContext = new javax.naming.InitialContext(environment); try { // Replace the existing lookup // Object objRef = initialContext.lookup(jndiName); // This is the new mapped lookup Object objRef; try { // try JBoss EAP mapping objRef = initialContext.lookup("global/"+jndiName); } catch(java.lang.Exception e) { objRef = initialContext.lookup(jndiName); } // only narrow if necessary if (java.rmi.Remote.class.isAssignableFrom(narrowTo)) return javax.rmi.PortableRemoteObject.narrow(objRef, narrowTo); else return objRef; } finally { initialContext.close(); } }
- 运行 Ant,对于
ejbdoclet
任务设置 template 属性以使用改动的lookup.xdt
。 - 修改服务器配置文件里的
naming
子系统来映射旧的 JNDI 名称到新的有效 JNDI 名称。<subsystem xmlns="urn:jboss:domain:naming:1.2"> <bindings> <lookup name="java:global/ejb21/UserAttributeEntity" lookup="java:global/ejb2CMP/ejb/UserAttribute!de.wfink.ejb21.cmp.cmr.UserAttributeLocalHome"/> </bindings> <remote-naming/> </subsystem>
JBoss EAP 6 不再支持下列文件。
- jboss.xml
- Jboss EAP 6 不再支持
jboss.xml
部署描述符文件且没有在部署归档里包含它。 - standardjbosscmp-jdbc.xml
- Jboss EAP 6 不再支持
standardjbosscmp-jdbc.xml
配置文件。这个配置信息现在包含在org.jboss.as.cmp
模块里且不再是可以定制的了。 - standardjboss.xml
- JBoss EAP 6 不再支持
standardjboss.xml
配置文件。运行独立服务器时配置信息现在包含在standalone.xml
文件里,而运行在受管域时包含在domain.xml
文件里。
3.2.12. JBoss AOP 的修改
3.2.12.1. 更新使用 JBoss AOP 的应用程序
ejb3-interceptors-aop.xml
里以前的标准 EJB3 配置现在已经在服务器配置文件里完成了。对于独立的服务器,就是standalone/configuration/standalone-full.xml
文件。如果您在受管域里运行服务器,这个文件将是domain/configuration/domain.xml
。- 您应该修改服务器端的 AOP 拦截器来使用标准的 Java EE
拦截器(Interceptor)
。关于容器拦截器的更多信息以及如何使用客户端拦截器,请参考客户门户 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 开发指南》里的《容器拦截器》章节。
- 如果您无法重构代码,您可以获取 JBoss AOP 库的拷贝并将其与应用程序捆绑。AOP 库可以在 JBoss EAP 6 里运行,但不能进行部署。您可以使用下列命令行参数启动服务器来进行手动部署:
Djboss.aop.path=PATH_TO_AOP_CONFIG
。注意
虽然 JBoss AOP 库可以在 JBoss EAP 6 里运行,但它并非受支持的配置。
3.2.13. 移植 Seam 2.2 应用程序
3.2.13.1. 移植 Seam 2.2 归档到 JBoss EAP 6
当您移植 Seam 2.2 应用程序时,您需要配置数据源并指定任何模块化依赖关系。您也需要确定应用程序是否依赖于 JBoss EAP 6 不附带的归档并将依赖的 JAR 复制到应用程序的 lib/
目录。
重要
过程 3.25. 移植 Seam 2.2 归档
更新数据源配置
某些 Seam 2.2 示例使用名为java:/ExampleDS
的默认 JDBC 数据源。JBoss EAP 6 里已经修改了这个默认的数据源为java:jboss/datasources/ExampleDS
。如果您的应用程序使用了 example 数据库,您可以:关于如何配置数据源的更多信息,请参考 第 3.1.6.2 节 “更新数据源配置”。- 如果您想使用 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>
- 您也可以用管理 CLI 命令行接口添加数据源定义。下面是您添加数据源所必须使用的语法。结尾的 "\" 表示下一行仍是命令行的一部分。
例 3.3. 添加数据源定义的语法示例
$ 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
添加所需的依赖关系
因为 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 节 “修改日志依赖关系”.如果您的应用程序使用了 Hibernate 3.x,请首先用 Hibernate 4 库来运行应用程序。
如果您的应用程序没有使用 Seam Managed Persistence Context、Hibernate search、validation 或其他 Hibernate 4 里已修改的功能,您应该可以用 Hibernate 4 库来运行它。然而,如果您看到指向 Hiberante 类的ClassNotFoundExceptions
或ClassCastExceptions
,或者看到类似于下面的错误信息,您就可能需要遵循下一步的说明并修改应用程序以使用 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
从外部框架或其他位置复制依赖的归档
如果您的应用程序使用了 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 的应用程序的配置”。调试和解决 Seam 2.2 JNDI 错误
当您移植 Seam 2.2 应用程序时,您可能会在日志里使用javax.naming.NameNotFoundException
错误:javax.naming.NameNotFoundException: Name 'jboss-seam-booking' not found in context ''
如果您不想修改代码里的 JNDI 查找,您可以修改应用程序的components.xml
文件:替换现有的 core-init 元素
首先,您需要替换现有的 core-init 元素:<!-- <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/> --> <core:init debug="true" distributable="false"/>
找到服务器日志里的 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
添加组件元素
对于日志里的每个 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.13.2 节 “Seam 2.2 归档移植的问题”。
Seam 2.2 归档成功地在 JBoss EAP 6 里运行。
3.2.13.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
。 - 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 规格的改变而引起的。解决办法时修改CheckoutAction
和ShowOrdersAction
类里的持久化上下文为transactional
并在cancelOrder
和detailOrder
方法里使用实体管理者的合并操作。 - 在 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>
- 从非 EJB 线程调用 EJB Seam 组件导致了 javax.naming.NameNotFoundException
- 这个问题是 JBoss EAP 里为实现新的模块化类加载系统和采用新的标准化 JNDI 命名空间规则而进行修改的结果。
java:app
命名空间是用于单个应用程序里所有组件共享的名称。非 EE 线程,如 Quartz 异步线程,必须使用java:global
命名空间,它是部署在应用服务器实例里的所有应用程序共享的。如果您试图从 Quartz 异步方法里调用 EJB Seam 组件时遇到javax.naming.NameNotFoundException
,您必须修改components.xml
文件来使用全局 JNDI 名称,例如:<component class="org.jboss.seam.example.quartz.MyBean" jndi-name="java:global/seam-quartz/quartz-ejb/myBean"/>
For more information on JNDI changes, refer to the following topic: 第 3.1.8.1 节 “更新应用程序 JNDI 命名空间的名称” . For more information on this specific issue, refer to BZ#948215 - Seam2.3 javax.naming.NameNotFoundException trying to call EJB Seam components from quartz asynchronous methods in the 2.2.0 Release Notes for Red Hat JBoss Web Framework Kit on the Red Hat Customer Portal.
3.2.14. 移植 Spring 应用程序
3.2.14.1. 移植 Spring 应用程序
3.2.15. 其他影响移植的修改
3.2.15.1. 熟悉其他可能影响到移植的修改
3.2.15.2. 修改 Maven 插件名称
jboss-maven-plugin
还没有更新,无法在 JBoss EAP 6 里使用。您必须使用 org.jboss.as.plugins:jboss-as-maven-plugin
来部署到正确的目录里。
3.2.15.3. 修改客户端应用程序
jboss-client.jar
且位于 EAP_HOME/bin/client/
目录。它代替了 EAP_HOME/client/jbossall-client.jar
并包含所有从远程客户连接 JBoss EAP 6 所需的依赖关系。
第 4 章 工具和提示
4.1. 协助移植的资源
4.1.1. 可协助移植的资源
- 工具
- 这是几个自动化某些配置修改的工具。详情请参考: 第 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 日志里看到 ClassNotFoundException
或 ClassCastException
跟踪信息。要解决这些错误,您需要找到包含这些异常指定的类的 JAR。
jboss-deployment-structure.xml
文件。
过程 4.1. 安装并运行 Tattletale 来查找应用程序的依赖关系
注意
4.1.4. 下载和安装 Tattletale
过程 4.2. 下载和安装 Tattletale
- http://sourceforge.net/projects/jboss/files/JBoss%20Tattletale 下载 Tattletale 1.2.0.Beta2 或更新版本。
- 解压文件到您指定的目录。
- 修改
TATTLETALE_HOME/jboss-tattletale.properties
文件:- 添加
ee6
和as7
到profiles
属性。profiles=java5, java6, ee6, as7
- 取消
scan
和reports
属性的注释。
4.1.5. 创建和复查 Tattletale 报告
- 用下列命令创建 Tattletale 报告:
java -jar
TATTLETALE_HOME/tattletale.jar
APPLICATION_ARCHIVE
OUTPUT_DIRECTORY
例如:java -jar tattletale-1.2.0.Beta2/tattletale.jar ~/applications/jboss-seam-booking.ear ~/output-results/
- 在浏览器里打开
OUTPUT_DIRECTORY/index.html
文件并点击 "Reports" 下的 JBoss AS 7"。- 左侧的列列出了应用程序使用的归档。点击 ARCHIVE_NAME 链接来查看归档的细节,如位置、Manifest 信息和它包含的类。
- 右侧列上的
jboss-deployment-structure.xml
链接显示了如何为左侧列命名的归档指定模块依赖关系。点击这个链接来查看如何为这个归档定义部署依赖关系模块信息。
4.1.6. 使用 IronJacamar 工具来移植数据源和资源适配器配置
在以前的应用服务器版本里,数据源和资源适配器都是使用后缀为 *-ds.xml
的文件来配置和部署的。IronJacamar 1.1 版本包含了一个移植工具,它可以用来将这些文件转换为 JBoss EAP 6 所要求的格式。这个工具解析了之前版本的源配置文件,然后创建 XML 配置并写入新格式的输出文件。这个 XML 文件可以在 JBoss EAP 6 服务器配置文件里的正确的子系统下复制和粘贴。这个工具尽量将旧的属性和元素转换为新的格式,然而,您还是有必要对生成的文件进行额外的修改。
过程 4.3. 安装和运行 IronJacamar 移植工具
注意
4.1.7. 下载和安装 IronJacamar 移植工具
注意
- 在这里下载最新年的 IronJacamar:http://www.ironjacamar.org/download.html
- 解压下载的文件到您指定的目录。
- 在 IronJacamar 里找到转换脚本。
- Linux 脚本在这里:
IRONJACAMAR_HOME/doc/as/converter.sh
- Windows 批处理文件在这里:
IRONJACAMAR_HOME/doc/as/converter.bat
4.1.8. 使用 IronJacamar 移植工具来转换数据源配置文件
注意
过程 4.4. 转换数据源配置文件
- 打开命令行并进入
IRONJACAMAR_HOME/doc/as/
目录。 - 请输入以下命令来运行转换脚本:
- 对于 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
。 - 从目标文件复制
<datasource>
元素并粘贴到<subsystem xmlns="urn:jboss:domain:datasources:1.1">
<datasources>
元素下的服务器配置文件。重要
要使修改在服务器重启后仍然生效,您必须在编辑服务器配置文件前停止服务器。- 如果服务器运行在受管域里,请将 XML 复制到
EAP_HOME/domain/configuration/domain.xml
文件里。 - 如果运行的是独立服务器,请将 XML 复制到
EAP_HOME/standalone/configuration/standalone.xml
文件里。
- 修改新配置文件里生成的 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.1"> <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> --> <!-- Specify the driver, which is defined later in the datasource --> <driver>h2<driver> <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>
4.1.9. 使用 IronJacamar 移植工具来转换资源适配器配置文件
注意
- 打开命令行并进入
IRONJACAMAR_HOME/docs/as/
目录。 - 请输入以下命令来运行转换脚本:
- 对于 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
。 - 从目标文件将整个
<resource-adapters>
元素复制并粘贴到<subsystem xmlns="urn:jboss:domain:resource-adapters:1.1">
元素下的服务器配置文件里。重要
要使修改在服务器重启后仍然生效,您必须在编辑服务器配置文件前停止服务器。- 如果服务器运行在受管域里,请将 XML 复制到
EAP_HOME/domain/configuration/domain.xml
文件里。 - 如果运行的是独立服务器,请将 XML 复制到
EAP_HOME/standalone/configuration/standalone.xml
文件里。
- 修改新配置文件里生成的 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.1"> <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>
4.2. 调试移植的问题
4.2.1. 调试和解决移植问题
4.2.2. 调试和解决 ClassNotFoundExceptions 和 NoClassDefFoundErrors
ClassNotFoundExceptions 通常是由未解决的依赖关系引起的。这意味着您必须显性地定义对其他模块的依赖关系或从外部源复制 JAR。
- 首先,找到缺失的依赖关系。详情请参考 第 4.2.3 节 “查找 JBoss 模块依赖关系”
- 如果缺失的类没有模块,请找到前一个版本里的 JAR 文件。详情请访问 第 4.2.4 节 “在以前的安装里查找 JAR”
4.2.3. 查找 JBoss 模块依赖关系
EAP_HOME/modules/system/layers/base/
目录找到包含 ClassNotFoundException
指定的类的模块。如果您找到这个类的模块,您必须在 manifest 条目里添加一个依赖关系。
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.5. 找到依赖关系
- 首先确定这个类是否有明显的模块。
- 进入
EAP_HOME/modules/system/layers/base/
目录并查找对应ClassNotFoundException
里命名的类的模块路径。找到模块路径org/apache/commons/logging/
。 - 打开
EAP_HOME/modules/system/layers/base/org/apache/commons/logging/main/module.xml
文件并找到模块名称。在这个例子里是 "org.apache.commons.logging"。 - 添加模块名到
MANIFEST.MF
文件里的依赖关系:Manifest-Version: 1.0 Dependencies: org.apache.commons.logging
- 如果这个类没有明显的模块路径,您可能需要在另一个位置寻找依赖关系。
- 在 Tattletale 报告里找到名为
ClassNotFoundException
的类。 - 在
EAP_HOME/modules
目录里找到包含 JAR 的模块以及和前一步骤里相同的模块名。
4.2.4. 在以前的安装里查找 JAR
lib/
目录里查找这个 JAR。
ClassNotFoundException
跟踪信息:
Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/ClassValidator at java.lang.Class.getDeclaredMethods0(Native Method)通过下列步骤来查找包含这个类的 JAR:
- 打开一个终端窗口并进入
EAP5_HOME/
目录。 - 执行这个命令:
grep 'org.hibernate.validator.ClassValidator' `find . \-name '*.jar'`
- 您可能会看到多个结果。在这个例子里,下面的结果就是我们需要的 JAR:
Binary file ./jboss-eap-5.1/seam/lib/hibernate-validator.jar matches
- 将这个 JAR 复制到
lib/
目录。如果您发现您需要大量的 JAR,为这些类定义一个模块可能更加便利。关于更多的信息,请参考 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》里的『开发企业级应用程序起步』章节中的『模块』 。 - 重建和重部署这个应用程序。
4.2.5. 调试和解决 ClassCastExceptions
- 搜索应用程序来查找包含
ClassCastException
里命名的类的所有 JAR。如果为这个类定义了模块,请从应用程序的 WAR 或 EAR 里查找并删除重复的 JAR。 - 找到包含这个类的 JBoss 模块并显性地在
MANIFEST.MF
文件或jboss-deployment-structure.xml
文件里定义依赖关系。关于更多的信息,请查看 https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/ 上的《JBoss EAP 6 部署指南》里『类加载和模块』章节中的『类加载和子部署』。 - 如果您无法使用上面的步骤解决这个问题,您可以将类加载器的信息输出到日志里确定原因。例如,您可以在日志里看下列
ClassCastException
:java.lang.ClassCastException: com.example1.CustomClass1 cannot be cast to com.example2.CustomClass2
- 在您的代码里,将
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());
- 日志里的信息显示了哪些模块在加载类,且您需要根据您的应用程序删除或移走冲突的 JAR。
4.2.6. 调试和解决 DuplicateServiceException
- 将 JAR 文件重命名为与 WAR 不同的名称,这样生成的 Web 和 WAR 上下文就是唯一的。
- 在
jboss-web.xml
文件里提供一个<context-root>
元素。 - 在
jboss-webservices.xml
文件里提供一个<context-root>
元素。 - 在
application.xml
文件为 WAR 自定义<context-root>
元素。
4.2.7. 调试和解决 JBoss Seam 调试页面的错误
图 4.1. JBoss Seam 调试页面
- 展开页面上的
Component
部分并查找org.jboss.seam.caughtException
组件。 - 其原因和跟踪信息应该指向缺失的依赖关系。
图 4.2. 组件
org.jboss.seam.caughtException
的信息 - 使用 第 4.2.2 节 “调试和解决 ClassNotFoundExceptions 和 NoClassDefFoundErrors” 里描述的技术来解决模块依赖关系。在上面的例子里,最简单的办法是添加
org.slf4j
到MANIFEST.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.x_HOME/jboss-eap-5.x/seam/examples/jpa/
目录下找到。
重要
过程 4.6. 移植 Seam 2.2 JPA 例程
删除 jboss-web.xml 文件
从jboss-seam-jpa.war/WEB-INF/
目录删除jboss-web.xml
文件。jboss-web.xml
里定义的类加载现在是默认的行为。像下面这样修改
jboss-seam-jpa.jar/META-INF/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"/> -->
- 在
jboss-seam-booking.jar/META-INF/persistence.xml
文件里添加 Provider Module 属性:<property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
- 修改
jta-data-source
属性以使用默认的 JDBC 数据源 JNDI 名称:<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
添加 Seam 2.2 依赖关系
从 Seam 2.2 的SEAM_HOME/lib/
目录复制下列 JAR 到jboss-seam-jpa.war/WEB-INF/lib/
目录:- antlr.jar
- slf4j-api.jar
- slf4j-log4j12.jar
- hibernate-entitymanager.jar
- hibernate-core.jar
- hibernate-annotations.jar
- hibernate-commons-annotations.jar
- hibernate-validator.jar
创建一个 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"/> <module name="org.hibernate" 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 例程移植的文档。要移植这个程序,您必须:
- 初始化 JSF 1.2 而不是默认的 JSF 2。
- 捆绑旧版的 Hibernate JAR 而不是使用 JBoss EAP 6 附带的版本。
- 修改 JNDI 绑定以使用新的 Java EE 6 JNDI 可移植的语法。
重要
过程 4.7. 移植 Seam 2.2 Booking 例程
创建
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="javax.faces.api" slot="1.2" export="true"/> <module name="com.sun.jsf-impl" slot="1.2" export="true"/> <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"/> </dependencies> <exclusions> <module name="org.hibernate" slot="main"/> </exclusions> </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>
像下面这样修改
jboss-seam-booking.jar/META-INF/persistence.xml
文件。- 删除或注释 cache provider 类的 hibernate 属性:
<!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
- 在
jboss-seam-booking.jar/META-INF/persistence.xml
文件里添加 Provider Module 属性:<property name="jboss.as.jpa.providerModule" value="hibernate3-bundled" />
- 修改
jta-data-source
属性以使用默认的 JDBC 数据源 JNDI 名称:<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
从 Seam 2.2 复制 JAR 文件
从 Seam 2.2 的EAP5.x_HOME/jboss-eap5.x/seam/lib/
目录复制下列 JAR 到jboss-seam-booking.ear/lib
目录:antlr.jar slf4j-api.jar slf4j-log4j12.jar hibernate-core.jar hibernate-entitymanager.jar hibernate-validator.jar hibernate-annotations.jar hibernate-commons-annotations.jar
修改 JNDI 查找名称
修改jboss-seam-booking.war/WEB-INF/components.xml
文件里的 JNDI 查找字符串。由于新的 JNDI 可移植规则,JBoss EAP 6 现在使用 JNDI 可移植语法规则绑定 EJB,您无法使用 JBoss EAP 5 里使用的单个的 jndiPattern。应用程序的 EJB JNDI 查找字符串必须修改成:java:global/jboss-seam-booking/jboss-seam-booking/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching java:app/jboss-seam-booking/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching java:module/HotelSearchingAction!org.jboss.seam.example.booking.HotelSearching java:global/jboss-seam-booking/jboss-seam-booking/HotelSearchingAction java:app/jboss-seam-booking/HotelSearchingAction java:module/HotelSearchingAction
Seam 2.2 框架 EJB 的 NDI 查找字符串必须修改成:java:global/jboss-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/jboss-seam-booking/jboss-seam/EjbSynchronizations java:app/jboss-seam/EjbSynchronizations java:module/EjbSynchronizations
您可以使用下列方式中的一个:添加组件元素
您可以为每个 EJB 添加一个jndi-name
至WEB-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/jboss-seam-booking/AuthenticatorAction" /> <component class="org.jboss.seam.example.booking.BookingListAction" jndi-name="java:app/jboss-seam-booking/BookingListAction" /> <component class="org.jboss.seam.example.booking.RegisterAction" jndi-name="java:app/jboss-seam-booking/RegisterAction" /> <component class="org.jboss.seam.example.booking.HotelSearchingAction" jndi-name="java:app/jboss-seam-booking/HotelSearchingAction" /> <component class="org.jboss.seam.example.booking.HotelBookingAction" jndi-name="java:app/jboss-seam-booking/HotelBookingAction" /> <component class="org.jboss.seam.example.booking.ChangePasswordAction" jndi-name="java:app/jboss-seam-booking/ChangePasswordAction" />
- 您可以修改代码,添加
@JNDIName(value="")
注解指定 JNDI 路径。下面时一个已修改的 stateless session bean 代码示例。关于这个产品的详细说明可以在 Seam 2.2 的参考文档里找到。@Stateless @Name("authenticator") @JndiName(value="java:app/jboss-seam-booking/AuthenticatorAction") public class AuthenticatorAction implements Authenticator { ... }
Seam 2.2 Booking 例程在 JBoss EAP 6 上成功部署和运行。
4.3.4. 移植 Seam 2.2 Booking 例程到 JBoss EAP 6:逐步说明
EAP6_HOME/standalone/deployments
目录,对现有归档无需进行修改。这允许您在遇到问题时可以轻易地修改归档里的 XML 文件从而解决问题。
重要
过程 4.8. 移植应用程序
4.3.5. 构建和部署 Seam 2.2 Booking 例程的 JBoss EAP 5.X 版本。
过程 4.9. 构建和部署 EAR:
- 构建 EAR:
$ cd /EAP5_HOME/jboss-eap5.x/seam/examples/booking $ ANT_HOME/ant explode
用实际移植的 JBoss EAP 版本替换 jboss-eap5.x。 - 复制 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
- 启动 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
- 创建一个名为
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.10. 调试和解决部署错误和异常
- 问题 - 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
文件来重新部署应用程序。 - 问题 - 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
文件来重新部署应用程序。 - 问题 - 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
文件来重新部署应用程序。 - 问题 - 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.X 部署的一部分。在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.jar
到jboss-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
文件来重新部署应用程序。 - 问题 - 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.ConfigurationException
和java.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.FacesException
ClassNotFoundException
所做的一样,您希望在 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
文件来重新部署应用程序。 - 问题 - 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
文件来重新部署应用程序。 - 问题 - 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-name
是java: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
文件来重新部署应用程序。 - 此时,应用程序应该已部署无误了,但当您访问 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.11. 调试和解决运行时错误和异常
- 问题 - 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
文件来重新部署应用程序。 - 问题 - 应用程序部署和运行无误。当您访问 http://localhost:8080/seam-booking/ 并试图登陆时失败,错误消息为 "Login failed. Transaction failed."。您应该在服务器日志里看到如下异常跟踪信息:
13:36:04,631 WARN [org.jboss.modules] (http-/127.0.0.1:8080-1) Failed to define class org.jboss.seam.persistence.HibernateSessionProxy in Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader: java.lang.LinkageError: Failed to link org/jboss/seam/persistence/HibernateSessionProxy (Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader) .... Caused by: java.lang.LinkageError: Failed to link org/jboss/seam/persistence/HibernateSessionProxy (Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader) ... Caused by: java.lang.NoClassDefFoundError: org/hibernate/engine/SessionImplementor at java.lang.ClassLoader.defineClass1(Native Method) [rt.jar:1.7.0_45] ... Caused by: java.lang.ClassNotFoundException: org.hibernate.engine.SessionImplementor from [Module "deployment.jboss-seam-booking.ear.jboss-seam.jar:main" from Service Module Loader] ...
它表示:ClassNotFoundException 表示缺失的 Hibernate 库。在这个例子里是
hibernate-core.jar
。如何解决这个问题:从
EAP5_HOME/seam/lib/
目录复制hibernate-core.jar
JAR 到jboss-seam-booking.ear/lib
目录。通过删除standalone/deployments/jboss-seam-booking.ear.failed
文件并在相同目录里创建一个空白的jboss-seam-booking.ear.dodeploy
文件来重新部署应用程序。 - 问题 - 应用程序部署和运行无误。当您访问 http://localhost:8080/seam-booking/,您可以成功登录。然而,当您试图预订酒店时,您会看到如下异常跟踪信息:为了进行调试,您必须首先删除
jboss-seam-booking.ear/jboss-seam-booking.war/WEB-INF/lib/jboss-seam-debug.jar
,因为它标记了真正的错误。此时,您应该看到如下错误:java.lang.NoClassDefFoundError: org/hibernate/annotations/common/reflection/ReflectionManager
它表示:ClassNotFoundException 表示有缺失的 Hibernate 库。
如何解决这个问题:从
EAP5_HOME/seam/lib/
目录复制hibernate-annotations.jar
和hibernate-commons-annotations.jar
到jboss-seam-booking.ear/lib
目录。通过删除standalone/deployments/jboss-seam-booking.ear.failed
文件并在相同目录里创建一个空白的jboss-seam-booking.ear.dodeploy
文件来重新部署应用程序。 - 运行时和应用程序错误应该被解决此时,应用程序已部署且运行无误。
4.3.8. 当移植 Seam 2.2 Booking 例程时所作修改的总结
重要
- 您在 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>
- 您可以将下列 JAR 从
EAP5_HOME/jboss-eap-5.X/seam/lib/
目录(用实际移植的 EAP 5 版本替换 5.X)复制到jboss-seam-booking.ear/lib/
目录以解决ClassNotFoundExceptions
问题:- hibernate-core.jar
- hibernate-validator.jar
- 您修改了
jboss-seam-booking.jar/META-INF/persistence.xml
文件。- 您修改了
jta-data-source
元素以使用 JBoss EAP 6 附带的示例数据库。<!-- <jta-data-source>java:/bookingDatasource</jta-data-source> --> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
- 您注释了 hibernate.cache.provider_class 属性:
<!-- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> -->
- 您修改了 WAR 的
lib/components.xml
文件来使用新的 JNDI 绑定。- 您替换了现有的
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"/>
附录 A. 修订
修订历史 | |||
---|---|---|---|
修订 6.3.0-24 | Wednesday July 30 2014 | Sande Gilda | |
|