级别: 中级 刘 冬清 (liudongq@cn.ibm.com), 工程师,中国软件实验室(CSDL BJ)
2007 年 6 月 28 日 IBM WebSphere JCA Adapter 是沟通 WebSphere Process Server 和 EIS 之间的桥梁,本文通过一个简单实例介绍了开发 WebSphere JCA Adapter 的基本步骤。
JCA (Java 2 Enterprise Edition (J2EE) Connector Architecture)是J2EE规范中重要的一环,为J2EE应用程序和其它企业信息系统(EIS)之间的交互制定了标准。IBM WebSphere Process Server(WPS)是构筑在J2EE之上的企业应用集成服务器,IBM WebSphere JCA Adapter就是配合WPS使用的,既符合JCA规范又支持WPS优秀集成特性的产品家族。目前最新的是6.0.2版,主要包括如下Adapter:
- IBM WebSphere Adapter for JDBC
- IBM WebSphere Adapter for Flat Files
- IBM WebSphere Adapter for PeopleSoft Enterprise
- IBM WebSphere Adapter for SAP Software
- IBM WebSphere Adapter for Siebel Business Applications
- IBM WebSphere Adapter for Email
- IBM WebSphere Adapter for FTP
- IBM WebSphere Adapter for JD Edwards EnterpriseOne
- IBM WebSphere Adapter for Oracle E-Business Suite
IBM WebSphere JCA Adapter 架构
 |
所需知识背景
本文假定读者熟悉J2EE,并对JCA规范有所了解。如果不熟悉JCA,最好先读一下JCA 1.5规范的以下章节:概述,包括1,2,3章;Outbound 相关,包括6,15章。
WebSphere Adapter使用Service Data Object(SDO)规范来表示数据,如果读者有SDO相关知识,将有助于理解相关内容。
|
|
IBM WebSphere JCA Adapter完全符合JCA 1.5规范。JCA Adapter规范规定了连接和操作EIS的协议,但是对所交换的数据规范没有指定。在WPS中,核心的数据规范是Business Object,简称BO。BO从技术实现的角度讲是Service Data Object(SDO),一种标准化的数据接口。WebSphere JCA Adapter使用SDO与WPS来交换数据。
连接不同的EIS的Adapters有很多共性,对每个Adapter都完全从实现JCA接口开始白手起家显然是没有必要的。因此,IBM开发了Adapter Foundation Classes作为WebSphere JCA Adapter的开发框架。它提供了JCA 接口的抽象实现,以及在Adapter之间可以重用的基本功能,并提供了Assured Event Delivery等QoS服务,而将与EIS密切相关的功能留给具体的Adapter来实现,从而大大简化了Adapter的开发工作。
下图表明WebSphere Adapter的基本架构:
图 1. Adapter的基本架构
从上图可以看出,Adapter Foundation Classes提供了JCA 1.5规范的框架实现,Adapter特有部分扩展并细化这一框架,实现对EIS的操作,并通过SDO规范与WPS交换数据。有了Adapter Foundation Classes,开发Adapter的工作量大为减少,只要实现上图中的蓝色部分,开发主要关注点为对不同EIS的操作。
古人云:绝知此事要躬行,以下本文将以一个连接虚拟的EIS(Mock EIS)的MockAdapter为例简要描述了如何基于WebSphere Adapter Foundation Classes 6.0.2 版,一步步开始开发符合WebSphere 框架的JCA Adapter。以此来更好的了解Adapter的工作原理。MockAdapter将只支持由J2EE应用程序发起的对EIS的操作(在JCA规范中称为Outbound操作)。
开发工具和Adapter Foundation Classes库文件
和WPS配套的开发工具是WebSphere Integration Developer(WID),它是基于Eclipse技术的企业应用集成开发环境。我们需要使用WID这个工具来开发WebSphere JCA Adapter。在WID 6.0.2的安装目录下,可以找到Resource Adapters目录,其中包括了所有可用的JCA Adapter。将某个Adapter,如FlatFile的RAR文件导入WID中,你会在connectModule目录下发现CWYBS_AdapterFoundation.jar,这就是Adapter Foundation Classes。
Mock EIS
为了简化,本文不使用任何现实的EIS,而是用一个Mock EIS 类来模拟一个EIS,这样的好处是你无需了解任何EIS API的调用,通常那是很复杂的。Mock EIS是一个二元计算服务,可以计算加减乘除。
使用Mock EIS的程序,首先要用setOperation()设置运算符,然后调用calculate()计算得出结果。
代码清单 1.MockEIS.java
package com.ibm.eis.mock;
public class MockEIS {
String operation = "+";
public MockEIS(){
}
/**
* @param operation The operation to set.
*/
public void setOperation(String operation) {
this.operation = operation;
}
/**
* Calculate the result.
* @param x
* @param y
* @return
*/
public float calculate(float x, float y){
float result = Float.NaN;
if(operation.equals("+")){
result = x + y;
}else if(operation.equals("-")){
result = x - y;
}else if(operation.equals("*")){
result = x * y;
}else if(operation.equals("/")){
result = x / y;
}
return result;
}
}
|
先期准备:创建Business Object定义
由于SDO是一种需要预先定义数据格式的规范,并且使用XML Schema文件来描述数据格式,在WPS/WID中称为Business Object 定义。我们首先要确定EIS所需要处理的数据格式,并将它映射到一个适当的Business Object 定义。在Mock EIS中需要处理的数据包括运算数和结果,可以作为属性封装在一个BO中。
我们是用WID的BO编辑器来创建BO。首先创建一个新Business Object,命名为MockBO,为它添加X,Y,Result三个属性,然后为它生成一个Business Graph(BG),BG是SDO规范中规定的BO的包装器,用来纪录BO的变化和某些附加信息,可以看作是一种特殊的BO。在WebSphere Adapter中,接受和发送的数据对象都是以BG包装的BO。
创建完成的BO如下图所示:
图 2. MockBO 定义
BO的定义会保存为XML Schema文件,切换到资源透视图,可以在模块工程目录下找到MockBO.xsd和MockBOBG.xsd两个文件。
第一步:创建Connector Project
在WID中切换到J2EE perspective,创建一个Connector Project。注意要选择WebSphere Process Server 6.0作为该Project的server,然后,把Foundation Classes的Jar文件CWYBS_AdatperFoundation.jar导入到该project的connectorModule 目录下,并添加到build path中。接下来,将刚才创建的BO定义文件,即MockBO.xsd和MockBOBG.xsd复制到connectorModule,以便将来BO定义随Adapter一同部署。
第二步:创建Resource Adapter主类
用文本方式打开ra.xml文件,目前只有对MockAdapter的简单描述,将各项描述添全。
<display-name>IBM Mock Adapter for QA</display-name>
<vendor-name>IBM CSDL AIA</vendor-name>
<eis-type>Mock EIS</eis-type>
<resourceadapter-version>1.0.0</resourceadapter-version>
|
接着创建Mock Adapter的ResourceAdapter类,类名为com.ibm.j2ca.mock.MockAdapter,将此添加到ra.xml中。
<resourceadapter>
<resourceadapter-class>com.ibm.j2ca.mock.MockAdapter</resourceadapter-class>
......
</resourceadapter>
|
通过继承WBIResourceAdapter类创建MockAdapter类。WBIResourceAdapter类在com.ibm.j2ca.base包中(大部分Adapter需要继承的基类都在这个包中,如不特别指出,以下文中提到的需要继承的类都属于这个包)。唯一必须实现的方法是getResourceAdapterMetadata,返回描述该Adapter的WBIResourceAdapterMetadata实例。注意,该类需要符合Java Bean规范,即必须有public无参数的缺省构造函数。
代码清单 2.MockAdapter.java
package com.ibm.j2ca.mock;
import javax.resource.ResourceException;
import com.ibm.j2ca.base.WBIResourceAdapter;
import com.ibm.j2ca.base.WBIResourceAdapterMetadata;
public class MockAdapter extends WBIResourceAdapter {
public MockAdapter()
super();
}
public WBIResourceAdapterMetadata getResourceAdapterMetadata()
throws ResourceException {
return new WBIResourceAdapterMetadata(
"IBM Mock Adapter", "IBM CSDL ", "1.0.0", false);
}
}
|
第三步:实现对EIS的连接管理逻辑
WebSphere Adapter要连接EIS,必须实现JCA规范中的Connection管理逻辑。包括四个主要接口:Connection,ManagedConnection,ManagedConnectionFactory,ConnectionFactory。
首先要确定的是连接特定的EIS需要的属性值,必需的属性要加在实现ManagedConnectionFactory接口的类里,如hostname,port,username,password之类。Mock EIS唯一需要设置的就是操作符Operation。因此Mock Adapter只需要一个ManagedConnectionFactory属性:Operation。
各个接口的实现类以及ManagedConnectionFactory的属性需要在ra.xml里加以描述。在ra.xml <resourceadapter>标签下加上如下信息,来描述Adapter的outbound功能,指明各个接口的实现类名。
<outbound-resourceadapter>
<connection-definition>
<managedconnectionfactory-class>
com.ibm.j2ca.mock.MockManagedConnectionFactory
</managedconnectionfactory-class>
<config-property>
<config-property-name>Operation</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value></config-property-value>
</config-property>
<connectionfactory-interface>
javax.resource.cci.ConnectionFactory
</connectionfactory-interface>
<connectionfactory-impl-class>
com.ibm.j2ca.mock.MockConnectionFactory
</connectionfactory-impl-class>
<connection-interface>javax.resource.cci.Connection</connection-interface>
<connection-impl-class>com.ibm.j2ca.mock.MockConnection</connection-impl-class>
</connection-definition>
<transaction-support>NoTransaction</transaction-support>
<reauthentication-support>false</reauthentication-support>
</outbound-resourceadapter>
|
接着分别实现这四个接口:
Connection:Foudation Classes提供了Connection接口的抽象实现WBIConnection,MockConnection继承并该类。唯一必须实现的方法是createInteraction,提供outbound操作需要的Interaction接口的实现MockInteraction(将在下一步中描述)。
代码清单 3.MockConnection.java
package com.ibm.j2ca.mock;
import javax.resource.ResourceException;
import javax.resource.cci.Interaction;
import com.ibm.j2ca.base.WBIConnection;
import com.ibm.j2ca.base.WBIManagedConnection;
public class MockConnection extends WBIConnection {
public MockConnection(WBIManagedConnection mc) throws ResourceException {
super(mc);
}
public Interaction createInteraction() throws ResourceException {
return new MockInteraction(this);
}
}
|
ManagedConnection:对EIS真正的连接通过实现ManagedConnection接口完成。Foundation Classe提供了抽象实现WBIManagedConnection。Mock Adapter要继承WBIManagedConnection并完成对EIS连接的操作,如创建和关闭连接。MockManagedConnection所要做的是创建一个MockEIS对象来代表一个连接。需要实现的方法有:
- getMetaData方法返回关于EIS的一些信息。
- getWBIConnection方法,进行认证并返回一个对应的JCA Connection对象,这里将是MockConnection。
- destory方法,释放对EIS的连接。
- getMockEIS方法,使得Adapter其他部分可以访问EIS。
代码清单 4.MockManagedConnection.java
package com.ibm.j2ca.mock;
import javax.resource.ResourceException;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import com.ibm.eis.mock.MockEIS;
import com.ibm.j2ca.base.WBIConnectionRequestInfo;
import com.ibm.j2ca.base.WBIManagedConnection;
import com.ibm.j2ca.base.WBIManagedConnectionFactory;
import com.ibm.j2ca.base.WBIManagedConnectionMetaData;
/**
* @author Donny
*
*/
public class MockManagedConnection extends WBIManagedConnection {
private MockEIS mockEIS ;
public MockManagedConnection(WBIManagedConnectionFactory factory,
Subject sub, WBIConnectionRequestInfo request,MockEIS eis)
throws ResourceException {
super(factory, sub, request);
mockEIS = eis;
}
public ManagedConnectionMetaData getMetaData() throws ResourceException {
return new WBIManagedConnectionMetaData("Mock EIS",
"1.0.0",10,super.getPasswordCredential().getUserName());
}
public Object getWBIConnection(PasswordCredential arg0, boolean arg1)
throws ResourceException {
return new MockConnection(this);
}
public void destroy() throws ResourceException {
}
public MockEIS getMockEIS() {
return mockEIS;
}
}
|
ManagedConnectionFactory:J2EE容器通过ManagedConnectionFactory来创建ManagedConnection。AFC提供了ManagedConnectionFactory的抽象实现WBIManangedConnectionFactory。MockManagedConnectionFactory只需要继承这个类,实现特定的属性,在这里就是Operation。同时要实现方法createConnectionFactory供J2EE服务器取得ConnectionFactory的实例,这里将是MockConnectionFactory对象;最重要的是要实现createManagedConnection返回MangedConnection对象。还必须实现在前面确定并在ra.xml文件里制定的属性所对应的get和set方法,这里即getOperation和setOperation。MockManagedConnectionFactory根据Operation属性值构造一个MockEIS实例,并由此实例构造出MockMangedConnection的一个实例。
MockManagedConnectionFactory实现如下,注意必须提供缺省构造函数使其满足Java Bean规范要求:
代码清单 5.MockManagedConnectionFactory.java
package com.ibm.j2ca.mock;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.security.auth.Subject;
import com.ibm.j2ca.base.WBIConnectionRequestInfo;
import com.ibm.j2ca.base.WBIManagedConnectionFactory;
public class MockManagedConnectionFactory extends WBIManagedConnectionFactory {
private String operation = "+";
public MockManagedConnectionFactory() {
super();
}
public ManagedConnection createManagedConnection(Subject sub,
ConnectionRequestInfo info) throws ResourceException {
//“连接” Mock EIS,创建对MockManagedConnection
MockEIS eis = new MockEIS();
eis.setOperation(this.operation);
return new MockManagedConnection(this, sub, (WBIConnectionRequestInfo) info,eis);
}
public Object createConnectionFactory(ConnectionManager arg0)
throws ResourceException {
return new MockConnectionFactory(arg0,this);
}
public String getOperation(){
return operation;
}
public void setOperation(String operation){
this.operation = operation;
}
} |
MockConnectionFactory:MockConnectionFactory需继承WBIConnectionFactory,该类已经实现了基本逻辑,子类只要实现构造函数即可。
代码清单 6.MockConnectionFactory.java
package com.ibm.j2ca.mock;
import javax.resource.spi.ConnectionManager;
import com.ibm.j2ca.base.WBIConnectionFactory;
import com.ibm.j2ca.base.WBIManagedConnectionFactory;
public class MockConnectionFactory extends WBIConnectionFactory {
public MockConnectionFactory(ConnectionManager cm,
WBIManagedConnectionFactory factory){
super(cm, factory);
}
} |
到这里,连接管理的逻辑已经全部实现。
第四步:实现Interaction接口以支持outbound操作
为支持outbound操作,必须实现Interaction接口,如上一步所示,Interaction对象由MockConnection创建。Foundation Classes提供了抽象实现WBIInteraction。继承该类需要实现的唯一方法是execute,在其中根据传入的参数实现对EIS的操作。
为符合Websphere Adapter的标准,Record execute(InteractionSpec ixSpec,Record in) 函数的传入参数有相应的限制,ixSpec应当是 WBIInteractionSpec的实例,in应该是DataObjectRecord的实例。DataObjectRecord包含了一个SDO规范所定义的DataObject,即BO。
MockAdatper非常简单的实现了execute方法,分析输入的BO,取出操作数,交给MockEIS进行计算后返回一份BO副本。
 |
如何操作BO
WPS提供了BO操作的各种服务,比如创建,复制等等。Foundation Classes将这些服务封装在com.ibm.j2ca.base.AdapterBOUtil工具类中以方便调用。
|
|
代码清单 7.MockInteraction.java
package com.ibm.j2ca.mock;
import javax.resource.ResourceException;
import javax.resource.cci.InteractionSpec;
import javax.resource.cci.Record;
import com.ibm.eis.mock.MockEIS;
import com.ibm.j2ca.base.AdapterBOUtil;
import com.ibm.j2ca.base.DataObjectRecord;
import com.ibm.j2ca.base.WBIConnection;
import com.ibm.j2ca.base.WBIInteraction;
import com.ibm.j2ca.base.WBIInteractionSpec;
import commonj.sdo.DataObject;
public class MockInteraction extends WBIInteraction {
public MockInteraction(WBIConnection arg0) {
super(arg0);
}
public Record execute(InteractionSpec ixSpec, Record record)
throws ResourceException {
if(!(ixSpec instanceof WBIInteractionSpec)){
throw new ResourceException("Unknown InteractionSpec type!");
}
if(!(record instanceof DataObjectRecord)){
throw new ResourceException("Invalid Record type!");
}
//1. 分析输入的BO,得到运算数
DataObject bg = ((DataObjectRecord)record).getDataObject();
DataObject mockBO = bg.getDataObject("MockBO");
float x = mockBO.getFloat("X");
float y = mockBO.getFloat("Y");
//2. 通过相应的连接,得到Mock EIS服务,并计算结果
MockManagedConnection mcon = (MockManagedConnection)(
(WBIConnection)this.getConnection()).getManagedConnection();
MockEIS eis = mcon.getMockEIS();
float result = eis.calculate(x,y);
//3. 创建输出Record
DataObject outputBG = AdapterBOUtil.copyBusinessObject(bg);
outputBG.getDataObject("MockBO").setFloat("Result",result);
DataObjectRecord outputRecord = new DataObjectRecord();
outputRecord.setDataObject(outputBG);
outputRecord.setRecordName(record.getRecordName());
return outputRecord;
}
} |
到这里,Adapter对Outbound的支持已经完全实现,可以部署运行了。
测试:CCI 方式调用Adapter
为了在WPS上部署运行Adapter,需要将Adapter打包。首先,用wid的export功能,将Adapter所有类文件打包成为一个jar文件,比如MockAdapter.jar,并将该文件保存在工程的connectorModule目录下,这样可以在WID的集成测试环境中成功部署。
必须要有应用程序来调用Adapter,可以是jsp,ejb等。这里我们创建一个动态Web工程,使用jsp来做测试。根据J2EE的规范,还需要创建EAR工程,将Adapter工程和Web工程都包括进去。
调用Adapter很简单,首先通过JNDI查找获取ConnectionFactory实例以创建连接;然后调用Interaction的execute方法执行操作;最后关闭连接即可。
代码清单 8.MockTest.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.ibm.j2ca.base.*"%>
<%@ page import="commonj.sdo.*"%>
<%@ page import="javax.resource.cci.*"%>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<META name="GENERATOR" content="IBM Software Development Platform">
<TITLE>MockTest.jsp</TITLE>
</HEAD>
<BODY>
<%
try{
//Step 1: 获取连接
// 1.1: 通过JNDI查找ConnetionFactory
javax.naming.InitialContext initCx = new javax.naming.InitialContext();
String jndi = "Mock/Mock_Multi";
ConnectionFactory cxf = (ConnectionFactory) initCx.lookup(jndi);
// 1.2: 创建连接
Connection connection = null ;
connection = cxf.getConnection();
//Step 2: 执行操作
// 2.1: 创建Interaction
Interaction interaction = null;
interaction = connection.createInteraction();
// 2.2: 准备InteractionSpec
WBIInteractionSpec interactionSpec = new WBIInteractionSpec();
// 2.3: 准备BO,必须以BG为包装器
DataObject inputObject = null;
inputObject = AdapterBOUtil
.createDataObject(
"http://MockTest",
"MockBOBG");
DataObject bo = inputObject.createDataObject("MockBO");
bo.set("X", "1.01");
bo.set("Y", "3.21");
// 2.4: 准备Record
DataObjectRecord inputRecord = new DataObjectRecord();
inputRecord.setDataObject(inputObject);
// 2.5: 调用执行方法
Record outputRecord = interaction.execute(interactionSpec,
inputRecord);
// 2.6: 获取返回值并输出
DataObject outputObject = ((DataObjectRecord) outputRecord)
.getDataObject();
out.println(AdapterBOUtil.serializeDataObject(outputObject));
//Step 3: 关闭连接
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
%></BODY>
</HTML>
|
以上代码中,第一步引用了JNDI名字Mock/Mock_Multi,需要在ear部署后手工创建。首先启动WID的集成测试服务器,将ear工程添加到服务器上。接着通过WPS的管理控制台,找到相应的企业应用程序,为其建立J2C连接工厂,将其JNDI设为Mock/Mock_Multi,并且定制其属性opertaion为“*”(即乘法)。
图 3. 定义J2C连接工厂
然后可以通过浏览器来执行index.jsp。如果完全按照如上设置,将得到以下输出:
图 4. 输出结果
总结及展望
以上我们实现了MockAdapter的连接管理和Outbound操作功能。WebSphere JCA Adapter还有很多其它需要考虑的特性,比如事务的管理,Inbound通讯等。Foundation Classes也有很多其他功能,如Command管理框架,统一的日志功能等。这些本文都没有触及。实际可用的Adapter无疑要复杂的多,但是基本的实现步骤是大体相同的。
参考资料 学习
获得产品和技术
关于作者  | |  | 刘冬清是中国软件实验室(CSDL BJ)的工程师,加入IBM以来一直从事Websphere Business Integration相关软件的开发和测试工作。可以通过电子邮件:liudongq@cn.ibm.com来联系他。 |
对本文的评价
|