Pages

Wednesday, July 23, 2014

Developing a dynamic JEE service client with CDI (part 1)

With JEE6 CDI was introduced in and standardized for the Java Enterprise world. The main scope for the CDI extension was for enterprise services as it simplified various mechanisms in the JEE world such as EJB lookup or invocation interception. Unfortunately the new features fall a bit short on the consumer side of services implemented with CDI, in particular the lookup or service locator pattern, and client side invocation interception.
In this first article I want to describe a dynamic service client that performs lazy service dependency resolution (lookup) and that can be injected into a CDI injection point.
It is a good base for being extended to use sophisticated configuration or error handling mechanisms.

Defining a Dynamic Service Locator

In JEE6 its quite easy to perform a JNDI lookup for an EJB using the @EJB annotation. Unfortunately this is done during construction of the service consumers. When you have a multi-module project where each module should be deployable independently from the others, deployment will fail if the dependencies defined by the @EJB annotation could not be satisfied, that is, the EJB lookup fails.
The key to this problem is a lazy lookup, that is done first time the dependency is actually needed. But that is not supported by the @EJB annotation.
In our current project we solved the problem by implementing a dynamic service proxy that implements the interface of the service and performs the lookup using a JNDI lookup or another strategy on the first actual service call.

Creating a Proxy

Creating a dynamic proxy is relatively simple, you need a custom implementation of an InvocationHandler that performs the actual method invocation and need to specify the interfaces the dynamic proxy should implement. A factory method is used to create such a proxy instance. The the following code example shows such a factory method:
public abstract class DynamicServiceClient {

  public static <T> T newInstance(final Class T serviceInterface) {
    return (T) java.lang.reflect.Proxy.newProxyInstance(
      getClassLoader(),
      new Class[] { serviceInterface }, 
      new ProxyServiceInvocationHandler T(config));
    }
 
  private static ClassLoader getClassLoader(){
     ...
  }
}

Invocation Handler

The invocation handler delegates all service call to an instance of the actual service. The service is kept as an instance field of the invocation handler that is initialized upon the first service call using a Service Locator. The invoke method of the Invocation Handler delegates the call to the service instance.

class ProxyServiceInvocationHandler<T> 
  implements InvocationHandler {
 /**
  * The service reference that is lazily initialized
  */
  private T service; 

 /**
  * The service locator that performs the actual lookup
  */
  private final ServiceLocator<T> locator;

    ...

  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args) 
    throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    //do the lookup if not already done
    if (this.service == null) {
      this.service = this.locator.locate();
    }
    //do the actual service call and return the result
    return method.invoke(this.service, args);
  }
}

Service Locator

The service locator that is used in the the above listing performs the actual lookup. In our project I used an implementation that allowed to define a location strategy in a configuration, with the JNDI lookup being implemented in one of the strategies. The following example however shows a simple implementation that performs a straightforward JNDI lookup using a lookupName.

public class ServiceLocator<T>{
 /**
  * the name for the JNDI lookup
  */
  private String lookupName;

    ...

  public T locate() {
    final Context context = new InitialContext();
    T service = (T)context.lookup(this.lookupName);
    return service;
  }
}

Service Injection

In order to use the dynamic service shown above and inject it via CDI, I used a producer method that creates instances of the dynamic service and a qualifier to distinguish the dynamic service client from the actual service implementation that might be available in the CDI container as well.
The creation of the instance itself is done by the static factory method shown in the first listing. The producer extends the DynamicServiceClient I described earlier and uses its static factory method. In order to keep the producer class simple, we need to extend some methods to the DynamicServiceClient.

The extended DynamicServiceClient that is shown in the following listing contains the logic for the actual service proxy instantiation and an abstract method returning the interface class of the service to be instantiated that has to be implemented by the producer.

public abstract class DynamicServiceClient<T>{
  /**
   * Instance of the service client
   */
  private T serviceInstance;

  /**
   * The method has to return the interface of the service 
   */
  protected abstract Class<T> getServiceInterface();

  /**
   * Creates a new instance of the service client. 
   */
  protected T getInstance() {
   if (serviceInstance == null) {
       serviceInstance = newInstance(getServiceInterface());
   }
   return serviceInstance;
 }

  /**
   * The factory method to create a new service instance
   */
  public static <T> T newInstance(final Class serviceInterface) {
    return (T) Proxy.newProxyInstance(
        getClassLoader(),
        new Class[] { serviceInterface }, 
        new ProxyServiceInvocationHandler());
    }
  }

  private static ClassLoader getClassLoader(){
     ...
  }
}

To qualify an injection point and the producer method to create and inject instances of the service client that implement the service interface and not instances of the actual service, I used a qualifier as shown in the next listing.

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface ServiceClient {}

Now we're ready to define a producer using the DynamicServiceClient as base class. The producer is relatively simple as it only contains two simple methods.


public class MyServiceClientProducer 
  extends DynamicServiceClient<MyBusinessService> {

  @Produces @ServiceClient 
  public MyBusinessService getInstance() {
    return super.getInstance();
  }

  protected Class<MyBusinessService> getServiceInterface() {
    return MyBusinessService.class;
  }
}
To inject the dynamic service client into an injection point we simply have to define the Service interface class and use the qualifier. That's it.

public class MyServiceConsumer {

  @Inject @ServiceClient
  private MyBusinessService service;

  ... 
}

Instead of
public class MyServiceConsumer {

  @EJB(lookup="...")
  private MyBusinessService service;

  ... 
}

Conclusion

In this article I described how to implement a dynamic service locator that can be used in a CDI container. The service locator performs a lookup upon the first call of a service method and therefore provides a mechanism for fault tolerant dependency resolving and further a foundation for implementing client-side service call interception - which I will a describe in a future post.

No comments: