Amazon E-Commerce Web服务演示生成包装样式与非包装样式客户端

Amazon E-Commerce是亚马逊网站中的一个web服务,它可以分别基于SOAP和Rest风格的方式进行调用。WSDL地址如下:http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsdl

生成具有包装样式的电子商务客户端

该web服务的WSDL中绑定部分定义如下:

<binding name="AWSECommerceServiceBinding" type="tns:AWSECommerceServicePortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="ItemSearch">
        <soap:operation soapAction="http://soap.amazon.com/ItemSearch"/>
        <input>
            <soap:body use="literal"/>
        </input>
        <output>
            <soap:body use="literal"/>
        </output>
    </operation>
    ......

在电子商务服务的WSDL绑定部分,绑定的样式设置为默认值:document,编码方式也同样设置为默认值:literal。因此默认的包装约定也就发挥了作用,正如下面这段来自types(WSDL类型定义部分)的文档片断所展示的那样:

<xs:element name="ItemSearch">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="MarketplaceDomain" type="xs:string" minOccurs="0"/>
            <xs:element name="AWSAccessKeyId" type="xs:string" minOccurs="0"/>
            <xs:element name="AssociateTag" type="xs:string" minOccurs="0"/>
            <xs:element name="XMLEscaping" type="xs:string" minOccurs="0"/>
            <xs:element name="Validate" type="xs:string" minOccurs="0"/>
            <xs:element name="Shared" type="tns:ItemSearchRequest" minOccurs="0"/>
            <xs:element name="Request"type="tns:ItemSearchRequest" 
                minOccurs="0"maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

上面这个片段定义了ItemSearch元素,这个元素作为请求的SOAP消息体中的包装类型。下面是WSDL中message定义的片段,可以看到请求的message类型正是上面所定义的:

<message name="ItemSearchRequestMsg">
    <part name="body" element="tns:ItemSearch"/>
</message>

服务响应message中的ItemSearchResponse包装类型,如下所示:

<message name="ItemSearchResponseMsg">
    <part name="body" element="tns:ItemSearchResponse"/>
</message>

下面是WSDL中types中定义:

<xs:element name="ItemSearchResponse">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="tns:OperationRequest" minOccurs="0"/>
            <xs:element ref="tns:Items" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

由此可见,默认情况下使用绑定样式为:document,编码方式为:literal,以及默认的包装样式。

现在我们可以通过如下代码生成包装样式的客户端支持代码:

% wsimport -keep -p awsClient1 
        http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsdl

回顾前面内容不难理解,"-p awsClient"指定生成的包名称为"awsClient"(同时awsClient也是一个子目录)。

接下来,我们详细了解由wsimport针对亚马逊电子商务服务生成的相关资源文件,尤其是itemSearch相关的java注解。方法itemSearch在java接口AWSECommerceServicePortType中定义:

package awsClient1;

import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Holder;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2.4-b01
 * Generated source version: 2.2
 */
@WebService(name = "AWSECommerceServicePortType", 
    targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
@XmlSeeAlso({
    ObjectFactory.class
})
public interface AWSECommerceServicePortType {
    /**
     * @param shared
     * @param operationRequest
     * @param xmlEscaping
     * @param items
     * @param marketplaceDomain
     * @param request
     * @param awsAccessKeyId
     * @param validate
     * @param associateTag
     */
    @WebMethod(operationName = "ItemSearch", action = "http://soap.amazon.com/ItemSearch")
    @RequestWrapper(localName = "ItemSearch", 
    targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
    className = "awsClient1.ItemSearch")
    @ResponseWrapper(localName = "ItemSearchResponse", 
    targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
    className = "awsClient1.ItemSearchResponse")
    public void itemSearch(
        @WebParam(name = "MarketplaceDomain", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        String marketplaceDomain,
        @WebParam(name = "AWSAccessKeyId", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        String awsAccessKeyId,
        @WebParam(name = "AssociateTag", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        String associateTag,
        @WebParam(name = "XMLEscaping", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        String xmlEscaping,
        @WebParam(name = "Validate", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        String validate,
        @WebParam(name = "Shared", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        ItemSearchRequest shared,
        @WebParam(name = "Request", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
        List<ItemSearchRequest> request,
        @WebParam(name = "OperationRequest", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
        mode = WebParam.Mode.OUT)
        Holder<OperationRequest> operationRequest,
        @WebParam(name = "Items", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
        mode = WebParam.Mode.OUT)
        Holder<List<Items>> items);
  ......

最后两个参数operationRequest和items分别被声明为WebParam.Mode.OUT模式,表示这两个参数用来描述返回值。OUT模式的参数作为一个java Holder类的实例返回。对程序员来说,比较难处理的就是针对方法:itemSearch的调用,由于参数众多反而变得不那么清晰明了。

为什么包装样式的web服务,其客户端会如此复杂呢?包装样式的主要目的就是希望Document绑定样式的服务描述看起来同RPC绑定样式的服务描述一样直观,同时没有牺牲Document绑定样式的优势。包装的Document绑定样式需要一个经过包装的XML元素,同时这个XML元素的名称为Web服务操作的名称,比如:ItemSearch,针对每一个操作参数均拥有一个类型化的XML元素。在itemSearch服务操作的例子中,有8个输入或请求参数,两个输出或响应参数。用来描述参数的XML子元素出现在一个“xs:sequence”XML节点之中,意指这些参数都是有顺序的。比如,@ResponseWrapper处在@RequestWrapper之后,而参数Items又在@ResponseWrapper的最后给出,这就要求响应参数Items必须出现在这10个参数的最后位置。结果就导致在封装样式下,itermSearch方法的调用显得尤为复杂。

itermSearch方法的客户端调用示例如下:

import awsClient1.AWSECommerceService;
import awsClient1.AWSECommerceServicePortType;
import awsClient1.ItemSearchRequest;
import awsClient1.ItemSearch;
import awsClient1.Items;
import awsClient1.Item;
import awsClient1.OperationRequest;
import awsClient1.SearchResultsMap;
import javax.xml.ws.Holder;
import java.util.List;
import java.util.ArrayList;

class AmazonClientW {
    public static void main(String[] args) {
        AWSECommerceService service = new AWSECommerceService();
        AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
        ItemSearchRequest request = new ItemSearchReqest();
        request.setSearchIndex("Books");
        request.setKeywords("quantum gravity");
        ItemSearch search = new ItemSearch();
        search.getRequest().add(request);
        Holder<OperationRequest> operation_request = null;
        Holder<List<Items>> items = new Holder<List<Items>>();
        port.itemSearch(search.getMarketplaceDomain(),
                seaarch.getAwsAccessKeyId(),
                search.getAssociateTag(),
                search.getXmlEscaping(),
                search.getValidate(),
                search.getShared(),
                search.getRequest(),
                operation_request,
                items);
        Items retval = items.value.get(0);
        List<Item> item_list = retval.getItem();
        for(Item item : item_list)
            System.out.rintln(item.getItemAttributes().getTitle());
    }
}

注意:operation_requestitems两个参数的使用。

生成具有非包装样式的电子商务客户端

具有讽刺意味的是,相比前面那个结构复杂的客户端,产生非包装样式客户端的相关支持代码却显得稍微复杂了一些。如:

% wsimport -keep -p awsClient2 http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsdl   -b custom.xml

执行命令最后的“-b”标识指定了一个定制化的、文件名称为 custom.xml 的 jaxws:bingding,用custom.xml 文件中的内容替换wsimport的默认值。在本例中默认WrapperStyle的值为true,我们在custom.xml中设置其为false,custom.xml内容如下:

<jaxws:bindings wsdlLocation = "http://ecs.amazonaws.com/AWSECommerceService/
    AWSECommerceService.wsdl" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
    <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>

定制化所产生的客户端代码发生了明显的变化。比如,在AWSECommerceServicePortType类型中,接口的声明部分变成了如下内容:

package awsClient2;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2.4-b01
 * Generated source version: 2.2
 * 
 */
@WebService(name = "AWSECommerceServicePortType", 
    targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
    ObjectFactory.class
})
public interface AWSECommerceServicePortType {
    /**
     * @param body
     * @return
     * returns awsClient2.ItemSearchResponse
     */
    @WebMethod(operationName = "ItemSearch", action = "http://soap.amazon.com/ItemSearch")
    @WebResult(name = "ItemSearchResponse", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
        partName = "body")
    public ItemSearchResponse itemSearch(
        @WebParam(name = "ItemSearch", 
        targetNamespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01", 
        partName = "body")
        ItemSearch body);
    ......
}

在wsimport产生的接口“AWSECommerceServicePortType”中,@SOAPBinding中的参数样式变成了@SOAPBinding.ParameterStyle.BARE值,这里除了BARE还可以使用WRAPPED值。JWS的parameterStyle属性主要用来说明在web服务中针对包装和非包装的文档绑定样式,SOAP消息体中的参数应该以何种样式呈现。在非包装样式下,参数直接作为SOAP消息体的子元素顺序出现。而在包装样式下,参数被包装在以服务操作名称命名的XML元素的子元素中,同时以服务操作名称命名的元素作为SOAP消息体的唯一直接子元素出现。    

尽管改变了包装样式,但令人意想不到的是,无论是请求还是响应的基础SOAP消息,其结构并没有发生变化。(为什么?这可是你们说要改变的!!!!)当然,最明显的区别还是表现在java客户端代码上。具有非包装的parameterStyle样式的简化客户端在调用itemSearch方法时,只需要一个ItemSearch类型的参数就可以了,同时只需返回一个ItemSearchResponse类型的响应。于是parameterStyle取值为BARE时就直接避免了拥有10个参数的方法itemSearch的复杂调用,其中有8个参数绑定到@RequestWrapper子元素中,两个参数绑定到@ResponseWrapper子元素中。非包装样式的客户端调用示例如下:

import awsClient2.AWSECommerceService;
import aswClient2.AWSECommerceServicePortType;
import awsClient2.ItemSearchRequest;
import awsClient2.ItemSearchResponse;
import awsClient2.ItemSearch;
import awsClient2.Items;
import awsClient2.Item;
import java.util.List;

class AmazonClientU{
    public static void main(String[] args){
        AWSECommerceService service = new AWSECommerceService();
        AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
        ItemSearchRequest request = new ItemSearchRequest();
        request.setSearchIndex("books");
        request.setKeywords("quantum grayity");
        ItemSearch item_search = new ItemSearch();
        item_search.setAWSAccessKeyId(access_key);
        item_search.getRequest().add(request);
        ItemSearchResponse response = port.itemSearch(item_search);
        List<Items> item_list = response.getItems();
        for(Items next : item_list)
            for(Item item :next.getItem())
                System.out.println(item.getItemAttributes().getTitle());
    }
}

你可能感兴趣的:(java-web服务)