随笔-128  评论-55  文章-5  trackbacks-0

Java实现基于SOAPXML文档网络传输及远程过程调用(RPC)

 

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

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

1. 环境配置

为了运行程序,我们首先必须配置好环境:

共要下载四个软件包,它们都是开源免费的。其中,前两个是Apache的,后两个是SUN网站的,如下所示:

n        SOAPhttp://apache.freelamp.com/ws/soap/version-2.3.1/

n        Xerceshttp://xml.apache.org/dist/xerces-j/  

n        JavaMailhttp://java.sun.com/products/javamail/downloads/index.html 

n        JAFhttp://java.sun.com/products/javabeans/glasgow/jaf.html 

下载后将它们分别解压缩。分别在这四个包的解压目录中找到:xerces.jarsoap.jarmail.jaractivation.jarJAF的),则是四个jar文件是我们所需要的。

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

配置步骤:

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

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”目录下,这个目录是TomcatWEB应用所在目录。重新启动TomcatTomcat会自动将其解压,并配置好路径。可以尝试在浏览器中输入“http://localhost:8080/soap/”, 看SOAP是否安装好。

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

6、重启Tomcat服务。这时Tomcat会将“Tomcat 5.0\common\lib”目录下新加入的包加载到内存中。

到此,我们已经配置好了程序运行所需要的环境。

 

2. 基于SOAPXML文档网络传输

SOAP规范主要定义了四个元素:SOAP信封规范,传输和协议绑定,编码规则和一个RPC协定。用于实现消息的网络传输。

n        SOAP信封规范,SOAP信封规范对计算机间传递的数据如何封装定义了具体的规则。这包括应用特定的数据,如要调用的方法名,方法参数和返回值;还包括谁将处理封装内容,失败时如何编码错误消息等信息。

n        数据编码规则,为了交换数据,计算机必须在编码特定数据类型的规则上达成一致,SOAP也有自己的一套编码数据类型的约定。大部分约定都基于W3C XML Schema规范。

n        RPC协定,SOAP能用于单向和双向等各种消息接发系统。SOAP为双向消息接发定义了一个简单的协定来进行远程过程调用和响应,这使得客户端应用可以指定远程方法名,获取任意多个参数并接受来自服务器的响应。

n        传输和协议绑定,提供了更底层协议传输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封套绑定到一个协议中。我们来仔细分析一下代码。

n       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)。

n       SOAP

    SOAP头和体在语法上非常类似。SOAP1.1SOAP1.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>

 

n       SOAP主体

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

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

n       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

    }

}

n       带附件的SOAP消息

XMLSOAP能够很好的描述数据,但是许多应用程序的数据并不适合XML来描述,比如图像的二进制数据。SWASOAP With Attachments)可以解决这个问题。SWASOAP协议和MIME格式组合到一起,从而使SOAP消息可以包含任意的数据。这个模型和在email中包含附件的方法是一样的。

MIME协议允许在消息中包含任意多个数据块。每个数据块都被一个MIME头分开。在SWA中,整个消息包含多个MIME部分,第一部分(部分0)SOAP封套,剩下的部分(1- n)都是附件。所有这些部分都包装在底层协议中。

构造带附件的SOAPSOAP封套的创建和组装和以前一样,消息的创建议和以前一样。下面给出心田代码,它创建一个MimeBodyPart对象,然后我们从附件中读出文本作为它的内容体(假定附件为一个TXT文本文件)

            //创建消息

            org.apache.soap.messaging.Message msg = new org.apache.soap.messaging.Message();

 

            //添加附件

            if(m_attachment != null)

            {

              BufferedReader attachmentReader =

                new BufferedReader(new FileReader(m_attachment));

              StringBuffer buffer = new StringBuffer();

              for(String line = attachmentReader.readLine(); line != null;

                    line = attachmentReader.readLine())

              {

                buffer.append(line);

              }

              MimeBodyPart attachment = new MimeBodyPart();

              attachment.setText(buffer.toString());

接下来,我们需要让接收端能够引用附件。为了使接收端能够分析这个消息,我们在XML文档中添加了一个元素,它用the-attachment作为href的值。我们用这个值作为附件的content-id.

            attachment.setHeader("Content-ID", "the-attachment");

最后,我们在消息中添加附件部分然后发送。Apache SOAP会知道你在消息中添加了附件,并且会正确的转化为消息格式:

msg.addBodyPart(attachment);

            }

            msg.send (new java.net.URL(m_hostURL), URI, envelope);

            System.out.println("Sent SOAP Message with Apache HTTP SOAP Client.");

接收带附件的SOAP

//处理SOAP

org.apache.soap.Body body = requestEnvelope.getBody();

    java.util.Vector bodyEntries = body.getBodyEntries();

   

    writer.write("\nBody====>\n");

    for (java.util.Enumeration e = bodyEntries.elements(); e.hasMoreElements();)

    {

        org.w3c.dom.Element el = (org.w3c.dom.Element)e.nextElement();

        org.apache.soap.util.xml.DOM2Writer.serializeAsXML((org.w3c.dom.Node)el, writer);

我们通过XML文档中的<attachment href=cid:the-attachment/>元素来查找附件。首先我们通过名字查找〈attachment>元素。然后我们取出href属性中的content-id的值。一旦我们得到了id,就能够使用SOAPContext对象中的getBodyPart()方法,通过content-id来查找MIME附件。

org.w3c.dom.Element attachmentEl = (org.w3c.dom.Element)el.getElementsByTagName("attachment").item(0);

        if (attachmentEl != null)

        {

            writer.write("\nAttachment==>\n");

            cid = attachmentEl.getAttribute("href").substring(4);//get rid of cid:

            writer.write("Content-ID = "+cid+"\n");

            MimeBodyPart attachment = requestContext.getBodyPart(cid);

            try

            {

                writer.write("The attachment is...\n"+attachment.getContent()+"\n");

            }catch(Exception ex)

            {

                throw new SOAPException(Constants.FAULT_CODE_SERVER,

                    "Error writing response", ex);

            }

        }else

            writer.write("The Content-ID is null!\n");

    }

System.out.println(writer.toString());

3. 基于SOAP的远程过程调用(RPC)

    SOAP-RPC使用SOAP底层结构定义了一个用来表示RPC以及RPC响应的模型。它并不要求一定要紧紧地绑定一个同步的请求/响应模型或者一个HTTP协议。实际上SOAP1.11.2规范都明确声明了SOAP-RPC的使用和协议的绑定是无关的。规范承认,当SOAP-RPC绑定到HTTP时,RPC调用就自动和HTTP请求相匹配,但是这个匹配纯粹是偶然的。因此真正重要的是SOAP定义了一个统一的模型,来表示RPC及其一个或多个返回值。RPC调用的基本要求是,体元素包含方法名和参数,并且参数可以通过存取方法来访问。SOAP还提供了对方法签名,头数据和代表目的地的URI进行编码的方法。

我们先来看一个SOAP-RPC的发送端内容:

POST /soap/servlet/rpcrouter HTTP/1.0

Host: localhost:5555

Content-Type: text/xml; charset=utf-8

Content-Length: 678

SOAPAction: ""

 

<?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:Body>

<ns1:getPriceList

xmlns:ns1="urn:examples:pricelistservice" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<sku xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/"

xsi:type="ns2:Array" ns2:arrayType="xsd:string[3]">

<item xsi:type="xsd:string">A350001</item>

<item xsi:type="xsd:string">A350002</item>

<item xsi:type="xsd:string">A350005</item>

</sku>

</ns1:getPriceList>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

可以看到,在封套体内包含了方法名以及过程调用所需的参数。如在这个SOAP消息中,

<ns1:getPriceList>是自动产生的标签,它代表方法名。<sku>参数用xsi:type="ns2:Array"类型表示。

n       SOAP-Encoding属性

SOAP编码是一套规则,说明线路上传输的数据类型如何进行编码或者序列化。在上面这个消息中,encodingStyle属性值设为"http://schemas.xmlsoap.org/soap/encoding/"这个特定的URL定义了基于SOAP1.1模式的编码规则。SOAP编码包括了序列化任何数据类型的规则,从简单的标量类型到复杂的数据类型。

n       SOAP-RPC方法签名

方法签名只是简单的声明〈body〉元素包含一个SOAP结构。结构中的每一个元素都可被一个存取方法所引用。在SOAP中,带有存取方法的元素能够直接用命名的标签或者一个顺序值来标识。如果有多个参数,它们必须和所调用的方法中的参数列表的顺序一致,类型也必须匹配。响应的规则也是类似的。响应端返回的内容为:

HTTP/1.1 200 OK

Set-Cookie: JSESSIONID=AAD5E6B20535EDACC7637062DF663979; Path=/soap

Content-Type: text/xml; charset=utf-8

Content-Length: 696

Date: Wed, 04 May 2005 14:01:35 GMT

Server: Apache-Coyote/1.1

Connection: close

 

<?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:Body>

<ns1:getPriceListResponse

xmlns:ns1="urn:examples:pricelistservice" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<return xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/"

xsi:type="ns2:Array" ns2:arrayType="xsd:double[3]">

<item xsi:type="xsd:double">54.99</item>

<item xsi:type="xsd:double">19.99</item>

<item xsi:type="xsd:double">36.24</item>

</return>

</ns1:getPriceListResponse>

 

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

 

n       SOAP-RPC的发送和接收

下面是一个SOAP-RPC发送端的代码:

public double[] getPriceList (String skus[])

    throws SOAPException, MalformedURLException {

    Parameter skuParam;

 

    //建立调用

    Call call = new Call ();

    // 服务使用标准的SOAP编码

    call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

    // 设置目标URI

call.setTargetObjectURI ("urn:examples:pricelistservice");

//设置调用方法的名称

    call.setMethodName ("getPriceList");

    //创建参数对象

    Vector paramList = new Vector ();

    Parameter param = new Parameter("sku", String[].class,

        skus, Constants.NS_URI_SOAP_ENC);

paramList.addElement (param);

//设置参数

    call.setParams (paramList);

    //设置Web服务的URL

    URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");

    //调用Web服务

    Response resp = call.invoke (url, null);

 

    //查看响应

    if (!resp.generatedFault()) {

      //返回结果值

      Parameter result = resp.getReturnValue ();

      double priceList[] = (double []) result.getValue();

      return priceList;

    }

    //查看错误

    else {

      Fault f = resp.getFault();

      String faultCode = f.getFaultCode();

      String faultString = f.getFaultString();

      System.err.println("Fault Occurred (details follow):");

      System.err.println("Fault Code:  "+faultCode);

      System.err.println("Fault String:  "+faultString);

      return null;

    }

  }

 

public static void main (String[] args) {

    System.out.println ("Price List Checker:  SOAP Client");

    String skus[] = new String [args.length];

    for (int i=0; i<args.length; i++)

      skus[i] = new String (args[i]);

    PriceListClient priceListClient = new PriceListClient();

    try {

        //进行方法调用并返回结果

      double price[] = priceListClient.getPriceList(skus);

      for (int i=0; i<price.length; i++) {

        System.out.print ("SKU:  "+skus[i]);

        System.out.println (" --> "+price[i]);

      }

    } catch (SOAPException e) {

      System.err.println (e);

    } catch (MalformedURLException e) {

      System.err.println (e);

    }

  }

在上面的客户程序中,设置好参数后,call对象的invoke()方法会调用这项服务。这个RPC调用是同步的。即invoke()方法阻塞,直到返回一个响应,当接收到一个响应时,首先检查调用是否产生了一个SOAP Fault。如果调用成功,就从响应对象中取出返回值。

下面是SOAP-RPC的接收端代码:

public class PriceListService {

  protected Hashtable products;   // Product "Database"

 

  public PriceListService () {

    products = new Hashtable();

    //  Red Hat Linux

    products.put("A350001", new Double (54.99));

    //  McAfee PGP Personal Privacy

    products.put("A350002", new Double (19.99));

    products.put("A350003", new Double (13.99));

    products.put("A350004", new Double (15.21));

    products.put("A350005", new Double (36.24));

    products.put("A350006", new Double (104.25));

    products.put("A350007", new Double (56.23));

    products.put("A350008", new Double (15.23));

    products.put("A350009", new Double (58.23));

  }

 

  public double[] getPriceList (String sku[]) {

    double prices[] = new double [sku.length];

    for (int i=0; i<sku.length; i++) {

      Double price = (Double) products.get(sku[i]);

      prices[i] = price.doubleValue();

    }

    return prices;

  }

}

这个程序非常简单,在此不再解释。

另外我们还需要一个部署描述文件,用来向服务器注册此服务。

<?xml version="1.0"?>

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" id="urn:examples:pricelistservice" checkMustUnderstands="false">

    <isd:provider type="java" scope="Request" methods="getPriceList">

        <isd:java class="com.ecerami.soap.PriceListService" static="false"/>

    </isd:provider>

</isd:service>

在命令行下运行Java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter deploy PriceListService.xml 即可成功注册此服务。

 

通过以上的介绍,我们可以学习和掌握到SOAP的两种常用模型:即文档交换模型(两个端点交换数据的默认方法)和RPC调用模型(把多路单向异步消息组合成一个请求/应答方式的一种特殊情况)。

 



Author: orangelizq
email: orangelizq@163.com

欢迎大家访问我的个人网站 萌萌的IT人
posted on 2007-06-04 14:32 桔子汁 阅读(21999) 评论(2)  编辑  收藏 所属分类: Java技术

评论:
# re: 用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC) 2013-01-25 15:58 | xiao东
call.setTargetObjectURI ("http://WebBooking.com/");---这个的URL地址是那里的地址,我没看懂,好像1.1-1.2的找不到这个地址。  回复  更多评论
  
# re: 用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)[未登录] 2014-08-26 10:28 | Michael
接受SOAP端代码中,Enumeration e = bodyEntries.elements();
//如果当前情况下,向量中有更多条目,怎么选择想要的条目  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: