如今似乎任何的软件开发都离不开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) 
    { 
        …… 
    } 
  } 
   
 
  |