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网站的,如下所示:
nSOAP:http://apache.freelamp.com/ws/soap/version-2.3.1/
n Xerces:http://xml.apache.org/dist/xerces-j/
n JavaMail:http://java.sun.com/products/javamail/downloads/index.html
n 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协定。用于实现消息的网络传输。
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封套绑定到一个协议中。我们来仔细分析一下代码。
SOAP是用XML文件来做为数据转输的载体的,走HTTP的线路。一般企业的防火墙都开放HTTP的80端口,所以SOAP不会被防火墙阻断,这算是SOAP的一个优点。信息转输的双方都要求支持SOAP服务,因为XML文件发过去,则对方需要有SOAP服务来接收,然后对方会有反馈也是XML文件,这时你也需要安装SOAP服务来接收。
1. 环境配置
为了运行程序,我们首先必须配置好环境:
共要下载四个软件包,它们都是开源免费的。其中,前两个是Apache的,后两个是SUN网站的,如下所示:
nSOAP:http://apache.freelamp.com/ws/soap/version-2.3.1/
n
n
n
下载后将它们分别解压缩。分别在这四个包的解压目录中找到: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
3、将 \
4、将soap解压目录的webapps目录下的soap.war文件,复制到Tomcat的“Tomcat 5.0\webapps”目录下,这个目录是Tomcat的WEB应用所在目录。重新启动Tomcat,Tomcat会自动将其解压,并配置好路径。可以尝试在浏览器中输入“http://localhost:8080/soap/”, 看SOAP是否安装好。
5、注意在编写程序时,需要将所得到的四个jar文件路径设置到所使用的Java编程环境中,因为在程序中需要用到其中的类文件。具体步骤略。
6、重启 Tomcat
到此,我们已经配置好了程序运行所需要的环境。
2. 基于SOAP的XML文档网络传输
SOAP规范主要定义了四个元素:SOAP信封规范,传输和协议绑定,编码规则和一个RPC协定。用于实现消息的网络传输。
n
n
n
n
而以上四个部分统称为一个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封套绑定到一个协议中。我们来仔细分析一下代码。
用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC) 二
2010-12-03 13:35:17| 分类: 技术资料 | 标签: |字号大中小 订阅
n
带附件的SOAP消息
XML和SOAP能够很好的描述数据,但是许多应用程序的数据并不适合XML来描述,比如图像的二进制数据。SWA(SOAP With Attachments)可以解决这个问题。SWA把SOAP协议和MIME格式组合到一起,从而使SOAP消息可以包含任意的数据。这个模型和在email中包含附件的方法是一样的。
MIME协议允许在消息中包含任意多个数据块。每个数据块都被一个MIME头分开。在SWA中,整个消息包含多个MIME部分,第一部分(部分0)是SOAP封套,剩下的部分(1- n)都是附件。所有这些部分都包装在底层协议中。
构造带附件的SOAP:SOAP封套的创建和组装和以前一样,消息的创建议和以前一样。下面给出心田代码,它创建一个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.1和1.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>
XML和SOAP能够很好的描述数据,但是许多应用程序的数据并不适合XML来描述,比如图像的二进制数据。SWA(SOAP With Attachments)可以解决这个问题。SWA把SOAP协议和MIME格式组合到一起,从而使SOAP消息可以包含任意的数据。这个模型和在email中包含附件的方法是一样的。
MIME协议允许在消息中包含任意多个数据块。每个数据块都被一个MIME头分开。在SWA中,整个消息包含多个MIME部分,第一部分(部分0)是SOAP封套,剩下的部分(1- n)都是附件。所有这些部分都包装在底层协议中。
构造带附件的SOAP:SOAP封套的创建和组装和以前一样,消息的创建议和以前一样。下面给出心田代码,它创建一个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.1和1.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编码是一套规则,说明线路上传输的数据类型如何进行编码或者序列化。在上面这个消息中,encodingStyle属性值设为"http://schemas.xmlsoap.org/soap/encoding/"。这个特定的URL定义了基于SOAP1.1模式的编码规则。SOAP编码包括了序列化任何数据类型的规则,从简单的标量类型到复杂的数据类型。
n
方法签名只是简单的声明〈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>
用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC) 三
2010-12-03 13:35:02| 分类: 技术资料 | 标签: |字号大中小 订阅
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 = new String (args );
PriceListClient priceListClient = new PriceListClient();
try {
//进行方法调用并返回结果
double price[] = priceListClient.getPriceList(skus);
for (int i=0; i<price.length; i++) {
System.out.print ("SKU: "+skus );
System.out.println (" --> "+price );
}
} 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 );
prices = 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调用模型(把多路单向异步消息组合成一个请求/应答方式的一种特殊情况)。
下面是一个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
PriceListClient priceListClient = new PriceListClient();
try {
//进行方法调用并返回结果
double price[] = priceListClient.getPriceList(skus);
for (int i=0; i<price.length; i++) {
System.out.print ("SKU: "+skus
System.out.println (" --> "+price
}
} 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
prices
}
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调用模型(把多路单向异步消息组合成一个请求/应答方式的一种特殊情况)。