内容


处理 SOAP 中的附件

使用 Apache SOAP 2.2 传递外部对象

Comments

Web 服务已被业界广泛接受用来解决复杂的问题和跨多个平台与系统的分布式过程。Web 服务通过使用基于标准的协议如 SOAP、WSDL 和 UDDI 以及标准组的开发工作实现了这一点。这些标准仍在不断改进,为所需的全部领域巩固解决方案。在这些解决方案尚不完善的领域,您仍需要解决如何在服务间传输定制数据类型,比如数据结构数组,以及如何处理附件来传输二进制和其它复杂的数据文件。

在本文中,我将说明目前支持处理 Web 服务中的定制数据编码和附件的协议和工具。我还将提出一个简单的个案研究,它演示如何使用现有的工具和标准来创建文件上传和下载 Web 服务。在我谈论到这些主题之前,让我们回顾一下 SOAP 和 WSDL 在工作时如何处理数据编码。

SOAP 和数据类型

一条 SOAP 消息包含一个 XML 文档定义,该定义可以用于在分散式、分布式环境中在对等端之间交换有一定结构和类型的信息。SOAP 消息通常是按照 SOAP 规范的第 5 部分的定义被编码。但是,并没有约束不准使用其它的编码模式如“XML 模式”(XML Schema)或 RDF,只要应用程序能够理解它们即可。例如,WSDL 中指定的 literal 编码方法使用 XML 模式进行编码。SOAP 规范的第 5 部分被创建用来弥补“XML 模式”的类型系统的早期版本中的缺陷,特别是 SOAP 所需的功能,例如数组和其它复杂的类型系统。但是,关于既然 XML 模式已经发展到支持 SOAP 所需的大多数功能,为什么还需要一个备用的特定编码模式这个问题已经在业界和标准工作组织之间引起了很大争议。

SOAP 消息交换可以被模型化为 远程过程调用(Remote Procedure Call)(RPC),在这种模型中有一个定义完好的消息交换方法,它基于定义完好的方法调用说明(也即方法名称和参数)及其返回值/异常。SOAP 消息也可以看作被交换的 文档(Document),其中被交换数据的语义只在应用程序级(发送方和接收方)被了解。

WSDL(Web services Definition Language,Web 服务定义语言

WSDL 描述了一组 SOAP 消息,以及如何在 SOAP 处理器间交换这些消息。这使得公司能够描述它们的服务,也使得客户能够按标准方法消费服务,而不必对有关较低级别的交换协议(绑定),如 SOAP,了解很多。服务的这种高级抽象限制了人类的交互并使得能够为 Web 服务自动生成代理。(这些代理可以是静态的或者动态的)。WSDL 允许面向文档和面向 RPC 的消息描述。

WSDL 中有两种消息编码可用:literal 和 SOAP 编码。Literal 编码意味着将按照“XML 模式”构建消息。SOAP 编码使用 SOAP 规范的第 5 部分指定的规则。WSDL 中的另一个重要的概念是 消息传递格式(message transfer format),使用这种格式,可以将消息作为“文档”或 RPC 参数传递。文档传递格式意味着消息部件是在消息主体中,而 RPC 意味着消息部件只是带有特定说明的调用的参数,包在 XML 元素中。这些传递格式符合 SOAP 消息交换的 SOAP 语义。

记住这些概念,我可以讨论 SOAP 和 Web 服务的一些有趣的方面,包括定制类型映射和附件支持。

类型映射

SOAP 中的类型映射被用于确定如何通过将本机语言对象组织或者重新格式化为 XML 来传递它们,这样就可以在 SOAP 消息中传送它们。这些类型映射被存储在客户机和服务器上的映射注册表中。大多数工具箱都有一些预定义的用于基本数据类型、集合、有时候甚至是 MIME 附件的转译程序(translator)。为传送定制类对象,您将需要创建新的序列化器和反序列化器,还需要在客户机和服务器中都注册新的类型映射。这种类型映射基于 SOAP 的 encodingStyle 功能。

通常每个类型映射都可能包含下面的信息:

  • encodingStyle ― 一个描述要使用的编码风格的 URI;例如,http://schemas.xmlsoap.org/soap/encoding/
  • XML 元素的 Qname ― 为特定类型定义提供的 URI,例如 http://www.w3.org/2001/XMLSchema:int
  • “本机”语言类(从其中进行编码或解码至其中)
  • 充当序列化器的“本机”语言类名称
  • 充当反序列化器的“本机”语言类名称

现有的 SOAP 工具箱大多数都支持基于 SOAP 编码模式和 XML 模式的复杂类型映射。

Apache SOAP 2.2 的定制类型映射支持

为处理定制类型编码,必须在服务器和客户机中都定义新的类型映射。使用 Apache SOAP 2.2 工具箱有两种方法在服务器端注册类型映射:

  • 使用部署描述符。
  • 用新的类型映射重设缺省的映射注册表。新注册表必须是 SOAPMappingRegistry 的一个子类。

Apache 工具箱中还有两种方法在客户端注册类型映射:

  • 创建一个 SOAPMappingRegistry 实例并使用 mapTypes() 方法添加新类型。
  • 用所有预注册的映射创建 SOAPMappingRegistry 的一个子类。

为帮助开发者,Apache SOAP 还提供了一个“Java Bean 序列化器/反序列化器”(Java Bean Serializer/Deserializer)类,它使得所有按 JavaBeans 技术标准构建的类都能够被序列化或反序列化而不必写任何定制代码。这个“JavaBeans 序列化器/反序列化器”类, org.apache.soap.encoding.soapenc.BeanSerializer 可以在类型映射注册表中作为序列化器和反序列化器使用。

开发者还可以尝试创建定制序列化器和反序列化器(它们是通过分别实现 org.apache.soap.util.xml.Serializerorg.apache.soap.util.xml.Deserializer 创建的)进行定制处理。

在下面的示例中我将说明如何进行这种定制编码。

SOAP 协议和附件

您可能经常需要和各种附件(如图像、图画、xml 文档等)一起发送 SOAP 消息。这些数据通常是特殊的二进制格式。 带附件的 SOAP规范详细说明了使用“MIME multipart/related” 媒体类型和 URI 模式引用 MIME 部件。并不是所有的 Web 服务工具箱都提供 SOAP 附件支持。Microsoft 已经提出了另一个基于 DIME 的附件解决方案(请参阅 参考资料)。

现在我将说明如何构造带附件的 SOAP 消息以及如何从 SOAP 消息引用附件。

SOAP 消息包

SOAP 消息包包含 XML 格式的主 SOAP 消息以及 SOAP 信封中未定义但与消息有关的任意数据格式(例如 gif、jpg 和 xml 等)的其它实体。

图 1所示,SOAP 消息包是用 MIME 的 Multipart/related 媒体类型构建的,每个部件都嵌入 MIME 边界(在 Context-Type 报头中定义)。每个 MIME 部件都有报头信息比如 Context-Type (它指定嵌入这个 MIME 部件的数据的类型)、 Content Transfer-encoding (指定用于这个 MIME 部件的编码)、 Content-ID 和/或 Content-Location (作为从 MIME 包的任何地方引用这些内容的标识符)。MIME 消息的根部件包含 SOAP 信封, Content-Type 被设置为 text/xml

图 1. SOAP 消息包
SOAP 消息包
SOAP 消息包

SOAP 对附件的引用

SOAP 报头和 SOAP 消息的主体都可以引用消息包中的其它实体。根据 SOAP 1.1 编码规则,SOAP 的“href”属性(请参阅 图 1)可用于引用任何资源。

任何资源都可以使用 Content-ID 或者 Content-Location 引用。如果您正在使用 Content-ID 作为标识符,那么模式属性(例如 href)必须使用如 图 1 所示的 URL 模式 CID(根据 rfc 2153)。如果是使用 Content-Location 作为标识符,那么必须根据 带附件的 SOAP规范中指定的规则进行解析,其中解析是基于下列要素之一:

  • 绝对 URI 引用 ― 绝对 URL 是在 SOAP 信封中的 Content-Location 和“href”属性中指定的。
  • 相对 URI 引用 ― MIME 消息主报头的 Content-Location 中指定了一个基础(base)URL,对所有的相对 URL 都是使用这个基础 URL 进行引用。
  • 不带基础 URI 的相对 URI ― 主报头的 Content-Location 中没指定基础 URI,但使用消息的基础 URL,并且对所有的相对 URL 都是使用这个基础 URL 进行引用。

通常由 SOAP 处理器决定是否需要解析 URI。而且,消息包中可能有在 SOAP 信封中没有 URI 引用的附件。

HTTP 绑定
如果 HTTP 绑定被 SOAP 用来发送附件,就需要修改 HTTP 报头使其包含来自 SOAP MIME 包报头的 Content-Type 信息。HTTP 报头中不包含来自 MIME 包报头的其它报头信息。Apache 忽略了报头信息比如 Content-Transfer-Encoding 和 MIME Version 。所有的 MIME 部件都包含 SOAP 信封部件和其它附件,构成 HTTP 主体。另一方面,对于 SMTP 绑定来说,所有的多部件 MIME 报头都会被作为 SMTP 报头的一部分存储。这样,SOAP 处理器和应用程序就应该知道这些报头与不同种类的 SOAP 绑定的不兼容性。

WSDL 支持描述带附件的 Web 服务
如下面的 WSDL 文档所示,binding 元素的 input 和/或 output 元素被扩展,并且包住了带有不同 MIME 部件的 MIME:multipart-related,其中一个 MIME 部件用于 SOAP 主体,其它的用于附件。每个带附件的 MIME 部件也都可以指定其内容类型。

清单 1:为 MIME 扩展 WSDL 绑定
<binding name="DocManagementService_Binding"  ... />
  <operation  name="SubmitArrayOfDocuments">
    <soap:operation soapAction="http://www.DocManagementService.com/SubmitArrayOfData"/>
    <input>
      <mime:multipartRelated>
          <mime:part>
            <soap:body
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    namespace="urn:DocManagement-service"
    parts="SubmitArrayOfDocuments_inType1 SubmitArrayOfDocuments_inType2"
    use="encoded"/>
          </mime:part>
          <mime:part>
            <mime:content part="SubmitArrayOfDocuments_inType3" type="*/*"/>
          </mime:part>
    </mime:multipartRelated>
    </input>
    <output>
      <soap:body
    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    namespace="urn:DocManagement-service" 
    use="encoded"/>
    </output>
  </operation>
</binding>

Apache SOAP 2.2 工具箱支持带附件的 SOAP

根据 W3C 的“带附件的 SOAP 注解”(SOAP with Attachments Note),Apache SOAP 工具箱版本 2.2 支持带附件的 SOAP。根据所需的服务类型和细粒度控制,工具箱中有不同的技术可用来处理附件:

  • 有一些内建序列化器/反序列化器使用 javax.activation.Datasource 对象和 javax.activation.DataHandler 对象处理附件。这主要用于基于 RPC 的客户机/服务器调用。
  • 为获得对附件处理的更细粒度控制,客户端类如 org.apache.soap.messaging.Message、org.apache.soap.rpc.Call 和 org.apache.soap.rpc.Response 以及服务器端的 SoapContext 类提供了一些方法(addBodyPart(..)、findBodyPart(..)、getBodyPart(..)......)。这些方法允许向 SOAP 消息添加或检索一个 javax.mail.internet.MimeBodyPart。

有许多与使用带附件的 SOAP 有关的问题:

  • 带附件的 SOAP规范基于“MIME Multipart/related”。在 SOAP 社区有关于“MIME Application/Multiplexed”的使用的讨论,使用它可以在主 XML 文档中交叉存取二进制内容。这使得应用程序不需立刻将所有数据(包括附件)都加载到内存,有助于更好地伸缩。
  • 有一些工具箱支持 带附件的 SOAP规范标准。Apache SOAP 2.2、WASP 也在其中。
  • 目前大多数 SOAP 工具箱都不支持流模型(streaming model)来处理以附件或者消息的形式过来的大量数据。这导致了 SOAP 处理器的可伸缩性和性能问题。

一次简单的实现研究

对于这次实现研究,我将创建一个基于 Web 的应用程序使最终用户能够从 Web 服务器上传和下载他们的文件。该应用程序提供一个基于 Web 服务的调用模型。这个 Web 服务应用程序将使用户能够向服务器上传任意数量的文件,获得对这些文件的引用,还可以使用这些引用下载他们想要的文件。

通过发送多个附件和高级 WSDL 文档构造来支持 MIME 附件,这个示例将有助于说明定制类型映射。它还将有助于描述如何将定制数据作为数组来发送和检索。这个简单的应用程序可以增强为全球基于 Web 的文件管理应用程序。但是,这个示例没有考虑更复杂的安全性、国际化、隐私和性能方面的问题,在大规模的应用程序中,这些问题都是必须考虑的。

前端客户机和后端服务器的服务描述(WSDL)包含关于从“文档”管理器应用程序公开的服务的信息。已经扩展了 WSDL 绑定使其支持 multipart/related 类型的 MIME 格式。后端 WSDL 文件显示在 清单 2中,前端文件显示在 清单 3中。

清单 2:DocumentManagement-Interface.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="DocumentManagementService_Interface"
  targetNamespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
  xmlns:tns=
"http://www.DocumentManagementService.com/DocumentManagementService_interface"
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
  xmlns:types=
"http://www.DocumentManagementService.com/DocumentManagementService_interface/types/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<types>
  <xsd:schema targetNamespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface/types/"
   xmlns="http://www.w3.org/2001/XMLSchema/">
    
  <xsd:complexType name="User">
    <xsd:sequence>
        <xsd:element name="userName" type="xsd:string"/>
        <xsd:element name="password" type="xsd:string"/>
    </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="DocumentInformation">
    <xsd:sequence>
        <xsd:element name="userDefinedDocName" type="xsd:string"/>
        <xsd:element name="localPathInfo" type="xsd:string"/>
    </xsd:sequence>
    </xsd:complexType>
    
    <xsd:complexType name="ArrayOfDocumentInformation">
        <xsd:complexContent>
            <xsd:restriction base="SOAP-ENC:Array">
                <xsd:sequence>
                    <element minOccurs="0" maxOccurs="unbounded" 
                    name="docInfo" type=" DocumentInformation"/>
                </xsd:sequence>
                <xsd:attribute ref="SOAP-ENC:arrayType" 
                wsdl:arrayType="DocumentInformation[]"/>
            </xsd:restriction>
        </xsd:complexContent>
    </xsd:complexType>
    <xsd:complexType name="DocumentReference">
        <xsd:sequence>
            <xsd:element name="docCreateDateTime" type="xsd:datetime"/>
            <xsd:element name="docRefType" type="xsd:string"/>
            <xsd:element name="serverDocPath" type="xsd:string"/>
            <xsd:element name="localPathInfo" type="xsd:string"/>
            <xsd:element name="userDefinedDocName" type="xsd:string"/>
            <xsd:element name="docRef" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="ArrayOfDocumentReference">
        <xsd:complexContent>
            <xsd:restriction base="SOAP-ENC:Array">
                <xsd:sequence>
                    <element minOccurs="0" maxOccurs="unbounded" 
                    name="docRef" 
                    type="DocumentReference"/>
                </xsd:sequence>
                <xsd:attribute ref="SOAP-ENC:arrayType" 
                wsdl:arrayType="DocumentReference[]"/>
            </xsd:restriction>
        </xsd:complexContent>
    </xsd:complexType>
    
    <xsd:complexType name="ArrayOfString">
        <xsd:complexContent>
            <xsd:restriction base="SOAP-ENC:Array">
                <xsd:sequence>
                    <element maxOccurs="unbounded" name="docs" 
                    type="xsd:string"/>
                </xsd:sequence>
                <xsd:attribute ref="SOAP-ENC:arrayType" 
                wsdl:arrayType="xsd:string[]"/>
            </xsd:restriction>
        </xsd:complexContent>
    </xsd:complexType>
  </xsd:schema>
</types>
<message name="InSubmitArrayOfDocumentsRequest">
  <part name="SubmitArrayOfDocuments_inType1" type="types:User"/>
  <part name="SubmitArrayOfDocuments_inType2" 
type="types:ArrayOfDocumentInformation"/>
  <part name="SubmitArrayOfDocuments_inType3" 
type="types:ArrayOfString"/>
</message>
<message name="OutSubmitArrayOfDocumentsResponse">
  <part name="SubmitArrayOfDocuments_outType" type=
"types:ArrayOfDocumentReference"/>
</message>
<message name="InFetchDocumentsRequest">
 <part name="FetchDocuments_inType1" type="types:User"/>
  <part name="FetchDocuments_inType2"  type=
"types:ArrayOfDocumentReference"/>
</message>
<message name="OutFetchDocumentsResponse">
<part name=" FetchDocuments_outType1" type="xsd:string"/>
</message>
<message name="InPingDocumentMangerRequest">
 <part name="PingDocumentManger_inType1" type="xsd:string"/>
</message>
<message name="OutPingDocumentMangerResponse">
  <part name="PingDocumentManger_outType1"  type="xsd:string"/>
</message>
<portType  name="DocumentManager_portType">
  <operation  name="SubmitArrayOfDocuments">
    <input message="tns:InSubmitArrayOfDocumentsRequest"/>
    <output message="tns:OutSubmitArrayOfDocumentsResponse"/>
  </operation>
  <operation name="FetchDocuments">
    <input  message="tns:InFetchDocumentsRequest"/>
    <output message="tns:OutFetchDocumentsResponse"/>
  </operation>
  <operation name="PingDocumentManger">
    <input  message="tns:InPingDocumentMangerRequest"/>
    <output message="tns:OutPingDocumentMangerResponse"/>
  </operation>
</portType>
<binding name="DocumentManagementService_Binding_Rpc" type=
"tns:DocumentManager_portType">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
   <operation  name="SubmitArrayOfDocuments">
    <soap:operation soapAction="http://www.DocManagementService.com/SubmitArrayOfData"/>
    <input>
      <mime:multipartRelated>
          <mime:part>
            <soap:body 
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
        namespace="urn:DocumentManagement-service"
        parts="SubmitArrayOfDocuments_inType1 SubmitArrayOfDocuments_inType2"
        use="encoded"/>
          </mime:part>
          <mime:part>
            <mime:content part="SubmitArrayOfDocuments_inType3" type="text/html"/>
          </mime:part>
    </mime:multipartRelated>
    </input>
    <output>
      <soap:body
          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          namespace="urn:DocumentManagement-service"
      use="encoded"/>
    </output>
   </operation>
   <operation name="FetchDocuments">
    <soap:operation soapAction="http://www.DocManagementService.com/FetchDocuments"/>
    <input>
     <mime:multipartRelated>
          <mime:part>
             <soap:body
                encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                namespace="urn:DocManagement-service"
              parts="FetchDocuments_inType1 FetchDocuments_inType2"
              use="encoded"/>
        </mime:part>
     </mime:multipartRelated>
  </input>
  <output>
       <soap:body 
            encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
            namespace="urn:DocManagement-service" use="encoded"/>
  </output>
</operation>
  <operation name="PingDocumentManger">
    <soap:operation soapAction=
"http://www.DocumentManagementService.com/PingDocumentManger"/>
    <input>
      <soap:body
          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          namespace="urn:DocumentManagement-service"
          use="encoded"/>
    </input>
    <output>
      <soap:body
          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
          namespace="urn:DocumentManagement-service" use="encoded"/>
    </output>
  </operation>
</binding>
</definitions>
清单 3:DocumentManager.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="DocManagementServiceDefinition" 
    xmlns="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:interface=
    http://www.DocumentManagementService.com/DocumentManagementService_interface" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    targetNamespace=
    "http://www.DocManagementService.com/DocManagementService" >
<import location=
"http://joshyjjp:4040/DocumentManager/DocumentManager-Interface.wsdl" 
namespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface" />
<service name="DocManagementService">
  <documentation>Document Management Web Service: 
This service will allow user to manage their documents 
</documentation>
  <port binding=
"interface:DocumentManagementService_Binding_Rpc" 
name="DocManagementService_RPC">
    <soap:address location=
"http://joshyjjp:4040/dmsoap/servlet/rpcrouter"/>
  </port>
  <port binding="interface:DocumentManagementService_Binding_Message"
name="DocManagementService_Message">
    <soap:address location=
"http://joshyjjp:4040/dmsoap/servlet/messagerouter"/>
  </port>
</service>
</definitions>

后端服务实现

后端服务实现(显示在 清单 4 中)是在类 DocumentManagementImpl 中使用公共可访问的服务方法如 SubmitArrayOfDocumentsFetchDocuments 处理的。这里的 SubmitArrayOfDocuments 服务,是一个基于 RPC 的 SOAP 服务方法,其参数包括 SOAPContext (用来处理附件)、 User (用户信息)和一个 DocumentInfo 数组(提交到服务器的附加文档)。 FetchDocuments 方法是一个基于 RPC 的服务,用户信息和文档引用对象都作为其参数。文档引用对象包含有关已经提交给服务器并保存在服务器中的文档的信息( docRefId )。

清单 4:DocumentManagementImpl 类
// apache soap
import org.apache.soap.*;
import org.apache.soap.rpc.*;
import org.apache.soap.util.xml.XMLParserUtils;
import org.apache.soap.encoding.SOAPMappingRegistry;
// MIME
import javax.activation.*;
import javax.mail.*;
import javax.mail.internet.*;
// java
import java.io.*;
import java.util.*;
public class DocumentManagementImpl implements DocumentManagement{
    
/* 
    Submit an array of documents to the Document Manger; Attached data has to 
    be retrieved from the SOAPContext.
    Once the attached data is retrieved it will be saved to the disk at 
    the server and a document reference will    be returned to the user.
    */
public DocumentReference[] SubmitArrayOfDocuments
(SOAPContext requestContext, User user, DocumentInfo[] localDocInfo) 
throws Exception{
    
    DocumentReference [] refArray = null;
        try{
            // get the document information 
            int docCount = localDocInfo.length;
            if(docCount >0){
                // get attachment count
                int attachmentCount = requestContext.getCount();
// there docCount docs to be saved and if successful there will be 
// that many document reference 
// for the client
            refArray = new DocumentReference[docCount]; 
                
MimeBodyPart mbp = null;
                // Get root part
                    MimeBodyPart rootPart = requestContext.getRootPart();
                // Detach Attachments if present
       for (int i = 0; i < requestContext.getCount(); i++){
                  // Get next attachment
        mbp = (MimeBodyPart)requestContext.getBodyPart(i);
            if (!(mbp.equals(rootPart))){ 
            // the root element is the soap envelope
            String type = mbp.getContentType();
            String Id = mbp.getContentID();
            String cntType = mbp.getContentType();
String location = mbp.getHeader
(org.apache.soap.Constants.HEADER_CONTENT_LOCATION, null);
        String name = mbp.getFileName();
            Object content = mbp.getContent();
            String classType = content.getClass().getName();
    
            if (mbp.isMimeType("text/*")){
                System.out.println("The MIME data is in text format");
        }else{
                System.out.println("The MIME data is in binary format");
            }
            // save each attachment to the file system
            DocumentReference dofRef = 
            saveDocument(user,localDocInfo[i-1],content);
        refArray[i-1]= dofRef; // set the return document reference
         }
    }
}       
            }catch(Exception e){
            System.out.println
            ("There is an exception "+ e.toString());
            throw e;
            }
        return refArray;
       }
/* Get all the documents that already saved in the server with the respective 
document reference number. All the documents should be send back 
to the user as attachments. */
public  void FetchDocuments (DocumentReference[] docRef){
.............................................................................................
}
private synchronized DocumentReference saveDocument(User user, DocumentInfo 
userDefinedDocName, Object content){
    // save the document in the server and return a reference to the user.
..............................................................................
}   
}

如服务代码中所示,我使用 SOAPContext 请求对象来获取附件信息。这个 SOAPContext 对象包含主体部件,主体部件是一组 MIME 部件,其中根部件包含 SOAP 信封。您可以从 MIME 主体部件抽取许多信息,包括内容标识、内容类型和位置。

定制类型映射注册表是使用 Deployment Descriptor.xml 文件中所示的部署描述符建立的(请参阅 isd:mapping 部分)。SOAP 服务器加载类型映射信息以及必需的序列化和反序列化类并执行将这些类型转换为定制 Java 技术类(如 User、DocumentInfo 等)所必需的组织(marshall)和解组( unmarshall)操作。

可以添加基于文档的方法来提供对附件的细粒度控制。通过使用 SOAPContext 的“reponse”对象(该对象是基于“文档”的方法调用的参数)创建 MIME 附件可以实现这一点。这个 SOAPContext 对象包含主体部件,主体部件是一组 MIME 部件,其中根部件包含 SOAP 信封。

使用 XML 文档(DocumentManger.xml)将控制信息(用户名、上传文件信息和文件引用标识)存储在服务器中。

客户机服务实现

客户机代码在 SOAPMappingRegistry 中创建类型映射信息来组织和解组 Java 技术类如 User、DocumentInfo 和 DocumentReference 并将其和请求对象绑定在一起,如下所示:

   // set the SOAP mapping registry
      BeanSerializer beanSer = new BeanSerializer(); 
    // java bean based standard serializer
    SOAPMappingRegistry smr = new SOAPMappingRegistry();
    smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName
    ("urn:xml-docmanger-webservice", "user"),User.class, beanSer, beanSer); 
    
// set the mapping registry
    request.setSoapMappingRegistry(smr);

它使用 bean 序列化器类(BeanSerializer)对类(如 User,它遵守 JavaBean 的设计模式)进行序列化和反序列化。

   Attachments are created using DataSource, DataHandler 
    and MimeBodyPart classes and bind to the request object as show below, 
    // Now create attachments and add to the request
DataSource ds = new ByteArrayDataSource(new File("Compile.bat"), null);

在上面的示例中文件 Compile.bat被作为附件发送。

DataHandler dh = new DataHandler(ds);
      MimeBodyPart mbp = new MimeBodyPart();
      mbp.setDataHandler(dh);
      // Add the attachment to the service request
      request.addAttachment(mbp);

客户机的一个实现显示在 清单 5中。代码大部分是自描述的。

清单 5:“文档”客户机代码
   // Service Implementation URL
        String wsdlSvcImplURL = Util.formatURL(WSTKConstants.SERVER_HOSTNAME,
                                      WSTKConstants.SERVER_PORT,
                                      svcImplUrl);
         // get the WSDL document
    WSDLDocument wsdlDoc = new WSDLDocument(new URL(wsdlSvcImplURL));
        // Get the service proxy you will use to invoke service based on the WSDL document
        ServiceProxy serviceProxy = ServiceProxyFactory.getServiceProxy
(serviceName,portName,wsdlDoc);
        // SubmitArrayOfDocuments RPC based operation expects 2 input arguments
        Object oArgs = new Object[2];
    
    // the parameters are 
    // 1. user information
    User user = new User (userName,userPassword);
    
    // 2. an array of submited document information
    DocumentInfo[] docInfo = new DocumentInfo[1];
    docInfo[0] = new DocumentInfo("compile.bat","d:\\data");
    // Setting up the type mapping registry to handle custom java objects
    // setup the serializers; 
    BeanSerializer beanSer = new BeanSerializer(); 
    // java bean based standard serializer
    // set the soap mapping registry
    SOAPMappingRegistry smr = new SOAPMappingRegistry();
    // input parameters
    smr.mapTypes(Constants.NS_URI_SOAP_ENC,
                new QName("urn:xml-docmanger-webservice", "user"),
            User.class, beanSer, beanSer);
    smr.mapTypes(Constants.NS_URI_SOAP_ENC,
                new QName("urn:xml-docmanger-webservice", "documentinfo"),
            DocumentInfo.class, beanSer, beanSer);
    // output parameters
    smr.mapTypes(Constants.NS_URI_SOAP_ENC,
                       new QName("urn:xml-docmanger-webservice", "documentreference"),
                      DocumentReference.class, beanSer, beanSer);
 // Set input arguments
     oArgs[0] = user;
     oArgs[1] = docInfo;
    // Set up the request
        SoapServiceRequest request = new SoapServiceRequest 
("SubmitArrayOfDocuments", oArgs);
    // set the mapping registry
    request.setSoapMappingRegistry(smr);
    // Now create attachments and add to the request
            DataSource ds = new ByteArrayDataSource(new File("Compile.bat"), null);
        DataHandler dh = new DataHandler(ds);
    MimeBodyPart mbp = new MimeBodyPart();
    mbp.setDataHandler(dh);
        mbp.setFileName("Compile.bat");
    mbp.setHeader
    (org.apache.soap.Constants.HEADER_CONTENT_LOCATION, "Attachment-1");
        // Add the attachment to the service request
    request.addAttachment(mbp);
        // Invoke the operation
        ServiceResponse sr = serviceProxy.invoke(request);
    // Pull the soap response object from the results
        Response response = (Response) sr.getOneResult(1);
    // If the call to the service failed, then display error information
    if (response.generatedFault()){
            handleFault(response);
    } else {
        // Get return value
          Parameter parameter = response.getReturnValue();
          // Display results
            System.out.println("Message returned from service provider: \n");
        retRef = (DocumentReference[])parameter.getValue();
        for(int i=0;i<retRef.length;i++){
            System.out.println("Document reference id = "+retRef[i].getDocRef());
            }
       }
    
    // Now fetch the documents uploaded to the server by using the document references
........................................................................    
    // Pull the soap response object from the results
        response = (Response) sr.getOneResult(1);
    // If the call to the service failed, then display error information
    if (response.generatedFault()){
          handleFault(response);
      }else{
            // Get return value
          int attachCount = sr.getAttachmentCount();
            // Display results
            System.out.println("Number of attachments = " +attachCount);
       }
    }

最后,服务部署描述符被显示在 清单 6中。

清单 6:文档管理服务的部署描述符
<root>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" 
id="urn:DocumentManagement-service" checkMustUnderstands="false">
  <isd:provider type="java" scope="Application" 
methods="PingDocumentManger SubmitArrayOfDocuments FetchDocuments">
    <isd:java class="DocumentManagementImpl" static="false"/>
  </isd:provider>
 <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
  <isd:mappings>
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:x="urn:xml-docmanger-webservice" qname="x:user"
             javaType="User"
             java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
             xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:x="urn:xml-docmanger-webservice" qname="x:documentreference"
             javaType="DocumentReference"
             java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
             xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:x="urn:xml-docmanger-webservice" qname="x:documentinfo"
             javaType="DocumentInfo"
             java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
             xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
  </isd:mappings>    
</isd:service>
</root>

将来的趋势

XML 协议委员会发起了一项工作来定义一组新的软件规则和使用情景。这包括多异步消息传递、高速缓存、路由和对大数据流的处理。实现标准化可能要花费一段时间但 SOAP 处理器可望带着这些新功能(包括数据流功能,而不是只有使用直接附件的块传输)面市。BEEP(Blocks Extensible Exchange Protocol,块可扩展交换协议)是一个新协议,支持单个 TCP 连接上有多个通道,这一点可用于这种数据通讯的异步和流类型。

“直接因特网消息封装”(Direct Internet Message Encapsulation,DIME)是另一个轻量级的二进制消息格式,可以用于把一个或多个应用程序定义的任意类型和大小的有效负载封装到一个单一的消息构造中。DIME 在两方面有优势。发送方应该预先估计有效负载的大小或者将有效负载“分块”,分成固定大小的记录。这有助于服务器应用程序预先计算内存需求并因此而提高性能。使用 URI 机制指定新媒体类型的能力可以允许接收应用程序为新媒体类型加载处理程序。但是,这依然首先是一个 Microsoft 定义的规范。

Apache AXIS,Apache SOAP 的最新版本,目前正处于 alpha 测试阶段,但通过深入了解 AXIS 工具箱源代码和样本以及 IBM 的 WSTK 3.0 工具箱,可以领悟一些 AXIS 中带附件的 SOAP 的实现即将发生的变化。对于基于 RPC 的服务方法,附件作为 Axis DataHandlers 被传送到服务器,而对于基于“文档”的服务方法,它将 MessageContext 对象作为参数(该参数包含 Message 和 Attachments 对象,这两个对象反过来包含附件和 SOAP 数据作为“部件”)。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=21220
ArticleTitle=处理 SOAP 中的附件
publish-date=02012002