可扩展标记语言(xml)作为一种简单的、中性的、易读的数据表示形式已经变得越来越流行,许多软件厂商宣布的“支持xml",意味着他们的产品将能生成或处理xml数据。xml也被看作再企业间交换数据最佳格式。它允许企业在所交换的数据的xml的文档类型定义(document type definitions,dtds)或模式(schema)上取得一致。这些dtds或schema是独立于企业使用的数据库模式的。
本文将用研究在不同计算机之间通讯与处理xml数据的分布式系统的构建方法,主要是运行在不同的虚拟机上的java应用之间的xml通讯。
xml通讯 万维网协会(world wide web consortium, w3c)在xml规范中定义了xml的语法和语义。为了处理xml数据,xml文档必须经过解析。w3c定义了文档对象模型(dom),它是应用程序员处理xml数据的接口。dom已经有包括java在内的许多语言的实现。java应用程序可以通过dom api来访问xml数据。xml解析器将产生xml文档的dom表示。
图1说明了处理xml文档的java分布式应用的简单模型。这个模型假设数据可以从诸如关系数据库之类的数据源得到。java代码处理数据并最终产生dom表示,这些代码表示为图中的处理器。
处理器代码将dom代表的xml数据传给发送者。发送者是与接收者进行xml数据通讯的java代码。接收者java代码来接受xml数据,产生dom表示的数据并把它传送给另一个处理器。简而言之,发送者和接收者抽象了dom表示的xml数据的通讯。
发送者和接收者不是在同一个java虚拟机上执行的。他们是通过分布式系统的构件来相连的。无论是接收者还是发送者都既是客户端又是服务器端,两者的数据传输都是双向的。
xbeans
就像将要看到的一样,在本文中描述的发送者和接收者的三种实现方法都都是通过xbeans来实现。xbeans是一种接受xml数据作为输入,处理这个输入然后向下一个xbeans输出xml结果的软件构件。xbeans的输入输出都是xml的dom文档,亦即传送给xbeans的不是需要xml解析器解析的字符串,而是通过w3c的标准dom api解析成了文档对象。图2说明了一个xbeans。
xbeans是javabeans,支持封装、重用、连接和客户化java代码。通过适当的一些xbeans和javabeans的设计工具,我们就能编很少的代码构建非常有用的分布式应用。 xbeans从ibm的xml的java开发工具包而来,在其上作了少量修改以便更适合分布式的应用。xbeans能够从www.xbeans.org的开放源码项目中免费获得。
实现发送方和接收方
下面将介绍用java实现发送者和接收者的三种不同的方法。然后对每种方法作一个简单的分析。
方法一:用标准的web 服务器 这种方法将只是简单的将xml作为文本发送给远程计算机上的web服务器。发送方必须将dom表示的xml转化为文本来与接收方进行通讯。然后,接受方必须将文本还原为dom表示,如图3:
以下代码段用http来实现发送者。这里用到了ibm java开发包中的domwriter类来实现dom表示到文本xml表示的转换。
public void documentready(domevent evt) throws xbeansexception { try { url receiver = new url (getremoteurl ()); urlconnection receiverconnection = receiver.openconnection(); receiverconnection.setdooutput(true); //向发送者打开一个输出流然后发送文本形式的xml数据 outputstream out = receiverconnection.getoutputstream(); domwriter writer = new domwriter(); writer.setprintwriter(new printwriter(out)); writer.documentready( new com.ibm.xml.xpk4j.dom.domevent( this,evt.getdocument())); out.close(); // 为结果打开一个输入流 bufferedreader in = new bufferedreader( new inputstreamreader(receiverconnection.getinputstream())); // 处理结果:"ok" 表示成功;"exception" 表示输入流串行化异常 ... in.close(); } catch (throwable e) { e.printstacktrace(system.err); } }
|
注意到以上的documentready()方法用remoteurl属性得到服务器上的cgi脚本的url。为了与http兼容,cgi脚本类用字符串”content-type: text/html"封装接收者的输出。这个脚本然后调用服务器上的the receivermain()方法。 main()函数只是简单的实例化接收者然后调用其receivedocument()方法。
import org.xbeans.communication.stdio.receiver.*; public class receivermain { static bean thereceiver = new bean(); public static void main(string[] args) { thereceiver.receivedocument(); } }
|
最后receivedocument()方法的代码段将重新生成dom表示以便进一步处理。这里用到了ibm的xml解析器。
domparser parser = new domparser(); // 构造解析器 try { // 调用解析器 parser.parse(new inputsource(system.in)); } catch (throwable e) { throw new xbeansexception("","receiver","io error parsing incoming document", "io error parsing incoming document "+e); } //将文档传向下一个bean domlistener.documentready(new domevent(this,parser.getdocument()));
|
方法二:通过java远程方法调用串行化文档 这个方法通过java远程方法调用(java rmi)和dom串行化(serialization)来从发送者向接收者传输xml dom 文档。如图4:
以下代码用java远程方法调用实现发送方与接受方的通讯.
public void documentready(domevent evt) throws xbeansexception { if (domlistener==null) { try { domlistener = (domlistener)naming.lookup(getreceivername()); } catch (exception e) { throw new xbeansexception( evt.getdocument().getnodename(), "sender", "error obtaining remote receiver", "the name may be wrong or the network may be down."); } } domlistener.documentready(evt); }
|
以下是接受方的java 远程方法调用的实现。setname()方法将接受这传送给rmi注册(registry),documentready()方法仅仅将接收到的文档传送给下一个组件。
public void setreceivername(string newname) { try { if (receivername!=null) naming.unbind(receivername); receivername = newname; naming.rebind(receivername, this ); } catch( exception e ) { system.out.println( e ); } } public void documentready(document incomingdocument) throws remoteexception, xbeansexception { if (domlistener==null) { throw new xbeansexception(incomingdocument.getnodename(),"rmireceiver", "next component not established", "the component needs to be configured."); } domlistener.documentready(new domevent(this,incomingdocument)); }
|
方法三:corba-iiop 第三方法用corba-iiop(corba over internet inter-orb protocol)来传输数据。对象管理组织(omg)正在建议扩展接口定义语言(idl)将xml数据类型包括进去。这样,将来corba产品将能传输xml数据。如图5所示:
以下的omg idl给出了发送者和接收者corba实现的接口。
exception remotereceiverexception { string remoteidentifier; string documentname; string componentname; string message; string moremessage; }; typedef sequence bytearray; interface xmlreceiver { void documentready(in bytearray serializeddocument) raises(remotereceiverexception); };
|
以下代码用java串行化dom和corba实现发送者。
public void documentready(domevent evt) throws xbeansexception { document documenttosend = evt.getdocument(); try { bytearrayoutputstream bastream = new bytearrayoutputstream(); objectoutputstream p = new objectoutputstream(bastream); p.writeobject(documenttosend); p.flush(); org.omg.corba.orb orb = org.omg.corba.orb.init( new string[0], system.getproperties()); xmlreceiver receiver = urltoobject(orb,getreceiverurl()); receiver.documentready(bastream.tobytearray()); } catch (remotereceiverexception rre) { throw new xbeansexception(rre.remoteidentifier, rre.documentname, rre.componentname, rre.message,rre.moremessage); } catch (throwable e) { throw new xbeansexception("","sender", "error sending document "+e, "error sending document "+e); } }
|
以下代码用java串行化dom和corba实现接收者。
public void documentready(byte[] serializeddocument)throws remotereceiverexception { // 反串行化字节流 bytearrayinputstream bais = new bytearrayinputstream(serializeddocument); document thedocument; try { objectinputstream ois = new objectinputstream(bais); thedocument = (document)ois.readobject(); } catch(throwable e) { throw new remotereceiverexception(corbaname,"incoming document","receiver", "error deserializing document","error deserializing document"+e); } try { //将文档传向监听者 local.domlistener.documentready(new domevent(this,thedocument)); } catch (xbeansexception xbe) { throw new remotereceiverexception( xbe.remoteidentifier(), xbe.documentname(),xbe.componentname(), xbe.message(),xbe.moremessage()); } }
|
分析:测试表明,纯文本表示的xml要比dom串行化表示性能更好。同时,解析dom和文本所用的时间也要比用java直接串行化和法串行化所用的时间少。
标准的web服务器方式的优势是其应用基础要广泛许多。cgi脚本能够在绝大多数web服务器上运行,而且,接受方能够很容易的通过url标识。而对于rmi,则需要rmi注册。corba的解决办法则需要在服务器上安装对象请求代理(object request broker,orb ),而且,corba发送者的实现使用的是一个url的命名模式而不是接收者的corba对象引用,用一个字符串与一个url相联系,然后在客户端转化。
corba 和rmi支持java 客户端到java服务器的解决方案。没有cgi脚本也不需要从标准输入中读取编码异常。而且,不需要在发送者每次用xml通讯时都启动一个java虚拟机。他们两则均支持接收者的自动激活。
java rmi方式只能在java代码之间工作,对于web服务器包括corba理论上能在任何编程语言之间通讯。对于java串行化的dom来说,即便是客户端和服务器端均需要是java代码的要求不是问题,它还存在另外一个困难,即java串行化要求客户端和服务器运行的是相同的dom实现。
结论 正如上面所述,有许多方法可以实现在java分布式应用中发送xml数据,每一种方法的性能和互操作性都是不同的。重要的是应该把xml通讯从分布式应用逻辑中抽取出来。也就是,实现发送和接受xml的代码应和应用逻辑的代码中分离出来。通过把代码打包成软件组件,就能够改变发送方和接受方的代码而不会影响到应用其余实现。