惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

博客园 - 憋大招的cat

Windows mobile应用程序开发环境搭建(C++) AJAX初探-XMLHTTP与服务端交互的小示例修正 SQL Server维护中Insert语句的生成工具 整理C# 二进制,十进制,十六进制 互转附加(BCC异或校验法) 转一个数据导入导出功能的帖子(转链) 通过控制按键来控制编辑框控件的小BUG - 憋大招的cat - 博客园 近期项目知识点总结之AJAX实用联动实例-(文1) AutoCompleteExtender控件使用示例以及BUG vs2005中利用自动生成数据集、ObjectDataSource、Gridview进行组合查询 SQL Server 2005 中附加只有mdf文件的数据库(修复置疑的数据库) 防止利用Xp_cmdshell进行漏洞攻击 SQL复制数据的强大命令集-BCP知识点之二 SQL复制数据的强大命令集-BCP知识点之一 Windows XP Home Edition安裝 IIS 的方法 收缩数据库出现的问题与解决方案 理解杀毒软件技术的意义 脱壳、虚拟、启发式介绍 WebForm中弹出页面关闭的同时返回值 c#.NET和VB.NET语法的比较 本地计算机上的MSSQLSERVER服务启动后又停止了。一些服务自动停止,如果它们没有什么可做的
Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)-----...
憋大招的cat · 2011-05-05 · via 博客园 - 憋大招的cat

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
    }
}