High Performance Dynamic Client-Side Web Services in J2EE

Digg this!

As Web Service component models become more prominent (thanks to the push for Service-Oriented Architecture (SOA) style of design), more and more J2EE applications are starting to contain dynamic client-side Web Services that link together many SOA capabilities. Such applications make use of dynamic Web Services in that the endpoints for the Web Service are only known at runtime -perhaps as a result of user profile information or dynamic configuration.

The most common and widely used way to perform Web Service client invocations is through the use of a stub, using the model described in JSR 109. An auxiliary tool, such as WSDL2JAVA, is used to read the WSDL description of the Web Service interface and generate Java code that can be used to call the Web Service. This code presents itself to the user in the form of a Stub, which is a piece of code whose interface hides the entry point into the Web Services runtime used to make the actual invocation. Typically, the stub contains operations and arguments that map between XSD schema types and Java types; the idea being to hide the details of the Web Service invocation infrastructure from the programmer. Note: some of this will be alleviated by the new JAX WS specification for Web Services, described in JSR 224.

There are two things to be aware of regarding stubs and dynamic Web Services:

  1. Stubs can be created three ways: 1) Instantiating the stub directly, 2) Using the JSR109 APIs to create an instance, and 3) Looking up a service in JNDI and obtaining the stub. The problem with these approaches is that each of them cause the stub to be loaded each time. Loading requires searching the classpath (causing I/O latency which can produce a bottleneck) and, depending on the load method, initializing JAX-RPC handler chains. The solution is to cache the stub instance to avoid loading but...
  2. In order to set the dynamic endpoint, the user must typically call setEndpoint() or an equivalent first and then invoke the operation. In a multi-threaded environment such as a Web or EJB container, this becomes problematic. This two step process must be atomic. In addition, it's undesirable to share the same static instance between threads, as this creates a synchronization bottleneck and reduces parallelization (particularly for synchronous Web Services that must access the network).

The solution to this problem lies in the use of a Java trick by using the ThreadLocal class. ThreadLocal allows the creation of variables that live on the thread stack. Each time a thread local variable is initialized, a copy local to the thread is created and then manipulated only by the thread. The use of ThreadLocal is necessary because the programmer does not control the creation of threads; threads typically live in a thread pool and are assigned by the application server as needed. The programmer can load the stub once and cache it in a thread local variable. This avoids the need for any locking, as the thread can then set the dynamic endpoint and perform the invocation at will. In addition, because threads live in a pool, the thread local variables will only be initialized once and live throughout the lifetime of the pool. Since the number of stubs for most applications is relatively small and fixed, this behavior provides the optimal caching.

The use of ThreadLocal takes some getting used to. This shows an example using a stub class called MyStub.

public class ThreadLocalMyStub {

  private static ThreadLocal cached = new ThreadLocal() {
    protected Object initialValue() {
      return new MyStub();
    }
  }

  public static MyStub getInstance() {
    return (MyStub)cached.get();
  }

  // ....

}