3.4. Designing a Service's Interaction Layer

A service's interaction layer has several major responsibilities, and chief among them is the design of the interface the service presents to the client. Since clients access the service through it, the interface is the starting point of a client's interaction with the service. The interaction layer also handles other responsibilities, such as receiving client requests, delegating requests to appropriate business logic, and creating and sending responses. This section examines the responsibilities of the interaction layer and highlights some guidelines for its design.

3.4.1. Designing the Interface

There are some considerations to keep in mind as you design the interface of your Web service, such as issues regarding overloading methods, choosing the endpoint type, and so forth. Before examining these issues, decide on the approach you want to take for developing the service's interface definition.

Two approaches to developing the interface definition for a Web service are:

  1. Java-to-WSDL— Start with a set of Java interfaces for the Web service and from these create the Web Services Description Language (WSDL) description of the service for others to use.

  2. WSDL-to-Java— Start with a WSDL document describing the details of the Web service interface and use this information to build the corresponding Java interfaces.

How do these two approaches compare? Starting with Java interfaces and creating a WSDL document is probably the easier of the two approaches. With this approach, you need not know any WSDL details because you use vendor-provided tools to create the WSDL description. While these tools make it easy for you to generate WSDL files from Java interfaces, you do lose some control over the WSDL file creation.

With the Java-to-WSDL approach, keep in mind that the exposed service interface may be too unstable from a service evolution point of view.

With the Java-to-WSDL approach, it may be hard to evolve the service interface without forcing a change in the corresponding WSDL document, and changing the WSDL might require rewriting the service's clients. These changes, and the accompanying instability, can affect the interoperability of the service itself. Since achieving interoperability is a prime reason to use Web services, the instability of the Java-to-WSDL approach is a major drawback. Also, keep in mind that different tools may use different interpretations for certain Java types (for example, java.util.Date might be interpreted as java.util.Calendar), resulting in different representations in the WSDL file. While not common, these representation variations may result in some semantic surprises.

On the other hand, the WSDL-to-Java approach gives you a powerful way to expose a stable service interface that you can evolve with relative ease. Not only does it give you greater design flexibility, the WSDL-to-Java approach also provides an ideal way for you to finalize all service details—from method call types and fault types to the schemas representing exchanged business documents—before you even start a service or client implementation. Although a good knowledge of WSDL and the WS-I Basic Profile is required to properly describe these Web services details, using available tools helps address these issues.

After you decide on the approach to take, you must still resolve other interface design details, which are described in the next sections.

3.4.1.1. Choice of the Interface Endpoint Type

In the J2EE platform, you have two choices for implementing the Web service interface—you can use a JAX-RPC service endpoint (also referred to as a Web tier endpoint) or an EJB service endpoint (also referred to as an EJB tier endpoint). Using one of these endpoint types makes it possible to embed the endpoint in the same tier as the service implementation. This simplifies the service implementation, because it obviates the need to place the endpoint in its own tier where the presence of the endpoint is solely to act as a proxy directing requests to other tiers that contain the service's business logic.

When you develop a new Web service that does not use existing business logic, choosing the endpoint type to use for the Web service interface is straightforward. The endpoint type choice depends on the nature of your business logic—whether the business logic of the service is completely contained within either the Web tier or the EJB tier:

Use a JAX-RPC service endpoint when the processing layer is within the Web tier.

Use an EJB service endpoint when the processing layer is only on the EJB tier.

When you add a Web service interface to an existing application or service, you must consider whether the existing application or service preprocesses requests before delegating them to the business logic. If so, then keep the following guideline in mind:

When you add a Web service interface for an existing application, choose an endpoint type suited for the tier on which the preprocessing logic occurs in the existing application. Use a JAX-RPC service endpoint when the preprocessing occurs on the Web tier of the existing application and an EJB service endpoint when preprocessing occurs on the EJB tier.

If the existing application or service does not require preprocessing of the incoming request, choose the appropriate endpoint that is present in the same tier as the existing business logic. Besides these major considerations for choosing an endpoint type, there are other, more subtle differences between an EJB service endpoint and a JAX-RPC service endpoint. You may find it helpful to keep in mind these additional points when choosing a Web service endpoint type:

  • Multi-threaded access considerations— An EJB service endpoint, because it is implemented as a stateless session bean, need not worry about multi-threaded access since the EJB container is required to serialize requests to any particular instance of a stateless session bean. For a JAX-RPC service endpoint, on the other hand, you must do the synchronization yourself in the source code.

  • A JAX-RPC service endpoint has to handle concurrent client access on its own, whereas the EJB container takes care of concurrent client access for an EJB service endpoint.

  • Transaction considerations— The transactional context of the service implementation's container determines the transactional context in which a service implementation runs. Since a JAX-RPC service endpoint runs in a Web container, its transactional context is unspecified. There is also no declarative means to automatically start the transaction. Thus, you need to use JTA to explicitly demarcate the transaction.

    On the other hand, an EJB service endpoint runs in the transaction context of an EJB container. You as the developer need to declaratively demarcate transactions. The service's business logic thus runs under the transactional context as defined by the EJB's container-transaction element in the deployment descriptor.

    If the Web service's business logic requires using transactions (and the service has a JAX-RPC service endpoint), you must implement the transaction-handling logic using JTA or some other similar facility. If your service uses an EJB service endpoint, you can use the container's declarative transaction services. By doing so, the container is responsible for handling transactions according to the setting of the deployment descriptor element container-transaction.

  • Considerations for method-level access permissions— A Web service's methods can be accessed by an assortment of different clients, and you may want to enforce different access constraints for each method.

    When you want to control service access at the individual method level, consider using an EJB service endpoint rather than a JAX-RPC service endpoint.

    Enterprise beans permit method-level access permission declaration in the deployment descriptor—you can declare various access permissions for different enterprise bean methods and the container correctly handles access to these methods. This holds true for an EJB service endpoint, since it is a stateless session bean. A JAX-RPC service endpoint, on the other hand, does not have a facility for declaring method-level access constraints, requiring you to do this programmatically. See Chapter 7 for more information.

  • HTTP session access considerations— A JAX-RPC service endpoint, because it runs in the Web container, has complete access to an HttpSession object. Access to an HttpSession object, which can be used to embed cookies and store client state, may help you build session-aware clients. An EJB service endpoint, which runs in the EJB container, has no such access to Web container state. However, generally HTTP session support is appropriate for short duration conversational interactions, whereas Web services often represent business processes with longer durations and hence need additional mechanisms. See “Correlating Messages” on page 359 for one such strategy.

3.4.1.2. Granularity of Service

Much of the design of a Web service interface involves designing the service's operations, or its methods. You first determine the service's operations, then define the method signature for each operation. That is, you define each operation's parameters, its return values, and any errors or exceptions it may generate.

It is important to consider the granularity of the service's operations when designing your Web service interface.

For those Web services that implement a business process, the nature of the business process itself often dictates the service's granularity. Business processes that exchange documents, such as purchase orders and invoices, by their nature result in a Web service interface that is coarse grained. With more interactive Web services, you need to carefully choose the granularity of these operations.

You should keep the same considerations in mind when designing the methods for a Web service as when designing the methods of a remote enterprise bean. This is particularly true not only regarding the impact of remote access on performance but also with Web services; it is important with Web services because there is an underlying XML representation requiring parsing and taking bandwidth. Thus, a good rule is to define the Web service's interface for optimal granularity of its operations; that is, find the right balance between coarse-grained and fine-grained granularity.

Generally, you should consolidate related fine-grained operations into more coarse-grained ones to minimize expensive remote method calls.

More coarse-grained service operations, such as returning catalog entries in sets of categories, keep network overhead lower and improve performance. However, they are sometimes less flexible from a client's point of view. While finer-grained service operations, such as browsing a catalog by products or items, offer a client greater flexibility, these operations result in greater network overhead and reduced performance.

Keep in mind that too much consolidation leads to inefficiencies.

For example, consolidating logically different operations is inefficient and should be avoided. It is much better to consolidate similar operations or operations that a client is likely to use together, such as querying operations.

When exposing existing stateless session beans as Web service endpoints, ensure that the Web service operations are sufficiently coarse grained.

If you are planning to expose existing stateless session beans as Web service endpoints, remember that such beans may not have been designed with Web services in mind. Hence, they may be too fine grained to be good Web service endpoints. You should consider consolidating related operations into a single Web service operation.

Good design for our airline reservation Web service, for example, is to expect the service's clients to send all information required for a reservation—destination, preferred departure and arrival times, preferred airline, and so forth—in one invocation to the service, that is, as one large message. This is far more preferable than to have a client invoke a separate method for each piece of information comprising the reservation. To illustrate, it is preferable to have clients use the interface shown in Code Example 3.1.

Code example 3.1. Using Consolidation for Greater Efficiency (Recommended)
public interface AirlineTicketsIntf extends Remote {
   public String submitReservationRequest(
          AirReservationDetails details) throws RemoteException;
}

Code Example 3.1 combines logically-related data into one large message for a more efficient client interaction with the service. This is preferable to receiving the data with individual method calls, as shown in Code Example 3.2.

Code example 3.2. Retrieving Data with Separate Method Calls (Not Recommended)
public interface AirlineTicketsIntf extends Remote {
   public String submitFlightInformation(FlightDetails fltInfo)
                     throws RemoteException;
   public String submitPreferredDates(Date depart, Date arrive)
                 throws RemoteException;
   // other similar methods
}

However, it might not be a good idea to combine in a single service invocation the same reservation with an inquiry method call.

Along with optimal granularity, you should consider data caching issues. Coarse-grained services involve transferring large amounts of data. If you opt for more coarse-grained service operations, it is more efficient to cache data on the client side to reduce the number of round trips between the client and the server.

3.4.1.3. Parameter Types for Web Service Operations

A Web service interface exposes a set of method calls to clients. When invoking a service interface method, a client may have to set values for the parameters associated with the call. When you design an interface's methods, choose carefully the types of these parameters. Keep in mind that a method call and its parameters are sent as a SOAP message between the client and the service. To be part of a SOAP message, parameters must be mapped to XML. When received at the client or service end, the same parameters must be mapped from XML to their proper types or objects. This section describes some guidelines to keep in mind when defining method call parameters and return values.

Note: Since each call potentially may return a value, the discussion in this section about parameter values applies equally to return values.

Parameters for Web service method calls may be standard Java objects and types, XML documents, or even nonstandard types. Whether you use the Java-to-WSDL approach or the WSDL-to-Java approach, each type of parameter must be mapped to its XML equivalent in the SOAP message. Figure 3.3 shows how the binding happens for various types of parameters.

Figure 3.3. Binding Parameters and Return Values with JAX-RPC


3.4.1.3.1. Java Objects as Parameters

Parameters for Web service calls can be standard Java types and objects. If you use the Java-to-WSDL approach, you specify the parameter types as part of the arguments of the method calls of your Java interface. If you use the WSDL-to-Java approach, you specify the parameter types as the type or element attributes of the part element of each message in your WSDL. The type of a parameter that you use has a significant effect on the portability and interoperability of your service.

The platform supports the following Java data types. (Refer to the JAX-RPC specification at http://java.sun.com/xml/jaxrpc/ for the equivalent WSDL mappings for these Java data types.)

  • Java primitive types boolean, byte, short, int, long, float, and double, along with their corresponding wrapper Java classes

  • Standard Java classes: String, Date, Calendar, BigInteger, BigDecimal, QName, and URI

  • Java arrays with JAX-RPC-supported Java types as members

  • JAX-RPC value types—user-defined Java classes, including classes with JavaBeansTM component-like properties

    When designing parameters for method calls in a Web service interface, choose parameters that have standard type mappings. (See Figure 3.3.) Always keep in mind that the portability and interoperability of your service is reduced when you use parameter types that by default are not supported.

As Figure 3.3 shows, parameters that have standard type mappings are bound implicitly. However, the developer must do more work when using parameters that do not have standard type mappings. See “Handling Nonstandard Type Parameters” on page 76 for more details on using nonstandard Java types and possible side effects of such use.

Here are some additional helpful points to consider when you use Java objects with standard type mappings as parameters.

  1. Many applications and services need to pass lists of objects. However, utilities for handling lists, such as ArrayList and Collection, to name a few, are not supported standard types. Instead, Java arrays provide equivalent functionality, and have a standard mapping provided by the platform.

  2. JAX-RPC value types are user-defined Java classes with some restrictions. They have constructors and may have fields that are public, private, protected, static, or transient. JAX-RPC value types may also have methods, including set and get methods for setting and getting Java class fields.

    However, when mapping JAX-RPC value types to and from XML, there is no standard way to retain the order of the parameters to the constructors and other methods. Hence, avoid setting the JAX-RPC value type fields through the constructor. Using the get and set methods to retrieve or set value type fields avoids this mapping problem and ensures portability and interoperability.

  3. The J2EE platform supports nested JAX-RPC value types; that is, JAX-RPC value types that reference other JAX-RPC value types within themselves. For clarity, it is preferable to use this feature and embed value type references rather than to use a single flat, large JAX-RPC value type class.

  4. The J2EE platform, because of its support for the SOAP message with attachment protocol, also supports the use of MIME-encoded content. It provides Java mappings for a subset of MIME types. (See Table 3.1.)

    Table 3.1. Mapping of MIME Types
    MIME TypeJava Type
    image/gifjava.awt.Image
    image/jpegjava.awt.Image
    text/plainjava.lang.String
    multipart/*javax.mail.internet.MimeMultipart
    text/xml or application/xmljavax.xml.transform.Source

Since the J2EE container automatically handles mappings based on the Java types, using these Java-MIME mappings frees you from the intricacies of sending and retrieving documents and images as part of a service's request and response handling. For example, your service, if it expects to receive a GIF image with a MIME type of image/gif, can expect the client to send a java.awt.Image object. A sample Web service interface that receives an image might look like the one shown in Code Example 3.3:

Code example 3.3. Receiving a java.awt.Image Object
import java.awt.Image;
public interface WeatherMapService extends Remote {
   public void submitWeatherMap(Image weatherMap)
                 throws RemoteException, InvalidMapException;
}

In this example, the Image object lets the container implementation handle the image-passing details. The container provides javax.activation.DataHandler classes, which work with the Java Activation Framework to accomplish the Java-MIME and MIME-Java mappings.

Considering this mapping between Java and MIME types, it is best to send images and XML documents that are in a Web service interface using the Java types shown in Table 3.1. However, you should be careful about the effect on the interoperability of your service. See “Interoperability” on page 86 for more details.

3.4.1.3.2. XML Documents as Parameters

There are scenarios when you want to pass XML documents as parameters. Typically, these occur in business-to-business interactions where there is a need to exchange legally binding business documents, track what is exchanged, and so forth. Exchanging XML documents as part of a Web service is addressed in a separate section—see “Handling XML Documents in a Web Service” on page 105 for guidelines to follow when passing XML documents as parameters.

3.4.1.3.3. Handling Nonstandard Type Parameters

JAX-RPC technology, in addition to providing a rich standard mapping set between XML and Java data types, also provides an extensible type mapping framework. Developers can use this framework to specify pluggable, custom serializers and deserializers that support nonstandard type mappings.

Extensible type mapping frameworks, which developers may use to support nonstandard type mappings, are not yet a standard part of the J2EE platform.

Vendors currently can provide their own solutions to this problem. It must be emphasized that if you implement a service using some vendor's implementation-specific type mapping framework, then your service is not guaranteed to be portable and interoperable.

Because of portability limitations, you should avoid passing parameters that require the use of vendor-specific serializers or deserializers.

Instead, a better way is to pass these parameters as SOAP document fragments represented as a DOM subtree in the service endpoint interface. (See Figure 3.3.) If so, you should consider binding (either manually or using JAXB) the SOAP fragments to Java objects before passing them to the processing layer to avoid tightly coupling the business logic with the document fragment.

3.4.1.4. Interfaces with Overloaded Methods

In your service interface, you may overload methods and expose them to the service's clients. Overloaded methods share the same method name but have different parameters and return values. If you do choose to use overloaded methods as part of your service interface, keep in mind that there are some limitations, as follows:

  • If you choose the WSDL-to-Java approach, there are limitations to representing overloaded methods in a WSDL description. In the WSDL description, each method call and its response are represented as unique SOAP messages. To represent overloaded methods, the WSDL description would have to support multiple SOAP messages with the same name. WSDL version 1.1 does not have this capability to support multiple messages with the same name.

  • If you choose the Java-to-WSDL approach and your service exposes overloaded methods, be sure to check how any vendor-specific tools you are using represent these overloaded methods in the WSDL description. You need to ensure that the WSDL representation of overloaded methods works in the context of your application.

Let's see how this applies in the weather service scenario. As the provider, you might offer the service to clients, letting them look up weather information by city name or zip code. If you use the Java-to-WSDL approach, you might first define the WeatherService interface as shown in Code Example 3.4.

Code example 3.4. WeatherService Interface for Java-to-WSDL Approach
public interface WeatherService extends Remote {
   public String getWeather(String city) throws RemoteException;
   public String getWeather(int zip) throws RemoteException;
}

After you define the interface, you run the vendor-provided tool to create the WSDL from the interface. Each tool has its own way of representing the getWeather overloaded methods in the WSDL, and your WSDL reflects the particular tool you use. For example, if you use the J2EE 1.4 SDK from Sun Microsystems, its wscompile tool creates from the WeatherService interface the WSDL shown in Code Example 3.5.

Code example 3.5. Generated WSDL for WeatherService Interface
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="WeatherWebService" .......>
   <types/>
   <message name="WeatherService_getWeather">
      <part name="int_1" type="xsd:int"/>
   </message>
   <message name="WeatherService_getWeatherResponse">
      <part name="result" type="xsd:string"/>
   </message>
   <message name="WeatherService_getWeather2">
      <part name="String_1" type="xsd:string"/>
   </message>
   <message name="WeatherService_getWeather2Response">
      <part name="result" type="xsd:string"/>
   </message>
   ...
</definitions>

Notice that the WSDL represents the getWeather overloaded methods as two different SOAP messages, naming one getWeather, which takes an integer for the zip code as its parameter, and the other getWeather2, which takes a string parameter for the city. As a result, a client interested in obtaining weather information using a city name invokes the service by calling getWeather2, as shown in Code Example 3.6.

Code example 3.6. Using Weather Service Interface with Java-to-WSDL Approach
...
Context ic = new InitialContext();
WeatherWebService weatherSvc = (WeatherWebService)
       ic.lookup("java:comp/env/service/WeatherService");
WeatherServiceIntf port = (WeatherServiceIntf)
       weatherSvc.getPort(WeatherServiceIntf.class);
String returnValue = port.getWeather2("San Francisco");
...

For example, to obtain the weather information for San Francisco, the client called port.getWeather2("San Francisco"). Keep in mind that another tool may very likely generate a WSDL whose representation of overloaded methods is different.

You may want to avoid using overloaded methods in your Java interface altogether if you prefer to have only intuitive method names in the WSDL.

If instead you choose to use the WSDL-to-Java approach, your WSDL description might look as follows. (See Code Example 3.7.)

Code example 3.7. WSDL for Weather Service with Overloaded Methods Avoided
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="WeatherWebService" ...>
   <types/>
   <message name="WeatherService_getWeatherByZip">
      <part name="int_1" type="xsd:int"/>
   </message>
   <message name="WeatherService_getWeatherByZipResponse">
      <part name="result" type="xsd:string"/>
   </message>
   <message name="WeatherService_getWeatherByCity">
      <part name="String_1" type="xsd:string"/>
   </message>
   <message name="WeatherService_getWeatherByCityResponse">
      <part name="result" type="xsd:string"/>
   </message>
   ...
</definitions>

Since the messages in a WSDL file must have unique names, you must use different message names to represent methods that you would otherwise overload. These different message names actually convert to different method calls in your interface. Notice that the WSDL includes a method getWeatherByZip, which takes an integer parameter, and a method getWeatherByCity, which takes a string parameter. Thus, a client wishing to obtain weather information by city name from a WeatherService interface associated with the WSDL in Code Example 3.7 might invoke the service as shown in Code Example 3.8.

Code example 3.8. Using Weather Service with WSDL-to-Java Approach
...
Context ic = new InitialContext();
WeatherWebService weatherSvc = (WeatherWebService)
       ic.lookup("java:comp/env/service/WeatherService");
WeatherServiceIntf port = (WeatherServiceIntf)
       weatherSvc.getPort(WeatherServiceIntf.class);
String returnValue = port.getWeatherByCity("San Francisco");
...

3.4.1.5. Handling Exceptions

Just like any Java or J2EE application, a Web service application may encounter an error condition while processing a client request. A Web service application needs to properly catch any exceptions thrown by an error condition and propagate these exceptions. For a Java application running in a single virtual machine, you can propagate exceptions up the call stack until reaching a method with an exception handler that handles the type of exception thrown. To put it another way, for non-Web service J2EE and Java applications, you may continue to throw exceptions up the call stack, passing along the entire stack trace, until reaching a method with an exception handler that handles the type of exception thrown. You can also write exceptions that extend or inherit other exceptions.

However, throwing exceptions in Web service applications has additional constraints that impact the design of the service endpoint. When considering how the service endpoint handles error conditions and notifies clients of errors, you must keep in mind these points:

  • Similar to requests and responses, exceptions are also sent back to the client as part of the SOAP messages.

  • Your Web service application should support clients running on non-Java platforms that may not have the same, or even similar, error-handling mechanisms as the Java exception-handling mechanism.

A Web service application may encounter two types of error conditions. One type of error might be an irrecoverable system error, such as an error due to a network connection problem. When an error such as this occurs, the JAX-RPC runtime on the client throws the client platform's equivalent of an irrecoverable system exception. For Java clients, this translates to a RemoteException.

A Web service application may also encounter a recoverable application error condition. This type of error is called a service-specific exception. The error is particular to the specific service. For example, a weather Web service might indicate an error if it cannot find weather information for a specified city.

To illustrate the Web service exception-handling mechanism, let's examine it in the context of the weather Web service example. When designing the weather service, you want the service to be able to handle a scenario in which the client requests weather information for a nonexistent city. You might design the service to throw a service-specific exception, such as CityNotFoundException, to the client that made the request. You might code the service interface so that the getWeather method throws this exception. (See Code Example 3.9.)

Code example 3.9. Throwing a Service-Specific Exception
public interface WeatherService extends Remote {
   public String getWeather(String city) throws
              CityNotFoundException, RemoteException;
}

Service-specific exceptions like CityNotFoundException, which are thrown by the Web service to indicate application-specific error conditions, must be checked exceptions that directly or indirectly extend java.lang.Exception. They cannot be unchecked exceptions. Code Example 3.10 shows a typical implementation of a service-specific exception, such as for CityNotFoundException.

Code example 3.10. Implementation of a Service-Specific Exception
public class CityNotFoundException extends Exception {
   private String message;
   public CityNotFoundException(String message) {
      super(message);
      this.message = message;
   }
   public String getMessage() {
      return message;
   }
}

Code Example 3.11 shows the service implementation for the same weather service interface. This example illustrates how the service might throw CityNotFoundException.

Code example 3.11. Example of a Service Throwing a Service-Specific Exception
public class WeatherServiceImpl implements WeatherService {
   public String getWeather(String city)
                            throws CityNotFoundException {
      if(!validCity(city))
         throw new CityNotFoundException(city + " not found");
      // Get weather info and return it back
   }
}

Chapter 5 describes the details of handling exceptions on the client side. (In particular, refer to “Handling Exceptions” on page 230.) On the service side, keep in mind how to include exceptions in the service interface and how to throw them. Generally, you want to do the following:

Convert application-specific errors and other Java exceptions into meaningful service-specific exceptions and throw these service-specific exceptions to the clients.

Although they promote interoperability among heterogeneous platforms, Web service standards cannot address every type of exception thrown by different platforms. For example, the standards do not specify how Java exceptions such as java.io.IOException and javax.ejb.EJBException should be returned to the client. As a consequence, it is important for a Web service—from the service's interoperability point of view—to not expose Java-specific exceptions (such as those just mentioned) in the Web service interface. Instead, throw a service-specific exception. In addition, keep the following points in mind:

  • You cannot throw nonserializable exceptions to a client through the Web service endpoint.

  • When a service throws java or javax exceptions, the exception type and its context information are lost to the client that receives the thrown exception. For example, if your service throws a javax.ejb.FinderException exception to the client, the client may receive an exception named FinderException, but its type information may not be available to the client. Furthermore, the type of the exception to the client may not be the same as the type of the thrown exception. (Depending on the tool used to generate the client-side interfaces, the exception may even belong to some package other than javax.ejb.)

As a result, you should avoid directly throwing java and javax exceptions to clients. Instead, when your service encounters one of these types of exceptions, wrap it within a meaningful service-specific exception and throw this service-specific exception back to the client. For example, suppose your service encounters a javax.ejb.FinderException exception while processing a client request. The service should catch the FinderException exception, and then, rather than throwing this exception as is back to the client, the service should instead throw a service-specific exception that has more meaning for the client. See Code Example 3.12.

Code example 3.12. Converting an Exception into a Service-Specific Exception
...
try {
    // findByPrimaryKey
    // Do processing
    // return results
} catch (javax.ejb.FinderException fe) {
   throw new InvalidKeyException(
      "Unable to find row with given primary key");
}

Exception inheritances are lost when you throw a service-specific exception.

You should avoid defining service-specific exceptions that inherit or extend other exceptions. For example, if CityNotFoundException in Code Example 3.10 extends another exception, such as RootException, then when the service throws CityNotFoundException, methods and properties inherited from RootException are not passed to the client.

The exception stack trace is not passed to the client.

The stack trace for an exception is relevant only to the current execution environment and is meaningless on a different system. Hence, when a service throws an exception to the client, the client does not have the stack trace explaining the conditions under which the exception occurred. Thus, you should consider passing additional information in the message for the exception.

Web service standards make it easier for a service to pass error conditions to a client in a platform-independent way. While the following discussion may be of interest, it is not essential that developers know these details about the J2EE platform's error-handling mechanisms for Web services.

As noted previously, error conditions are included within the SOAP messages that a service returns to clients. The SOAP specification defines a message type, called fault, that enables error conditions to be passed as part of the SOAP message yet still be differentiated from the request or response portion. Similarly, the WSDL specification defines a set of operations that are possible on an endpoint. These operations include input and output operations, which represent the request and response respectively, and an operation called fault.

A SOAP fault defines system-level exceptions, such as RemoteException, which are irrecoverable errors. The WSDL fault denotes service-specific exceptions, such as CityNotFoundException, and these are recoverable application error conditions. Since the WSDL fault denotes a recoverable error condition, the platform can pass it as part of the SOAP response message. Thus, the standards provide a way to exchange fault messages and map these messages to operations on the endpoint.

Code Example 3.13 shows the WSDL code for the same weather Web service example. This example illustrates how service-specific exceptions are mapped just like input and output messages are mapped.

Code example 3.13. Mapping a Service-Specific Exception in WSDL
<?xml version="1.0" encoding="UTF-8"?>
<definitions ...>
   ...
   <message name="WeatherService_getWeather">
      <part name="String_1" type="xsd:string"/>
   </message>
   <message name="WeatherService_getWeatherResponse">
      <part name="result" type="xsd:string"/>
   </message>
   <message name="CityNotFoundException">
      <part name="CityNotFoundException"
                    element="tns:CityNotFoundException"/>
   </message>
   <portType name="WeatherService">
      <operation name="getWeather" parameterOrder="String_1">
         <input message="tns:WeatherService_getWeather"/>
         <output message=
                "tns:WeatherService_getWeatherResponse"/>
         <fault name="CityNotFoundException"
                   message="tns:CityNotFoundException"/>
      </operation>
   </portType>
   ...
</definitions>

3.4.1.6. Use of Handlers

As discussed in Chapter 2, and as shown in Figure 3.1, JAX-RPC technology enables you to plug in SOAP message handlers, thus allowing processing of SOAP messages that represent requests and responses. Plugging in SOAP message handlers gives you the capability to examine and modify the SOAP requests before they are processed by the Web service and to examine and modify the SOAP responses before they are delivered to the client.

Handlers are particular to a Web service and are associated with the specific port of the service. As a result of this association, the handler's logic applies to all SOAP requests and responses that pass through a service's port. Thus, you use these message handlers when your Web service must perform some SOAP message-specific processing common to all its requests and responses. Because handlers are common to all requests and responses that pass through a Web service endpoint, keep the following guideline in mind:

It is not advisable to put in a handler business logic or processing particular to specific requests and responses.

You cannot store client-specific state in a handler: A handler's logic acts on all requests and responses that pass through an endpoint. However, you may use the handler to store port-specific state, which is state common to all method calls on that service interface. Note also that handlers execute in the context of the component in which they are present.

Do not store client-specific state in a handler.

Also note that handlers work directly on the SOAP message, and this involves XML processing. You can use handlers to pass client-specific state through the message context. (See “Passing Context Information on Web Service Calls” on page 366.)

Use of handlers can result in a significant performance impact for the service as a whole.

Use of handlers could potentially affect the interoperability of your service. See the next section on interoperability. Keep in mind that it takes advanced knowledge of SOAP message manipulation APIs (such as SAAJ) to correctly use handlers. To avoid errors, Web service developers should try to use existing or vendor-supplied handlers. Using handlers makes sense primarily for writing system services such as auditing, logging, and so forth.

3.4.1.7. Interoperability

A major benefit of Web services is interoperability between heterogeneous platforms. To get the maximum benefit, you want to design your Web service to be interoperable with clients on any platform, and, as discussed in Chapter 2, the Web Services Interoperability (WS-I) organization helps in this regard. WS-I promotes a set of generic protocols for the interoperable exchange of messages between Web services. The WS-I Basic Profile promotes interoperability by defining and recommending how a set of core Web services specifications and standards (including SOAP, WSDL, UDDI, and XML) can be used for developing interoperable Web services.

In addition to the WS-I protocols, other groups, such as SOAPBuilders Interoperability group (see http://java.sun.com/wsinterop/sb/index.html), provide common testing grounds that make it easier to test the interoperability of various SOAP implementations. This has made it possible for various Web services technology vendors to test the interoperability of implementations of their standards. When you implement your service using technologies that adhere to the WS-I Basic Profile specifications, you are assured that such services are interoperable.

Apart from these standards and testing environments, you as the service developer must design and implement your Web service so that maximum interoperability is possible. For maximum interoperability, you should keep these three points in mind:

  1. The two messaging styles and bindings supported by WSDL

  2. The WS-I support for attachments

  3. The most effective way to use handlers

WSDL supports two types of messaging styles: rpc and document. The WSDL style attribute indicates the messaging style. (See Code Example 3.14.) A style attribute set to rpc indicates a RPC-oriented operation, where messages contain parameters and return values, or function signatures. When the style attribute is set to document, it indicates a document-oriented operation, one in which messages contain documents. Each operation style has a different effect on the format of the body of a SOAP message.

Code example 3.14. Specifying WSDL Bindings
<?xml version="1.0" encoding="UTF-8"?>
<definitions .......>
<binding name="WeatherServiceBinding" type="tns:WeatherService">
   <operation name="getWeather">
      <input>
         <soap:body use="literal"
                   namespace="urn:WeatherWebService"/>
      </input>
      <output>
         <soap:body use="literal"
                   namespace="urn:WeatherWebService"/>
      </output>
      <soap:operation soapAction=""/></operation>
      <soap:binding style="rpc"
             transport="http://schemas.xmlsoap.org/soap/http" />
      </binding>
      <service .....>
</definitions>

Along with operation styles, WSDL supports two types of serialization and deserialization mechanisms: a literal and an encoded mechanism. The WSDL use attribute indicates which mechanism is supported. (See Code Example 3.14.) A literal value for the use attribute indicates that the data is formatted according to the abstract definitions within the WSDL document. The encoded value means data is formatted according to the encodings defined in the URI specified by the encodingStyle attribute. Thus, you can choose between an rpc or document style of message passing and each message can use either a literal or encoded data formatting.

Because the WS-I Basic Profile 1.0, to which J2EE1.4 platform conforms, supports only literal bindings, you should avoid encoded bindings.

Literal bindings cannot represent complex types, such as objects with circular references, in a standard way.

Code Example 3.14 shows a snippet from the WSDL document illustrating how the sample weather service specifies these bindings.

It is important to keep in mind these message styles and bindings, particularly when you design the interface using the WSDL-to-Java approach and when you design the WSDL for your service. When you use the Java-to-WSDL approach, you rely on the vendor-provided tools to generate the WSDL for your Java interfaces, and they can be counted on to create WS-I-compliant WSDL for your service. However, note that some vendors may expect you to specify certain options to ensure the creation of a WS-I-compliant WSDL. For example, the J2EE 1.4 SDK from Sun Microsystems provides a wscompile tool, which expects the developer to use the -f:wsi flag to create the WS-I-compliant WSDL for the service. It is also a good idea to check the WSDL document itself to ensure that whatever tool you use created the document correctly.

Regarding the second issue, you should note that the WS-I Basic Profile 1.0 (which is the profile supported by the J2EE 1.4 platform) does not address attachments. The section, “Parameter Types for Web Service Operations” on page 72, which discussed Java-MIME type mappings provided by the J2EE platform, advised that an efficient design is to use these mappings to send images and XML documents within a completely Java environment. Because the WS-I Basic Profile, version 1.0 does not address attachments, a Web service that uses these mappings may not be interoperable with clients on a non-Java platform.

Since the WS-I Basic Profile 1.0 specification does not address attachments, a Web service using the Java-MIME mappings provided by the J2EE platform is not guaranteed to be interoperable.

Since most Web services rely on an exchange of business documents, and interoperability is not always guaranteed, it is important that you properly understand the options for handling XML documents. The section, “Exchanging XML Documents” on page 107, explains the various options available to Web services for exchanging XML documents in an interoperable manner. It should also be noted that the next version of the WS-I Basic Profile specification addresses a standard way to send attachments, and later versions of the J2EE platforms will incorporate this.

Last is the issue of handlers. Handlers, which give you access to SOAP messages, at the same time impose major responsibilities on you.

When using handlers, you must be careful not to change a SOAP message to the degree that the message no longer complies with WS-I specifications, thereby endangering the interoperability of your service.

This ends the discussion of considerations for designing a Web service interface. The next sections examine other responsibilities of the interaction layer, such as receiving and delegating requests and formulating responses.

3.4.2. Receiving Requests

The interaction layer, through the endpoint, receives client requests. The platform maps the incoming client requests, which are in the form of SOAP messages, to method calls present in the Web service interface.

Before delegating these incoming client requests to the Web service business logic, you should perform any required security validation, transformation of parameters, and other required preprocessing of parameters.

As noted in “Parameter Types for Web Service Operations” on page 72 and elsewhere, Web service calls are basically method calls whose parameters are passed as either Java objects, XML documents (javax.xml.transform.Source objects), or even SOAP document fragments (javax.xml.soap.SOAPElement objects).

For parameters that are passed as Java objects (such as String, int, JAX-RPC value types, and so forth), do the application-specific parameter validation and map the incoming objects to domain-specific objects in the interaction layer before delegating the request to the processing layer.

You may have to undertake additional steps to handle XML documents that are passed as parameters. These steps, which are best performed in the interaction layer of your service, are as follows:

1.
The service endpoint should validate the incoming XML document against its schema. For details and guidelines on how and when to validate incoming XML documents, along with recommended validation techniques, refer to “Validating XML Documents” on page 139.

2.
When the service's processing layer and business logic are designed to deal with XML documents, you should transform the XML document to an internally supported schema, if the schema for the XML document differs from the internal schema, before passing the document to the processing layer.

3.
When the processing layer deals with objects but the service interface receives XML documents, then, as part of the interaction layer, map the incoming XML documents to domain objects before delegating the request to the processing layer. For details and guidelines on mapping techniques for incoming XML documents, refer to “Mapping Schemas to the Application Data Model” on page 143.

It is important that these three steps—validation of incoming parameters or XML documents, translation of XML documents to internal supported schemas, and mapping documents to domain objects—be performed as close to the service endpoint as possible, and certainly in the service interaction layer.

A design such as this helps to catch errors early, and thus avoids unnecessary calls and round-trips to the processing layer. Figure 3.4 shows the recommended way to handle requests and responses in the Web service's interaction layer.

Figure 3.4. Web Service Request Processing


The Web service's interaction layer handles all incoming requests and delegates them to the business logic exposed in the processing layer. When implemented in this manner, the Web service interaction layer has several advantages, since it gives you a common location for the following tasks:

  • Managing the handling of requests so that the service endpoint serves as the initial point of contact

  • Invoking security services, including authentication and authorization

  • Validating and transforming incoming XML documents and mapping XML documents to domain objects

  • Delegating to existing business logic

  • Handling errors

It is generally advisable to do all common processing—such as security checks, logging, auditing, input validation, and so forth—for requests at the interaction layer as soon as a request is received and before passing it to the processing layer.

3.4.3. Delegating Web Service Requests to Processing Layer

After designing the request preprocessing tasks, the next step is to design how to delegate the request to the processing layer. At this point, consider the kind of processing the request requires, since this helps you decide how to delegate the request to the processing layer. All requests can be categorized into two large categories based on the time it takes to process the request, namely:

  • A request that is processed in a short enough time so that a client can afford to block and wait to receive the response before proceeding further. In other words, the client and the service interact in a synchronous manner such that the invoking client blocks until the request is processed completely and the response is received.

  • A request that takes a long time to be processed, so much so that it is not a good idea to make the client wait until the processing is completed. In other words, the client and the service interact in an asynchronous manner such that the invoking client need not block and wait until the request is processed completely.

Note: When referring to request processing, we use the terms synchronous and asynchronous from the point of view of when the client's request processing completes fully. Keep in mind that, under the hood, an asynchronous interaction between a client and a service might result in a synchronous invocation over the network, since HTTP is by its nature synchronous. Similarly, SOAP messages sent over HTTP are also synchronous.

The weather information service is a good example of a synchronous interaction between a client and a service. When it receives a client's request, the weather service must look up the required information and send back a response to the client. This look-up and return of the information can be achieved in a relatively short time, during which the client can be expected to block and wait. The client continues its processing only after it obtains a response from the service. (See Figure 3.5.)

Figure 3.5. Weather Information Service Interaction


A Web service such as this can be designed using a service endpoint that receives the client's request and then delegates the request directly to the service's appropriate logic in the processing layer. The service's processing layer processes the request and, when the processing completes, the service endpoint returns the response to the client. (See Figure 3.6.)

Figure 3.6. Synchronous Interaction Between Client and Service


Code Example 3.15 shows the weather service interface performing some basic parameter validation checks in the interaction layer. The interface also gets required information and passes that information to the client in a synchronous manner:

Code example 3.15. Performing a Synchronous Client Interaction
public class WeatherServiceImpl implements
                     WeatherService, ServiceLifecycle {

   public void init(Object context) throws JAXRPCException {....}

   public String getWeather(String city)
                        throws CityNotFoundException {

      /** Validate parameters **/
      if(!validCity(city))
         throw new CityNotFoundException(....);

      /** Get weather info form processing layer and **/
      / **return results **/
      return (getWeatherInfoFromDataSource(city));
   }

   public void destroy() {....}
}

Now let's examine an asynchronous interaction between a client and a service. When making a request for this type of service, the client cannot afford to wait for the response because of the significant time it takes for the service to process the request completely. Instead, the client may want to continue with some other processing. Later, when it receives the response, the client resumes whatever processing initiated the service request. Typically in these types of services, the content of the request parameters initiates and determines the processing workflow—the steps to fulfill the request—for the Web service. Often, fulfilling a request requires multiple workflow steps.

The travel agency service is a good example of an asynchronous interaction between a client and a service. A client requests arrangements for a particular trip by sending the travel service all pertinent information (most likely in an XML document). Based on the document's content, the service performs such steps as verifying the user's account, checking and getting authorization for the credit card, checking accommodations and transportation availability, building an itinerary, purchasing tickets, and so forth. Since the travel service must perform a series of often time-consuming steps in its normal workflow, the client cannot afford to pause and wait for these steps to complete.

Figure 3.7 shows one recommended approach for asynchronously delegating these types of Web service requests to the processing layer. In this architecture, the client sends a request to the service endpoint. The service endpoint validates the incoming request in the interaction layer and then delegates the client's request to the appropriate processing layer of the service. It does so by sending the request as a JMS message to a JMS queue or topic specifically designated for this type of request.

Delegating a request to the processing layer through JMS before validating the request should be avoided.

Figure 3.7. Asynchronous Interaction Between Client and Service


Validation ensures that a request is correct. Delegating the request before validation may result in passing an invalid request to the processing layer, making error tracking and error handling overly complex. After the request is successfully delegated to the processing layer, the service endpoint may return a correlation identifier to the client. This correlation identifier is for the client's future reference and may help the client associate a response that corresponds to its previous request. If the business logic is implemented using enterprise beans, message-driven beans in the EJB tier read the request and initiate processing so that a response can ultimately be formulated.

Figure 3.8 shows how the travel agency service might implement this interaction, and Code Example 3.16 shows the actual code that might be used.

Code example 3.16. Implementing Travel Agency Service Interaction
public class ReservationRequestRcvr {
   public ReservationRequestRcvr() throws RemoteException {....}

   public String receiveRequest(Source reservationDetails) throws
                        RemoteException, InvalidRequestException{

      /** Validate incoming XML document **/
      String xmlDoc = getDocumentAsString(reservationDetails);
      if(!validDocument(xmlDoc))
         throw new InvalidRequestException(...);

      /** Get a JMS Queue and delegate the incoming request **/
      /** to the queue **/
      QueueConnectionFactory queueFactory =
         serviceLocator.getQueueConnectionFactory(....);
      Queue reservationRequestQueue =
                    serviceLocator.getQueue(...);
      QueueConnection connection =
         queueFactory.createQueueConnection();
      QueueSession session = connection.createQueueSession(false,
                       Session.AUTO_ACKNOWLEDGE);
      QueueSender queueSender = session.createSender(queue);
      TextMessage message = session.createTextMessage();
      message.setText(xmlDoc);
      queueSender.send(message);
      /** Generate and return a correlation identifier **/
      return generateCorrelationID();
   }
}

Figure 3.8. Travel Agency Service Interaction


In Figure 3.8, the vertical lines represent the passage of time, from top to bottom. The vertical rectangular boxes indicate when the entity (client or service) is busy processing the request or waiting for the other entity to complete processing. The half arrow type indicates asynchronous communication and the dashed vertical line indicates that the entity is free to work on other things while a request is being processed.

One question remains: How does the client get the final result of its request? The service may make the result of the client's request available in one of two ways:

  • The client that invoked the service periodically checks the status of the request using the correlation identifier that was provided at the time the request was submitted. This is also known as polling, and it appears as Option 1 in Figure 3.8.

  • Or, if the client itself is a Web service peer, the service calls back the client's service with the result. The client may use the correlation identifier to relate the response with the original request (Option 2 in Figure 3.8).

Often this is decided by the nature of the service itself. For example, if the service runs a business process workflow, the workflow requires the service to take appropriate action after processing the request.

3.4.4. Formulating Responses

After you delegate the request to the business logic portion of the application, and the business logic completes its processing, you are ready for the next step: to form the response to the request.

You should perform response generation, which is simply constructing the method call return values and output parameters, on the interaction layer, as close as possible to the service endpoint.

This permits having a common location for response assembly and XML document transformations, particularly if the document you return to the caller must conform to a different schema from the internal schema. Keeping this functionality near the endpoint lets you implement data caching and avoid extra trips to the processing layer. (See Figure 3.9.).

Figure 3.9. Web Service Response Processing


Consider response generation from the weather information service's point-of-view. The weather information service may be used by a variety of client types, from browsers to rich clients to handheld devices. A well-designed weather information service would render its responses in formats suitable for these different client types.

However, it is not good design to have a different implementation of the service's logic for each client type. Rather, it is better to design a common business logic for all client types. Then, in the interaction layer, transform the results per client type for rendering. It is thus important to consider the above guidelines, especially when your service has a common processing logic but potentially has different response rendering needs to fit its varied client types.

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

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