4 Creating Conversational Web Services

The following sections describe how to create a conversational Web service:

Overview of Conversational Web Services

A Web service and the client application that invokes it may communicate multiple times to complete a single task. Also, multiple client applications might communicate with the same Web service at the same time. Conversations provide a straightforward way to keep track of data between calls and to ensure that the Web service always responds to the correct client.

Conversations meet two challenges inherent in persisting data across multiple communications:

  • Conversations uniquely identify a two-way communication between one client application and one Web service so that messages are always returned to the correct client. For example, in a shopping cart application, a conversational Web service keeps track of which shopping cart belongs to which customer. A conversational Web service implements this by creating a unique conversation ID each time a new conversation is started with a client application.

  • Conversations maintain state between calls to the Web service; that is, they keep track of the data associated with a particular client application between its calls to the service. Conversations ensure that the data associated with a particular client is saved until it is no longer needed or the operation is complete. For example, in a shopping cart application, a conversational Web service remembers which items are in the shopping cart while the customer continues shopping. Maintaining state is also needed to handle failure of the computer hosting the Web service in the middle of a conversation; all state-related data is persisted to disk so that when the computer comes up it can continue the conversation with the client application.

WebLogic Server manages this unique ID and state by creating a conversation context each time a client application initiates a new conversation. The Web service then uses the context to correlate calls to and from the service and to persist its state-related data.

Conversations between a client application and a Web service have three distinct phases:

  • Start—A client application initiates a conversation by invoking the start operation of the conversational Web service. The Web service in turn creates a new conversation context and an accompanying unique ID, and starts an internal timer to measure the idle time and the age of the conversation.

  • Continue—After the client application has started the conversation, it invokes one or more continue operations to continue the conversation. The conversational Web service uses the ID associated with the invoke to determine which client application it is conversing with, what state to persist, and which idle timer to reset. A typical continue operation would be one that requests more information from the client application, requests status, and so on.

  • Finish—A client application explicitly invokes the finish operation when it has finished its conversation; the Web service then marks any data or resources associated with the conversation as deleted.

Conversations typically occur between two WebLogic Web services: one is marked conversational and defines the start, continue, and finish operations and the other Web service uses the @ServiceClient annotation to specify that it is a client of the conversational Web service. You can also invoke a conversational Web service from a stand-alone Java client, although there are restrictions.

As with other WebLogic Web service features, you use JWS annotations to specify that a Web service is conversational.

Note:

The client Web service that invokes a conversational Web service is not required to also be conversational. However, if the client is not conversational, there is a danger of multiple instances of this client accessing the same conversational Web service stub and possibly corrupting the saved conversational state. If you believe this might true in your case, then specify that the client Web service also be conversational. In this case you cannot use a stand-alone Java client, because there is no way to mark it as conversational using the WebLogic APIs.

A conversational Web service on its own does not guarantee message delivery or that the messages are delivered in order, exactly once. If you require this kind of message delivery guarantee, you must also specify that the Web service be reliable. See Using Web Service Reliable Messaging: Main Steps and Using the Asynchronous Features Together.

Creating a Conversational Web Service: Main Steps

The following procedure describes how to create a conversational Web service, as well as a client Web service and stand-alone Java client application, both of which initiate and conduct a conversation. The procedure shows how to create the JWS files that implement the two Web services from scratch. If you want to update existing JWS files, you can also use this procedure as a guide.

It is assumed that you have set up an Ant-based development environment and that you have a working build.xml file to which you can add targets for running the jwsc Ant task and deploying the generated conversational Web service. It is further assumed that you have a similar setup for the WebLogic Server instance that hosts the client Web service that initiates the conversation. For more information, see the following sections in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server:

Table 4-1 Steps to Create a Conversational Web Service

#
Step Description

1

Create a new JWS file, or update an existing one, that implements the conversational Web service.

Use your favorite IDE or text editor. See Programming Guidelines for the Conversational JWS File.

2

Update your build.xml file to include a call to the jwsc Ant task to compile the conversational JWS file into a Web service.

See "Running the jwsc WebLogic Web Services Ant Task" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.

3

Run the Ant target to build the conversational Web service.

For example:

prompt> ant build-mainService

4

Deploy the target Web service as usual.

See "Deploying and Undeploying WebLogic Web Services" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.

5

Create a new JWS file, or update an existing one, that implements the client Web service.

If the client application is a stand-alone Java client, see Updating a Stand-Alone Java Client to Invoke a Conversational Web Service. Skip Steps 6-9.

If the client application is itself a Web service, follow Steps 6-9.

6

Create a new JWS file, or update an existing one, that initiates and conducts the conversation with the conversational Web service.

Use your favorite IDE or text editor. It is assumed that the client Web service is deployed to a different WebLogic Server instance form the one that hosts the conversational Web service. See Programming Guidelines for the JWS File That Invokes a Conversational Web Service.

7

Update the build.xml file that builds the client Web service.

See Updating the build.xml File for a Client of a Conversational Web Service.

8

Run the Ant target to build the client Web services.

For example:

prompt> ant build-clientService

9

Deploy the client Web service as usual.

See "Deploying and Undeploying WebLogic Web Services" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.


Programming Guidelines for the Conversational JWS File

The following example shows a simple JWS file that implements a conversational Web service; see the explanation after the example for coding guidelines that correspond to the Java code in bold.

package examples.webservices.conversation;

import java.io.Serializable; 

import weblogic.jws.WLHttpTransport;
import weblogic.jws.Conversation; 
import weblogic.jws.Conversational; 
import weblogic.jws.Context; 
import weblogic.wsee.jws.JwsContext; 
import weblogic.wsee.jws.ServiceHandle; 

import javax.jws.WebService;
import javax.jws.WebMethod;
@Conversational(maxIdleTime="10 minutes", 
                maxAge="1 day", 
                runAsStartUser=false, 
                singlePrincipal=false ) 
@WebService(name="ConversationalPortType",
            serviceName="ConversationalService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="conv",
                 serviceUri="ConversationalService",
                 portName="ConversationalServicePort")

/**
 * Conversational Web service.
 */

public class ConversationalServiceImpl implements Serializable { 

  @Context 
  private JwsContext ctx; 
  public String status = "undefined"; 

  @WebMethod
  @Conversation (Conversation.Phase.START) 
  public String start() {

    ServiceHandle handle = ctx.getService(); 
    String convID = handle.getConversationID(); 

    status = "start";
    return "Starting conversation, with ID " + convID + " and status equal to " + status;

  }

  @WebMethod
  @Conversation (Conversation.Phase.CONTINUE) 
  public String middle(String message) {

    status = "middle";
    return "Middle of conversation; the message is: " + message + " and status is " + status;

  }

  @WebMethod
  @Conversation (Conversation.Phase.FINISH) 
  public String finish(String message ) {

    status = "finish";
    return "End of conversation; the message is: " + message + " and status is " + status;

  }

}

Follow these guidelines when programming the JWS file that implements a conversational Web service. Code snippets of the guidelines are shown in bold in the preceding example.

  • Conversational Web services must implement java.io.Serializable, so you must first import the class into your JWS file:

    import java.io.Serializable;
    
  • Import the conversational JWS annotations:

    import weblogic.jws.Conversation;
    import weblogic.jws.Conversational;
    
  • If you want to access runtime information about the conversational Web service, import the @Context annotation and context APIs:

    import weblogic.jws.Context;
    
    import weblogic.wsee.jws.JwsContext;
    import weblogic.wsee.jws.ServiceHandle;
    

    See "Accessing Runtime Information about a Web service" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server for more information about the runtime Web service context.

  • Use the class-level @Conversational annotation to specify that the Web service is conversational. Although this annotation is optional (assuming you are specifying the @Conversation method-level annotation), it is a best practice to always use it in your JWS file to clearly specify that your Web service is conversational.

    Specify any of the following optional attributes: maxIdleTime is the maximum amount of time that the Web service can be idle before WebLogic Server finishes the conversation; maxAge is the maximum age of the conversation; runAsStartUser indicates whether the continue and finish phases of an existing conversation are run as the user who started the conversation; and singlePrincipal indicates whether users other than the one who started a conversation are allowed to execute the continue and finish phases of the conversation.

    @Conversational(maxIdleTime="10 minutes",
                    maxAge="1 day",
                    runAsStartUser=false,
                    singlePrincipal=false )
    

    If a JWS file includes the @Conversational annotation, all operations of the Web service are conversational. The default phase of an operation, if it does not have an explicit @Conversation annotation, is continue. However, because a conversational Web service is required to include at least one start and one finish operation, you must use the method-level @Conversation annotation to specify which methods implement these operations.

    See "weblogic.jws.Conversational" in WebLogic Web Services Reference for Oracle WebLogic Server for additional information and default values for the attributes.

  • Your JWS file must implement java.io.Serializable:

    public class ConversationalServiceImpl implements Serializable {
    
  • To access runtime information about the Web service, annotate a private class variable, of data type weblogic.wsee.jws.JwsContext, with the field-level @Context JWS annotation:

     @Context
     private JwsContext ctx;
    
  • Use the @Conversation annotation to specify the methods that implement the start, continue, and finish phases of your conversation. A conversation is required to have at least one start and one finish operation; the continue operation is optional. Use the following parameters to the annotation to specify the phase: Conversation.Phase.START, Conversation.Phase.CONTINUE, or Conversation.Phase.FINISH. The following example shows how to specify the start operation:

      @WebMethod
      @Conversation (Conversation.Phase.START)
      public String start() {...
    

    If you mark just one method of the JWS file with the @Conversation annotation, then the entire Web service becomes conversational and each operation is considered part of the conversation; this is true even if you have not used the optional class-level @Conversational annotation in your JWS file. Any methods not explicitly annotated with @Conversation are, by default, continue operations. This means that, for example, if a client application invokes one of these continue methods without having previously invoked a start operation, the Web service returns a runtime error.

    Finally, if you plan to invoke the conversational Web service from a stand-alone Java client, the start operation is required to be request-response, or in other words, it cannot be annotated with the @Oneway JWS annotation. The operation can return void. If you are going to invoke the Web service only from client applications that run in WebLogic Server, then this requirement does not apply.

    See "weblogic.jws.Conversation" in WebLogic Web Services Reference for Oracle WebLogic Server for additional information.

  • Use the JwsContext instance to get runtime information about the Web service.

    For example, the following code in the start operation gets the ID that WebLogic Server assigns to the new conversation:

    ServiceHandle handle = ctx.getService();
    String convID = handle.getConversationID();
    

    See "Accessing Runtime Information about a Web service" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server for detailed information on using the context-related APIs.

Programming Guidelines for the JWS File That Invokes a Conversational Web Service

The following example shows a simple JWS file for a Web service that invokes the conversational Web service described in Programming Guidelines for the Conversational JWS File; see the explanation after the example for coding guidelines that correspond to the Java code in bold.

package examples.webservices.conversation;

import weblogic.jws.WLHttpTransport;
import weblogic.jws.ServiceClient; 

import weblogic.wsee.conversation.ConversationUtils; 

import javax.jws.WebService;
import javax.jws.WebMethod;

import javax.xml.rpc.Stub;

import examples.webservices.conversation.ConversationalPortType; 

import java.rmi.RemoteException;

@WebService(name="ConversationalClientPortType",
            serviceName="ConversationalClientService",
            targetNamespace="http://examples.org/")

@WLHttpTransport(contextPath="convClient",
                 serviceUri="ConversationalClient",
                 portName="ConversationalClientPort")

/**
 *  client that has a conversation with the ConversationalService.
 */

public class ConversationalClientImpl {

  @ServiceClient( 
     wsdlLocation="http://localhost:7001/conv/ConversationalService?WSDL", 
     serviceName="ConversationalService", 
     portName="ConversationalServicePort") 
 
  private ConversationalPortType port; 

  @WebMethod
  public void runConversation(String message) {

    try {

      // Invoke start operation
      String result = port.start(); 
      System.out.println("start method executed.");
      System.out.println("The message is: " + result);

      // Invoke continue operation
      result = port.middle(message ); 
      System.out.println("middle method executed.");
      System.out.println("The message is: " + result);

      // Invoke finish operation
      result = port.finish(message ); 
      System.out.println("finish method executed.");
      System.out.println("The message is: " + result);
      ConversationUtils.renewStub((Stub)port); 

    }
    catch (RemoteException e) {
      e.printStackTrace();
    }

  }

}

Follow these guidelines when programming the JWS file that invokes a conversational Web service; code snippets of the guidelines are shown in bold in the preceding example:

  • Import the @ServiceClient JWS annotation:

    import weblogic.jws.ServiceClient;
    
  • Optionally import the WebLogic utility class for further configuring a conversation:

    import weblogic.wsee.conversation.ConversationUtils;
    
  • Import the JAX-RPC stub of the port type of the conversational Web service you want to invoke. The actual stub itself will be created later by the jwsc Ant task. The stub package is specified by the packageName attribute of the <clientgen> child element of <jws>, and the name of the stub is determined by the WSDL of the invoked Web service.

    import examples.webservices.conversation.ConversationalPortType;
    
  • In the body of the JWS file, use the @ServiceClient JWS annotation to specify the WSDL, name, and port of the conversational Web service you want to invoke. You specify this annotation at the field-level on a private variable, whose data type is the JAX-RPC port type of the Web service you are invoking.

    @ServiceClient(
         wsdlLocation="http://localhost:7001/conv/ConversationalService?WSDL",
         serviceName="ConversationalService",
         portName="ConversationalServicePort")
    
      private ConversationalPortType port;
    
  • Using the stub you annotated with the @ServiceClient annotation, invoke the start operation of the conversational Web service to start the conversation. You can invoke the start method from any location in the JWS file (constructor, method, and so on):

    String result = port.start();
    
  • Optionally invoke the continue methods to continue the conversation. Be sure you use the same stub instance so that you continue the same conversation you started:

    result = port.middle(message );
    
  • Once the conversation is completed, invoke the finish operation so that the conversational Web service can free up the resources it used for the current conversation:

    result = port.finish(message );
    
  • If you want to reuse the Web service conversation stub to start a new conversation, you must explicitly renew the stub using the renewStub() method of the weblogic.wsee.conversation.ConversationUtils utility class:

    ConversationUtils.renewStub((Stub)port);
    

    Note:

    The client Web service that invokes a conversational Web service is not required to also be conversational. However, if the client is not conversational, there is a danger of multiple instances of this client accessing the same conversational Web service stub and possibly corrupting the saved conversational state. If you believe this might true in your case, then specify that the client Web service also be conversational.

ConversationUtils Utility Class

WebLogic Server provides a utility class for use with the conversation feature. Use this class to perform common tasks such as getting and setting the conversation ID and setting configuration options. Some of these tasks are performed in the conversational Web service, some are performed in the client that invokes the conversational Web service. See Programming Guidelines for the JWS File That Invokes a Conversational Web Service for an example of using this class.

See "weblogic.wsee.conversation.ConversationUtils" in Oracle WebLogic Server API Reference for details.

Updating the build.xml File for a Client of a Conversational Web Service

You update a build.xml file to generate the JWS file that invokes a conversational Web service by adding taskdefs and a build-clientService target that looks something like the following example. See the description after the example for details.

<taskdef name="jwsc"
    classname="weblogic.wsee.tools.anttasks.JwscTask" />

  <target name="build-clientService">

    <jwsc
        enableAsyncService="true"
        srcdir="src"
        destdir="${clientService-ear-dir}" >

        <jws file="examples/webservices/conversation/ConversationalClientImpl.java" >
          <clientgen 
            wsdl="http://${wls.hostname}:${wls.port}/conv/ConversationalService?WSDL" 
            packageName="examples.webservices.conversation"/> 

        </jws>

    </jwsc>

  </target>

Use the taskdef Ant task to define the full classname of the jwsc Ant tasks.

Update the jwsc Ant task that compiles the client Web service to include a <clientgen> child element of the <jws> element so as to generate and compile the JAX-RPC stubs for the deployed ConversationalService Web service. The jwsc Ant task automatically packages them in the generated WAR file so that the client Web service can immediately access the stubs. You do this because the ConversationalClientImpl JWS file imports and uses one of the generated classes.

Updating a Stand-Alone Java Client to Invoke a Conversational Web Service

The following example shows a simple stand-alone Java client that invokes the conversational Web service described in Programming Guidelines for the Conversational JWS File. See the explanation after the example for coding guidelines that correspond to the Java code in bold.

package examples.webservices.conv_standalone.client;

import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;
import javax.xml.rpc.Stub;
import weblogic.wsee.jaxrpc.WLStub; 

/**
 * stand-alone client that invokes and converses with ConversationlService.
 */

public class Main {

  public static void main(String[] args)
      throws ServiceException, RemoteException{

      ConversationalService service = new ConversationalService_Impl(args[0] + "?WSDL");
      ConversationalPortType port = service.getConversationalServicePort();

      // Set property on stub to specify that client is invoking a Web service
      // that uses advanced features; this property is automatically set if
      // the client runs in a WebLogic Server instance.

      Stub stub = (Stub)port; 
      stub._setProperty(WLStub.COMPLEX,  "true"); 

      // Invoke start operation to begin the conversation
      String result = port.start(); 
      System.out.println("start method executed.");
      System.out.println("The message is: " + result);

      // Invoke continue operation
      result = port.middle("middle" ); 
      System.out.println("middle method executed.");
      System.out.println("The message is: " + result);

      // Invoke finish operation
      result = port.finish("finish" ); 
      System.out.println("finish method executed.");
      System.out.println("The message is: " + result);

  }

}

Follow these guidelines when programming the stand-alone Java client that invokes a conversational Web service. Code snippets of the guidelines are shown in bold in the preceding example.

  • Import the weblogic.wsee.jaxrpc.WLStub class:

    import weblogic.wsee.jaxrpc.WLStub;
    
  • Set the WLStub.Complex property on the JAX-RPC stub of the ConversationalService using the _setProperty method:

    Stub stub = (Stub)port;
    stub._setProperty(WLStub.COMPLEX,  "true");
    

    This property specifies to the Web services runtime that the client is going to invoke an advanced Web service, in this case a conversational one. This property is automatically set when invoking a conversational Web service from another WebLogic Web service.

  • Invoke the start operation of the conversational Web service to start the conversation:

          String result = port.start();
    
  • Optionally invoke the continue methods to continue the conversation:

    result = port.middle(message );
    
  • Once the conversation is completed, invoke the finish operation so that the conversational Web service can free up the resources it used for the current conversation:

    result = port.finish(message );
    

Example Conversational Web Service .NET Client

This section demonstrates how to create a .NET WSE3.0 client for a WebLogic conversational Web service. The example includes the following files:

  • ConversationService.java -- JWS file that uses the @Conversation and @Callback annotations to implement a conversational Web service. ConversationService.java can optionally communicate results to its client via a callback.

  • Service.cs -- The C# source file of the ConversationClient .NET Web service that acts as a client to the ConversationService Web service.

    The sample .NET client can participate in conversations with ConversationService, as well as receiving results via callback.

  • build.xml -- Ant build file that contains targets for building and deploying the Conversational Web service.

These files are described in detail in the sections that follow.

ConversationService.java File

The example ConversationService.java file is shown in Example 4-1. The example includes extensive comments that describe its function.

Example 4-1 ConversationService.java File

package conv;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.Oneway;
 
import weblogic.jws.Conversation;
import weblogic.jws.Callback;
import weblogic.jws.CallbackService;
 
import java.io.Serializable;
 
/**
 * Demonstrates use of the @Conversation annotation to manage the lifetime of the service
 * and provide data persistence and message correlation.
 * 
 * Remember that multiple clients might invoke a web service simultaneously.  When the
 * web service stores data relevant to the client or calls additional services
 * in order to process a client's request, the service must be able to process returned
 * data from the external services in the context of the specific client it relates
 * to.  This is all automatic when conversations are used.
 * 
 * Remember that not all clients are capable of accepting callbacks.  Specifically,
 * clients operating from behind firewalls may not be able to receive asynchronous
 * callbacks.  You may wish to provide a synchronous interface, like this one,
 * for such clients.  If a client can accept callbacks, it must send a callback endpoint refrence
 * as part of any "start conversation" method invocation.
 * 
 * To see the behavior in the Test View, invoke startRequest and then getRequestStatus
 * several times quickly.
 */
@WebService(serviceName = "ConversationService", portName = "conversation", targetNamespace = "http://www.openuri.org/")
public class ConversationService implements Serializable {
 
  @Callback
  public CallbackInterface callback;
  private boolean useCallbacks;
  private int num;
 
  /**
   * Starts the conversation
   */
  @Conversation(Conversation.Phase.START)
  @WebMethod
  public void startRequest(boolean useCallbacks) {
    this.useCallbacks = useCallbacks;
  }
 
  @WebMethod
  @Conversation(Conversation.Phase.CONTINUE)
  public String getRequestStatus() {
 
    num++;
    if (num == 1)
      return "This is the first time you call getRequestStatus method.";
    if (num == 2 && useCallbacks) {
      callback.onResultReady("finished");
      return "This is the second time you call  getRequestStatus method, the conversation has been terminated automtically when the onResultReady callback method is invoked.";
    } else
      return "You have called getRequestStatus method " + num + " times";
 
  }
 
  /**
   * Used to tell Conversation.jws that the current conversation is
   * no longer needed.
   */
  @WebMethod
  @Conversation(Conversation.Phase.FINISH)
  public void terminateRequest() {
 
  }
 
  @CallbackService(serviceName = "ConversationCallbackService")
  public interface CallbackInterface {
 
    /**
     * Callback to invoke on the client when the external service
     * returns its result. Will only be called it the client can
     * accept callbacks and told us where to send them.
     * <p/>
     * If this callback is used, it implicitly terminates the
     * conversation with no action required on the part of the
     * client.
     */
    @WebMethod
    @Oneway
    @Conversation(Conversation.Phase.FINISH)
    public void onResultReady(String result);
  }
 
}

Service.cs File

The example Service.cs file is shown in Example 4-2.

This conversation proxy file was created using the Microsoft WSDL to Proxy Class tool WseWsdl3.exe (see http://msdn.microsoft.com/en-us/library/aa529578.aspx) and the ConversationService Web service's WSDL file.

The example includes extensive comments that describe its function.

Example 4-2 Service.cs File

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Diagnostics;
using System.IO;
using System.Xml;
using Microsoft.Web.Services3.Addressing;
using Microsoft.Web.Services3;
using System.Collections.Generic;
using Microsoft.Web.Services3.Design;
 
 
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    
    public Service () {
 
        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }
 
    /*
    * start invokes the Conversation web service's startRequest
    * operation.
    * 
    * Since the Conversation web service is conversational,
    * we must also persist the ReplyTo endpoint reference SOAP header
    * for subsequent calls.
    * 
    * Since the Conversation web service can optionally communicate
    * the result of it's work via a callback, we must prepare a
    * second SOAP header CallbackTo SOAP header, which is the endpoint reference 
    * of the recipient to which callbacks should be sent.
    */
    [WebMethod(EnableSession = true)]
    public void start(Boolean useCallbacks, Boolean useIPAddress)
    {
        /*
         * The Conversation proxy was created using .NET WSE3.0's WseWsdl3.exe
         * application and the Conversation.jws's WSDL file.  The WSDL
         * file for any WLS web service may be obtained
         * by hitting the web service's URL with "?WSDL" appended to
         * the end.  For example:
         * 
         * http://somehost:7001/samples/async/Conversation.jws?WSDL
         * 
         * WseWsdl3.exe produces a C# proxy class.  Place the resulting
         * ConversationService.cs file in your .NET project, then use Visual
         * Studio's Project->Add Existing Item menu action to "import"
         * the class into the project.
         */
        ConversationServiceSoapBinding conv;
        String callbackLocation;
        int asmxIndex;
 
        /*
         * Construct the callback URL from various pieces of
         * server and HttpRequest info.
         */
        Uri requestUrl = Context.Request.Url;
 
        if (useIPAddress)
        {
            /*
             * if useIPAddress is true, construct the callback address
             * with the IP address of this host.
             */
            callbackLocation = requestUrl.Scheme + "://" + System.Net.Dns.GetHostByName(requestUrl.Host).AddressList[0] +
                ":" + requestUrl.Port + requestUrl.AbsolutePath;
        }
        else
        {
            /*
             * if useIPAddress is false, construct the callback address
             * with the hostname of this host.
             */
            callbackLocation = requestUrl.Scheme + "://" + requestUrl.Host +
                ":" + requestUrl.Port + requestUrl.AbsolutePath;
        }
 
        // Remove everything after ".asmx"
        asmxIndex = callbackLocation.IndexOf(".asmx") + 5;
        callbackLocation = callbackLocation.Remove(asmxIndex,
                                 callbackLocation.Length - asmxIndex);
 
        /*
         * Create an instance of the proxy for the Conversation
         * web service.
         * 
         */
        conv = new ConversationServiceSoapBinding();
 
        
        /*
         * When callback is enabled, a custom callback header should be added into 
         * the outbound soap message. 
         * 
         */
        if (useCallbacks)
            enableSoapFilterToAddCallbackHeader(conv, callbackLocation);
        
    
        /*
         * Invoke the startRequest method of the web service. The
         * single boolean parameter determines whether the Conversation
         * web service will use callbacks to communicate the result
         * back to this client.
         * 
         * If the argument is true, an onResultReady callback will
         * be sent when the result is ready.  This client must implement
         * a method with that name that expects the message shape defined
         * by the target web service (returns void and accepts a single
         * string argument).  See the onResultReady method below.
         * 
         * If the argument to startRequest is false, callbacks will not
         * be used and this client must use the getRequestStatus method
         * to poll the Conversation web service for the result.
         */
        conv.startRequest(useCallbacks);
        /*
         * Persist the ReplyTo header  in session state so that it can
         * be used in other methods that take part in the conversation.
         * 
         * This is not safe since one session could start multiple
         * conversations, but there is no other apparent way to persist
         * this information.  Member variables of WebService classes
         * are not persisted across method invocations.
         */
        Session["ConversationReplyTo"] = conv.ResponseSoapContext.Addressing.ReplyTo;
 
 
    }
 
    /* the CallbackTo header defintion isn't exposed by WLS9x/WLS10x callback service. 
     * So we need to use SOAPFilter to add the CallbackTo header. 
     * 
     */
    private static void enableSoapFilterToAddCallbackHeader(ConversationServiceSoapBinding conv, String callbackLocation)
    {
        //Create a custom policy.
        Policy myPolicy = new Policy();
        // Create a new policy assertion
        MyPolicyAssertion myAssertion = new MyPolicyAssertion(callbackLocation);
        // Add the assertion to the policy
        myPolicy.Assertions.Add(myAssertion);
        //Set the custom policy you have created on the client proxy 
        conv.SetPolicy(myPolicy);
    }
 
     /*
     * getStatus invokes Conversation's getRequestStatus method.
     * getRequestStatus is a polling method that is an alternative
     * for web services that cannot recieve callbacks.
     * 
     * Note that a conversation must be started with startRequest before
     * this method may be invoked.  If not, or if this method is invoked
     * outside of a conversation for any reason, it will get back a SOAP
     * fault indicating that the conversation does not exist.
     */
    [WebMethod(EnableSession = true)]
    public String getStatus()
    {
        String result;
 
        /*
         * Create an instance of the proxy for the Conversation
         * web service.  We could probably persist the proxy instance
         * in session state, but chose not to.
         */
        ConversationServiceSoapBinding conv = new ConversationServiceSoapBinding();
 
        /*
         * change the destination to the ReplyTo endpoint reference we cached on session state in
         * the start method.
         */
        conv.RequestSoapContext.Addressing.Destination = (EndpointReference)Session["ConversationReplyTo"];
        /* 
         * Invoke the getRequestStatus method of the web service.
         */
        result = conv.getRequestStatus();
        return result;
    }
 
    /*
     * finish invokes Conversation's terminateRequest method, which
     * terminates the current conversation.
     * 
     * Note that a conversation must be started with startRequest before
     * this method may be invoked.  If not, or if this method is invoked
     * outside of a conversation for any reason, it will get back a SOAP
     * fault indicating that the conversation does not exist.
     */
    [WebMethod(EnableSession = true)]
    public void finish()
    {
        /*
         * Create an instance of the proxy for the Conversation
         * web service.  We could probably persist the proxy instance
         * in session state, but chose not to.
         */
        ConversationServiceSoapBinding conv = new ConversationServiceSoapBinding();
 
        /*
         * change the destination to the ReplyTo endpoint reference we cached on session state in
         * the start method. Both "continue" and "finish" methods use the same destination.
         */
        conv.RequestSoapContext.Addressing.Destination = (EndpointReference)Session["ConversationReplyTo"];
        /* 
         * Invoke the terminateRequest method of the web service.
         */
        conv.terminateRequest();
    }
 
    /*
     * onResultReady is a callback handler for the onResultReady
     * callback that Conversation.jws can optionally use to return
     * its results.
     * 
     * .NET WSE3.0 does not support callbacks directly, but a callback is just
     * a method invocation message.  So if you construct a WebMethod with
     * the same signature as the callback and set the XML namespace
     * properly, it serves as a callback handler.
     * 
     */
    [WebMethod]
    [SoapDocumentMethod(OneWay = true, 
        Action = "http://www.openuri.org/ConversationService_CallbackInterface/onResultReady", 
        RequestElementName = "http://www.openuri.org/", 
        ResponseNamespace = "http://www.openuri.org/"
        )]
    public void onResultReady(String result)
    {
        /*
         * When the callback is invoked, log a message to the
         * hardcoded file c:\temp\ConversationClient.log.
         * 
         * Note: if c:\temp does not exist on this server, an
         * Exception will be raised.  Since it is not handled here,
         * it will be returned as a SOAP fault to the Conversation
         * web service.
         */
        TextWriter output;
        output = File.AppendText("c:\\temp\\ConversationClient.log");
        String msg = "[" + DateTime.Now.ToString() + "] callback received";
        output.WriteLine(msg);
        output.Flush();
        output.Close();
    }
 
    
}
 
public class MyFilter : Microsoft.Web.Services3.SoapFilter
{
    private String callbackLocation;
 
    public MyFilter(String callbackLocation)
    {
        this.callbackLocation = callbackLocation;
    }
    
    public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
    {
 
       //create the CallbackTo soap element.
       XmlDocument xmldoc = new XmlDocument();
       XmlElement xmlEle = xmldoc.CreateElement("callback", "CallbackTo", "http://www.openuri.org/2006/03/callback");
       
       //create the CallbackTo endpoint reference.
       Address callbacto = new Address(new Uri(callbackLocation));      
       XmlElement xmlEle2 =new EndpointReference(callbacto).GetXml(xmldoc);
       //add the CallbackTo endpoint reference into CallbackTo SOAP element.
       xmlEle.AppendChild(xmlEle2.FirstChild);
       //add the whole CallbackTo SOAP element into SOAP header. 
       XmlNode callbackheader = envelope.ImportNode(xmlEle, true);
       envelope.DocumentElement.FirstChild.AppendChild(callbackheader);
       return SoapFilterResult.Continue;
 
    }
}
 
public class MyPolicyAssertion : Microsoft.Web.Services3.Design.PolicyAssertion
{
    private String callbackLocation;
 
    public MyPolicyAssertion(String callbackLocation)
    {
        this.callbackLocation = callbackLocation;
    }
    
    public override  SoapFilter CreateClientInputFilter(FilterCreationContext context)
    {
        return null;
    }
 
 
    public override  SoapFilter CreateClientOutputFilter(FilterCreationContext context)
    {
        //use MyFilter to add the CallbackTo header in the outbound soap message. 
        return new MyFilter(callbackLocation);
    }
    
    public override  SoapFilter CreateServiceInputFilter(FilterCreationContext context)
    {
        return null;
    }
    
    public override  SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
    {
        return null;
    }
 
}

build.xml File

The example build.xml file is shown in Example 4-3.

build.xml assumes that you copy the example source files to a new directory WL_HOME\samples\server\examples\src\examples\webservices\conv, where WL_HOME is the directory containing your WebLogic Server installation.

build.xml also requires that you first set your examples environment correctly via WL_HOME\samples\domains\wl_server>setExamplesEnv.cmd(sh and that the examples server is already started.

The example includes comments that describe the build file function and targets.

Example 4-3 build.xml File

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="webservices.conversation" default="all" basedir=".">
 
  <!-- set global properties for this build -->
  <property file="../../../examples.properties"/>
 
  <property name="client.dir" value="${client.classes.dir}/webservices_conversation" />
  <property name="package.dir" value="examples/webservices/conv"/>
  <property name="package" value="examples.webservices.conv"/>
  <property name="ear.dir" value="${examples.build.dir}/webservicesConversationEar" />
 
  <path id="client.class.path">
    <pathelement path="${java.class.path}"/>
  </path>
 
  <!-- Web service WLS Ant task definitions -->
  <taskdef name="jwsc"
    classname="weblogic.wsee.tools.anttasks.JwscTask" />
  <taskdef name="clientgen"
      classname="weblogic.wsee.tools.anttasks.ClientGenTask" />
 
  <target name="all" depends="build, deploy"/>
 
 
  <target name="clean">
    <delete dir="${ear.dir}"/>
  </target>
 
  <!-- Target that builds the conversational  Web service -->
  <target name="build" description="Target that builds the MTOM Web service">
    <jwsc
      srcdir="${examples.src.dir}/${package.dir}"
      sourcepath="${examples.src.dir}"
      destdir="${ear.dir}"
      classpath="${java.class.path}"
      keepGenerated="true"
      deprecation="${deprecation}"
      debug="${debug}">
        <jws file="ConversationService.java">
                <WLHttpTransport contextPath="/samples/async" serviceURI="conversation.jws"/>
                </jws>
    </jwsc>
  </target>
 
  
 
  <!-- Target that deploys the conversational Web service -->
  <target name="deploy" description="Target that deploys the conversational Web service">
    <wldeploy
      action="deploy"
      source="${ear.dir}"
      user="${wls.username}"
      password="${wls.password}"
      verbose="true"
      adminurl="t3://${wls.hostname}:${wls.port}"
      targets="${wls.server.name}"
      failonerror="${failondeploy}"/>
  </target>
 
  <!-- Target that undeploys the conversational Web service -->
  <target name="undeploy" description="Target that deploys the conversational Web service">
    <wldeploy
      action="undeploy"
      name="webservicesConversationEar"
      user="${wls.username}"
      password="${wls.password}"
      verbose="true"
      adminurl="t3://${wls.hostname}:${wls.port}"
      targets="${wls.server.name}"
      failonerror="${failondeploy}"/>
  </target>
 
</project>

Client Considerations When Redeploying a Conversational Web Service

WebLogic Server supports production redeployment, which means that you can deploy a new version of an updated conversational WebLogic Web service alongside an older version of the same Web service.

WebLogic Server automatically manages client connections so that only new client requests are directed to the new version. Clients already connected to the Web service during the redeployment continue to use the older version of the service until they complete their work, at which point WebLogic Server automatically retires the older Web service. If the client is connected to a conversational Web service, its work is considered complete when the existing conversation is explicitly ended by the client or because of a timeout.

For additional information about production redployment and Web service clients, see "Client Considerations When Redeploying a Web service" in Getting Started With JAX-RPC Web Services for Oracle WebLogic Server.