通过JCA实现企业级应用程序的“即插即用”
副标题#e#
对付那些有多个企业信息系统的公司,把这些企业信息系统整合起来是实现最高效率是至关重要的。JCA(Java Connector Architecture) 提供了一个应用处事器和企业信息系统毗连的尺度Java办理方案,以及把这些系统整合起来实现最好的事情效率的要领。因为J2EE对企业级应用措施集成的支持已经很是小了(本质上,JMS和XML可以利用JAX应用措施编程接口支持它),Sun和它的Java Community Process店员发起把J2EE Connector Architecture ( J2EE毗连器体系布局,JCA)作为J2EE类型的1.3版的一部门。因为JCA提供了整合差异种类的企业信息系统的一套尺度的体系布局,利用它的企业信息系统供给商就不再需要为每个应用处事器定制它们的产物。遵守这个类型的应用措施处事器供给商在它们想增加新的企业信息系统毗连的时候将不需要添加自界说代码了。最好的工作就是,需要从J2EE应用措施中会见企业信息系统产物的用户不需要进修可能再进修差异的应用措施编程接口,因为JCA界说了一套民众的客户接口。
JCA黑幕
JCA界说了一套尺度的接口,用于让毗连器把兼容的应用措施处事器无缝的整合起来。同时,另一套尺度接口答允客户(可能应用措施处事器的应用措施主机)用一种统一的要领利用毗连器。这样通过JCA毗连器对付跨应用措施处事器来说就是可移植的,而客户措施也是很精练的毗连器。
遵守JCA类型的毗连器被称作资源适配器(resource adapter)。每个资源适配器都被要求支持两套尺度接口∶一组接口被应用措施处事器利用来与适配器交互浸染,而另一套由客户/消费者利用与企业信息系统(虽然也是通过适配器)彼此浸染。
JCA思量到资源适配器可以把客户端措施作为专有接口的替代,可是这大概会在后续的版本改变。我强烈地发起你始终支持尺度客户端。其实也并没有几多特另外事情要做,就像我在这个例子资源适配器中要演示的一样。可是,在我们深入研究这个例子之前,让我们先来看看系统-客户端接口。每个遵守JCA类型的资源适配器必需支持一套应用措施处事器用来打点适配器的尺度接口。这套接口是在适配器和应用措施处事器之间的系统级协约,是由JCA委托的。
一台应用处事器可以有多个资源适配器,可是每个企业级信息系统范例只有一个适配器。举例来说,一台应用处事器可觉得SAP,Oracle各自分派一个资源适配器。应用处事器和资源适配器干系细密配合打点企业信息系统会见中的三个要害性方面∶毗连,事务和安详。为此目标,JCA界说了一套尺度接口,在javax.resource.spi和javax.resource.spi.security包中界说,是所有的资源适配器必需实现的。这些接口答允应用处事器与资源适配器彼此浸染而且节制毗连,事务和安详打点的处理惩罚。
建设资源适配器首先要正确地实现系统协定,并且它也带来了很多长处,我们举例子来说明个中一个长处,利用一个有毗连打点协定的资源适配器能使应用措施处事器毗连到一个根基企业级信息系统。这使一个可缩放的应用措施情况可以支持许多的需要会见企业级信息系统的客户。
另一个长处可以通过实现事务打点协定来实现,这就是支持对企业级信息系统资源打点者的事务性会见。这个协定使一个应用措施处事器可以或许利用事项打点措施在多个资源打点措施之间打点事务。好比说,这将答允一个事务会话bean通过Java数据库毗连(JDBC)和一个应用措施处事器节制的沟通事务中的SAP系统会见一个干系数据库。事务协约也支持那些被一个企业级信息系统资源打点措施内部打点的事务,也就是当地事务,而不必涉及企业外部事务打点。
尚有一个长处就是充实思量会见企业级信息系统的安详性协定。这个协定提供了对安详的应用情况的支持,淘汰了威胁信息系统安详的大概,而且还可以或许掩护信息系统打点的有贸易代价的信息资源。
这些利益均可用于应用措施处事器的资源适配器,而不必再写任何自界说代码。这就大大刺激了企业信息系统的提供商为系统建设高级的资源适配器。
另外,JCA还界说了客户/消费者利用的另一套与企业信息系统交互的接口。JCA挪用了民众客户接口( Common Client Interface,CCI)。CCI是一个应用措施开拓者和客户措施可以配合毗连和会见后端系统的措施接口。它是一个雷同于JDBC的低端的API。CCI打点应用措施和系统之间的数据活动,而不会让我们看到任何的容器和应用措施处事器所做的工作。CCI是为了某些出格的目标设计的。首先,它可以或许超过很多种范例的企业信息系统;其次,这个应用措施接口被设计的很是易用,并且是可扩展的。CCI可以更进一步的构建更详细的企业信息系统成果。
#p#分页标题#e#
CCI被分成四部门(见表1)。所有的详细CCI类和接口都可以利便的在javax.resource.cci措施包中被找到。
接口范例 | 名称 |
与毗连有关的接口,描写一个工场类毗连和一个应用措施类毗连。 | javax.resource.cci.ConnectionFactory
javax.resource.cci.Connection javax.resource.cci.ConnectionSpec javax.resource.cci.LocalTransaction |
与交互有关的接口,能使组件驱动一个与EIS实例的交互。 | javax.resource.cci.Interaction
javax.resource.cci.InteractionSpec |
与数据表示有关的接口,用来描写与EIS实例交互中涉及到的数据布局。 | javax.resource.cci.RecordFactory
javax.resource.cci.Record, javax.resource.cci.MappedRecord, javax.resource.cci.IndexedRecord javax.resource.cci.IndexedRecord javax.resource.cci.ResultSet java.sql.ResultSetMetaData |
与元数据有关的接口,提供了一个资源适配器与EIS毗连的根基的元信息。 | javax.resource.cci.ConnectionMetaData
javax.resource.cci.ResourceAdapterMetaData |
#p#副标题#e#
上下文情况中的资源适配器
为了可以或许把问题表明得清清楚楚,让我们看一下图一。留意,存在于应用措施处事器历程空间中的资源适配器,通过资源适配器实现的系统接口执行毗连、事务和安详打点。像EJB,servlet和JSP这样的客户应用措施,就可以通过CCI与资源适配器交互。
资源适配器的例程
此刻我们开始一个资源适配器的例程。不外先声明一点,我在这里的方针不是要提供一个高质量的适配器来与像SAP这样的ERP系统交互,也就是说只能供各人参考研究而不能用于贸易上的目标。我的目标是举例说明一个最根基的资源适配器。别的,我想把留意力都放在建设资源适配器的毗连打点这一点上,我们也可以称之为资源适配器的" hello world "措施吧,主要因为根基观念照旧较量重要的。用于这个资源适配器例程的企业级信息系统是一个属性文件,所以最后,你将获得一个可以从属性文件中读取属性完整的可运行的资源适配器(并且,更重腹地是我们要领略它是如何运行)。
下面,我给出了所有的本文所涉及的代码。在我们开始研究这段资源适配器源代码之前,让我们看一下从一个客户应用措施(EJB,servlet等等)抉择利用资源适配器到应用措施利用它的步调。首先,应用措施处事器启动。为了设置(或插入)资源适配器,应用措施处事器建设了一个ManagedConnectionfactory并在其上挪用createmanagedconnection()要领。假如应用措施处事器委托建设10个毗连(这个数目在资源适配器陈设期间已经被指定了),它反复这个步调10次。ManagedConnection的每个实例都处于应用措施处事器的毗连池内。毗连池实现与我们接头的问题无关。资源适配器陈设设置能节制毗连池的参数,譬喻插入池中的初始毗连有几多,池中的最大的毗连数,每个毗连的生命周期等等。应用措施处事器也利用JavaBean挪用约定挪用每个ManagedConnectionFactory上的配置要领来配置详细的资源适配器的属性,这也被作为陈设信息的一部门来指定。譬喻,在我们的资源适配器实例中指定的属性是ConnectionURL,将指定要毗连到的属性文件。应用措施处事器挪用ManagedConnectionFactory上的setConnectionURL ()要领来配置这个属性。
此时,应用措施已经启动而且在应用措施处事器中已经有了我们指定命量的ManagedConnections。一个要求会见企业信息系统的客户先要从引用JNDI的ConnectionFactory实例开始。应用措施处事器在这里监听,然后挪用ManagedConnectionFactory实例的createConnectionFactory ()要领
ConnectionFactory利用应用措施处事器ConnectionManager初始化后的实例。
一旦客户措施取得ConnectionFactory的引用,它就挪用getConnection ()要领。getConnection ()要领是可以用来挪用ConnectionManager的allocateConnection ()要领的代码。这个要领最后挪用ManagedConnectionFactory上的matchManagedConnections ()要领。假如matchManagedConnections ()要领抉择了ManagedConnection中的一个毗连可以被利用,它返回到这个应用措施处事器的毗连,而在这样环境下处事器只是简朴地挪用返回的ManagedConnection实例上的getConnection ()要领。假如存在的ManagedConnection没有可以被利用的,那么应用措施处事器建设一个新的毗连直到到了最大值(陈设期间指定的)。然后应用措施处事器挪用新建设的managedconnection实例的getConnection ()要领。用两种要领,城市把一个毗连实例返回到客户措施:通过私有接口可能CCI与企业信息系统交互浸染。 代码段1是一个客户端措施(一个EJB)的片断,利用属性文件资源适配器 来会见一个名为Message的属性。留意CCI要实现的属性文件适配器,是EJB用来会见企业信息系统(属性文件)的。
当一个客户措施运用CCI与资源适配器交互浸染时,毗连上的全部的输入和输出都是通过一个交互工具实现的(见代码段2,InteractionImpl.java)
#p#分页标题#e#
交互工具通过一个Record工具来起浸染。Record工具封装客户措施请求并且还也封装企业信息系统响应,封装呈现的要领对付详细的供给厂商来说都纷歧样。譬喻,在我们的例子中,客户措施只是建设一个MappedRecord 而且添加它在Record工具中所要作为键检索的所有的属性名(见代码段2,MappedRecord的一个例子) 。客户措施然后挪用交互浸染的execute ()要领,当要领返回时,Mapped Record中的每个属性值却将被放入Record工具中,作为对应要害字的值,参看代码段2中的exec ()要领。
当客户措施竣事利用毗连时,它就会挪用close ()要领。 实现close ()要领是为了通知应用措施处事器ManageConnection建设的Connection可以被放回可用毗连池。应用措施处事器挪用ManagedConnection的排除要领,然后把它送回毗连池中,除此之外,假如建设一个新的满意接续申请的ManagedConnection的话,应用措施处事器还可以挪用destroy ()要领。最终,当应用措施处事器封锁可能卸载资源适配器时,它会挪用毗连池中的每个ManagedConnection的destroy ()要领。
图2是一幅UML序列图表描写时间的序列
发起按下面的顺序研究源文件∶
要想遵循系统协定,就凭据ManageConnectionFactoryImpl.java, ManagedConnectionImpl.java, ConnectionImpl.java, 和ConnectionEventListenerImpl.java的顺序读。
要想遵循客户措施协定,就请凭据ConnectionFactoryImpl.java, ConnectionImpl.java, RecordFactoryImpl.java, InteractionImpl.java, and MappedRecordImpl.java.
剩下的其他的类。
测试资源适配器
我是利用BEA的应用措施处事器WebLogic 6.1来测试资源适配器,本教程就想用这个应用软件处事器来做演示。
进入属性文件目次,然后从呼吁行方法运行build.cmd,该文件假定你的应用措施处事器被安装在C:\bea\wlserver6.1_beta目次。假如不是的话,请在运行之前在build.cmd文件中做出相应的窜改。运行这个文件将生成一个资源适配器存档文件( Resource Adapter Archive,RAR),设置WebLogic应用措施处事器。想知道RAR文件的具体信息,请参看JCA类型。假如你的应用措施处事器还没有运行的话,就请先启动它。你将看到应用措施处事器建设10个打点毗连。在实际事情中,这个数目将取决于你在详细的企业信息系统中能获得的同时毗连许可证的数目。
要想看看在运行中的资源适配器,请在test目次下打开一个呼吁行,然后运行build.cmd。这将设置一个利用实例资源适配器的无状态会话bean而且编译一个测试措施(Client.java),这个措施挪用bean的sayHello ()要领。在呼吁行里输入Client.cmd之前,请先确定在C:\temp目次下有一个名为test.properties的属性文件。这个属性文件应该至少有一个属性Message (区分巨细写)配置为某个字符串,譬喻Message = Hello World。确定你遵守应用措施处事器窗口中的信息,而且遵循从我们的资源适配器输出的信息。这些信息将辅佐你相识在应用措施处事器,资源适配器和无状态会话bean之间的交互浸染。这些信息还将有助于证实描写在图2中的步调流程。
然而,JCA尚有许多缺点,象缺乏对付异步企业信息系统会见的支持以及对付基于XML会见的直接支持。但愿在后头的版本中可以或许办理这些问题。不消猜疑,JCA填充了J2EE技能的一个庞大的缺陷,可以给处事器应用措施的开拓带来很好的前景。
代码段一:
initCtx = new InitialContext();
javax.resource.cci.ConnectionFactory cf =
(javax.resource.cci.ConnectionFactory)
initCtx.lookup("java:comp/env/eis/
PropertiesFileAdapter");
System.out.println("Got ConnectionFactory. Now
calling getConnection()");
javax.resource.cci.Connection myCon =
cf.getConnection();
javax.resource.cci.Interaction interaction =
myCon.createInteraction();
javax.resource.cci.MappedRecord recordIn =
cf.getRecordFactory().createMappedRecord("");
recordIn.put("Message","");
javax.resource.cci.MappedRecord recordOut =
(javax.resource.cci.MappedRecord)
interaction.execute(null,
(javax.resource.cci.Record)recordIn);
myCon.close();
retVal = (String)recordOut.get("Message");
代码段二:
#p#分页标题#e#
package adapters.propertiesfile;
import java.util.*;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.IllegalStateException;
import javax.resource.cci.*;
import java.lang.reflect.*;
import java.lang.*;
public class InteractionImpl implements
Interaction {
Connection con = null;
public InteractionImpl(Connection con) {
System.out.println("InteractionImpl::
Constructor called wth a connection of
class " + con.getClass().toString());
this.con = con;
}
public javax.resource.cci.Connection
getConnection() {
return con;
}
public void close() throws ResourceException {
con = null;
}
public boolean execute (InteractionSpec ispec,
Record input, Record output)
throws ResourceException {
if(!(input instanceof MappedRecord) ||
!(output instanceof MappedRecord))
throw new ResourceException("Both input and
output records must be Mapped Records.");
output = exec((MappedRecord)input,(
MappedRecord)output);
if (output != null) {
return true;
} else {
return false;
}
}
public Record execute (InteractionSpec ispec,
Record input)
throws ResourceException {
if(!(input instanceof MappedRecord))
throw new ResourceException(
"Input record must be a Mapped Record.");
MappedRecord output = new MappedRecordImpl();
return exec((MappedRecord)input,output);
}
public ResourceWarning getWarnings()
throws ResourceException {
return null;
}
public void clearWarnings()
throws ResourceException {
}
Record exec(MappedRecord input, MappedRecord
output) throws ResourceException {
try
{
System.out.println(
"InteractionImpl::exec called");
Properties props =
((ConnectionImpl)con).getProperties();
Set keys = input.keySet();
Iterator it = keys.iterator();
while (it.hasNext()) {
String key = (String)it.next();
output.put(key,props.get(key));
}
return output;
}
catch(Exception e)
{
throw new ResourceException(e.getMessage());
}
}
}
代码段三:
package adapters.propertiesfile;
import java.util.*;
public class MappedRecordImpl implements
javax.resource.cci.MappedRecord {
private String recordName;
private String description;
private HashMap mappedRecord;
public MappedRecordImpl() {
mappedRecord= new HashMap();
}
public MappedRecordImpl (String name) {
mappedRecord = new HashMap();
recordName = name;
}
public String getRecordName() {
return recordName;
}
public void setRecordName(String name) {
recordName = name;
}
public String getRecordShortDescription() {
return description;
}
public void setRecordShortDescription(
String description) {
description = description;
}
public boolean equals(Object other) {
if(!(other instanceof MappedRecordImpl))
return false;
MappedRecordImpl m = (MappedRecordImpl)other;
return (recordName == m.recordName) &&
mappedRecord.equals(m.mappedRecord);
}
public int hashCode() {
String result = "" + recordName;
return result.hashCode();
}
public Object clone() throws
CloneNotSupportedException {
return this.clone();
}
public void clear()
{
mappedRecord.clear();
}
public boolean containsKey(Object key)
{
return mappedRecord.containsKey(key);
}
public boolean containsValue(Object value)
{
return mappedRecord.containsValue(value);
}
public Set entrySet()
{
return mappedRecord.entrySet();
}
public Object get(Object o)
{
return mappedRecord.get(o);
}
public boolean isEmpty()
{
return mappedRecord.isEmpty();
}
public Set keySet()
{
return mappedRecord.keySet();
}
public Object put(Object key, Object value)
{
return mappedRecord.put(key,value);
}
public void putAll(Map c)
{
mappedRecord.putAll(c);
}
public Object remove(Object o)
{
return mappedRecord.remove(o);
}
public int size()
{
return mappedRecord.size();
}
public Collection values()
{
return mappedRecord.values();
}
}