Tutorial of SOAP with Attachments API for Java

随着web 服务、SOA 概念的兴起,和诸多 SOA 应用的实践,Java 阵营也审时度势,趁势推出相应的 API 以应对这一变化。SOAP 是 Java web 服务栈中很基础的一项技术,web 服务间以 SOAP 消息来互相通信。
SOAP with Attachments API for Java(简称 SAAJ) 就是 Java 阵营为访问 web 服务所提供的基础设施,它简化了对 SOAP 的处理。
SAAJ 包含 2 部分 APIs,其一用于创建消息,添加消息内容,其二则用于建立连接,发送 SOAP 消息。

一. 创建 SOAP 消息

SOAP 消息的 XML 文档结构在前一篇文章中已有图示。其中,SAAJ APIs 中的 SOAPMessage 类表示一个 SOAP 消息,SOAPPart 类表示 SOAP part,SOAPEnvelope 接口表示 SOAP envelope,等等。很多 SAAJ APIs 的接口都继承了 DOM 接口。
当创建一个 SOAPMessage 时,该 SOAPMessage 会自动创建并包含 SOAP 消息所必须的部分:一个新的 SOAPMessage 对象会包含一个 SOAPPart 对象,这个 SOAPPart 则包含有一个 SOAPEnvelope 对象,该 SOAPEnvelope 对象会包含一个空的SOAPHeader 对象,紧接着是一个空的 SOAPBody 对象。SOAPHeader 在 SOAP 消息中是可选的,这里之所以自动创建它,是因为我们在 SOAP 消息中经常会使用到它。

MessageFactory factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();

默认是创建 SOAP 1.1 的消息,如果想要创建 SOAP 1.2 消息,那么代码如下:

MessageFactory factory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);

如果既想创建 SOAP 1.1 消息,又想创建 SOAP 1.2 消息,那么代码如下:

MessageFactory factory = MessageFactory.newInstance(SOAPConstants.DYNAMIC_SOAP_PROTOCOL
);

接着,向消息中添加内容。

首先取得 SOAPMessage 中的 SOAPPart 对象:

SOAPPart soapPart = message.getSOAPPart();

然后,我们从 SOAPPart 中取出 SOAPEnvelope 对象:

SOAPEnvelope envelope = soapPart.getEnvelope();

有了 SOAPEnvelope 对象,就可以得到它的 SOAPHeader 和 SOAPBody 了。

SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();

不过,你可能觉得做了这么多的工作才取得 SOAPHeader 和 SOAPBody 是不是太麻烦了? SAAJ APIs 提供了便捷通道,直接通过 SOAPMessage 就可以获得 SOAPHeader 和 SOAPBody 对象:

SOAPHeader header = message.getSOAPHeader();
SOAPBody body = message.getSOAPBody();

由于 SOAPHeader 是可选的,如果你不需要它,便可以去掉它,可以使用 DOM 接口的方法来删除:

header.detachNode();

下面向 SOAPBody 对象中添加内容:

SOAPBody body = message.getSOAPBody();
QName bodyName = new QName("http://wombat.ztrade.com", "GetLastTradePrice", "m");
SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
QName name = new QName("symbol");
SOAPElement symbol = bodyElement.addChildElement(name);
symbol.addTextNode("SUNW");

这时的 SOAPBody 内容如下所示:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m="http://wombat.ztrade.com">
<symbol>SUNW</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

如果你有一个以文件形式存在的 SOAP 消息,那么如下方式会使用创建一个 SOAPMessage 更便捷:

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder builder = dbFactory.newDocumentBuilder();
Document document = builder.parse("file:///music/order/soap.xml");
DOMSource domSource = new DOMSource(document);

SOAPPart soapPart = message.getSOAPPart();
soapPart.setContent(domSource);

当然,如果已有一个 DOM document,想将它通过 SOAP 发送出去,SAAJ APIs 也提供了便捷方式:

SOAPBody body = message.getSOAPBody();
SOAPBodyElement docElement = body.addDocument(document);

一个 SOAPMessage 可以包含一个或多个 attachment part,在 SAAJ APIs 中,使用 AttachmentPart 类表示 attachment part。由于 AttachmentPart 在 SOAPMessage 中是可选的,因此必须自己亲自动手去创建和添加它。如果一个 SOAPMessage 对象有一个或多个 attachments, 则每个 AttachmentPart 对象必须有一个 MIME header 用于指示它所包含的数据的类型。

二. 创建连接并发送 SOAP 消息

当消息已经创建好后,可以使用各种机制发送(例如JMS 或 JAXM)。在 SAAJ APIs 中,也提供了简单的发送机制。它使用 SOAPConnection 代表连接,SOAPConnection 对象是一中 point-to-point 连接,意味着它是直接从发送方到目标。
首先创建连接 SOAPConnection 对象:

SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = soapConnectionFactory.createConnection();

发送消息:

java.net.URL endpoint = new URL("http://wombat.ztrade.com/quotes");
SOAPMessage response = connection.call(message, endpoint);

一个连接使用了相当多的资源,所以使用结束后应尽早关闭:

connection.close();

处理响应内容:

SOAPBody soapBody = response.getSOAPBody();
java.util.Iterator iterator = soapBody.getChildElements(bodyName);
SOAPBodyElement bodyElement = (SOAPBodyElement)iterator.next();
String lastPrice = bodyElement.getValue();
System.out.print("The last price for SUNW is ");
System.out.println(lastPrice);

三. 带附件的 SOAP 消息

SAAJ APIs 中有 3 种方式创建附件。
第一种:创建一个没有内容的附件

AttachmentPart attachment = message.createAttachmentPart();
String stringContent = "Update address for Sunny Skies " + "Inc., to 10 Upbeat Street, Pleasant Grove, CA 95439";
attachment.setContent(stringContent, "text/plain");
attachment.setContentId("update_address");
message.addAttachmentPart(attachment);

setContent 方法的第一个参数 Object 是附件内容,第二个参数 String 则是 MIME 内容类型。MIME 内容类型会写入到 AttachmentPart 的 Header,且这个 MIME content type 是唯一强制要有的。SAAJ 提供了 Content-Type, Content-Id, and Content-Location headers 的 getter 和 setter 方法。

另外 2 个 SOAPMessage.createAttachment 方法会创建带内容的附件。其中一个方法,其参数类似于 AttachmentPart.setContent,它的 Object 可以是一个 String, 一个 stream, 一个 javax.xml.transform.Source object, 或者一个 javax.activation.DataHandler object。
另外一个方法则需要参数 DataHandler object,来自于 JavaBeans Activation Framework (JAF) APIs. 使用一个 DataHandler object 是相当直观的。首先,为想要添加到附件中的文件创建一个 java.net.URL object。然后创建一个 DataHandler object:

URL url = new URL("http://greatproducts.com/gizmos/img.jpg");
DataHandler dataHandler = new DataHandler(url);
AttachmentPart attachment = message.createAttachmentPart(dataHandler);
attachment.setContentId("attached_image");
message.addAttachmentPart(attachment);

这里之所以没有设置 MIME content type header,是因为 DataHandler 会含有这方面的信息。

访问附件内容:

java.util.Iterator iterator = message.getAttachments();
while (iterator.hasNext()) {
AttachmentPart attachment = (AttachmentPart)iterator.next();
String id = attachment.getContentId();
String type = attachment.getContentType();
System.out.print("Attachment " + id + " has content type " + type);
if (type.equals("text/plain")) {
Object content = attachment.getContent();
System.out.println("Attachment contains:\n" + content);
}
}

四. 创建 SOAPFault
for SOAP 1.1

SOAPBody body = message.getSOAPBody();
SOAPFault fault = body.addFault();
QName faultName =
new QName(SOAPConstants.URI_NS_SOAP_ENVELOPE, "Server");
fault.setFaultCode(faultName);
fault.setFaultActor("http://gizmos.com/orders");
fault.setFaultString("Server not responding");

还可以向 SOAPFault 对象添加详细信息:

Detail detail = fault.addDetail();
QName entryName = new QName("http://gizmos.com/orders/", "order", "PO");
DetailEntry entry = detail.addDetailEntry(entryName);
entry.addTextNode("Quantity element does not have a value");
QName entryName2 = new QName("http://gizmos.com/orders/", "order", "PO");
DetailEntry entry2 = detail.addDetailEntry(entryName2);
entry2.addTextNode("Incomplete address: no zip code");

注意:一个 SOAPFault 对象只能有一个 Detail 对象。

获取 SOAPFault 信息:

SOAPBody body = newMessage.getSOAPBody();
if ( body.hasFault() ) {
SOAPFault newFault = body.getFault();
QName code = newFault.getFaultCodeAsQName();
String string = newFault.getFaultString();
String actor = newFault.getFaultActor();
System.out.println("SOAP fault contains: ");
System.out.println(" Fault code = " + code.toString());
System.out.println(" Local name = " + code.getLocalPart());
System.out.println(" Namespace prefix = " + code.getPrefix() + ", bound to " + code.getNamespaceURI());
System.out.println(" Fault string = " + string);
if ( actor != null ) {
System.out.println(" Fault actor = " + actor);
}

访问详细信息:

Detail newDetail = newFault.getDetail();
if (newDetail != null) {
Iterator entries = newDetail.getDetailEntries();
while ( entries.hasNext() ) {
DetailEntry newEntry = (DetailEntry)entries.next();
String value = newEntry.getValue();
System.out.println(" Detail entry = " + value);
}
}

五. 参考资料

  1. The Java™ Web Services Tutorial (For Java Web Services Developer’s Pack, v2.0)
By javafuns on October 21, 2009 at 15:42 · Views: 76 · Permalink
Categorized in: SOA · Tagged with: , , ,
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Leave a Reply


  • Highest Rated

  • My PicasaPhotos

    60320ef34907e2d80b46e079.jpg

    A father cries next to the recovered body of his son (R) that is laid out with other bodies at the playground of a school at the earthquake-hit Hanwang Town of Mianzhu County, May 14, 2008. China poured more troops into the earthquake-ravaged province of Sichuan on Wednesday to quicken a search for survivors as time ran out for thousands of people still buried under rubble and mud.  REUTERS/Jason Lee (CHINA)

    vim-editor-icon.png

  • RSS My del.icio.us

  • My RSS