|
The following sections describe how to create a conversational Web Service:
| WARNING: | This feature can be implemented only for a JAX-RPC 1.1-based Web Service; you cannot implement it for a JAX-WS 2.0 Web Service. |
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:
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:
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.
| WARNING: | 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. |
| Caution: | 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 Services Reliable Messaging, and Using the Asynchronous Features Together. |
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:
See Programming Guidelines for the Conversational JWS File.
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.
prompt> ant build-mainService
See Deploying and Undeploying WebLogic Web Services.
See Programming Guidelines for the JWS File That Invokes a Conversational Web Service.
build.xml file that builds the client Web Service.See Updating the build.xml File for a Client of a Conversational Web Service.
prompt> ant build-clientService
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.
java.io.Serializable, so you must first import the class into your JWS file:import java.io.Serializable;
import weblogic.jws.Conversation;
import weblogic.jws.Conversational;
@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 Using the JwsContext for more information about the runtime Web Service context.
@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 for additional information and default values for the attributes.
java.io.Serializable:public class ConversationalServiceImpl implements Serializable {weblogic.wsee.jws.JwsContext, with the field-level @Context JWS annotation:@Context
private JwsContext ctx;
@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 for additional information.
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 Using the JwsContext for detailed information on using the context-related APIs.
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 operationString result = port.start();System.out.println("start method executed.");
System.out.println("The message is: " + result);
// Invoke continue operationresult = port.middle(message );System.out.println("middle method executed.");
System.out.println("The message is: " + result);
// Invoke finish operationresult = 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:
@ServiceClient JWS annotation:import weblogic.jws.ServiceClient;
import weblogic.wsee.conversation.ConversationUtils;
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;
@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;
@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();result = port.middle(message );
result = port.finish(message );
renewStub() method of the weblogic.wsee.conversation.ConversationUtils utility class:ConversationUtils.renewStub((Stub)port);
| WARNING: | 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. |
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 for details.
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.
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 conversationString result = port.start();System.out.println("start method executed.");
System.out.println("The message is: " + result);
// Invoke continue operationresult = port.middle("middle" );System.out.println("middle method executed.");
System.out.println("The message is: " + result);
// Invoke finish operationresult = 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.
weblogic.wsee.jaxrpc.WLStub class:import weblogic.wsee.jaxrpc.WLStub;
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.
String result = port.start();result = port.middle(message );
result = port.finish(message );
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.
|