Introduction
BEA Workshop provides the facilities to create two types of WebService:
- Stateless: No state is stored between method invocations
- Conversational: The WebServices keeps and persists it's state between method invocations
For WebService clients, invocing methods on a stateless service is pretty straight forward, however conversational services require a much more complex interaction.
This article shows how a Conversational WebService works on the wire.
The Conversational Phases
Webservices are turned into Conversational services by marking certain methods with the conversation-phase annotation. Just to recap, the following image shows a WebService with methods marked as start, continue and finish.

The ConversationID
With standard Servlets HTTP sesion state is recorded on the server and accessed via the client providing a session token. The same method is used with conversational WebServices however the session token is called the conversationID.
The major difference between a session token and a conversationID is that it's the responsibility of the client to generate the ConversationID. The converationID can be any combination of numbers and letters.
The Conversational SOAP Headers
The ConversationID can be thought of as 'meta data' about the current method invocation in that it's not actually part of the invocation but rather provides context information to the WebService runtime. For this reason the ConversationID is passed in a Conversational Header, transmitted in the SOAP Envelope Header
There are two different types of conversational header formats used when accessing WebServices:
- StartHeader - This header is only used to call a method marked with the start conversation phase. It has the following shape:
<ns1:StartHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>123456789/conversationID>
<callbackURL>http://localhost/myCallbacks</callbackURL>
</ns1:StartHeader>
The callback URL is used to tell the Webservice runtime where any Conversational Callbacks should be sent. I won't be convering anything about callbacks in this article, however more information on using callbacks can be found in the BEA Workshop Documentation
- Continue Header - This header is used to call any subsquent conversational methods, it has the following shape:
<ns1:ContinueHeader xmlns:ns1="http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>123456789</conversationID>
</ns1:ContinueHeader>
Real World Example
The following examples were collected using using TCPMonitor and show how the above headers look in real world calls.
Calling a method marked as 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>
Calling a method marked as 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>
The WSDL
WebService clients discover the existance and shape of the headers through the services WSDL document. The following is the Header schema extracted from a Conversational services WSDL document:
<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>
The Callback header specified above is use to pass the conversationID back to the calling client during a callback method invocation.
WSDL Extensions
If you create a WebService control from a Converstional WebService WSDL document, the control is able to determine the conversational phase of each method.
To support this, special transition extensions are embedded in the document. The following gives an example of methods marked with these "cw:transition" extensions
Note : 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>
Accessing from a Weblogic WebService Client
When using the standard Weblogic Webservice client library, there is no requirement to add these conversational headers. This is because the client library is able to read the WSDL extensions and attach the appropriate headers to the call. The converationID is also generated by the client libraries and then reused when continue methods are called.
There are times however when you wish to explicitly specify the conversationID that you wish to use in the invocations. In order to do this, you much use an undocumented API and add the conversationID to the WebService context.
The following code shows an interaction with a Conversational Service with the conversationID being explictly set.
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());
}
}
|
|
|
For completeness, the following is the WebService to which the above client would communicate:
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!";
}
}
|
|
|
|