Web Service Design Considerations
You need to consider several architectural design choices when building a web service for WebLogic:
- You need to carefully architect the "signature" for each operation of the web service and decide whether the operation will be invoked in a synchronous request-reply fashion, or in a one-way asynchronous manner.
- You need to decide what type of backend components (if any) will be used to implement the operations of the web service.
- You need to determine whether your web service is designed to be process- or data-oriented. Based on this, you can decide whether the web service implementation is better suited to handling traditional RPC-style invocations or a single XML document that is exchanged between the client and the web service.
- If the SOAP request/response messages need additional processing before/after they are handled, you need to configure the chain of SOAP message handlers that will be associated with the web service operations.
- If the web service is designed to use custom datatypes, you need to determine the structure for these datatypes and how they can be converted between their Java and XML representations.
All these factors influence how you design the operations of a web service and choose a best-fit implementation.
19.2.1 RPC-Oriented Versus Document-Oriented Web Services
When creating a WebLogic web service, you can specify whether its operations are RPC-oriented or document-oriented. In the case of an RPC-oriented operation, the SOAP message encapsulates the parameters and return values using SOAP encoding. In the case of a document-oriented operation, the SOAP message encapsulates a single XML document using literal encoding. For this reason, methods that support RPC-oriented operations can declare any number of parameters, whereas methods that implement document-oriented operations must declare only one parameter (of any supported type, though typically an XML document). In addition, a document-oriented web service cannot declare out or in-out parameters. Thus, the choice between an RPC-oriented web service versus a document-oriented web service not only determines the structure of the SOAP message, but also impacts how its operations are implemented. WebLogic 8.1 introduces a variation of the standard document-oriented operations, namely document-wrapped operations. A document-wrapped operation can take any number of parameters. All of the parameters are wrapped into one complex datatype in the SOAP message.
By default, a WebLogic web service is RPC-oriented. In order to change the default behavior so that it's document-oriented, you need to adjust the style attribute of the web-service element in the web-services.xml descriptor file. The following example illustrates how you can declare a document-oriented WebLogic web service:
style="document">
Set the style to documentwrapped for document-wrapped operations. You also can use the servicegen task to generate document-oriented web services by adding a style="document" attribute. The following example shows how to create a document-oriented web service that wraps the specified Java class:
style="document"
Note that WebLogic does not allow you to mix both RPC-oriented and document-oriented operations in the same web service. You can mark the web service as either RPC-oriented or document-oriented only, in which case the setting applies to all operations exposed by the web service. Thus, any method that implements an operation of a document-oriented web service must declare only one parameter of any supported type. Of course, this restriction is relaxed for any methods that implement the operations of an RPC-oriented web service.
19.2.2 Synchronous Versus Asynchronous Operations
Web service operations can be either synchronous or asynchronous. The "synchronous request-response" behavior, which is the default, means that the client waits for a SOAP response each time the operation is invoked. Web service operations usually exhibit synchronous request-response behavior, which is analogous to traditional RPC-style communication. The client always receives a SOAP response in synchronous communications, even if the method that implements the operation has a void return type.
If an operation is marked as asynchronous, the client never receives a SOAP response, even if an exception or a SOAP fault is raised. Because no response is sent back to the client, asynchronous operations must not declare any out or in-out parameters. They can use only "in" parameters. Besides this, any method that implements an asynchronous operation must explicitly return a void type. For example, this is a valid implementation for an asynchronous web service operation:
public void async(String arg) { // no response is returned to the client, so it will not wait for the method to complete. }
In order to configure an asynchronous operation, you need to adjust the invocation-style attribute of the operation element in the web-services.xml descriptor file. The default value for the invocation-style attribute is "request-response," which means the operation receives a SOAP request and sends back a SOAP response to the client. The following portion of the web-services.xml descriptor shows how to mark a web service operation as being asynchronous by setting this attribute to "one-way":
invocation-style="one-way" method="async(java.lang.String)" component="jcComp0" name="async">
Remember, the method that implements an asynchronous operation must use a void return type and can accept only "in" parameters.
19.2.3 Datatypes
WebLogic supports a rich mixture of built-in and custom datatypes that can be used to define the parameters and return values for web service operations. Built-in datatypes are those that are natively supported by WebLogic's framework, which means all datatypes defined by the JAX-RPC specification. Using built-in datatypes is fairly straightforward because WebLogic is able to automatically convert the data between its Java and XML representations.
WebLogic also supports web services whose operations rely on custom datatypes any complex datatype that can be represented using an XML Schema.
19.2.4 Stateful Web Services
WebLogic's web services are inherently stateless because the only components that can live behind these web services are stateless. If a web service operation is implemented using a public method of a standard Java class, WebLogic uses a single instance of the class across all client invocations of the web service. Similarly, if the web service is supported by a stateless session EJB or a JMS destination, again no conversational state is maintained across client invocations.
Therefore, if you need to implement stateful web services, you must explicitly engineer the stateful behavior. One way is to require clients to pass a unique ID whenever they invoke a web service operation. The ID will uniquely identify the client that initiated the request. The backend code could then use the client's ID to persist any conversational state and later use the same ID to recover the client-specific state information from the persistent store.
19.2.4.1 Creating an HttpSession for a web service
WebLogic 8.1 provides a great feature for maintaining server-side session state for a web service, which avoids the need for explicitly passing some token to and fro during web service invocations. Simply use the weblogic.webservice.context.WebServiceSession class to access WebLogic's internal HTTP session infrastructure. Your web service implementation can obtain the session object from a weblogic.webservice.context.WebServiceContext object, and then use the setAttribute( ), getAttribute( ), and invalidate( ) methods as usual. Here is an example of a Java class backend for a web service that maintains session information:
public class Simple { public int getSessionValue( ) { WebServiceSession session = null; session = WebServiceContext.currentContext( ).getSession( ); Integer count = (Integer) session.getAttribute("count"); if (count == null) count = new Integer(0); session.setAttribute("count", new Integer(count.intValue( ) + 1)); return count.intValue( ); } }
Here, an operation backed by the getSessionValue( ) method increments a counter stored in the session each time the operation is invoked. All of the session-handling code is hidden in the web service implementation. The client doesn't need any special code to take advantage of the session state. It can simply invoke the web service operation as usual:
Simple ws = new Simple_Impl(where); SimplePort port = ws.getSimplePort( ); System.out.println(port.getSessionValue( )); System.out.println(port.getSessionValue( )); System.out.println(port.getSessionValue( )); //prints out 0,1,2...
Because the WebServiceSession object relies on an HTTP session to maintain the state, any client of the web service also must support HTTP cookies for this to work. You can also use it in a standalone client application, in which case the state is maintained on the client itself.
19.2.5 JMS Transport
The standard approach for invoking an operation on a web service is to use either the HTTP or HTTPS protocol. WebLogic 8.1 lets you use JMS as a transport too. The client need not create an HTTP connection to the server running the web service. Instead it can look up a JMS connection factory, interact with a destination for invoking an operation, and then receive any results through a temporary destination. All of this occurs under the hood though, and your client is mostly unchanged. The JMS transport can be configured for any web service, regardless of whether it is implemented using a JMS, EJB, or plain Java class backend. It merely alters the transport that is used to invoke the operations on the web service. Because this facility is nonstandard, your clients will be able to use this transport mechanism only for web services running on WebLogic.
To use the JMS transport, you need to first set up a JMS connection factory and JMS destination on the WebLogic instance that hosts the web service. The JMS server hosting the destination also must support temporary destinations so, you must ensure that you have set up a Temporary Template for the JMS server, as outlined in Chapter 8. Let's assume that a WebLogic instance hosts a connection factory myCF, and a JMS server with a destination called myQ.
Unfortunately, the servicegen Ant task doesn't let you set up a web service to use the JMS transport. You have to manually edit the web-services.xml descriptor file, and make the necessary changes. We suggest that you create the web service using supplied tools, and then modify the generated deployment descriptor. The only change needed to support JMS transport is to include the jmsUri attribute in the web-service element:
Here, the value of the jmsUri attribute matches the pattern connection-factory-name/queue-name. You can now go ahead and repackage and deploy the web service to WebLogic. The WSDL of the deployed web service will in fact have two ports for the web service a standard port for HTTP traffic, and an additional port for JMS traffic.
The client application JAR can be created in the usual way the clientgen task will use the jmsUri attribute to automatically create an additional port. Usually you get the port for a service say, Simple by calling the getSimplePort( ) method on a service object. WebLogic will provide an additional method, getSimplePortJMS( ), which lets the client invoke a web service using the JMS transport. Here, the code invokes a web service, first using the standard HTTP transport and then using the JMS transport:
Simple ws = new Simple_Impl(where); SimplePort portHTTP = ws.getSimplePort( ); String reply1 = portHTTP.makeUpper("hello World"); SimplePort portJMS = ws.getSimplePortJMS( ); String reply2 = portJMS.makeUpper("hello World");
The client application is now also a JMS client because it looks up a JMS connection factory and interacts with a JMS destination. So, you need to package the client with the appropriate libraries. In WebLogic 8.1, you need to include wlclient.jar and wljmsclient.jar, and in WebLogic 7.0 you must include the monolithic weblogic.jar.