内容


技巧

使用SAAJ发送和接收SOAP消息

Java API使得手工生成和发送消息时许多必需的步骤变得自动化

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 技巧

敬请期待该系列的后续内容。

此内容是该系列的一部分:技巧

敬请期待该系列的后续内容。

Web 服务的基础是以标准格式发送和接收消息,这样所有系统都可以理解消息。通常情况下,这种标准格式是 SOAP。SOAP 消息可以手工生成和发送,但 SOAP with Attachments API for Java (SAAJ)—— Java API for XML Messaging (JAXM)的一个分支——能够使许多必需的步骤变得自动化,例如创建连接,或者创建和发送实际消息。本技巧介绍了同步 SOAP 消息的创建和发送。

该过程包括 5 个步骤:

1. 创建 SOAP 连接
2. 生成 SOAP 消息
3. 填充消息
4. 发送消息
5. 检索响应

SAAJ 是 Java Web Services Developer Pack 1.2 的一部分(参见 参考资料)。这个软件包还包含了 Tomcat Web 服务器(因此您就可以建立自己的服务)和例子应用程序。

安装问题

安装 Java Web Services Developer Pack 1.2 是很容易的——只要通过包含的 Tomcat Web 服务器发送消息 即可。要通过独立的应用程序发送消息,像我这里所做的一样,需要采取以下步骤:

  1. 从网址 http://java.sun.com/webservices/downloads/webservicespack.html下载 JWSDP1.2。
  2. 将软件安装到相应目录中。
  3. 如果您正使用 Java 1.4,需要用下载的软件覆盖相关的 XML 类。创建以下目录:
    <JAVA_HOME>/jre/lib/endorsed
    并复制目录
    <JWSDP_HOME>/jaxp/lib/endorsed
    中的文件到该目录下。(JAVA_HOME和JWSDP_HOME 分别表示 Java 安装以及 JWSDP 安装的目录。)
  4. 将下列文件加入 classpath 中:
    • <JWSDP_HOME>/saaj/lib/saaj-api.jar
    • <JWSDP_HOME>/saaj/lib/saaj-impl.jar
    • <JWSDP_HOME>/jwsdp-shared/lib/commons-logging.jar
    • <JWSDP_HOME>/jwsdp-shared/lib/mail.jar
    • <JWSDP_HOME>/jwsdp-shared/lib/activation.jar
    • <JWSDP_HOME>/jaxp/lib/endorsed/dom.jar
    • <JWSDP_HOME>/jaxp/lib/endorsed/xercesImpl.jar
    • <JWSDP_HOME>/jaxp/lib/endorsed/sax.jar
    • <JWSDP_HOME>/jaxp/lib/endorsed/xalan.jar

现在您就可以使用独立的程序从自己系统的任意位置发送消息了。

SOPA 消息的结构

首先来看消息自身的结构。一条基本的 SOAP 消息由带有两个主要部分的信封(envelope)构成:头部和主体。应用程序确定如何使用这些部分,但整个消息必须遵循特定的 XML 结构,例如:

清单1. 一条示例 SOAP 消息
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
        xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/1999/XMLSchema"> 
    <SOAP-ENV:Header />
    <SOAP-ENV:Body> 
        <ns1:getPrice xmlns:ns1="urn:xmethods-BNPriceCheck" 
             SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 
                  <isbn xsi:type="xsd:string">0672324229</isbn> 
        </ns1:getPrice> 
            </SOAP-ENV:Body> 
</SOAP-ENV:Envelope>

这里,头部是空的,而主体包含了有效信息,或要传递的消息。在本例中,它是请求某本书价格的消息。

注意消息的结构。Envelope 包含 Header 和 Body 元素,这三者都是http://schemas.xmlsoap.org/soap/envelope/ namespace的一部分。应用程序使用 SOAPConnection 来发送消息。

创建连接和消息

第一步是要创建总体类和连接:

清单2. 创建连接
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
public class SOAPTip {
    
   public static void main(String args[]) {
        
      try {
      
                 //First create the connection
         SOAPConnectionFactory soapConnFactory = 
                            SOAPConnectionFactory.newInstance();
         SOAPConnection connection = 
                             soapConnFactory.createConnection();
         
         //Close the connection            
         connection.close();
            
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

应用程序可以直接使用 SOAPConnnection 或间接使用消息提供者来发送 SOAP 消息,现在 SOAPConnection 已经是 SAAJ 包的一部分了,而消息提供者仍然属于 JAXM 包。在本例中,应用程序使用工厂创建 SOAPConnection 对象。

工厂还创建了消息自身:

清单3. 创建消息对象
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
        import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPBody;
public class SOAPTip {
    
   public static void main(String args[]) {
        
      try {
     
         //First create the connection
         SOAPConnectionFactory soapConnFactory = 
                            SOAPConnectionFactory.newInstance();
         SOAPConnection connection = 
                             soapConnFactory.createConnection();
         
 
                //Next, create the actual message
         MessageFactory messageFactory = MessageFactory.newInstance();
         SOAPMessage message = messageFactory.createMessage();
         
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
         //Close the connection            
         connection.close();
            
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

首先,使用 MessageFactory 创建消息自身。这一消息已经包含了空的基本部分,比如 envelopeheader 。SOAPPart 包含了 envelope ,而 envelope 又包含了主体。创建对所需对象 (比如 SOAPBody)的引用。

接着,填充 SOAPBody:

清单4. 填充主体
...
import javax.xml.soap.SOAPBody;
        import javax.xml.soap.SOAPElement;
public class SOAPTip {
    
   public static void main(String args[]) {
        
      try {
...
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
                //Populate the body
        //Create the main element and namespace
        SOAPElement bodyElement = 
                  body.addChildElement(envelope.createName("getPrice" , 
                                                                "ns1", 
                                          "urn:xmethods-BNPriceCheck"));
        //Add content
        bodyElement.addChildElement("isbn").addTextNode("0672324229");
        //Save the message
        message.saveChanges();
        //Check the input
        System.out.println("\\nREQUEST:\\n");
        message.writeTo(System.out);
        System.out.println();
         //Close the connection            
         connection.close();
            
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

SOAP 消息的主体就好像是另一个 XML 元素,可以在其中添加孩子元素,比如 getPrice。然后就可以像处理典型的 DOM 元素一样,为它添加 isbn 元素和文本 节点。

使用 SAAJ 还有可能使用外部文件来直接创建消息的 SOAPPart。例如,第一个程序清单中,prepped.msg 文件包含了 XML 结构,在手工建立文档的地方可以调用该文件:

清单5. 从一个外部文件创建消息
...
import javax.xml.soap.SOAPElement;
        import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;
public class SOAPTip {
    
   public static void main(String args[]) {
...
         //Create objects for the message parts            
         SOAPPart soapPart =     message.getSOAPPart();
         SOAPEnvelope envelope = soapPart.getEnvelope();
         SOAPBody body =         envelope.getBody();
         
        //Populate the Message
        StreamSource preppedMsgSrc = new StreamSource( 
                 new FileInputStream("prepped.msg"));
        soapPart.setContent(preppedMsgSrc);
         //Save the message
         message.saveChanges();
...
    }
}

通常,StreamSource 类用作 XSL Transformation 的一部分,但在这儿可以简单地使用它来获得 FileInputStream。结果是一个 SOAP 消息已经就绪,可以发送了。

发送消息

对于同步消息,发送 SOAP 消息,并接收在同一步中发生的响应:

清单6. 发送消息
...
public class SOAPTip {
    
   public static void main(String args[]) {
        
...
         //Check the input
         System.out.println("\\nREQUEST:\\n");
         message.writeTo(System.out);
         System.out.println();
                //Send the message and get a reply   
            
        //Set the destination
        String destination = 
            "http://services.xmethods.net:80/soap/servlet/rpcrouter";
        //Send the message
        SOAPMessage reply = connection.call(message, destination);
         //Close the connection            
         connection.close();         
...
    }
}

实际的消息是使用 call()方法发送的,该方法接收消息本身和目的地作为参数,并返回第二个 SOAPMessage 作为响应。在早期版本的 JAXM 中,目的地必须是一个 Endpoint 对象或一个 URLEndpoint,但现在它只要是一个对象就可以了。这个例子使用了由 XMethods 支持的“书价检验程序” Web 服务,该服务返回请求中给定 ISBN 的书的价格。

call()方法将会阻塞,直到它接收到返回的 SOAPMessage 为止。

响应

相应地,返回的 SOAPMessage 是一个与发送消息具有相同形式的 SOAP 消息,这样该消息操作时就可以像另一个 XML 消息一样。SOAP 允许直接使用 XSLT 来转换响应:

清单7.读取响应
...
        import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
public class SOAPTip {
    
   public static void main(String args[]) {
        
      try {
      
...
         //Send the message
         SOAPMessage reply = connection.call(message, destination);
                //Check the output
        System.out.println("\\nRESPONSE:\\n");
        //Create the transformer
        TransformerFactory transformerFactory = 
                           TransformerFactory.newInstance();
        Transformer transformer = 
                        transformerFactory.newTransformer();
        //Extract the content of the reply
        Source sourceContent = reply.getSOAPPart().getContent();
        //Set the output for the transformation
        StreamResult result = new StreamResult(System.out);
        transformer.transform(sourceContent, result);
        System.out.println();
         //Close the connection            
         connection.close();
...            
     }
}

像在任意 XSLT 应用程序中一样,创建 Transformer 对象。在本例中,只需要输出内容,因此没有样式表。这里,内容本身就是消息的整个 SOAP 部分(与 SOAP 消息本身相反,它可能包含附件)。您也可以在处理前提取信封和主体。在本例中,结果就是 System.out,但一般情况下,可以是一次转换可用的任意选择。像平常一样地进行转换。

图1. SOAP 请求和响应
Request and response
Request and response

接下来的步骤

这个简单的应用程序仅仅输出所接收到的消息,但是您可以同样简单地从 XML 文档中提取信息。同样,虽然这一技巧仅演示了消息的同步发送和接收,但是 JAXM API 也允许用于同步传送的消息提供者的使用,只是通过使用 ProviderConnection 对象而不是 SOAPConnection 进行。提供者保持消息,直到该消息成功传送为止。

JAXM 也允许配置文件的使用,配置文件可以简化特定 SOAP 消息的创建,比如 SOAP-RP 或 ebXML 消息。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=20838
ArticleTitle=技巧: 使用SAAJ发送和接收SOAP消息
publish-date=09012003