Web service正在作为一种用于构建客户端-服务器应用程序的标准出现,而且您可以使用各种技术来编写它们,包括使用工具包。在各种工具包中,可用于Java的是Sun Microsystems编写的开源Axis 和JAX-RPC标准实现(参见参考资料)。
我们将集中描述一种用于编写Web service客户端的逐步式方法,该方法使用Sun的JAX-RPC标准实现(JAX-RPC SI)作为其Web service工具包。这个工具包是可靠的,能够生产部署在生成环境中的Web service,后者实质上是JAX-RPC标准的参考实现,而JAX-RPC规范受到了广泛的支持。例如,BEA和IBM都支持JAX-RPC,而且事实上,您可以把一个Sun实现生产的Web service部署到BEA WebLogic服务器中。另外,Sun的实现符合各种其他规范,比如WS-Security,这些规范正在变得越来越重要。您可以下载用于构建一个Web service客户端的源代码。 要编写一个客户端,您需要一台服务器进行编程。可用的服务器有几种,比如Google或eBay,但是这里我们将使用Amazon的Web service。但是,仅仅知道我们想要使用的服务还不够。我知道Amazon有一个Web service,这个事实并不能让我随心所欲。我需要知道如何调用该Web service。它支持哪些操作?我应该传递哪些参数?我会获得什么返回值? 这些问题说明,要编写一个Web service,必须存在针对该Web service的可用描述。除了客户端/服务器系统之外,服务器也有描述语言,例如,DCOM和CORBA均使用接口定义语言(Interface Definition Language,IDL)。DCOM和CORBA IDL并非同一种语言——也就是说,CORBA客户端无法使用DCOM服务器的IDL来描述对客户端可用的远程服务。 描述服务 通过提供对Web service的描述,Web service描述语言代替了IDL在Web service中的位置。WSDL是一种机器可以使用的XML语法。代码生成器可以读取它。人类也可以读取它,但是它不是使用或者生产起来最容易的语言。我的一个同事Simon Horrell,他喜欢说下面这句话,“WSDL就像太阳。没有它你无法生存,但是如果您盯着它的时间过长,您就会变瞎!” Amazon提供一个Web service工具包,您可以从Amazon站点下载它。这个工具包提供针对Amazon Web service的文档,而且它还指定了用于描述Web service的WSDL文档的位置,您也可以在Amazon的站点上找到这些文档。当您打开这份WSDL文档时,如果它看起来十分复杂(它的确也十分复杂),不要过于担心,但是您要明白, 它多多少少提供了对Web service的完整描述。当不需要Amazon工具包时,您将需要一个开发人员令牌(同样可以下载)。Amazon使用这个令牌来跟踪Web service的使用,并检查是否有滥用服务的情况发生(参见参考资料)。 编写Web service时,您可以采用很多方法,但是这些方法大致可以分为两类。您可以使用所选择的工具包提供的工具来使用WSDL,并生产进行调用的客户端桩;或者您可以使用底层API来手动编写Web service。 使用这两种方法时需要进行权衡。第一种方法(使用WSDL)相对较为容易,但是,它不是很灵活。许多可用的工具会为您做大量的工作。尽管工具易于使用,而且它们生成的代码也易于使用,您可能会发现,这些代码无法完成您想要的全部功能,而且它们的性能可能不够好。例如,您可能需要访问原始的HTTP通信,而生成的代码可能不会让您这样做。 第二种方法较难,但是您可以获得更大的灵活性。从头开始编写代码可能很困难而且耗时,但是这样做最终会获得很好的灵活性和性能。可以通过不同的方式做到这一点:您可以使用Java java.net.URL及相关类,或者可以使用java.net.Socket进行低级工作。也可以使用HTTP API,比如Jakarta Commons项目提供的那些API(参见参考资料)。通常,您会使用代码生成工具,而有时候,您可能需要手动地编写客户端的某些特定部分。 向下直到桩 一开始,我们将使用Sun的JAX-RPC SI构建客户端(和服务器)。您可以从Sun的Java开发人员网站上下载Java Web Services Development Pack (JWSDP)(参见参考资料)。您下载并安装了该工具包之后,它包括了JAX-RPC SI。在安装期间,您会被要求选择一台特定的Web服务器。您可以在此处选择下载一台Web服务器,或者不选择Web服务器。 有关安装的细节可以成为另一篇文章的主题了;您只要知道我们将使用%JWSDP-HOME%来指定安装JWSDP的位置就可以了。安装之后,确保您的系统路径中有%JWSDP-HOME%/jaxrpc/bin。这个目录包含了Windows批处理文件和Unix shell脚本,以便运行用作JWSDP一部分的工具。您还可以把%JWSDP_HOME%/jaxb/bin添加到其他工具的路径。 JAX-RPC提供一个可以读取WSDL并生成客户端桩的工具。这些桩是将为我们的代码所用的Java类和接口。这些桩给服务器端功能提供了一个客户端接口。例如,如果我们的服务器提供一个Maths服务,该服务带有一个叫做add的方法。我们的客户端代码将调用桩上的一个方法,而桩实现将对该方法使用参数,把Java方法调用变为Web service请求。这个请求将基于HTTP发送给服务器,而且将使用SOAP作为RPC协议(参见图1)。
客户端调用桩上的一个方法,而桩实现对该方法使用参数,把Java方法调用变为将基于HTTP发送给服务器的Web service请求。 <?xml version="1.0" encoding="UTF-8"?> 配置元素 特性列表 [client]$ wscompile.sh 这个参数会生成大量的输出,详细描述为Web service生成的代码。wscompile工具生成源代码并将其编译为类。如果没有传递-keep标志,源代码就会丢失。这里有着大量的代码,但是我们只会对客户端使用其中的一小部分。 [client]$ wscompile.sh 这个参数会生成大量的输出,详细描述为Web service生成的代码。wscompile工具生成源代码并将其编译为类。如果没有传递-keep标志,源代码就会丢失。这里有着大量的代码,但是我们只会对客户端使用其中的一小部分。 public class AmazonSearchService_Impl extends 为了使重要的数据更加显而易见,我们省略了很多实现代码。注意,正是getAmazonSearchPort() 方法创建了AmazonSearchport_Stub(就是桩)的一个实例。桩是使用handlerChain进行初始化的(参见清单2)。 public class AmazonSearchPort_Stub extends com.sun.xml.rpc.client.StubBase implements javapro.amazon.AmazonSearchPort { public AmazonSearchPort_Stub( HandlerChain handlerChain) { super(handlerChain); _setProperty(ENDPOINT_ADDRESS_PROPERTY, "http://soap.amazon.com/onca/soap3"); } public ProductInfo authorSearchRequest( AuthorRequest authorSearchRequest) throws java.rmi.RemoteException { try { StreamingSenderState _state = _start( _handlerChain); InternalSOAPMessage _request = _state.getRequest(); _request.setOperationCode( AuthorSearchRequest_OPCODE); AmazonSearchPort_AuthorSearchRequest_ RequestStruct requestStruct = new AmazonSearchPort_AuthorSearchRequest_ RequestStruct(); requestStruct.setAuthorSearchRequest( authorSearchRequest); SOAPBlockInfo _bodyBlock = new SOAPBlockInfo( ns1_AuthorSearchRequest_AuthorSearchRequest_ QNAME); _bodyBlock.setValue (requestStruct); _bodyBlock.setSerializer( ns1requestStruct_SOAPSerializer); _request.setBody(_bodyBlock); _state.getMessageContext().setProperty( HttpClientTransport.HTTP_SOAPACTION_PROPERTY, "http://soap.amazon.com"); _send((String) _getProperty( ENDPOINT_ADDRESS_PROPERTY), _state); AmazonSearchPort_AuthorSearchRequest_ ResponseStruct responseStruct = null; Object _responseObj = _state.getResponse(). getBody().getValue(); if (_responseObj instanceof SOAPDeserializationState) { responseStruct =( AmazonSearchPort_AuthorSearchRequest_ ResponseStruct) ((SOAPDeserializationState)_responseObj). getInstance(); } else { responseStruct = ( AmazonSearchPort_AuthorSearchRequest_ ResponseStruct) _responseObj; } return responseStruct.get_return(); } catch (RemoteException e) { // let this one through unchanged throw e; } catch (JAXRPCException e) { throw new RemoteException(e.getMessage(), e); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException)e; } else { throw new RemoteException(e.getMessage(), e); } } } } 同样,您可以安全地忽略这里的很多代码;它就是实现细节。然而,注意传递给该方法的是一个AuthorRequest结构,而该方法返回的是一个ProductInfo结构。此外还要注意,我们将会发送请求至的URL被设置为带有下列调用的方法的一部分: setProperty HttpClientTransport. HTTP_SOAPACTION_PROPERTY, "http://soap.amazon.com"); 创建客户端 public class Client { public static void main(String[] args) { AmazonSearchService_Impl impl = new AmazonSearchService_Impl(); AmazonSearchPort port = impl.getAmazonSearchPort(); AuthorRequest authorRequest = new AuthorRequest(); authorRequest.setAuthor("Kevin Jones"); authorRequest.setDevtag("[developer token here]"); authorRequest.setMode("books"); authorRequest.setKeywords("Web"); authorRequest.setType("lite"); try { ProductInfo productInfo = port.authorSearchRequest(authorRequest); Details[] details = productInfo.getDetails(); for (int ndx = 0; ndx < details.length; ndx++) { Details detail = details[ndx]; System.out.println(detail.getProductName()); } } catch (RemoteException e) { e.printStackTrace(); } } } URL硬编码在桩中。URL不会经常变化,但是假定您需要发送请求给另外一个URL,那该怎么办呢?您可以让一个服务位于多台服务器上,或许是出于故障恢复的理由或负载平衡的目的,或者可能您想通过跟踪工具(可以显示每个方向上正在发送的SOAP数据)发送请求和响应。要完成这些任务,您可以在桩中修改Web service地址: AmazonSearchPort_Stub port = ( AmazonSearchPort_Stub)impl. getAmazonSearchPort(); port._setProperty( AmazonSearchPort_Stub. ENDPOINT_ADDRESS_PROPERTY, "http://localhost:9090/onca/ soap3"); 注意,getAmazonSearchPort()的返回值被转给实现类而不是接口,所以我们可以设置端点地址,以便调用localhost。
HttpProxy跟踪工具被设置为监听客户端要调用的端口上的请求,并把它们转发给原来的端口。 public static void main( String[] args) { System.setProperty( "proxySet", "true" ); System.setProperty( "http.proxyHost", "myproxy" ); System.setProperty( "http.proxyPort", "8080" ); System.setProperty( "http.proxyUser", "xxx"); System.setProperty( "http.proxyPassword", "yyy"); ... } Web service正在快速成为构建客户端/服务器应用程序的一项标准。编写Web service有多种方式,包括手写和使用工具包。有各种可用于Java的工具包,包括开源的Axis和Sun编写的JAX-RPC。关于通过使用wscompile读取WSDL和生成必需的客户端代码,并使用JAX-RPC来实现Web service客户端,我们已经接触到了其方方面面。下一步将是了解如何编写服务器。请参见“编写一个Web service服务器”一文。
|
|