在TUXEDO 8.0中,客户机和服务器之间可以使用XML缓冲区进行数据交换,但由于8.0版本没有集成XML Parser,所以对XML的支持是有限的。从8.1版本开始,TUXEDO集成了Apache Xerces C++ Parser 1.7,这样服务器端就可以直接分析XML文档了,而不用再去调用其它XML Parser。 TUXEDO 8.1的samples中给了一个xmlstockapp例子,由于它太复杂(至少我这么认为),因此,本文将通过一个简单实用的例子来介绍XML缓冲区使用。
一.XML缓冲区的分配和传输 XML缓冲区支持的最大长度是4GB,分配方法与CARRAY缓冲区一样,需要指定长度。客户端需要打开一个XML文档,以字符方式读取文档内容,保存到缓冲区中,然后由tpcall()调用提交给服务进程。清单1-1是通信录应用程序的XML客户机代码段。
清单1-1 XML缓冲区客户机代码段,文件名:XML_cli.c #define XMLDOCSIZE 1048576 ... ... FILE *xml_fd; char *xml_buffer = NULL; if ((xml_fd = fopen("friends.xml", "r")) != NULL) { xml_buffer = (char *)malloc(sizeof(char) * XMLDOCSIZE); nsize = fread(xml_buffer, sizeof(char), XMLDOCSIZE, xml_fd); } sendlen = nsize + 1; /* 实际读取的长度 */ if((sendbuf = (char *) tpalloc((char *)"XML", NULL, sendlen)) == NULL) { fprintf(stderr,"Error allocating send buffer, tperrno=%ld\n",tperrno); tpterm(); return(1); } strncpy(sendbuf, xml_buffer, nsize); ret = tpcall((char *)"ADD_FRIEND", (char *)sendbuf, sendlen, (char **)&rcvbuf, &rcvlen, TPNOTIME); ... ...
复制到缓冲区的XML文件必须遵循1.0标准,可以包含任何自定义的标记。本例中用到了friends.xml,这个文件包含了两条要添加到数据库好友信息,内容如清单1-2所示。
清单1-2 XML文件示例,文件名:friends.xml <?xml version='1.0' encoding='UTF-8'?> <friends> <friend> <friend_id>1</friend_id> <fname>JQ</fname> <fmobile>13910793488</fmobile> </friend> <friend> <friend_id>2</friend_id> <fname>Snna</fname> <fmobile>13663129935</fmobile> </friend> </friends>
二.基于XML标记的DDR XML缓冲区支持基于标记值的DDR,对于通信录应用程序来说,如果我们要把friend_id取值在1-10范围内的记录保存在table1中,把取值在11-20范围内的记录保存在table2中,把其它取值范围的记录保存在table3中,则可以在ubb配置文件中定义下面的路由标准来实现:
*ROUTING symbol FIELD="friends/friend/ friend_id" BUFTYPE="XML" FIELDTYPE=LONG RANGES="1-10:GROUP1,11-20:GROUP2,*:GROUP3"
XML缓冲区的路由字段还可以是STRING类型的,这时RANGES的取值必须用单引号引起来,请看如下代码段:
*ROUTING symbol FIELD="stockquotes/stock_quote/symbol" BUFTYPE="XML" FIELDTYPE=STRING RANGES="'BEAS'-'BEAS':GROUP1, 'MSFT'-'MSFT':GROUP2"
三.XML缓冲区的分析 服务器端收到XML缓冲区后,需要对它进行分析,从中取出标记值。Tuxedo 8.1集成了Apache Xerces C++ Parser 1.7,并提供了两种类型的分析器:SAX Parser和DOM Parser,服务器端通常使用DOM Parser,客户端通常使用SAX Parser。使用Xerces C++ Parser的步骤一般是: · 初始化XMLC42系统; XMLPlatformUtils::Initialize(); · 根据XML缓冲区创建MemBufferInputSource; MemBufInputSource* memBufIS = new MemBufInputSource( (const XMLByte*)xmlbuf, strlen(xmlbuf), bufId, false); · 创建SAX Parser或DOM Parser; DOMParser *parser = new DOMParser; SAXPArser *parser = new SAXParser; · 对于SAX Parser,需要为文档和错误处理设置回调函数; SAXPrintHandlers handler; parser->setDocumentHandler(&handler); parser->setErrorHandler(&handler); · 调用Xerces C++ Parser分析XML缓冲区; parse->parse(*memBufIS); · 删除Parser和MemBufferInputSource; delete parser; delete memBufIS; · 退出XMLC42系统; XMLPlatformUtils::Terminate(); 对于通信录应用程序来说,客户机给服务器传递了friends.xml文件,服务器端创建了一个DOM Parser对它进行分析,并把结果存入数据库,代码如清单1-3所示。
清单1-3 XML缓冲区的服务器代码,文件名:XML_serv.pc #include <xercesc/parsers/DOMParser.hpp> #include <xercesc/util/PlatformUtils.hpp> #include <xercesc/framework/MemBufInputSource.hpp> #include <atmi.h> #include <userlog.h> #include <stdlib.h> #include <string.h>
char *localbuf=NULL; static const char* bufId = "mybuf";
EXEC SQL begin declare section; static long friend_id; static char fname[10]; static char fmobile[14]; EXEC SQL end declare section;
EXEC SQL INCLUDE sqlca;
void TopTree( DOM_Node node); void SubTree( DOM_Node node); void parseXMLBuffer(char** xmlbuf);
#ifdef __cplusplus extern "C" #endif void #if defined(__STDC__) || defined(__cplusplus) ADD_FRIEND(TPSVCINFO *rqst) #else ADD_FRIEND(rqst) TPSVCINFO *rqst; #endif { parseXMLBuffer(&rqstàdata); tpreturn(TPSUCCESS, 0, xmlbuf, rqstàlen, 0); } void parseXMLBuffer(char** xmlbuf) { int errorCount ; localbuf = (char *)malloc(sizeof(char *) * 2048); XMLPlatformUtils::Initialize();
MemBufInputSource* memBufIS = new MemBufInputSource( (const XMLByte*)*xmlbuf, strlen(*xmlbuf)-1, bufId, true);
DOMParser *parser = new DOMParser; parseràparse(*memBufIS); if ((errorCount = parseràgetErrorCount()) == 0) { DOM_Document document = parseràgetDocument(); DOM_Element topLevel = document.getDocumentElement(); TopTree(topLevel); } delete parser; delete memBufIS; XMLPlatformUtils::Terminate(); } void TopTree( DOM_Node node){ if (node.getNodeType() == DOM_Node::ELEMENT_NODE) { if (node.getNodeName().equals ("friend")) SubTree(node); else { DOM_NodeList children = node.getChildNodes(); for (int i=0; i<children.getLength(); i++) TopTree(children.item(i)); } } } void SubTree( DOM_Node node)\ { DOM_NodeList children = node.getChildNodes(); for (int i=0; i<children.getLength(); i++) { DOM_Node nod = children.item(i); if(nod.getNodeType()==DOM_Node::ELEMENT_NODE) { if(nod.getNodeName().equals("friend_id")) friend_id = atol(nod.getFirstChild().getNodeValue().transcode()); else if(nod.getNodeName().equals("fname")) strcpy(fname, nod.getFirstChild().getNodeValue().transcode()); else if(nod.getNodeName().equals("fmobile")) strcpy(fmobile, nod.getFirstChild().getNodeValue().transcode()); } } EXEC SQL insert into FRIEND (FRIEND_ID,NAME,MOBILE) values (:friend_id, :fname, :fmobile);
if (SQLCODE != SQL_OK) userlog("Cannot insert into FRIEND"); }
注意:服务器文件名为XML_serv.pc,先要使用esqlc命令对它作预编译,生成C源程序,然后把扩展名改为cpp,才能用buildserver来编译,原因是Xerces C++ Parser只提供了C++的编程接口。读者可以参考如下命令来编译: proc iname=XML_serv.pc oname=XML_serv.cpp buildserver -v -s ADD_FRIEND -f XML_serv.cpp -o XML_serv -f %TUXDIR%\lib\libtxml.lib
四.UBBConfigNT配置文件的编写 这个例子使用了Oracle8i数据库来测试,Tuxedo的配置文件如清单1-4所示。
清单1-4 Tuxedo的配置文件,文件名:UBBConfigNT *RESOURCES IPCKEY 123456 DOMAINID simpapp MASTER SITE1 MAXACCESSERS 10 MAXSERVERS 5 MAXSERVICES 10 MODEL SHM LDBAL N
*MACHINES DEFAULT: APPDIR="C:\TuxDAP\solutions\xml" TUXCONFIG="C:\TuxDAP\solutions\xml\tuxconfig" TUXDIR="G:\bea\tuxedo" TLOGNAME=TLOG TLOGDEVICE="C:\TuxDAP\solutions\xml\TLOG"
JQ LMID=SITE1
*GROUPS DEFAULT: LMID=SITE1
GROUP1 LMID=simple GRPNO=1 OPENINFO="Oracle_XA:Oracle_XA+Acc=P/scott/tiger+SqlNet=oradb+SesTm=120+ MaxCur=5+LogDir=." TMSNAME="TMS_ORA8i" TMSCOUNT=2
*SERVERS DEFAULT: RESTART=Y MAXGEN=5 REPLYQ=Y CLOPT="-A" XML_serv SRVGRP=GROUP1 SRVID=10
*SERVICES ADD_FRIEND
*ROUTING
五.运行测试 执行XML_Cli.exe进行测试,friends.xml文件已经通过XML缓冲区成功地发送到服务器端; 打开SQL*PLUS,查看一下FRIEND表的内容,发现里面已经插入了两条记录,这两条记录的数据就是通过friends.xml文件来传输的!
|