通用客户机接口
CCI 是应用组件使用的标准客户机 API。设计它是为了为 EIS 访问提供基础 API,EAI 和工具供应商们在此基础上构建更高级的功能。CCI 分为五部分,我们将在接下来的章节中讨论每个部分。CCI 的五部分是:
- 与连接相关的接口
- 与交互相关的接口
- 与数据表示相关的接口
- 与元数据相关的接口
- 异常和警告
CCI 不需要资源适配器提供支持。事实上,资源适配器也许会定义它自己的不同于 CCI 的客户机 API。
CCI 使用其 ConnectionFactory
接口来获取到 EIS 的连接句柄。ConnectionFactory
提供两个 getConnection()
方法:一个无参数的方法和一个将 ConnectionSpec
实例作为参数的方法。第二个 getConnection()
方法在必须和连接请求一起提供特定于该请求的信息时使用。
ConnectionFactory
也提供方法,返回关于资源适配器的元数据信息(getResourceAdapterMetaData()
),返回记录工厂(getRecordFactory()
)。
ConnectionSpec
是一个空接口,可能会扩展该接口来添加任何所需属性。两个标准属性 UserName
和 Password
是由 JCA 定义的,但可能支持也可能不支持额外的属性。当对 ConnectionManager
调用 allocateConnection()
时,资源适配器会将 ConnectionSpec
的属性映射到 ConnectionRequestInfo
的属性上。
Connection
接口是一个用于访问 EIS 的应用程序级的连接句柄。它提供下列方法来管理应用程序和 EIS 间的连接:
close()
方法允许应用程序关闭连接句柄,正如连接管理契约所要求的。
getLocalTransaction()
方法允许应用组件使用本地事务。
createInteraction()
方法返回一个Interaction
对象,该对象用于访问 EIS 功能。
getResultSetInfo()
方法返回关于由 EIS 支持的结果集功能的信息。
getMetaData()
函数返回关于该连接的元数据。
Interaction
实例允许应用组件执行 EIS 功能。它有两个 execute()
方法:一个方法接受一个 InteractionSpec
和一个输入 Record
,并返回一个输出 Record
,另一个方法接受一个 InteractionSpec
、一个输入 Record
和一个输出 Record
。此方法执行如 InteractionSpec
定义的相应的 EIS 功能,并更新输出 Record
。
Interaction
必须维护跟创建它的 Connection
之间的关联,并且 getConnection()
必须返回该 Connection
。
Interaction
上的 close()
方法应释放所有由 Interaction
维护的资源,但不应关闭 Connection
。
InteractionSpec
接口为 EIS 专用函数提供属性。它定义下列标准属性:
FunctionName
是一个表示 EIS 函数名称的字符串。
InteractionVerb
是一个整型,它指定与 EIS 交互的模式。InteractionVerb
允许的值为:
SYNC_SEND
:输入记录被发送到 EIS,不返回任何结果。
SYNC_SEND_RECEIVE
:输入记录被发送到 EIS,并返回一条结果。
SYNC_RECEIVE
:从 EIS 同步获取到一条结果。
ExecutionTimeout
是等待 EIS 执行该函数的毫秒数。
Record
接口为输入或输出到 EIS 的数据提供 Java 表示。Record
有两个标准属性:RecordName
和 RecordShortDescription
。 表示 EIS 记录数据的额外属性必须由实现程序定义。
另外,还提供了三个扩展 Record
的接口。MappedRecord
接口提供对键值映射集合记录元素的访问。IndexedRecord
接口提供对排好序的集合记录元素的访问。ResultSet
接口基于 JDBC ResultSet
并提供相似的功能用于访问 EIS 数据。
RecordFactory
接口提供方法来创建 MappedRecord
和 IndexedRecord
实例。RecordFactory
也许支持其中一类记录,也许两类都支持,也许全不支持。如果全不支持,随后,ConnectionFactory
的 getRecordFactory()
方法会抛出一个异常。
和 ManagedConnectionMetaData
接口相似,ConnectionMetaData
接口提供关于 EIS 名称、EIS 版本及用户名的信息。
ResourceAdapterMetaData
接口提供关于适配器名称、版本、供应商、描述以及资源适配器支持的 JCA 版本的信息。它也提供决定资源适配器下列功能的方法:是否使用 InteractionSpec
;支持哪个 execute()
方法变量;本地事务是否能根据应用组件进行划分。
ResourceException
是系统契约和 CCI 的异常层次的根。它提供一个字符串来描述错误、错误代码和对另一个异常的引用,这也许是引起 ResourceException
的低层次问题。
ResourceWarning
提供关于警告的信息,这些警告由 EIS 作为一个 Interaction
的结果返回。这些 ResourceWarning
构成一个链;调用 Interaction
上的 getWarnings()
获得第一条警告,通过该警告访问链的余下的部分。
样例资源适配器
在本节中,将介绍 JCA 资源适配器的一个简单实现。样例资源适配器在 helloworldra.rar
文件中。Java 类文件在这个 RAR 文件的 helloworldra.jar
文件中。所有类的源码也在 helloworldra.jar
中。
您也许能从它的名字里猜到,这个资源适配器可以实现无所不在的 “Hello World” 功能。同样地,它并不真的连接到一个企业系统,它的功能局限于仅返回一个包含这句十分常见的消息的字符串
。尽管资源适配器并不实现事务契约或安全性契约,但它的确实现 CCI;它使用一个 Interaction
、一个 InteractionSpec
、一个 RecordFactory
和多个 IndexedRecord
。
关于样例资源适配器最重要最有趣的部分将在接下来的探讨中提炼和解释。除了类和接口,我们还探讨部署描述符的源代码。从这个简单实现中,您将能够得到一些关于需要编写的类以及这些类之间的关系的体会。除了学习下面两个部分,您还应该花一些时间复习完整的源代码,您可以在 参考资料 中找到该代码。
HelloWorldConnectionFactoryImpl
类
HelloWorldConnectionFactoryImpl
类实现 CCI ConnectionFactory
接口,并提供应用组件使用的连接工厂,从而创建到 EIS 的连接。下面是创建 HelloWorldConnectionFactoryImpl
实例的代码,以及返回连接的客户机方法、记录工厂和元数据。
创建连接工厂实例
HelloWorldConnectionFactoryImpl
类的构造方法需要传入 ConnectionManager
和 ManagedConnectionFactory
,从而在 getConnection()
方法中使用,如下所示:
... public HelloWorldConnectionFactoryImpl( ManagedConnectionFactory mcf, ConnectionManager cm) { super(); this.mcf = mcf; this.cm = cm; } ... |
获取连接
HelloWorldConnectionFactoryImpl
类既不支持 ConnectionRequestInfo
也不支持 ConnectionSpec
,所以两个 getConnection()
方法做相同的事:它们调用 ConnectionManager
的 allocateConnection()
方法,针对 ConnectionRequestInfo
传入 ManagedConnectionFactory
和 null
,如下所示:
... public Connection getConnection() throws ResourceException { return (Connection) cm.allocateConnection(mcf, null); } public Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException { return getConnection(); } ... |
记录工厂和元数据方法
getRecordFactory()
和 getMetaData()
方法仅仅为各自的接口返回实现类,如下所示:
... public RecordFactory getRecordFactory() throws ResourceException { return new HelloWorldRecordFactoryImpl(); } public ResourceAdapterMetaData getMetaData() throws ResourceException { return new HelloWorldResourceAdapterMetaDataImpl(); } ... |
HelloWorldConnectionImpl
类实现 CCI Connection
接口,并为应用组件访问 EIS 提供连接句柄。下面是创建、验证和关闭连接的方法、为客户机提供 Interaction
和元数据的方法以及通知该类不支持本地事务和结果集的代码。
创建连接
HelloWorldConnectionImpl
类的构造函数需要传入 ManagedConnection
,以在 close()
中使用。在实例化的过程中设置了一个标记来指示有效连接,即,非关闭的连接。换言之,在恰当的地方通过检查此标记来决定能否执行所请求的动作。
... public HelloWorldConnectionImpl(ManagedConnection mc) { super(); this.mc = mc; valid = true; } ... |
使连接失效
invalidate()
方法设置 ManagedConnection
对 null
的引用。并将标志设置为表示连接不再有效。
... void invalidate() { mc = null; valid = false; } ... |
关闭连接
close()
方法根据所需委派其对 ManagedConnection
的调用。
... public void close() throws ResourceException { if (valid) { ((HelloWorldManagedConnectionImpl) mc).close(); } } ... |
交互和元数据方法
createInteraction()
和 getMetaData()
方法仅仅为各自的接口返回实现类。
... public Interaction createInteraction() throws ResourceException { if (valid) { return new HelloWorldInteractionImpl(this); } else { throw new ResourceException(CLOSED_ERROR); } } public ConnectionMetaData getMetaData() throws ResourceException { if (valid) { return new HelloWorldConnectionMetaDataImpl(); } else { throw new ResourceException(CLOSED_ERROR); } } ... |
抛出 NotSupportedException
由于此资源适配器既不支持本地事务也不支持结果集,所以 getLocalTransaction()
方法和 getResultSetInfo()
方法都抛出 NotSupportedException
。
... public LocalTransaction getLocalTransaction() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED); } public ResultSetInfo getResultSetInfo() throws ResourceException { throw new NotSupportedException(RESULT_SETS_NOT_SUPPORTED); } ... |
HelloWorldManagedConnectionFactoryImpl
类
HelloWorldManagedConnectionFactoryImpl
类实现了 ManagedConnectionFactory
接口,在下面的代码中,您将看到如何创建一个连接工厂、如何创建一个受管的连接以及类如何响应请求来匹配受管的连接。
创建一个连接工厂以及一个受管的连接
createConnectionFactory()
和 createManagedConnection()
方法分别简单地返回实现类。由于此资源适配器并未实现安全性契约,createManagedConnection()
并不使用 Subject
或者 ConnectionRequestInfo
参数来创建 ManagedConnnection
。
... public Object createConnectionFactory(ConnectionManager cm) throws ResourceException { return new HelloWorldConnectionFactoryImpl(this, cm); } public ManagedConnection createManagedConnection( Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { return new HelloWorldManagedConnectionImpl(); } ... |
匹配受管的连接
此资源适配器实现的简单性使每一个 ManagedConnection
之间难以区分。因此,matchManagedConnections()
方法仅仅返回输入的 Set
中的第一个 ManagedConnection
,如下所示:
... public ManagedConnection matchManagedConnections( Set connectionSet, Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { ManagedConnection match = null; Iterator iterator = connectionSet.iterator(); if (iterator.hasNext()) { match = (ManagedConnection) iterator.next(); } return match; } ... |
HelloWorldManagedConnectionImpl
类
HelloWorldManagedConnectionImpl
类实现 ManagedConnection
接口。在下面的代码中,您将看到 getConnection()
、 close()
、 cleanup()
以及 destroy()
方法如何协作来创建、关闭并清理一个连接,正如这些方法所暗示的,此资源适配器不支持事务。
创建一个连接
getConnection()
方法首先验证一个现存的连接句柄,如果句柄存在,就创建一个新的连接句柄,将其存储至一个实例变量中。然后返回一个到该实例变量的引用。这在连接句柄和 ManagedConnection
之间维护了一个一对一的关系。一个资源适配器可能具有多个连接句柄同一个 ManagedConnection
相关联,但是并不需要这么做。
... public Object getConnection( Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { if (connection != null) { connection.invalidate(); } connection = new HelloWorldConnectionImpl(this); return connection; } ... |
关闭连接
close()
方法通知已经发生 ConnectionEventListener
关闭操作,然后验证连接句柄。
... public void close() { Enumeration list = listeners.elements(); ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED); while (list.hasMoreElements()) { ((ConnectionEventListener) list.nextElement()).connectionClosed(event); } connection.invalidate(); } ... |
清理并销毁一个连接
cleanup()
和 destroy()
方法都可使连接句柄失效。为了将该连接从应用服务器的连接池移除,destroy()
方法也将 ManagedConnection
实例变量设置成 null
。
... public void cleanup() throws ResourceException { connection.invalidate(); } public void destroy() throws ResourceException { connection.invalidate(); connection = null; listeners = null; } ... |
抛出一个 NotSupportedException
由于此资源适配器并不支持事务,getXAResource()
和 getLocalTransaction()
方法都抛出 NotSupportedException
。
... public XAResource getXAResource() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR); } public LocalTransaction getLocalTransaction() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR); } ... |
HelloWorldInteractionImpl
类实现了 CCI Interaction
接口。在本节中,您将看到该类的一些更重要的元素是如何协作,从而创建、执行以及关闭一个 Interaction
的。
创建 Interaction
由于 JCA 需要一个 Interaction
维护跟创建它的连接句柄的关联,构造函数需要传入一个 Connection
。 在实例化的过程中设置了一个标志来指示有效 Interaction
,即,非关闭的。换言之,在恰当的地方通过检查此标志来决定能否执行所请求的动作。
... public HelloWorldInteractionImpl(Connection connection) { super(); this.connection = connection; valid = true; } ... |
执行一个 Interaction
HelloWorldInteractionImpl
类只支持带有一个 InteractionSpec
、一个输入记录以及一个输出记录的 execute()
方法(其他的 execute()
方法抛出 NotSupportedException
)。该方法确保:
Interaction
是有效的- 传入了正确的
InteractionSpec
实现实例 - 提供了正确的输入记录类型
- 提供了正确的输出记录类型
execute()
方法
如果所有的条件正确,"Hello World!" 就处于输出记录中。如果某一条件有误,就会抛出一个异常。
... public boolean execute(InteractionSpec ispec, Record input, Record output) throws ResourceException { if (valid) { if (((HelloWorldInteractionSpecImpl) ispec) .getFunctionName() .equals(HelloWorldInteractionSpec.SAY_HELLO_FUNCTION)) { if (input.getRecordName().equals(HelloWorldIndexedRecord.INPUT)) { if (output.getRecordName().equals(HelloWorldIndexedRecord.OUTPUT)) { ((HelloWorldIndexedRecord) output).clear(); ((HelloWorldIndexedRecord) output).add( OUTPUT_RECORD_FIELD_01); } else { throw new ResourceException(INVALID_OUTPUT_ERROR); } } else { throw new ResourceException(INVALID_INPUT_ERROR); } } else { throw new ResourceException(INVALID_FUNCTION_ERROR); } } else { throw new ResourceException(CLOSED_ERROR); } return true; } ... |
关闭 Interaction
close()
清除了连接句柄并将 Interaction
标为无效。
... public void close() throws ResourceException { connection = null; valid = false; } ... |
为了对使用此资源适配器的应用组件隐藏尽可能多的实现细节,HelloWorldInteractionSpec
接口扩展了 InteractionSpec
。InteractionSpec
接口允许我们提供应用组件所必须有的全部信息。在下一部分中,我们会介绍此接口的实现类。
... public interface HelloWorldInteractionSpec extends InteractionSpec { public static final String SAY_HELLO_FUNCTION = "sayHello"; public String getFunctionName(); public void setFunctionName(String functionName); } |
HelloWorldInteractionSpecImpl
类
HelloWorldInteractionSpecImpl
实现了 HelloWorldInteractionSpec
接口。它有一个属性:FunctionName
,有一个 getter 和 setter 方法来访问该属性。JCA 规范指出 InteractionSpec
实现中的属性必须是受限制的。此实现提供了受限的属性。
... public String getFunctionName() { return functionName; } public void setFunctionName(String functionName) { String oldFunctionName = functionName; this.functionName = functionName; firePropertyChange("FunctionName", oldFunctionName, functionName); } ... |
正如 HelloWorldInteractionSpec
, HelloWorldIndexedRecord
接口用于对应用组件隐藏实现细节。
... public interface HelloWorldIndexedRecord extends IndexedRecord { public static final String INPUT = "input"; public static final String OUTPUT = "output"; public static final int MESSAGE_FIELD = 0; } |
HelloWorldIndexedRecordImpl
类实现 HelloWorldIndexedRecord
接口。它具有两个属性:Name
和 ShortDescription
,以及访问这些属性的 getter 和 setter 方法。由于此类必须实现 List
接口,它维护了一个 ArrayList
作为实例变量并通过调用 ArrayList
的相应方法实现所有的 List
方法。
... public class HelloWorldIndexedRecordImpl implements HelloWorldIndexedRecord { private ArrayList list = new ArrayList(); private String name; private String description; ... |
HelloWorldRecordFactoryImpl
类实现 RecordFactory
接口。它不支持创建 MappedRecord
。
createIndexedRecord()
方法确保请求的记录名称是有效的,然后创建该记录并返回它。如果记录名是无效的,就抛出一个异常。
... public IndexedRecord createIndexedRecord(String recordName) throws ResourceException { HelloWorldIndexedRecordImpl record = null; if ((recordName.equals(HelloWorldIndexedRecord.INPUT)) || (recordName.equals(HelloWorldIndexedRecord.OUTPUT))) { record = new HelloWorldIndexedRecordImpl(); record.setRecordName(recordName); } if (record == null) { throw new ResourceException(INVALID_RECORD_NAME); } else { return record; } } ... |
部署描述符提供了下列 JCA 组件的全名:
ManagedConnectionFactory
实现类ConnectionFactory
接口ConnectionFactory
实现类Connection
接口Connection
实现类
它也表示既不支持事务也不支持重新认证。如果支持事务和安全性契约,部署描述符将包含额外的一些元素。
< !DOCTYPE connector PUBLIC '-//Sun Microsystems, Inc.// DTD Connector 1.0//EN' 'http://java.sun.com/dtd/connector_1_0.dtd'> <connector> <display-name>Hello World Sample</display-name> <vendor-name>Willy Farrell</vendor-name> <spec-version>1.0</spec-version> <eis-type>Hello World</eis-type> <version>1.0</version> <resourceadapter> <managedconnectionfactory-class> com.ibm.ssya.helloworldra.HelloWorldManagedConnectionFactoryImpl </managedconnectionfactory-class> <connectionfactory-interface> javax.resource.cci.ConnectionFactory </connectionfactory-interface> <connectionfactory-impl-class> com.ibm.ssya.helloworldra.HelloWorldConnectionFactoryImpl </connectionfactory-impl-class> <connection-interface> javax.resource.cci.Connection </connection-interface> <connection-impl-class> com.ibm.ssya.helloworldra.HelloWorldConnectionImpl </connection-impl-class> <transaction-support> NoTransaction </transaction-support> <reauthentication-support> false </reauthentication-support> </resourceadapter> </connector> |