JCA 事务概述
实际的示例会有助于展示一些常见的 JCA 事务问题。 在该例中,要完成的任务是要为一家公司构建电子商务应用程序,这家公司的主要业务是向客户销售工业制品。该公司决定构建一个 Web 站点,向更多的群体零售其产品。该 Web 站点允许任何使用浏览器的客户访问公司的主页;浏览可用产品的目录;查看详细的价格信息、可用性、可用项目的说明;向购物车中添加购买的产品;最后购买产品。为了举例说明必需的事务性行为,我把重点放在了客户决定购买商品的用例上。图 1 显示了该应用程序的设计。
图 1. 访问电子商务应用程序中的 EIS
公司现有的 IT 基础设施是围绕两大企业信息系统构建的。EIS1 是主机系统,在 CICS 下面运行 Cobol 事务。运行在这个系统上的事务将实现业务逻辑,并且还要访问订单录入和客户关系管理(CRM)所需要的数据。EIS2 是一个 IMS 系统,它包含产品说明目录、价格信息和库存控制。要支持所要求的功能,J2EE 应用程序必须能够访问来自这两个系统的数据,并把它们无缝集成在一起。而要进行采购,则需要把以下操作作为一个工作单元(即作为一个事务)来执行:
取得当前产品价格(EIS2)。
把客户订单输入订单系统(EIS1)。
给客户记帐(EIS1)。
更新产品可用性信息(EIS2)。
步骤 2、步骤 3 或步骤 4 中的任何一个步骤的失败,都会造成取消前面所做的所有操作。
在该示例 Web 应用程序中,客户机(Web 层中的 JSP 页面)保存了一个对 CustomerSession 状态会话 EJB 组件的一个实例的引用,并调用该实例上的方法。可以使用 CustomerSession 来保存购物车的内容、从目录得到的产品信息以及客户信息。如图 1 所示,在与最终客户交互期间,会话 bean 搜集并保存了采购商品所必需的、特定于产品选择和客户的一些信息。在 CustomerSession 中定义的方法,将调用无状态会话 EJB 组件的方法(这个 bean 起到一个外观(facade)的作用),例如 OrderService,从而调用 EIS 上的事务。
在这个设计中,无状态会话 bean OrderService 充当着 EIS1 的外观。它定义了以下方法:
OrderInfo addOrder(String customerId, ItemInfo itemInfo) 用 JCA 通用客户机接口(CCI)调用 EIS1 上的 SHIPTO 事务。该事务查找客户的送货地址,准备 EIS 需要与送货部门沟通的信息。返回的结构 OrderInfo 包含订单 ID 号(用作跟踪)、送货成本和送货地址信息。
BillingInfo billCustomer(String customerId, OrderInfo orderInfo) 也使用 JCA CCI 调用 EIS1 上的 BILLTO 事务。该事务查询客户的信用卡号码,并借记客户的帐户,记下订单数量。返回的结构 BillingInfo 包含客户的总成本,包括运输费用和成本结构、该订单的 ID 号(用作跟踪和取消),以及客户信息。
void cancelOrder(OrderInfo orderInfo) 用 JCA CCI 调用运行在 EIS1 上的 RMVORD 事务来取消订单。
会话 bean CatalogService(请参阅 图 1)充当 EIS2 的外观。它定义了以下方法:
double getItemPrice(String itemId) 用 JCA CCI 调用 EIS2 上的 ITMPRICE 事务。该事务将返回项目的最新价格信息。
void updateStockInfo(String itemId, int numItems) 用 JCA CCI 调用 EIS2 上的 UPDSTOCK 事务。这个事务根据项目 ID 号,更新当前的库存信息。输入参数 numItems 可以是正的,也可以是负的。
所有这 5 种方法和底层的 EIS 事务都运行在两个不同系统上,它们必须作为一个单一的业务事务的组成部分来执行。在下面一节中,您将看到如何实现这一点。
事务支持级别
不同的 EIS 有不同的事务性行为。有些 EIS 根本不支持事务。有些 EIS 支持事务,但是不支持双向提交(2PC)协议。这类事务被称为支持本地事务。有些 EIS 既支持本地事务,又支持 2PC。这类事务被称为支持分布式事务,或者全局事务。全局事务也被称为 XA 事务,因为它们包含 XAResource 接口。
清单 1 显示了 CICSECI 资源适配器的 ra.xml 中的一小段,您要用这个适配器访问 EIS1。 元素的 Localtransaction 值表明,这个资源适配器支持本地事务,但是不能参与全局事务。会话 bean OrderService 用这个资源适配器来访问包含 EIS1 的 CRM 事务。
清单 1. CICSECI 资源适配器的 ra.xml 描述符的代码片段
1
<!
DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"
2
"http://java.sun.com/dtd/connector_1_0.dtd"
>
3
4
<
connector
>
5
<
display-name
>
ECIResourceAdapter
</
display-name
>
6
<
description
>
CICS J2EE ECI Resource Adapter
</
description
>
7
<
vendor-name
>
IBM
</
vendor-name
>
8
<
spec-version
>
1.0
</
spec-version
>
9
<
eis-type
>
CICS
</
eis-type
>
10
<
version
>
5.0.0
</
version
>
11
<
license
>
12
<
description
>
</
description
>
13
<
license-required
>
true
</
license-required
>
14
</
license
>
15
<
resourceadapter
>
16
<
managedconnectionfactory-class
>
17
com.ibm.connector2.cics.ECIManagedConnectionFactory
</
managedconnectionfactory-class
>
18
<
connectionfactory-interface
>
19
javax.resource.cci.ConnectionFactory
</
connectionfactory-interface
>
20
<
connectionfactory-impl-class
>
21
com.ibm.connector2.cics.ECIConnectionFactory
</
connectionfactory-impl-class
>
22
<
connection-interface
>
javax.resource.cci.Connection
</
connection-interface
>
23
<
connection-impl-class
>
com.ibm.connector2.cics.ECIConnection
</
connection-impl-class
>
24
<
transaction-support
>
LocalTransaction
</
transaction-support
>
25
清单 2 显示了 IMS 资源适配器的 ra.xml 的代码片断,我用该适配器访问 EIS 2。在该例中, 元素的值 XATransaction 表示资源适配器支持全局事务或分布式事务。
清单 2. IMS 资源适配器的 ra.xml 描述符的代码片断
1
<!
DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"
2
"http://java.sun.com/dtd/connector_1_0.dtd"
>
3
<
connector
>
4
<
display-name
>
IMS Connector for Java
</
display-name
>
5
<
description
>
J2EE Connector Architecture resource adapter for IMS
6
accessing IMS transactions using IMS Connect
</
description
>
7
<
vendor-name
>
IBM Corporation
</
vendor-name
>
8
<
spec-version
>
1.0
</
spec-version
>
9
<
eis-type
>
IMS
</
eis-type
>
10
<
version
>
1.2.6
</
version
>
11
<
license
>
12
<
description
>
IMS Connector for Java is a component of the IMS Connect product and as such
13
is not separately licensed. It requires an IMS Connect license.
</
description
>
14
<
license-required
>
true
</
license-required
>
15
</
license
>
16
<
resourceadapter
>
17
<
managedconnectionfactory-class
>
18
com.ibm.connector2.ims.ico.IMSManagedConnectionFactory
</
managedconnectionfactory-class
>
19
<
connectionfactory-interface
>
20
javax.resource.cci.ConnectionFactory
</
connectionfactory-interface
>
21
<
connectionfactory-impl-class
>
22
com.ibm.connector2.ims.ico.IMSConnectionFactory
</
connectionfactory-impl-class
>
23
<
connection-interface
>
24
javax.resource.cci.Connection
</
connection-interface
>
25
<
connection-impl-class
>
26
com.ibm.connector2.ims.ico.IMSConnection
</
connection-impl-class
>
27
<
transaction-support
>
XATransaction
</
transaction-support
>
28
<
config-property
>
29
30
31
JCA 事务支持
清单 1 和清单 2 中所示的资源适配器在其事务支持上有所区别。但是,在我的电子交易应用程序中,我需要把两个不同系统上的事务协调起来。为了有助于实现这一点,JCA 事务合约定义了一套框架性接口,通过这套接口,应用程序服务器 style="COLOR: #000000" href="http://server.it168.com/" target=_blank>服务器和 EIS 可以协调事务。EJB 容器通过资源适配器实现的 ManagedConnection 接口与 EIS 对话,该接口表示了与 EIS 的实际连接。 ManagedConnection 的实现通常要使用专有的库,用 EIS 能够理解的协议与 EIS 进行对话。
如果资源适配器支持本地事务,那么它还要实现 javax.resource.spi.LocalTransaction 接口。当 EJB 容器需要初始化事务的时候,它要调用 ManagedConnection 实现的实例上的 getLocalConnection() 方法。然后,它要调用 Localtransactionbegin()、 commit() 和 rollback() 方法来控制事务。
如果资源适配器支持 XA 或全局事务,那么它还要实现 javax.transaction.xa.XAResource 接口。每个符合 J2EE 规范的应用程序服务器都有一个叫作事务管理器的内部组件。事务管理器实际是 javax.transaction.TransactionManager 接口的实现。这个组件帮助应用程序服务器管理事务的边界。事务管理器用 ManagedConnection 接口定义的 getXAResource() 方法得到 XAResource 的实例。事务管理器用 XAResource 接口定义的方法,在多个资源管理器之间协调 2PC 协议。