Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)-----(转)

SOAP(Simple Object Access Protocol,简单对象访问协议) 是一种基于XML的,用于计算机之间交换信息的协议。SOAP能应用于各种消息接发系统,并能通过各种传输协议进行消息传递,但最初的侧重点是通过HTTP传输的远程过程调用。SOAP是Web service的一个重要组成部份,如果把Web service比喻成Internet,那么SOAP就可以比喻成TCP/IP。SOAP是一种协议而非具体产品,微软也有自己的SOAP实现产品,而Java下比较流行的SOAP实现产品就是Apache SOAP,不过它的下一个版本已经改名成AXIS了。

SOAP是用XML文件来做为数据转输的载体的,走HTTP的线路。一般企业的防火墙都开放HTTP的80端口,所以SOAP不会被防火墙阻断,这算是SOAP的一个优点。信息转输的双方都要求支持SOAP服务,因为XML文件发过去,则对方需要有SOAP服务来接收,然后对方会有反馈也是XML文件,这时你也需要安装SOAP服务来接收。

1.环境配置

为了运行程序,我们首先必须配置好环境:共要下载四个软件包,它们都是开源免费的。其中,前两个是Apache的,后两个是SUN网站的,如下所示:(未测试是否)

soap:http://apache.freelamp.com/ws/soap/version-2.3.1/

Xerces:http://xml.apache.org/dist/xerces-j/

JavaMail: http://java.sun.com/products/javamail/downloads/index.html

JAF:http://java.sun.com/products/javabeans/glasgow/jaf.html

下载后将它们分别解压缩。分别在这四个包的解压目录中找到:xerces.jar、soap.jar、mail.jar、activation.jar(JAF的),则是四个jar文件是我们所需要的。

本机安装环境:WindowsXP(SP2) + JDK1.4.2_06 + Tomcat5.0.28 + SOAP2.3.1

配置步骤:

1、安装JDK和Tomcat。过程比较简单,这里不再详述。

2、将刚才所找到的四个jar文件复制到Tomcat的“Tomcat 5.0\common\lib”目录下,这个目录是Tomcat的默认包目录,在这个目录中的所有包在Tomcat启动时都会被自动加载。

3、将\JDK1.4.2\lib\路径下的tools.jar也复制到Tomcat的“Tomcat 5.0\common\lib”目录下。

4、将soap解压目录的webapps目录下的soap.war文件,复制到Tomcat的“Tomcat 5.0\webapps”目录下,这个目录是Tomcat的WEB应用所在目录。重新启动Tomcat,Tomcat会自动将其解压,并配置好路径。可以尝试在浏览器中输入“http://localhost:8080/soap/”, 看SOAP是否安装好。

5、注意在编写程序时,需要将所得到的四个jar文件路径设置到所使用的Java编程环境中,因为在程序中需要用到其中的类文件。具体步骤略。

6、重启Tomcat服务。这时Tomcat会将“Tomcat 5.0\common\lib”目录下新加入的包加载到内存中。到此,我们已经配置好了程序运行所需要的环境

2.基于SOAP的XML文档网络传输

SOAP规范主要定义了四个元素:SOAP信封规范,传输和协议绑定,编码规则和一个RPC协定。用于实现消息的网络传输。
1、SOAP信封规范,SOAP信封规范对计算机间传递的数据如何封装定义了具体的规则。这包括应用特定的数据,如要调用的方法名,方法参数和返回值;还包括谁将处理封装内容,失败时如何编码错误消息等信息。
2、数据编码规则,为了交换数据,计算机必须在编码特定数据类型的规则上达成一致,SOAP也有自己的一套编码数据类型的约定。大部分约定都基于W3C XML Schema规范。
3、RPC协定,SOAP能用于单向和双向等各种消息接发系统。SOAP为双向消息接发定义了一个简单的协定来进行远程过程调用和响应,这使得客户端应用可以指定远程方法名,获取任意多个参数并接受来自服务器的响应。
4、传输和协议绑定,提供了更底层协议传输SOAP封套的一套通用机制。

而以上四个部分统称为一个SOAP消息。我们先来看一篇XML文档是如何变成SOAP的。采用一个简单的购物订单文件PO.xml 。内容为:

<? xml version="1.0" encoding="UTF-8" ?>
< PurchaseOrder  xmlns ="urn:oreilly-jaws-samples" >
    
< shipTo  country ="US" >
        
< name > Joe Smith </ name >
        
< street > 14 Oak Park </ street >
        
< city > Bedford </ city >
        
< state > MA </ state >
        
< zip > 01730 </ zip >
    
</ shipTo >   
    
< items >
        
< item  partNum ="872-AA" >
            
< productName > Candy Canes </ productName >
            
< quantity > 444 </ quantity >
            
< price > 1.68 </ price >
            
< comment > I want candy! </ comment >
        
</ item >
    
</ items >
</ PurchaseOrder >

其对应的SOAP消息为:

POST /ServletTemp/HTTPReceive HTTP/1.0
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: 939
SOAPAction: "urn:oreilly-jaws-samples"

<? xml version=1.0 encoding=UTF-8 ?>
< SOAP-ENV:Envelope
xmlns:SOAP-ENV ="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema" >
< SOAP-ENV:Header >
< jaws:MessageHeader  xmlns:jaws ="urn:oreilly-jaws-samples"  SOAP-ENV:mustUnderstand ="1"  xmlns:SOAP-ENV ="urn:oreilly-jaws-samples" >< From > Me </ From >< To > You </ To >< MessageId > 9999 </ MessageId ></ jaws:MessageHeader >
</ SOAP-ENV:Header >
< SOAP-ENV:Body >
< PurchaseOrder  xmlns ="urn:oreilly-jaws-samples" >
    
< shipTo  country ="US" >
        
< name > Joe Smith </ name >
        
< street > 14 Oak Park </ street >
        
< city > Bedford </ city >
        
< state > MA </ state >
        
< zip > 01730 </ zip >
    
</ shipTo >
    
< items >
        
< item  partNum ="872-AA" >
            
< productName > Candy Canes </ productName >
            
< quantity > 444 </ quantity >
            
< price > 1.68 </ price >
            
< comment > I want candy! </ comment >
        
</ item >
    
</ items >
</ PurchaseOrder >
</ SOAP-ENV:Body >
</ SOAP-ENV:Envelope >

 一个SOAP消息包括:SOAP封套,SOAP头(可选),SOAP主体。

我们首先将XML文档包装到一个SOAP体中,然后再把SOAP体包装到一个SOAP封套中,可以在封套中再添加一个SOAP头(不是必须),最后将SOAP封套绑定到一个协议中。我们来仔细分析一下代码。

SOAP封套

SOAP封套的声明在XML标签的最外层,它表明了一个SOAP文档的边界。下面的封套标签显示了三个必要的属性,这些属性指明了封套中使用的名字空间和语法。

< SOAP-ENV:Envelope
xmlns:SOAP-ENV ="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd ="http://www.w3.org/2001/XMLSchema" >
 … …
</ SOAP-ENV:Envelope >

其中第一个属性:xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 是一个名字空间声明,防止多个XML文件组合时发生标签名字的冲突。第二个属性声明了XML模式实例的名字空间。前缀xsi必须放在这个名字空间定义的所有元素和属性之前。最后一个属性是另外一个名字空间声明,它定义了XML Schema名字空间,这个名字空间下的元素用来指定xsi:type 属性的值(如xsd:string)。

SOAP头

SOAP头和体在语法上非常类似。SOAP1.1和SOAP1.2都没有头里应该有些什么,它就是简单的存放一些指令,提供给接收消息的SOAP处理器。建立在SOAP之上的更高级协议(比如ebXML消息服务)就通过定义一些特殊元素来规范SOAP头的格式。另外当对RPC使用SOAP时,头部也可以用来表示一些底层信息。

< SOAP-ENV:Header >
< jaws:MessageHeader  xmlns:jaws ="urn:oreilly-jaws-samples"  SOAP-ENV:mustUnderstand ="1"  xmlns:SOAP-ENV ="urn:oreilly-jaws-samples" >< From > Me </ From >< To > You </ To >< MessageId > 9999 </ MessageId ></ jaws:MessageHeader >
</ SOAP-ENV:Header >
SOAP主体

SOAP主体用来存放实际数据或消息的有效负载(本例中为XML文档),从而提供给最终的接受者使用或处理。

SOAP协议绑定

当绑定到一个HTTP协议时,需要在SOAP封套前面添加HTTP头的信息。

POST /ServletTemp/HTTPReceive HTTP/1.0
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: 939
SOAPAction: "urn:oreilly-jaws-samples"

在SOAP1.1中SOAPAction是HTTP绑定所必须的部分,它的目的是让那些路由或分派的信息知道该做些什么,即使它们根本不知道SOAP或者没有解析SOAP封套的方法。而在SOAP1.2中SOAPAction已变成可选的了。

SOAP消息的发送与接收

我们已经看到了一个SOAP消息的各个组成块,接下来我们要了解消息是怎样被创建的,以及怎样在两个端点之间进行传输的。

SOAP的发送端的代码:

package  javaSoap01;

import  java.io. * ;
import  java.util. * ;

public   class  GenericHTTPSoapClient
{
    private 
static   final  StringDEFAULT_HOST_URL  =   " http://localhost:8080/ServletTemp/HTTPReceive " ;
    
private   static   final  String DEFAULT_DATA_FILENAME    =   " ./PO.xml " ;
    
private   static   final  String URI   =   " urn:oreilly-jaws-samples " ;
 
    
private  String m_hostURL;
    
private  String m_dataFileName;
 
    
public  GenericHTTPSoapClient(String hostURL, String dataFileName)  throws  Exception
    {
        m_hostURL 
=  hostURL;
        m_dataFileName    
=  dataFileName;
 
        System.out.println();
        System.out.println(
" _________________________________________________________ " );
        System.out.println(
" Starting GenericHTTPSoapClient: " );
        System.out.println(
"     host url        =  "   +  m_hostURL);
        System.out.println(
"     data file       =  "   +  m_dataFileName);
        System.out.println(
" ___________________________________________________________ " );
        System.out.println();
    }

    
// 实际的传送工作是由sendSOAPMessage()方法完成的
     public   void  sendSOAPMessage()
    {
        
try
        {  
//  首先读取XML文档,将其解析成DOM树。
            FileReader fr  =   new  FileReader (m_dataFileName);
 
        // 通过调用Apache getXMlDocBuilder()方法得到一个解析器,它返回一个DocumentBuilder对象。
            javax.xml.parsers.DocumentBuilder xdb  = org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder();
 
       // 通过解析器解析文档,得到一个Document对象。
            org.w3c.dom.Document doc  = xdb.parse ( new  org.xml.sax.InputSource (fr));
            
if  (doc  ==   null ) {
                
throw   new  org.apache.soap.SOAPException(org.apache.soap.Constants.FAULT_CODE_CLIENT,  " parsing error " );
            }
 
            
//  create a vector for collecting the header elements
            Vector headerElements  =   new  Vector();
 
            
//  Create a header element in a namespace
            org.w3c.dom.Element headerElement  = doc.createElementNS(URI, " jaws:MessageHeader " );
 
            headerElement.setAttributeNS(URI,
" SOAP-ENV:mustUnderstand " , " 1 " );
 
            
//  Create subnodes within the MessageHeader
            org.w3c.dom.Element ele  =  doc.createElement( " From " );
            org.w3c.dom.Text textNode 
=  doc.createTextNode( " Me " );
            org.w3c.dom.Node tempNode 
=  ele.appendChild(textNode);
            tempNode 
=  headerElement.appendChild(ele);
 
            ele 
=  doc.createElement( " To " );
            textNode 
=  doc.createTextNode( " You " );
            tempNode 
=  ele.appendChild(textNode);
            tempNode 
=  headerElement.appendChild(ele);
 
            ele 
=  doc.createElement( " MessageId " );
            textNode 
=  doc.createTextNode( " 9999 " );
            tempNode 
=  ele.appendChild(textNode);
            tempNode 
=  headerElement.appendChild(ele);
            headerElements.add(headerElement);
            
//  create a vector for collecting the body elements
            Vector bodyElements  =   new  Vector();
            
// 获取顶层DOM元素,放到向量中。顶层节点的下层节点元素的创建和添加工作由DOM解析器负责。
            bodyElements.add(doc.getDocumentElement ());
 
            
// Create the SOAP envelope
            org.apache.soap.Envelope envelope  =   new  org.apache.soap.Envelope();
 
            
// Add the SOAP header element to the envelope
            org.apache.soap.Header header  =   new  org.apache.soap.Header();
            header.setHeaderEntries(headerElements);
            envelope.setHeader(header);
 
            
// Create the SOAP body element
            org.apache.soap.Body body  =   new  org.apache.soap.Body();
            body.setBodyEntries(bodyElements);
 
            
// Add the SOAP body element to the envelope
            envelope.setBody(body);
 
            
//  Build the Message.
            org.apache.soap.messaging.Message msg = new  org.apache.soap.messaging.Message();
 
            msg.send (
new  java.net.URL(m_hostURL), URI, envelope);
            System.out.println(
" Sent SOAP Message with Apache HTTP SOAP Client. " );
 
            
//  从传输中接受响应并将其打印到屏幕上
            System.out.println( " Waiting for response.... " );
            org.apache.soap.transport.SOAPTransport st 
=  msg.getSOAPTransport ();
            BufferedReader br 
=  st.receive ();
            String line 
=  br.readLine();
            
if (line  ==   null )
            {
                System.out.println(
" HTTP POST was successful. \n " );
            }
            
else
            {
               
while  (line  !=   null )
               {   
          System.out.println (line);
          line 
=  br.readLine();
               }
            }
        }
        
catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
public   static   void  main(String args[]) {
        
// 省略… …
    }
}

 在上述程序中,当我们构造好SOAP封套后,它需要被送到一个目的地去。在ApacheSOAP中,有一个Message对象来处理异步的单向传送。Message.send()方法有三个参数:一个URL,用于指明传送地点;一个URI,用于表示SOAPAction头的值;还有一个SOAP封套。SOAPActionURI实际上是绑定HTTP的一部分,用来指定当一个消息到达传送目的地的时候调用哪个函数或服务。
Message接口用于异步单向通信,Message.seng()函数的返回值为void型。但这并不妨碍它在双向同步会话中使用。当这个Message接口是基于一个双向传输协议(例如HTTP)实现的时候,SOAPTransport.receive()方法就能用来接收一个响应。在SOAPTransport是基于HTTP实现的情况下,receive()方法阻塞并等待一个错误(例如一个HTTP请求超时),或者一个正确的返回代码(例如“HTTP 1.0 200 OK”)。

SOAP接收端代码:

public   class  HTTPReceive  extends  HttpServlet
{
    ......
    
// SOAP请求以HTTP POST形式接收
  public   void  doPost(HttpServletRequest request, HttpServletResponse response)
        
throws  IOException, ServletException
    {
        System.out.println(
" Received request. " );
        System.out.println(
" ----------------------- " );
 
        
//  Traverse the HTTP headers and show them on the screen
         for (Enumeration  enum   =  request.getHeaderNames();
          
enum .hasMoreElements(); ){
            String header 
=  (String) enum .nextElement();
            String value  
=  request.getHeader(header);
            System.out.println(
"    "   +  header  +   "  =  "   +  value);
        }
        System.out.println(
" ----------------------- " );
        
if (request.getContentLength()  >   0 )
        {
            
try
            {
                java.io.BufferedReader reader 
=  request.getReader();
 

                
//  获取DocumentBuilder
                javax.xml.parsers.DocumentBuilder xdb  =
                    org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder();
                   
                
//  接下来我们将文件解析为一个DOM树,得到一个Document对象。
                org.w3c.dom.Document doc  =
                    xdb.parse (
new  org.xml.sax.InputSource (reader));
                
if  (doc  ==   null )
                {
                    
//  Error occured
                    System.out.println( " Doc is null! " );
                    
throw   new  org.apache.soap.SOAPException
                        (org.apache.soap.Constants.FAULT_CODE_CLIENT, 
" parsing error " );
                }
                
else
                {
                    
// 在接收端我们已经有了一个发送过来的SOAP封套。SOAP封套是SOAP文档的最外层元素,也是根元素。我们可以遍历这个DOM树从而直接得到封套以及它的子节点。通过调用unmarshall()方法从文件中得到一个Envelope实例。
                    org.apache.soap.Envelope env  =
                        org.apache.soap.Envelope.unmarshall(doc.getDocumentElement());
 

                    
//  现在我们得到了一个封套,我们按照和前面相反的过程来操作它:从Envelope中取得BodyEntrys的向量Vector,然后从向量中取得Body。
                    org.apache.soap.Body body  =  env.getBody();
                    java.util.Vector bodyEntries 
=  body.getBodyEntries();
 

                    java.io.StringWriter writer 
=   new  java.io.StringWriter();
 

                    
for  (java.util.Enumeration e  =  bodyEntries.elements(); e.hasMoreElements();)
                    {
                        org.w3c.dom.Element el 
=  (org.w3c.dom.Element)e.nextElement();
 

             
// 在当前情况下,向量中只有一个条目:<PurchaseOrder>元素。从而我们也就得到了一个DOM对象。这个DOM对象和前面我们为建立PO.xml而建立的DOM对象完全一样。我们调用静态方法DOM2Writer.serializeAsXML()将PurchaseOrder元素及其子元素全部序列化为一个StringWriter对象。                       org.apache.soap.util.xml.DOM2Writer.serializeAsXML((org.w3c.dom.Node)el, writer);
                    }
                    System.out.println(writer.toString());
                }
            }
            
catch (Exception e)
            {
                System.out.println(e);
            }
        }
        System.out.println(
" ____________________________ " );
        response.setContentType(
" text/xml " );  //  Need this to prevent Apache SOAP from gacking
    }
}

 

转载于:https://www.cnblogs.com/lify0407/archive/2011/05/05/2037207.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值