如今似乎任何的软件开发都离不开xml技术支持,在图形图像、数据库、加密安全、软件工程、网络教育、电子商务、语音技术上都有xml施展拳脚的地方,xml应用大潮已经来临。
xml工作小组创始会员c.m. sperberg-mcqueen认为:“xml最大的影响在于xml软件大量兴起:xml剖析器、xml程序语言库、xslt处理器、xsl fo处理器、数据库接受xml—不只如此,还有网络浏览器也接受xml。”也正因为如此,ibm、微软、sun、惠普、oracle等大公司纷纷进入这个市场。
在学会了xgen等对象绑定工具后,相信大家已经是跃跃欲试,希望立刻用xgen来实战一下,体验一下xml对象绑定的优势。下面就介绍一下我经常用到的xml应用。
1. xml配置文件
每个系统可能都需要或大或小的配置文件,通过配置文件来初始化系统的参数,好处不用详细介绍了。一般配置文件的格式有以下种:
1. window系统中ini格式文件和java语言中使用的properties文件
2. xml格式的文件
3. 其他格式文件
第一种类型的配置文件是纯文本文件,基本采用“key = value”的格式来记录各种参数,便于手工书写和阅读。
基于xml schema的xml文件易于阅读,并且能非常好的显示各个元素之间的层次关系和约束关系。相对于ini文件格式使用xml格式的配置文件有以下优点:
1.1. 配置具有层次性
1.2. 取值有效性检查
1.3. 支持链表,枚举,复杂数据类型
1.4. 配置文件可以嵌套
1.5. 结合xml spy 等xml编辑工具编辑配置文件十分便捷
1.6. 存在大量第三方的xml对象绑定工具,并且功能强大、开发便捷。如java语言版的xgen、jaxb,c++版工具 xbind
现在就使开始实践使用xml作为程序使用的配置文件吧。
1.1. 设计xsd文件(xml schema)
xgen需要编译的是xsd文件,xsd文件是用来描述指定类型的xml文件的大纲文件,是个纯文本文件。通过本文编辑工具就可以手工创建、编辑xsd文件,但是通过一些xml编辑工具可以事半功倍的完成xsd编写工作。我也用过一些xml编辑工具,但是只有xml spy的功能最强大,并且使用非常方便。
xml spy 的一些特性:
l 在编辑xml、xsd等文件时具有提示输入功能,可以非常方便的选择。
l 同时具有xml文件合法性校验功能,可以判断element值的取值是否符合schema的定义。
l 支持dtd和xsd互转
l 提供xsd的样例xml实例文件功能
l 同样支持java,c++,c#的xml绑定,可以生成java,c++和c#代码,不要太强大哦!
通过xml spy编写alertserver.xsd 文件
<?xml version="1.0"?>
<xsd:schema targetnamespace="urn:com:lianchuang:smartsecurer:alert:config:configfile.xsd" xmlns:xsd="http://www.w3.org/2001/xmlschema" xmlns="urn:com:lianchuang:smartsecurer:alert:config:configfile.xsd" elementformdefault="qualified" attributeformdefault="unqualified" >
<xsd:element name="config" type="conifgtype"/>
<xsd:complextype name="conifgtype">
<xsd:sequence>
<xsd:element ref="globe"/>
</xsd:sequence>
</xsd:complextype>
<xsd:element name="globe" type="globetype"/>
<xsd:complextype name="globetype">
<xsd:sequence>
<xsd:element ref="alertserver"/>
<xsd:element ref="vomanager" maxoccurs="unbounded"/>
</xsd:sequence>
</xsd:complextype>
<xsd:element name="alertserver" type="alertservertype"/>
<xsd:complextype name="alertservertype">
<xsd:sequence>
<xsd:element name="id" type="xsd:int"/>
<xsd:element name="address" type="xsd:string" default="127.0.0.1" minoccurs="0"/>
<xsd:element name="port" type="xsd:int" default="1099" minoccurs="0"/>
<xsd:element name="alertservername" type="xsd:string" minoccurs="0"/>
<xsd:element name="registerinterval" type="xsd:int" default="1000" minoccurs="0"/>
<xsd:element name="cachesize" type="xsd:int" default="10000" minoccurs="0"/>
<xsd:element name="deliverthreadnum" type="xsd:int" default="2" minoccurs="0"/>
<xsd:element ref="dbopermode" default="default" minoccurs="0"/>
<xsd:element name="batchdbinsertsize" type="xsd:int" default="1000" minoccurs="0"/>
<xsd:element name="statisticsinterval" type="xsd:int" default="1000" minoccurs="0"/>
</xsd:sequence>
</xsd:complextype>
<xsd:element name="vomanager" type="vomanagertype"/>
<xsd:complextype name="vomanagertype">
<xsd:sequence>
<xsd:element name="address" type="xsd:string"/>
<xsd:element name="port" type="xsd:int" default="1099" minoccurs="0"/>
</xsd:sequence>
</xsd:complextype>
<xsd:element name="dbopermode">
<xsd:simpletype>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="default"/>
<xsd:enumeration value="native"/>
</xsd:restriction>
</xsd:simpletype>
</xsd:element>
</xsd:schema>
注意 <xsd:schema> 设置
elementformdefault="qualified" attributeformdefault="unqualified" 属性
,否则在unmarshal(inputstream ,valid)方法调用中会有异常。这样设置表示限制局部元素和属性,即对每个局部的元素都要设定前缀。
当将 elementformdefault 设置为 qualified 时,它表示在该语法的实例中,必须使用前缀或通过设置 {默认命名空间} 来显式限定所有元素。unqualified 设置意味着只有全局声明的元素才必须被显式限定,而局部声明的元素不得被限定。在此情形下,限定一个局部声明是错误的。同样,将 attributeformdefault 设置为 qualified 时,必须使用前缀显式限定实例文档中的所有属性。
1.2. 创建java对象
进入xgen安装目录,为了省事将alertserver.xsd文件拷贝到该目录下。执行以下脚本:
xgen alertserver.xsd
在当前目录下,会按照urn 路径生成 com\lianchuang\smartsecurer\alert\config\configfile_xsd 目录,并且新创建的class文件就保存在该目录下。若在xsd中定义了自定义复杂类型数据,则会在 configfile_xsd
目录下创建 \type 目录,并把相关的java class 放在该目录下。
1.3. 编写代码
对于定义xsd文档,每个复杂类型的elementa,会有elementa和elementatypecomplextype类来作为该java类的映射,通过getelementatypecomplextype()方法直接可以获取获取elementa的复杂对象类型的引用。
如在xsd文件中的globe,可以通过xxx.getconifgtypecomplextype().getglobe()来获取该java对象,通过config.getconifgtypecomplextype().getglobe().getglobetypecomplextype()来获取实际的该类型的对象。
对于一些基本类型element。xgen定义了一些x开头的类与之对应,如int,用xint表示。可以通过new xint(int i)来构造xint对象。
// 下面是读取指定的配置文件,返回 java 对象的代码
public static synchronized config getconfig(string filename) throws
filenotfoundexception
{
// 创建inputstream对象
file file = new file(filename);
fileinputstream ins = new fileinputstream(file);
chainedentityresolver er = new chainedentityresolver();
unmarshaller un = new unmarshaller(er);
globalelement ge = null;
try
{
ge = un.unmarshal(ins, false); // 注意,由于本人对xml和xsd也是一知半解,unmarshal的方法尝试了一遍,只有使用这个方法,才可以正常转化xml文件的对象。不知道为什么…
if (ge != null && ge instanceof config)
configinstance = (config) ge;
}
catch (validationexception ex)
{
m_log.error(ex);
}
catch (marshalexception ex)
{
m_log.error(ex);
}
catch (ioexception ex)
{
m_log.error(ex);
}
return configinstance;
}
// 下面是打印对象各个属性参数的样例代码
public static void printconfig(config config)
{
if (config == null)
return;
m_log.debug("================ alertserver parameters ==================");
m_log.debug("id = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getalertserver().
getalertservertypecomplextype().getid());
m_log.debug("address = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().
getalertserver().getalertservertypecomplextype().getaddress());
m_log.debug("port = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().
getalertserver().getalertservertypecomplextype().getport());
m_log.debug("alertservername = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getalertserver().
getalertservertypecomplextype().getalertservername());
m_log.debug("registerinterval = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getalertserver().
getalertservertypecomplextype().getregisterinterval());
m_log.debug("deliverthreadnum = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().
getalertserver().getalertservertypecomplextype().
getdeliverthreadnum());
m_log.debug("cachesize = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getalertserver().
getalertservertypecomplextype().getcachesize());
m_log.debug("batchdbinsertsize = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getalertserver().
getalertservertypecomplextype().getbatchdbinsertsize());
m_log.debug("================= vomanager parameters ==================");
for (int i = 0;
i <
config.getconifgtypecomplextype().getglobe().getglobetypecomplextype().
getvomanagercount();
++i)
{
m_log.debug("vomanger address = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getvomanager()[i].
getvomanagertypecomplextype().getaddress());
m_log.debug("vomanger port = " +
config.getconifgtypecomplextype().getglobe().
getglobetypecomplextype().getvomanager()[i].
getvomanagertypecomplextype().getport());
}
}
2. 利用xml格式的消息通信
编写自定义socket通信程序时,都需要自己定义一套通信协议的规范,尤其是异构的系统。我们可以使用xml文档作为通信协议,结合xml相关的对象绑定工具来将xml格式的报文转换为java、c++等语言的对象。
下面以java语言结合xgen使用样例描述通信的流程。
2.1. 通信流程
l 发送xml对象
通过程序创建相关的对象,并赋值。通过unmarshal方法转化为stringwriter对象,使用stringwriter的getbuffer().tostring()方法返回转化好的字符串。最后,利用socket发送该string。
l 接收xml对象
接收到xml文档后,marshal到java对象即可。
2.2. 具体技术
l 通信中加密
由于通过xml文档进行通信,数据包是明文的文本,对于一些敏感数据需要进行适当的加密。如直接对xmlw文档上进行des然后传递,或者根据情况采用更安全的加密方式。
l 发送xml文档
// udp socket
datagramsocket discoverysocket = null;
// 发送的数据包
datagrampacket dgp = null;
// 将xml对象转化stringwriter对象,即转化为字符串对象
stringwriter sw = new stringwriter();
// xml绑定对象
request req = null;
// 需要发送的字符数组
byte[] sendbytes = null;
……
……
try
{
// 创建xml文档对象
req = xmlobjanalysis.makerequset(m_agentdisc.m_configfilename,
getdiscoverrespseqno(),
xmlobjanalysis.discovery_request);
sw.getbuffer().setlength(0);
// 将请求对象序列化为xml字符串
req.getrequesttypecomplextype().setrequestagentip(new xstring(
agentkey.getrequestip()));
req.marshal(sw);
// 将xml文档通过des加密
sendbytes = getdesbytes(sw.getbuffer().tostring().
getbytes(), true);
if (sendbytes != null)
{
dgp = new datagrampacket(sendbytes, sendbytes.length);
dgp.setaddress(ia);
dgp.setport(agentkey.getport());
m_log.debug("send discovery pdu :" + agentkey.getrequestip() +
":" +
agentkey.getport() + ",size =" +
sendbytes.length);
// m_log.debug(new string(sw.getbuffer()));
discoverysocket.send(dgp);
}
}
catch (exception ex4)
{
m_log.error(ex4);
}
l 接收xml文档
ingram =
new datagrampacket
(inbuffer, inbuffer.length);
try
{
for (; ; )
{
try
{
insock.receive(ingram);
}
catch (ioexception ex)
{
m_log.error
("error reading input socket:"
+ ex.getmessage());
// break;
}
catch (exception ex)
{
ex.printstacktrace();
try
{
thread.sleep(100);
}
catch (interruptedexception ex2)
{
}
// return;
continue;
}
recvbytes = new byte[ingram.getlength()];
system.arraycopy(ingram.getdata(), 0, recvbytes, 0, ingram.getlength());
dealagentmessage(recvbytes, hostname);
}
}
finally
{
if (insock != null)
insock.close();
}
private void dealagentmessage(final byte[] origrecvbytes)
{
basicinfo resp = null;
// 解密xml文档
byte[] recvbytes = m_agentdisc.getdesbytes(origrecvbytes, false);
// 将 encoding 标签改为gb2312,这样可以分析出元素中的中文,否则,无法正确转化为中文
string orig = new string(recvbytes);
orig = orig.replaceall("encoding=\"utf-8\"", "encoding=\"gb2312\"");
// 将字符串转化为 bytearrayinputstream
bytearrayinputstream bais = new bytearrayinputstream(orig.trim().
getbytes());
try
{
// 将xml文档转换为对象
resp = xmlobjanalysis.getresponsefromxmlstream(bais);
}
catch (exception ex1)
{
m_log.error(ex1);
}
string requestagentip = resp.getbasicinfotypecomplextype().
getrequestagentip().get();
if (resp == null)
{
m_log.error("decode reponse error.ip = " + requestagentip);
return;
}
// 判断是否是代理自动发现的响应数据
if (resp.getbasicinfotypecomplextype().getresponse().
getresponsechoicecomplextype().getautodiscoveryaschoice() != null)
{
……
}
else if (resp.getbasicinfotypecomplextype().getresponse().
getresponsechoicecomplextype().gethealthcheckaschoice() != null)
{
……
}
}
