Other Considerations for Web Services

So far, the Web Services you have seen have used simple parameters, such as strings and integers and have been free from errors. However, in the real world, most systems will need to pass complex types such as arrays, classes, and data structures and will need to cope with issues such as error handling. During the rest of today, you will look at such issues.

Web Service Errors and Exceptions

The use of Web Service protocols implies that your application is making calls across network boundaries. As such, you must be prepared to handle any network-related errors that occur. As the Web Service interface is based around a Java remote interface, any client code must catch and handle java.rmi.RemoteException. A RemoteException will be generated in response to any unhandled runtime exceptions that occur. Such exceptions are mapped to SOAP fault elements that are included in the body of the response from the server.

You can define domain exceptions—for example, InvalidProductCodeException—on your Web Service remote interfaces. These exceptions are defined in the service WSDL as fault elements. Such fault elements are marshaled back to the client in the SOAP message that forms the response from the server. For a Java client, the exception will be regenerated in its original form and rethrown. The handling of fault elements by non-Java clients will depend on the support for WSDL-defined fault elements in that environment. A Java exception is a complex type, so the JAX-RPC runtime must be able to marshal and unmarshal at least some of its contents (such as its message). Therefore, exceptions must conform to certain rules to be successfully passed. These rules are similar to those for JavaBeans and are defined in section 5 of the JAX-RPC specification (JSR101).

Context and Lifecycle of JAX-RPC Web Services

The servers created so far have been very simple. However, real-world applications require more sophistication. It is important to know about and control the lifetime of your service instances. The service might need to initialize itself when it is created and release resources when it is destroyed. Similarly, it might need to obtain information relating to this call, such as the identity of the caller or message header settings.

The lifecycle and state management of your Web Service implementation will depend on the type of component that underlies it. If you have built a Web Service based on a stateless session EJB, a new bean will be instantiated for each call and it will follow the stateless session EJB lifecycle. If you are using a servlet-based implementation, there is no guarantee that successive calls will use the same instance. In both cases, JSR109 explicitly states that there should be no reliance on state shared across calls. This means that, although JAX-RPC programming looks like traditional RPC programming in which you communicate with a specific object, it does not behave this way. It has much in common with the design and use of stateless session EJBs in that no assumptions about users or their states can be made.

In some Web Service implementations, you can use HTTP cookies to maintain an ongoing conversation between client and server (as used by servlet and JSP session state). However, this is not very helpful because it ties the service implementation to the HTTP protocol. If you want to maintain any form of state in a Web Service, you should explicitly persist that state—probably in a database. You will then need to issue the client with some form of identifier for this state as a return value from the method. Although this can seem clunkier than the use of HTTP-based session state, this “exposed state” is more suited to Web Service interaction. Think of a service that allows you to place and track orders. When placing the order, you will be issued with some form of order identifier. If you need to track the progress of the order, you will submit the order identifier as part of the tracking request. This is the level of granularity at which you should think about Web Service state. It may be hours, or even days, between the placing of the order and the tracking request. You should not expect to keep a session open across this time period, and you should not expect all requests to be made across a particular protocol (HTTP). If this does not sit easily with your design, you are probably not using Web Services at the right level of component granularity and you may be better off using another protocol such as RMI for your client/server communication.

The lifecycle of a stateless session EJB is well documented. It has methods that tell it when it is being readied for use (setSessionContext/ejbCreate) and when it is being discarded (ejbRemove). If you are using a servlet-based Web Service implementation, your class can optionally implement the javax.xml.rpc.server.ServiceLifeCycle interface to receive the same information. This interface defines an init method that passes a context object and a destroy method that perform the same roles as setSessionContext/ejbCreate and ejbRemove, respectively.

The context passed into the ServiceLifeCycle.init method is an implementation of the javax.xml.rpc.server.ServletEndpointContext interface. The primary purpose of this interface is to provide access to the javax.xml.rpc.handler.MessageContext implementation for the current Web Service invocation. The basis of the message context for a SOAP message is extra information contained in the SOAP headers. You can also cast the MessageContext to a javax.xml.rpc.handler.soap.SOAPMessageContext to retrieve the SOAPMessage and the SOAP actor information. The following code fragment shows how you would obtain information from the MessageContext in a servlet-based Web Service:

public void init(Object context)
{
  ServletEndpointContext endpointContext = (ServletEndpointContext)context;

  MessageContext messageContext = endpointContext.getMessageContext();
  // Examine Web Service call headers
  String correlationId = (String)(messageContext.getProperty("CorrelationId"));
  ...
}

The MessageContext is made available to an enterprise bean through the getMessageContext method that has been added to the javax.ejb.SessionContext interface.

In some cases, you need to perform common processing of incoming messages, maybe message inspection and rejection, or some form of transformation of the message content. If your application design calls for a Front Controller or Intercepting Filter (see Day 18 for details), you would typically use a mechanism such as servlet filters to implement this functionality. The JAX-RPC specification provides a similar message pre- and post-processing mechanism that is independent of the container type called handlers. An in-depth discussion of handlers is beyond the scope of this chapter, but basically you can set up a chain of one or more handlers that will pre-process incoming messages or post-process message responses. The handlers have access to the message itself and the MessageContext, so they can communicate with the Web Service implementation by setting or altering values in the MessageContext.

Mapping Between Java and SOAP/WSDL Types

For simple types, SOAP and WSDL use the representations defined in “XML Schema Part 2: Datatypes” that is part of the W3C XML Schema standard. There is a straight mapping for all Java primitive types except for char. There is also a straight mapping for the Java String class.

If you defined a Web Service with the following (unlikely) method:

public void test(byte byteArg, short shortArg, int intArg, long longArg,
                     float floatArg, double doubleArg,
                     boolean boolArg, String stringArg)

this would map into WSDL as follows:

<definitions name="GreetingService" targetNamespace="urn:J2EE21Examples"
             xmlns:tns="urn:J2EE21Examples"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
  <types/>
  <message name="Greeting_test">
    <part name="byte_1" type="xsd:byte"/>
    <part name="short_2" type="xsd:short"/>
    <part name="int_3" type="xsd:int"/>
    <part name="long_4" type="xsd:long"/>
    <part name="float_5" type="xsd:float"/>
    <part name="double_6" type="xsd:double"/>
    <part name="boolean_7" type="xsd:boolean"/>
    <part name="String_8" type="xsd:string"/>
  </message>
  <message name="Greeting_testResponse"/>
  ...
</definitions>

Note that all the arguments are mapped to a type using the xsd prefix, which refers to the http://www.w3.org/2001/XMLSchema namespace. All these mappings are performed without any extra effort on your part, as are String, Date, Calendar, BigInteger, and BigDecimal. Arrays of these built-in types are also supported automatically. Sections 4 and 5 of the JAX-RPC specification (JSR101) define standard mappings between Java and XML types for an implementation of JAX-RPC.

NOTE

You may notice that the Java type char is missing. This is because there is no direct mapping between a Java char and an XML Schema type. Hence, it is not one of the Java types that is automatically marshaled.


When you start to work with other complex Java types, and arrays of those types, more effort must be put into the representation of these mappings. Consider what is done by RMI when you pass Java objects between a client and server:

  • RMI uses the Java serialization mechanism to convert the contents of the object into a byte stream that can be sent across the network.

  • Both client and server must have a definition for the type being passed (or must be able to get hold of one).

  • The remote interface definition uses standard Java syntax to indicate where objects are used as parameters or return values.

When using complex Java types as part of a Web Service, you must address the same issues. However, there is the added complication that you must do this in a platform- and language-independent way. Therefore, the following is needed to pass complex parameters as part of a Web Service method:

  • Provide a mechanism to marshal and unmarshal the contents of a complex Java type into an XML format that can be used as part of a SOAP message

  • Deliver the marshalling and unmarshalling mechanism on both the client and the server

  • Indicate in the WSDL definition that certain parameters or return values are complex types, and provide a mapping between the complex types and their associated marshalling/unmarshalling code

Consider also the situation where you are provided with WSDL that has been generated from a non-Java Web Service, such as a Web Service implemented using Microsoft .NET components. This may also contain definitions for complex types that must be mapped into Java types to use that Web Service from Java.

Somebody has to do this mapping, and it is not necessarily straightforward. Sometimes it can be done using automated tools, while at other times it may require custom code.

Marshaling Complex Types

A certain amount of complex type mapping is done for you. If your complex types are fairly straightforward, they can be defined according to JavaBeans principles (default constructor, getters and setters, serializable). Such types can be automatically marshaled into and out of XML by a JAX-RPC implementation, so in this case you will not be required to write any additional code to get these types across a Web Service interface.

Web Service design is heavily influenced by two aspects: statelessness and data-only passing. As discussed in the sidebar “Always Start from the WSDL,” a WSDL description defines data that is passed back and forth between clients and servers. There is no way of specifying functionality, so all the on-the-wire types are defined only in terms of their data. This fits well with coarse-grained component design principles where you should not really be handing out combinations of data and functionality but designing your interfaces so that functionality is encapsulated in the component and data is passed in and out. Complex data structures that cross the component boundary then become Data Transfer Objects (DTOs) as discussed on Day 18. In Java terms, a Data Transfer Object should be implemented as a JavaBean.

In some cases, a simple JavaBean style class is not enough to represent the data being manipulated. If you need more control over what is included in the XML messages being exchanged, you can define custom serializers and deserializers to be included as part of your Web Service. You briefly saw some of the automatically generated serializers and deserializers when looking at the structure of the deployed WAR file in the section “Deploying the Service.” A type registry is defined to associate the XML data structures in the message with particular Java types. The appropriate serializer or deserializer is called to marshal data between the two formats.

A complete discussion of complex type mapping is beyond the scope of this book; however, if you are interested, this is covered in sections 15 and 19 of the JAX-RPC specification (JSR101).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.134.130