SOAP document/literal与SOAP-RPC的比较
本文刚开始的时候,我就提到了SOAP规范中定义的两种消息编码样式:document literal和SOAP-RPC。WebLogic Workshop Web服务使用document literal作为默认的编码方式,但是在每Web服务(per-Web-service)或者每方法(per-method)基础中可以忽略这种规定。WebLogic Server (servicegen) Web服务只使用SOAP-RPC。
虽然不同的SOAP编码方式说明了用来调用Web服务操作的SOAP消息也必须以不同的方法格式化,但JAX-RPC隐藏了大部分的区别。作为Web服务客户端的开发者,你能看到的唯一区别在于由代理类生成的包。
在RegisterPerson.jws中,我囊括了setHomeContact和setWorkContact方法的document literal和SOAP-RPC两种版本。这些方法的SOAP-RPC版本被分别命名为setHomeContactRPC和setWorkContactRPC。
org.openuri.www.encodedTypes.Contact
用于document literal 和SOAP-RPC的proxy stub方法除了用于传递参数的代理类外是完全一样的。对于SOAP-RPC方法来说,代理类在包分层中生成了一层。在SOAP-RPC方法中只用到了Contact类,生成的代理类为:
org.openuri.www.encodedTypes.Contact
由于这个类表示的Web服务内部类和org.openuri.www.Contact表示的一样,它们具有同样的接口,所以必须严格地用相同的方法来使用它。RegisterClient.java中的示例在第343行。
m_proxy.setHomeContactRPC(soapRPCContactB, contHeader);
会话Web服务
在其他应用中,Web服务可能被用于展示一些业务操作,这些操作是那些可能是长时间的业务处理中的步骤。不同的操作可能需要不同的反应时间。有些操作甚至还需要人来查看数据并作出决策,这比起全自动的操作来慢多了。这种不可预测的发应时间要求Web服务必须能记录状态--当正在等待的慢操作完全结束时,Web服务要正确地记住它们的状态。不幸的是,Web服务默认的协议HTTP是一种无状态协议。Web服务器和浏览器通过cookie来逃避这个问题,但是Web服务体系结构并不能指望客户端可以管理cookie。
为了解决这个问题,WebLogic Workshop引入了会话式Web服务这个概念。Web服务的每个操作都被标识成一个会话的开始、继续和结束。无论会话处在哪个阶段,Web服务的状态(包括创建Web服务的人所需的数据)都是自动保持的。将Web服务操作标识为一个会话的某个参与阶段就像为一个操作设置属性一样简单。
每个会话都由一个独一无二的会话ID来标识。当Web服务客户端调用一个会话开始操作时,该客户端就负责生成一个惟一的会话ID,并且当客户端需要调用在同一个会话中执行的以后的Web服务操作时,客户端必须具有同样的会话ID。会话ID是一个字符串。在本例中的客户端,我使用了一种简单的策略来设计会话ID:先将当前的日期和时间表示成long型的值,然后转换成字符串。在RegisterClient.java中,会话头部在第264-266行被初始化:
java.util.Date now = new Date();
startHeader = new StartHeader((new Long(now.getTime())).toString(), null);
contHeader = new ContinueHeader((new Long(now.getTime())).toString());
注意到,继续头部与开始头部一样,都有相同的会话ID。因为我希望这个客户端的所有操作都在同一会话中发生。
如果Web服务操作被标识为会话开始操作(在JWS文件中),则代理stub方法将接收一个StartHeader对象作为一个额外参数。如果Web服务操作被标识为会话继续或会话结束操作,那么stub方法将接收一个ContinueHerder对象作为一个额外参数。正如前面我们在本文中对复杂类型所作的描述那样,Java包层次(其中定义了这些头部类)起源于WSDL文件中的数据结构的XML命名空间。对于这些会话头部类,包层次(以及命名空间)还包括版本信息。开始头部的完整的Java类是这样的:
org.openuri.www.x2002.x04.soap.conversation.StartHeader
从中可以得出,在WSDL文件中,会话头部的XML命名空间如下:
http://www.openuri.org/2002/04/soap/conversation/
构造了会话头部对象后,就将其传给需要这个对象的代理stub方法。在RegisterClient.java中,每个代理stub方法都需要重复这一过程。在321行就有这样的例子:
m_proxy.setHomeContact(soapLiteralContactA, contHeader);
你可能会问:"为什么它们叫做'头部'呢?"这里之所以将它们作为头部来讲,是因为它们将作为另一部分SOAP消息的SOAP头部。另一方面,stub方法所带的参数将被传给SOAP的主体。
会话和用于管理来自客户端的会话的SOAP头部机制是一种仅仅WebLogic Workshop Web服务才有的概念。然而,所有的Web服务都会被要求能够参与长时间运行的、异步的业务处理。EBA Systems公司正在与工业组织和标准化组织一道,致力于会话这一概念的发展。
总结
在最底层,调用一个Web服务操作是件相当复杂的事情,涉及到网络连接管理和数据的编组和解编组,数据的编组和解编组又牵涉到广义Java-XML转换。但是,正如我在此所作的演示那样,有了JAX-RPC API以及WebLogic Server所提供的clientgen工具,为Web服务编写Java客户端不是什么难事。虽然我在这里举的例I正好是一个WebLogic Workshop Web服务,你也应该可以将这些概念应用于为任意Web服务创建Java客户端。
