BEA Workshop 为创建两类 WebService 提供了便利,这两类 WebService 是:
- 无状态:两次方法调用之间不保存状态
- 会话式:WebServices 在两次方法调用间记录并保持状态
对于 WebService 客户机,调用无状态服务的方法非常简单,而对于会话式服务则需要更为复杂的交互。
在这里,我将为您展示在线会话式(Conversational)WebService 是如何工作的。
会话阶段
Webservices 通过使用会话阶段注释(conversation-phase annotation)标记特定的方法以转变成对话式服务。扼要地说,下图显示了具有标记开始、继续和结束方法的WebService。
ConversationID
使用标准Servlet,HTTP 会话状态被记录在服务器上,并且可以通过提供会话令牌的客户机访问。会话式 WebServices 也是用同样的方法,不过会话令牌称为conversationID。
会话令牌和 conversationID 的主要区别在于后者由客户机负责生成。converationID 可以是数字和字母的任意组合。
Conversational SOAP 头
ConversationID 不是调用的一部分,而是为 WebService 运行时提供上下文信息,从这个意义上可以将其看作当前方法调用的“元数据”。因此ConversationID 在Conversational Header 中传递,会话头在SOAP Envelope Header 中传输。
访问 WebService 使用两种不同的会话头格式:
- StartHeader——这种头只用于调用使用开始会话阶段标记的方法,形式如下:
<ns1:StartHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>123456789/conversationID>
<callbackURL>http://localhost/myCallbacks</callbackURL>
</ns1:StartHeader>
回调URL(callback URL)用于告诉Webservice 运行时Conversational Callback 发送到何处。本文不讨论回调,关于使用回调的更多信息请参阅BEA Workshop 文档。
- Continue Header——这种头用于调用所有的后续会话方法,形式为:
<ns1:ContinueHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>123456789</conversationID>
</ns1:ContinueHeader>
具体的例子
下面的例子来自Apache Axis TCP Monitor,说明上述头部在实际调用中的情况。
调用标记为 Start 的方法
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ns1:StartHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>1234567892</conversationID>
<callbackURL>http://localhost/myCallbacks</callbackURL>
</ns1:StartHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<StartMethod xmlns="http://www.openuri.org/">
<name>Dave</name>
</StartMethod>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
调用标记为Continue的方法
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ns1:ContinueHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>1234567892</conversationID>
</ns1:ContinueHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ContinueMethod xmlns="http://www.openuri.org/" />
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
WSDL
WebService 客户机通过服务 WSDL 文档发现头部的存在和形式。下面是摘自一个会话式服务 WSDL 文档的 Header 模式:
<s:schema elementFormDefault="qualified" targetNamespace="http://www.openuri.org/2002/04/soap/conversation/">
<s:element name="StartHeader" type="conv:StartHeader"/>
<s:element name="ContinueHeader" type="conv:ContinueHeader"/>
<s:element name="CallbackHeader" type="conv:CallbackHeader"/>
<s:complexType name="StartHeader">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="conversationID" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="callbackLocation" type="s:string"/>
</s:sequence>
</s:complexType>
<s:complexType name="ContinueHeader">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="conversationID" type="s:string"/>
</s:sequence>
</s:complexType>
<s:complexType name="CallbackHeader">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="conversationID" type="s:string"/>
</s:sequence>
</s:complexType>
</s:schema>
WSDL 扩展
如果从Converstional WebService WSDL 文档创建WebService 控件,该控件可以决定每个方法的会话阶段。
为此文档中内嵌了专门的转换扩展。下面的例子给出了一个用这种“cw:transition"”扩展标记的方法。
注意:xmlns:cw="http://www.openuri.org/2002/04/wsdl/conversation/"
converstational-phase:start
<operation name="StartMethod">
<soap:operation soapAction="http://www.openuri.org/StartMethod" style="document"/>
<cw:transition phase="start"/>
<input>
<soap:body use="literal"/>
<soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
wsdl:required="true"
message="s0:StartHeader_literal"
part="StartHeader"
use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
converstational-phase:continue
<operation name="continueMethod">
<soap:operation soapAction="http://www.openuri.org/continueMethod" style="document"/>
<cw:transition phase="continue"/>
<input>
<soap:body use="literal"/>
<soap:header xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
wsdl:required="true"
message="s0:ContinueHeader_literal"
part="ContinueHeader"
use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
从 WebLogic WebService 客户机访问
如果使用标准的 Weblogic Webservice 客户机库,不需要添加这些会话头。这是因为客户机库能够识别这种 WSDL 扩展,为调用附加适当的头。客户机库还生成converationID,并在调用继续方法的时候重用。
但有时候可能希望明确规定在调用中指定conversationID。为此,必须使用一个未公布的 API 并在 WebService 上下文中添加conversationID。
下面的代码显示了明确设定 conversationID 的Conversational Service 交互:
package bea.example;
import bea.example.convservice.client.TestConversational_Impl;
import bea.example.convservice.client.TestConversationalSoap;
import bea.example.convservice.client.TestConversational;
// Weblogic Extensions
import weblogic.webservice.context.WebServiceContext;
import weblogic.webservice.context.WebServiceHeader;
import weblogic.webservice.WLMessageContext;
public class ConvClient {
public static void main(String args[]) throws Exception {
// Get a copy of the service proxy
TestConversational service = new TestConversational_Impl();
TestConversationalSoap client = service.getTestConversationalSoap();
// Set the conversationID
WebServiceContext ctx = service.context();
ctx.getSession().setAttribute(WLMessageContext.CONVERSATION_PROP,"12345678922");
// Call the start method
System.out.println(client.startMethod("David Maddison"));
// Call the continue method
System.out.println("Continue Reply=" + client.continueMethod());
// And Clean up
System.out.println("Finish Reply=" + client.finishMethod());
}
}
为了完整起见,上述客户机通信的WebService 在下面给出:
public class TestConversational implements com.bea.jws.WebService
{
static final long serialVersionUID = 1L;
String _name;
/**
* @common:operation
* @jws:conversation phase="start"
*/
public String StartMethod(String name)
{
_name = name;
return "Hello From Start Method";
}
/**
* @common:operation
* @jws:conversation phase="continue"
*/
public String ContinueMethod()
{
return _name;
}
/**
* @common:operation
* @jws:conversation phase="finish"
*/
public String FinishMethod()
{
return "Conversation Ending!";
}
}
|