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());
}
}