|
Developer: J2EE & Web Services
Managing State in Service-Oriented Architecture
by Mark M. Davydov
An introduction to using explicit state identifiers to support conversational Web services in an SOA.
One of the most common misconceptions about Web services is that they are appropriate only for supporting synchronous request/response SOAP (Simple Object Access Protocol)-based interactions. One of the main reasons for this misperception is that many Web services are implemented using unreliable, stateless transport protocols such as HTTP.
As a result, many organizations are avoiding implementing Web services for handling complex business interactions, in which a service consumer may have multiple interactions with the same service or multiple interactions with several services, at a time. Such interactions are referred to by OASIS (Organization for the Advancement of Structured Information Standards) as Web service conversations, and Web services that support conversations are referred to as conversational Web services. Conversational Web services are the basis for asynchronous Web services, and they play an essential role in enabling long-lasting business transactions.
In this article, I will explain how to use explicit state identifiers to support conversational Web services. Furthermore, I'll offer an overview of how to implement such an approach using the Oracle JDeveloper 10g 10.1.3 J2EE Developer Preview with built-in support for J2EE 1.4 JAX-RPC.
Introducing Conversational Web Services
Most of the interactions between service consumers and service providers are Web service conversationsfor example, a service endpoint may receive documents of multiple types from multiple clients at a time, processes the documents, and contact the consumers with appropriate responses. In such applications, there is a critical requirement to recognize each client interaction properly, making Web service implementation significantly more complex. In order to avoid that complexity, many companies are reserving Web service implementations to primitive, single request/response type interactions (AuthorizeUser, CalculateTax, ConvertFunds, and so on).
In their ideal form, however, Web services should support single as well as complex interactions among service consumers and providers. Regardless of the transport protocol, synchronous or asynchronous Web services should be capable of maintaining state or resources on behalf of individual clients or within a particular business context across multiple interactions.
This need for addressing statefulness in Web services has long been a primary focus of standard bodies and leading vendors. Several draft Web services specifications WS-Addressing, WS-Resources, and WS-Coordination have emerged to address statefulness in mission-critical and lengthy business processes. Most important, the Business Process Execution Language (BPEL) 1.0 specification has incorporated many features of WS-* standard proposals for handling long-lasting business transactions. Nevertheless, the widespread use of new standards and related vendor technologies to address Web service conversations has yet to be realized.
In short, implementing conversational Web services is still quite a design challenge. Application developers have no choice but to build custom solutions when faced with complex applications that rely on Web services.
How Conversational Web Services Work
A conversational Web service, which is inherently asynchronous, has two important types of service element: callable methods and response methods (commonly referred to as callbacks). Each callable and callback method in a conversational Web service determines the communication behavior between the service, its clients, and any execution controls (Java controls, for example) the service uses. A callable method may start, continue, or end a conversation, and a callback can continue or stop a conversation.
In order to better describe a conversational Web service, let me introduce a scenario that will be used throughout the article. The example employs two conversations: one between a Web service and its client and another between two Web services (See Figure 1).
In this example, the BestTermInsurance Web service looks-up a variety of insurance providers on behalf of a customer by using another service: InsuranceQuote service. The first conversation begins when the client requests a quote from the BestTermInsurance service; the second conversation is originated when the BestTermInsurance service calls the InsuranceQuote service to request the quote. When the InsuranceQuote service finds the lowest quote, it responds to the BestTermInsurance service, and the second conversation is complete. The BestTermInsurance service then resumes the first conversation with the client.
| | Figure 1: BestTermInsurance Service |
Web service conversations maintain a Web service's state; the latter includes correlating data that link communications between the Web service, its clients, and other resources, and any data maintained by the service during conversations until their completion. Such state information is referred to as conversation state (also known as correlation state). In case of our example, the BestTermInsurance service maintains the individual quotes for the client and associated message attributes until the lowest quote is found.
Managing Conversation State
|
Building Stateful Web Services with Oracle
Oracle Application Server Web services toolset allows application developers to implement Web services using two types of J2EE components: Java classes or EJBs. Also, Oracle Application Server Web services developers can use the same set of command line utilities to create, package, and deploy Web services that they use for other Oracle Application Server Containers for J2EE (OC4J) applications.
As for deploying J2EE components, Oracle JDeveloper provides graphical tools for configuring the J2EE descriptors and simplifying the actual deployment process. In addition, the Oracle Application Server Web services toolset includes a wizard that assists developers in creating an EJB whose methods access and process XML-based messages. The same toolset generates WSDL and requester-side proxy stubs. This generation occurs when the Web service is assembled using the Web services Assembly (WSA) tool or, for a deployed Web service, the first time the WSDL or the requester-side stubs are requested.
Finally, in Oracle Application Server 10g EJB 3.0 Preview, Oracle has implemented the most valuable features of EJB 3.0 Early Draft 2 specification that makes exposing EJBs as Web services much simpler. In particular, with EJB 3.0, it is no longer necessary to implement home and component interfaces in order to use javax.ejb.EnterpriseBean interfaces, and the EJB bean class can be a basic Java class (its business interfaces can also be used as the EJB interfaces).
|
Using explicit state identifiers is the most straightforward and reasonably effective technique for managing the conversation state in Web service interactions. Although the name suggests a single ID, fully managed conversations require more information, especially when associated with service requests. As a result, a state identifier is really a control class that maintains the following set of state-related information with respect to each request-response interaction:
- Unique identifier of the Web service client
- Identifier of the Web service conversation originated for that client
- Identifier of the service request within that conversation
- Indicator of the conversation phase between the service requester (client) and service provider (Starting, Continuing, and Ending), which is used by the service for proper state maintenance
- Indicator of the interaction status between the service distribution function of the Web services infrastructure and the invocation interface (very helpful in dynamic invocations with late-binding of Web service interfaces)
For the purposes of this article, I am assuming static Web service invocations, and thus #5 will not be discussed here.
Generally speaking, the technical solution for correlating requests is to have the service requester add the state identifier information an unique identifier of the service request (Request ID), identifier of the conversation (Conversation ID), and conversation phase indicator (Phase) to the request message, and have the service provider copy the identifier information to the response message passed via callback, so that the requester can correlate the reply message to the request message.
There are several techniques for including the state identifier information within a message exchange, specifically:
- Pass the identifier information as an extra parameter in the Web service method; the Web service interface can be designed to accept not only the XML document message, but also to accept additional parameters that represent the state identifier information.
- Pass the identifier information as a part of the XML documentthe XML document can contain such information embedded within its body.
- Pass the identifier information in the SOAP message header.
However, both the first technique of including the identifier information as part of service interface by adding extra input parameters, and the second technique of including such information within the XML document itself, effectively make the identifier information handling a part of the service structure, which results in code that is harder to maintain. Moreover, in the future, as the new Web services standards become more widely accepted, custom solutions may not be needed anymore and it will be difficult to "dismantle" the embedded identifier-related logic from the service implementation.
As a result, the third option including the identifier information within the SOAP message header is the most appropriate. When using this technique, the identifier information is added as a new group of sub-elements into a message's SOAP header by service requesters. Service providers intercept the SOAP message, and then extract the header with appropriate state identifier information, leaving the message body untouched and unaffected.
In a nutshell, adding the identifier information to a SOAP header keeps related code separate from the document processing and associated business logic, as well as the concrete implementation of service interfaces.
Implementing Explicit State Identifiers An Example
Now, let's continue to review our sample scenario the BestTermInsurance service. (See Figure 2 for illustration of scenario.) As described in the sidebar, Oracle Application Server provides two options for the service implementation: either as a Stateless Java class (deployable within the Web container) or as a stateless session EJB. (The design is based on using EJBs with a service façade BestTermInsuranceServlet). Implementing the service as an EJB with a façade is recommended because of better service availability and scalability, and because it allow for the introduction of SOAP JAX-RPC handlers (to be discussed in detail later).
| | Figure 2: Sample Scenario |
Let's walk through this sample scenario and understand the control. Web service interactions start when the client submits a search request by calling the requestQuote method of the servlet, passing information about the customer age and health. The requestQuote() method has a void return value, and returns immediately allowing the client to continue without waiting for the actual result that will be sent via the onBestQuote() callback, later. Obviously, the client should be designed to accept messages from a callback method.
Next, the BestTermInsuranceServlet invokes the BestTermInsuranceEJB that maintains the customer age and health indicator, and a list of obtained quotes associated with the client ID and request ID as state-related data. BestTermInsuranceEJB then calls the obtainQuote() method of the InsuranceQuote service to get quotes from participating insurers. (See Figure 3, a sequence diagram that illustrates the most important interactions of the service.)
| | Figure 3: Sample UML Sequence Diagram using JDeveloper Modeler |
The InsuranceQuote service operates within a particular client scope, so the service needs access to the client ID. In terms of the conversation tracking, the InsuranceQuote service uses its own conversation and requests IDs.
On gathering all the insurance quotes for a given customer age and health indicator, the InsuranceQuote service returns its results using the onObtainQuoteComplete() callback. This callback finishes the second conversation, and clears all state-data maintained by the InsuranceQuote service. However, the first conversation controlled by the BestInsuranceQuoteServlet continues: the BestTermInsurance service may get additional request from the client, for example, to provide the best quote given a particular policy term (one year, 10 years, etc.). Results are sent to the client as messages via onBestQuoteCallback() method.
Coding Techniques
Now let's focus on specific coding techniques for implementing state identifiers. Unfortunately, given space limitation, only elements relevant to the article's topic will be covered in code snippets.
As discussed previously, assume that the state identifier information is passed in SOAP message headers. The Java APIs for XML-based Remote Procedure Call (JAX-RPC) are well suited for processing SOAP message headersspecifically, by using one of the most interesting features of JAX-RPC: message handlers. Message handlers provide additional message-handling logic to Web services as extensions to the business logic implementations of those services. Handlers can support the insertion of state identifiers in addition to managing authentication, encryption and decryption, logging and auditing, and so on. For these purposes, multiple handlers could be created for managing specific concerns. In JAX-RPC, such usage of handlers is referred to as a handler chain.
A handler chain represents an ordered group of handlers. The ordered grouping allows application developers to define handler invocation policies order of invocation, designation ("handle request" only or "handle response only", or both), and so on. The handler chain continues processing of the handlers until a SOAP fault is raised or the invocation policy indicates an explicit stop-point.
Therefore, for implementing Web services using state identifiers embedded within a SOAP header, I recommend a three-step development process:
- Design and code the basic service components.
- Develop SOAP message handlers.
- Construct Web services (expose) out of developed J2EE components.
When using the Oracle JDeveloper 10g J2EE Developer Preview, any compiled Java class or bean can be exposed as a Web service using the category "Business Tier" under the application navigator. From this category, developers can select a context menu item "Java Web services." This item has two options, "J2EE 1.4 (JAX-RPC) Web services" or "Oracle J2EE 1.3 Web services." Select "J2EE 1.4 (JAX-RPC) Web services."
The following listing illustrates a desired format for the SOAP message with a header that contain state identifiers for the BestTermInsurance service's outer (client-service) conversation:
<SOAP-ENV:Envelope>
<SOAP-ENV:Header>
<ns:stateIdentifier xmlns:ns="http://xxxxx">
<ns:ClientID>Client ID</ns:ClientID>
<ns:ConversationID>Conversation ID</ns:ConversationID>
<ns:RequestID>Request ID</ns:Request ID>
<ns:Phase>Phase</ns:Phase>
</stateIdentifier
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns:insuranceParms xmlns:ns="http://yyyyyyy">
<ns:Age>Age</ns:Age>
<ns:Health>Health</ns:Health>
<ns:Term>Term</ns:Term>
</ns:insuranceParms
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
However, in the beginning of the process, the SOAP message definition is without the state identifier information, as shown below:
<SOAP-ENV:Envelope>
<SOAP-ENV:Header>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns:insuranceParms xmlns:ns="http://yyyyyyy">
<ns:Age>Age</ns:Age>
<ns:Health>Health</ns:Health>
<ns:Term>Term</ns:Term>
</ns:insuranceParms
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This is what SOAP handlers are for: to handle the insertion of state qualifiers into the standard SOAP message definition. In JAX-RPC services, a handler is represented by a handler class that implements handleRequest() and handleResponse() methods for modifying the request and response message. The handler class can be configured to work on request and response either at the client or at the service levels. The javax.xml.soap.SOAPMessage class is used by the handler to manipulate the SOAP message. A SOAPMessage object contains a SOAPPart object that contains the actual SOAP XML document and a SOAPEnvelope object that is "decomposed" to access SOAP body and header.
The following listing illustrates the described technique:
package exp.oracle.jaxrrpc.headers;
.............
import javax.xml.rpc.*;
import javax.xml.soap.*;
public class BestTermInsuranceRequestHandler implements javax.xml.rpc.handler.Handler
{
...........................
/*
*The handleRequest method processes the request message.
*/
try {
SOAPMessageContext msg = (SOAPMessageContext) context;
SOAPEnvelope env = msg.getMessage().getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
exit=processRequestHeader(hdr);
}
catch (Exception ex) {
ex.printStackTrace();
}
return exit;
}
/*
* This version of the process header method inserts the state identifier information.
*/
private boolean processRequestHeader (SOAPHeader hdr) {
boolean exit = false;
SOAPFactory sFactory = SOAPFactory.newInstance();
try {
SOAPElement he1 = sFactory.createElement("stateIdentifier",PREFIX,URI);
SOAPElement che11 = sFactory.createElement("ClientID",PREFIX,URI);
che11.addTextNode("Cust1");
...............................
he1.addChildElement(che11);
he1.addChildElement(che12);
he1.addChildElement(che13);
// add the state identifier information to the SOAP header object
hdr.addChildElement(he1);
exit = true;
}
catch (Exception ex) {
ex.printStackTrace();
}
return exit;
}
After SOAP handlers are developed, you can configure them in the appropriate webservices.xml file using WSA. Then, you can proceed to create deployment descriptors and install a Web service into OC4J.
Conclusion
Clearly, using single request-reply interaction with stateless Web services is the easiest implementation approach. But with the added simplicity comes a significant drawback: the inability to implement Web services for handling complex and long-running business transactions.
The strategy underpinning the state management approach presented in this article models long-running business activities in a much more elegant way. Specifically, in this approach, services "enroll" in well-contained conversations that represent specific units of work as the business activity progresses. Such capability is crucial for connecting Web service-enabled applications within and across enterprises, with a set of features enabling management and monitoring of interactions between connected applications.
Mark M. Davydov [markdavydov@netscape.net] is an expert in software engineering and systems architecture practices, including SOA and Web services.
Send us your comments
|