Export (0) Print
Expand All
Expand Minimize
0 out of 1 rated this helpful - Rate this topic

Interoperability with Message Exchange Patterns Created Using BEA WebLogic 8.1.3

 

Jesús Rodríguez
January 2005

Applies to:
     Microsoft .NET Framework 1.1
     Microsoft Visual Studio .NET 2003
     BEA WebLogic 8.1.3

Summary: This is the first article in the WSIG (Web Services Interoperability Guidance) series to cover interoperability using message exchange patterns. This paper covers the definition of a pattern for a conversational Web service, shows how one can be implemented using BEA WebLogic 8.1.3, and explores the development of a .NET Framework client that has the ability to call these services. (13 printed pages)

Contents

Web Services Interoperability
Message Exchange Patterns
BEA WebLogic 8.1.3 and Message Exchange Patterns (Conversations)
Using Callbacks
Conversation Lifetime
Implementing in BEA WebLogic 8.1.3
Interoperating Using a .NET Client
Implementing the Callbacks
Conclusion

Web Services Interoperability

Interoperability between the Microsoft .NET Framework and BEA WebLogic 8.1.3 is often a key consideration for developers who write applications that run on both platforms.

The use of Web services is quickly becoming one of the most common ways to achieve interoperability between these two platforms. For some scenarios, however, even though Web services are based on standards like SOAP, WSDL, UDDI, and WS-*, achieving interoperability can still be a challenge. Challenges include complicated scenarios such as data type mapping and protocol compatibility.

In addition, vendors (such as Microsoft, BEA, IBM, and Sun) may also have slightly differing implementations based on these standards. These implementations make full use of the platform, but need extra consideration in a situation that demands interoperability.

This article explores one of the advanced aspects of interoperability between Microsoft .NET and BEA WebLogic 8.1.3, using a message exchange pattern that BEA call "Conversational Web services". I do not intend to cover Basic Profile interoperability or any recommendations between the two; instead, I recommend that you read Web Services Interoperability Guidance (WSIG): BEA WebLogic 8.1 SP3 (8.1.3) by Simon Guest.

Message Exchange Patterns

Web services can implement a number of message exchange patterns. Some of the most common patterns are Request-Response and One-Way. In the scope of this article, a conversation is a message exchange pattern that is the equivalent of a real-life conversation: for example, a series of operations that semantically identify a well-defined order can be grouped in a conversation.

The design of Web services is such that multiple clients can call a single endpoint. Using this pattern, the developer can guarantee the preservation of state between calls, provide a way to keep track of data between calls, and ensure that the Web service responds to the correct client.

BEA WebLogic 8.1.3 and Message Exchange Patterns (Conversations)

BEA WebLogic 8.1.3 identifies a conversation by using a conversationID, which is typically a string. WebLogic uses this identifier to correlate subsequent message exchanges in the lifetime of a single Web service. When a client initiates a conversation with a given Web service, WebLogic automatically creates a context to keep the related state of the conversation.

Web services that implement conversations are intrinsically stateful in the conversation's context. As a result, conversations are analogous to stateful Web services. With that said, not all the operations of a stateful Web service represent a conversation; but it is a required condition that a Web service that implements a conversation must be stateful.

In addition, a conversation must also require a well-defined order in the operation of a Web service. The operations that compose a conversation also define its state. An operation can start, continue, or finish a conversation. When a conversation is started, a context is created to maintain the state. When the conversation is finished, the context is destroyed and all the conversation state-related data is released.

Using Callbacks

Callbacks are the main mechanism used by BEA WebLogic 8.1.3 to send notifications from a Web service to another endpoint. The WebLogic callback implementation mandates that potential receivers of the notifications must be able to understand SOAP messages.

The use of callbacks, however, implies some performance considerations. When a Web service needs to send multiple callbacks it must wait for a message to be processed by the client before it can send the next callback. In many scenarios this solution is impractical due to performance implications. To overcome this, WebLogic provides an interesting solution to this problem: message buffers.

Message buffers can be applied to Web service methods and callbacks, such that you can add a message buffer to any method or callback, providing that the method or callback parameters are serializable and the return type is void. When a Web service sends a callback, the message is stored in a buffer and the service does not have to wait until the client processes the callback.

If you add a message buffer to a callback of a Web service, outgoing messages (invocations of the callback) are buffered on the local machine and the callback invocation returns immediately. This prevents your service from having to wait while the message is sent to the remote server and the (void) response is received. As a result, the Web service doesn't have to wait for the network roundtrip to occur. If you add a message buffer to a callback of a Java control, the buffer is implemented by the Web service. You can also specify when and whether to retry an invocation of the buffer if it fails.

The infrastructure provided by WebLogic to support callbacks is simple, but very useful. Looking forward, I believe this solution could be interchanged with implementations of some of the upcoming eventing and notification specifications (WS-Eventing, WS-BaseNotification, WS-BrokeredNotification, and WS-Topics).

Conversation Lifetime

Lifetime is a key aspect of conversations. As part of its definition, a conversation can include a series of metrics that control its consistent states. WebLogic provides a set of attributes that can be used to control the conversation lifetime:

  • Maximum Idle Time—Indicates amount of time that can go by between incoming messages before the Web service's conversation automatically ends.
  • Maximum Age—Indicates the maximum time a conversation instance will exist before it is finished.

The conversation pattern is, without a doubt, a powerful means of increasing the potential of applications that use Web services. In the following sections we will explore how to develop Web services that implement this pattern in BEA WebLogic 8.1.3, and also how to develop clients in Microsoft .NET that can interact with those Web services.

Implementing in BEA WebLogic 8.1.3

For this article, we'll be using a sample to show a simple Web service that performs mathematical operations on a given number. This Web service implements the following operations:

  • SetSeed—Accepts an integer as parameter to start the conversation. The number is assigned to a property called Seed.
  • Add—Increments Seed with a given number received as parameter. This method continues the conversation identified by the correlationID.
  • Sub—Decrements Seed with a given number received as parameter. This method continues the conversation identified by the correlationID.
  • ExceedValue—Is a callback that is raised when the seed value is greater than 10. This method continues the conversation identified by the correlationID.
  • GetResult—Returns the value of Seed and finishes the conversation.

The value of Seed semantically represents the state of the Web service. This state should be preserved between subsequent Add and Sub calls, demonstrating a real conversation.

The conversation implemented by this Web service can be represented as a series of steps. The conversation is created when a client invokes the SetSeed operation in the Web service. At this time the client must pass a string that uniquely identifies the conversation. For native clients, WebLogic automatically creates this identifier.

Next, the client can perform subsequent calls to the Add and Sub operations of the Web service using the correlationID to identify the conversation. If a client tries to invoke an Add operation without a valid correlationID a SoapFault will be returned.

Finally, the client invokes the GetResult operation to obtain the current value of the seed and also terminate the conversation. To begin another conversation the client must again invoke the SetSeed operation. Figure 1 illustrates the Web service design view in the BEA WebLogic WorkShop IDE.

ms978494.beaconvwebserv-01(en-us,MSDN.10).gif

Figure 1.   The math Web service design view

WebLogic uses a set of annotations to identify Web service operations and its role in the conversation. For example, the Add operation could look as follows:

/**
     * @common:operation
     * @jws:conversation phase="continue"
     * @common:message-buffer enable="true"
     */
    public void Add(int value)
    {
        FSeed= FSeed + value;
        if(FSeed > 10)
          callback.ExceedValue(FSeed);
    }

As you can see the @jws:conversation phase annotation identifies the role of the operation in the conversation's state. The possible values of this attribute are: start, continue, finish, and callback.

When this method is compiled and deployed to the server, the WSDL produced by WebLogic is very large, and includes three bindings (Soap, HttpGet, HttpPost) and three services. Within this WSDL the header declarations are similar to the following:

<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>

This section declares headers that will be passed in the SOAP messages to maintain the conversation with the Web service. The conversationID uniquely identifies a conversation. This header must be provided when the client invokes any operation included in the conversation. The callbackLocation identifies the URI to which the Web service sends the callback messages. This header should be provided when the client invokes an operation that starts the conversation.

The binding section of the SOAP-based service shows the following:

<binding name="MathServiceSoap" type="s0:MathServiceSoap">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> 
 <operation name="SetSeed">
  <soap:operation soapAction="http://www.openuri.org/SetSeed" 
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>
 <operation name="GetResult">
  <soap:operation soapAction="http://www.openuri.org/GetResult" 
style="document" /> 
  <cw:transition phase="finish" /> 
 <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>
 <operation name="Add">
  <soap:operation soapAction="http://www.openuri.org/Add" 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>
 <operation name="Sub">
  <soap:operation soapAction="http://www.openuri.org/Sub" 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>
 <operation name="ExceedValue">
  <soap:operation soapAction="http://www.openuri.org/ExceedValue" style="document" /> 
 <input>
  <soap:body use="literal" /> 
  </input>
 <output>
  <soap:body use="literal" /> 
  </output>
  </operation>
  </binding>

This section shows the header declared previously with the service's operations.

The key aspect of maintaining a conversation with WebLogic-based Web services relies on manipulating the headers that are passed as part of the SOAP messages. When you develop native clients, the support for conversation is handled automatically by the WebLogic client libraries—the headers required for the conversation are automatically generated and handled by the environment.

This assertion, however, is not true for non-native clients. In the next section we will explore how to develop .NET Framework-based clients that have the ability to maintain a conversation with a WebLogic Web service.

Interoperating Using a .NET Client

To participate in a conversation with a WebLogic-based Web service, a non-WebLogic client must use a set of headers that identifies each possible state in the conversation.

Using our math example, to start a conversation with the Web service the client needs to send the following SOAP request:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <StartHeader xmlns="http://www.openuri.org/2002/04/soap/conversation/">
      <conversationID>123456</conversationID>
      <callbackLocation>http://localhost/MyServices/Service1.asmx</callbackLocation>
      <callbackLocation />
    </StartHeader>
  </soap:Header>
  <soap:Body>
    <SetSeed xmlns="http://www.openuri.org/">
      <seed>12</seed>
    </SetSeed>
  </soap:Body>
</soap:Envelope>

The StartHeader indicates that the invoked operation should start a conversation identified by the conversationID elements. The callbackLocation element represents the URI to which the Web services can send callback messages in the conversation.

When we use wsdl.exe to generate a Web service proxy, the header is reflected in the following way:

[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/", IsNullable=false)]
public class ContinueHeader : System.Web.Services.Protocols.SoapHeader {
    
    /// <remarks/>
    public string conversationID;
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.openuri
.org/2002/04/soap/conversation/", IsNullable=false)]
public class StartHeader : System.Web.Services.Protocols.SoapHeader {
    
    /// <remarks/>
    public string conversationID;
    
    /// <remarks/>
    public string callbackLocation;
}

We now need to make some modifications to the traditional client code in order to participate in a conversation with a WebLogic Web service. Prior to invoking the start operations, we must initialize the StartHeader field in the Web service-generated proxy. For the conversationID, we generate a new GUID.

MathService proxy= new MathService();
proxy.StartHeaderValue= new StartHeader();
proxy.StartHeaderValue.conversationID= Guid.NewGuid().ToString();
proxy.SetSeed(6);

The next step is to include the necessary logic to invoke the continue operations in the Web service proxy. In the following code, we create a ContinueHeader with the same conversationID of the StartHeader.

[System.Web.Services.Protocols.SoapHeaderAttribute("ContinueHeaderValue")]    
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
("http://www.openuri.org/Add", RequestNamespace="http://www.openuri.org/", 
ResponseNamespace="http://www.openuri.org/", 
Use=System.Web.Services.Description.SoapBindingUse.Literal, 
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public void Add(int value) 
   {
       if(ContinueHeaderValue == null)
   {
     ContinueHeaderValue= new ContinueHeader();
     ContinueHeaderValue.conversationID= StartHeaderValue.conversationID;
   }
        this.Invoke("Add", new object[] {value});
    }

Putting these two together allows us to develop a conversation like the following:

MathService proxy= new MathService();
//start the conversation...
proxy.StartHeaderValue= new StartHeader();
proxy.StartHeaderValue.conversationID= Guid.NewGuid().ToString();
//continue with the conversation...
proxy.SetSeed(6);
proxy.Add(2);
proxy.Sub(1);
//finish the conversation...
int Result= proxy.GetResult();

Implementing the Callbacks

The previous section explored how to develop a .NET client that interacts with a Web service in a one-way communication —the client invokes operations involved in a conversation and the Web service performs these operations.

In the first part of this article I explained that callbacks are one of the most important features in conversations. How can I develop a .NET-based Web service that receives the events raised by a WebLogic Web service? It's actually fairly simple: A callback is just a call to an operation in the target Web service. The WebLogic Web service sends a SOAP request to a well-defined address and invokes an operation identified by a name and a namespace.

The following code shows a Web service that can act as a callback receiver in our example.

[WebService(Namespace="http://www.openuri.org/")]
   public class Service1 : System.Web.Services.WebService
   {
      public Service1()
      {
         InitializeComponent();
      }

      [WebMethod]
      public void ExceedValue(int CurrentValue)
      {
         TextWriter strm= File.AppendText("d:\\temp\\wlout.txt");
         strm.WriteLine(CurrentValue.ToString());
         strm.Close();         
      }      
   }

The namespace attribute matches with the incoming SOAP request and the operation's name. You can of course also use the WebMethod attribute to customize the operation's name:

[WebMethod(MessageName= "ExceedValue")]
public void ReceiveCallback(int CurrentValue)
{
  TextWriter strm= File.AppendText("d:\\temp\\wlout.txt");
  strm.WriteLine(CurrentValue.ToString());
  strm.Close();         
}

Finally, within the client code we set the callbacklocation as part of the StartHeader:

proxy.StartHeaderValue.callbackLocation=    
"http://localhost/TargetService/Service1.asmx";

Using these features, we can see how easy it is to create powerful .NET clients that can interact with conversational Web services running on BEA WebLogic 8.1.3.

Conclusion

This article has shown that by working with SOAP headers and serialization attributes it is possible to develop a .NET client that consumes conversational Web services in a simple way.

I would like to thank Simon Guest for his help during the writing of this article. He contributed a number of edits and provided great feedback to my questions.

 

About the Author

Jesus Rodriguez is an active contributor to the J2EE and .NET communities. He regularly participates in Microsoft newsgroups and writes articles covering interesting .NET and J2EE technologies. He is also a jazz music fan. Jesus can be reached via his blog at http://weblogs.asp.net/gsusx.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.