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_request与items两个参数的使用。
生成具有非包装样式的电子商务客户端
具有讽刺意味的是,相比前面那个结构复杂的客户端,产生非包装样式客户端的相关支持代码却显得稍微复杂了一些。如:
% 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()); } }