Showing posts with label design-patterns. Show all posts
Showing posts with label design-patterns. Show all posts

Monday, September 24, 2007

P of EAA: Active Record

http://www.martinfowler.com/eaaCatalog/activeRecord.html

I got referred to this from a blog entry about PHP and Ruby. I decided to take a quick look at it and make some observations. I guess this would be the first in a series of Design Pattern reviews on Martin Fowler's bliki.

I have seen this pattern employed in some of my projects (especially Curam projects), it looks like a merge between a Java Bean with database methods. However, I don't think this is a good pattern to follow.

The problem with this pattern are:

  • you are doing more than one thing in a single class
  • the class encapsulates on single record, so what do you do about a list?
  • the class knows about the way it is going to be stored

I would recommend breaking this apart into two separate classes: a table module and a POJO. The table module is responsible for CRUD and list retrieval operations on the POJO and can be replaced with many different implementations.

The POJO need not represent a single row either, but it can contain an entire graph containing everything about the POJO.

e.g., A case can contain all it's evidences and a list of it's participants.

However, you do need some tool support for this like Hibernate which can do lazy fetching so you don't kill your database.

Conclusion: Anti-pattern

Although, I disagree with this pattern, it does not mean that this is the beginning of a bashing Martin Fowler, I do agree with almost everything he says.

Monday, August 08, 2005

EJB Pattern - Service Locator with Remote Method Invocation

To start, I'm quite sure there are several implementation of this pattern already. However, this is just one of three parts. The first part introduces an implementation of the Service Locator pattern that performs remote method invocation. The second part is short and puts optimizations into the implementation. The final part involves converting it into a PicoContainer ComponentAdapter.

Context

Services are implemented as Java objects that can be run and tested outside the context of a container, but using an EJB to provide separation of the service implementation from clients.

Problem

The use of services as Java Objects rather than EJBs allows for easier testability without using a container. The problem comes when using EJBs as a transport and ending up with several EJBs that only perform transport functionality as this will yield in duplicate code and a lot of configuration done through the EJB descriptors and manifests. Using the Service Locator pattern in conjunction with Dynamic Proxies would reduce the amount of EJBs needed to one.

Forces

  • Reduce the number of EJBs that perform the same function of transporting data from client to the service implementation.
  • Prevent cut and paste coding from the service interface to the EJB Remote interface (that require java.rmi.RemoteException thrown).
  • Remove the need of hand-coded proxies for every service.

Solution overview

Create an EJB that instantiates implementations of services and stores them in a map. The EJB will have an exposed method

Serializable
invoke(Class serviceClassInterface,
    String methodName,
    Serializable[] args)
that would invoke the method on the service class implementation instance. This single EJB would be used for all services.

Depending on the application server. we are no longer taking full advantage of the EJB Cache provided by the as there is only one entry. Also, the application server might not provide the capability to create a pool of stateless session beans.

You will also lose access to metrics based on EJB usage since it all goes to one. This is similar to the Struts framework where everything goes into one servlet.

A service locator to performs the EJB lookup functionality and retrieve an RMI client stub for the clients that can connect to the server.

Solution Step 1: Creating the service test case

In keeping with the Test First Development methodology, a test case is made that shows how we plan to use the service. Starting simple makes things easier to understand so the first test case shows how the service would be used without a container.

public void testUpperServiceNoContainer() {
  UpperService upperService = new UpperServiceImpl();
  assertEquals("ABCDE", upperService.upper("Abcde"));
}
The implementation class implements the method as follows:
public String upper(String s) {
  return s.toUpperCase();
}

Solution Step 2: Introducing the service locator

The service locator pattern is pretty simple, you send a request for a service and it will give you an object that you can use to manipulate the service. Unlike the Core J2EE pattern the implementation discussed here avoids the Singleton antipattern.

public void testServiceLocator() {
  ServiceLocator locator = new MapServiceLocator();
  UpperService upperService =
    (UpperService) locator.getService(UpperService.class);
  assertEquals("ABCDE", upperService.upper("Abcde"));
}

At this point the application would now fail to run since we do not have ServiceLocator implemented yet. The next few sections discuss how to build the ServiceLocator class.

Solution Step 3: Creating the Service Locator class

We start the service locator using a simple map that instantiates the implementation. This will make the test case pass.

public class MapServiceLocator implements ServiceLocator {
  private Map serviceMap;
  public MapServiceLocator() {
    serviceMap = new HashMap();
    serviceMap.put(UpperService.class, new UpperServiceImpl());
  }
  public Object getService(Class clazz) {
    return serviceMap.get(UpperService.class);
  }
}

Our next step would be to remove knowledge of UpperServiceImpl on the MapServiceLocator implementation.

Solution Step 4: Introducing the Invoker

Currently the ServiceLocator has knowledge of the actual implementation classes. Separating the knowledge of the implementation would allow packaging code so interfaces and transfer objects are the only ones provided to the client and the implementation can be put elsewhere. An approach will use Java's reflection API and use it as an indirect way of invoking a method. By using this approach it is possible to invoke a method based on information obtained at runtime rather at compile time. The following test case shows how an invoker method would be used.

public void testInvoke() {
Invoker invoker = new Invoker();
assertEquals("ABCDE",
invoker.invoke(UpperService.class,
 "upper", new Serializable[] { "Abcde" }));
}
As shown, the client has no concept of the actual implementation. Similar to that of the ServiceLocator. The implementation of Invoker is as follows:
public class Invoker {
  private Map map;
  public Invoker() {
    map = new HashMap();
    map.put(UpperService.class, new UpperServiceImpl());
  }
  public Serializable invoke(Class clazz, String mName, Serializable[] args) {
    Object impl = map.get(clazz);
    Class[] parameterTypes = new Class[params.length];
    for (int i = 0; i < args.length; ++i) {
     parameterTypes[i] = args[i].getClass();
    }
    Method method = clazz.getMethod(mName, parameterTypes);
    return (Serializable)method.invoke(impl, args);
  }
}

Implementing the method above would make the test case pass.

Solution Step 5: Changing the service method to use the invoker

The java.lang.reflect.Proxy class allows us to create a custom invocation handler that would intercept method calls and perform an action based on it. In our case, it should simply invoke the implementation method. The following test case demonstrates how clients would use it. Care has to be taken to ensure that only Serializable data gets transmitted otherwise if the service is running on a remote server it would cause problems. The following service locator has been converted to use the invoker. An invocation handler acts similar to the testInvoke method. The locator creates a proxy that uses the invocation handler which invokes the actual method.

public class InvokerServiceLocator implements ServiceLocator {
private Invoker invoker = new Invoker();
public Object getService(Class clazz) {
InvocationHandler handler = new InvocationHandler() {
 public Object invoke(Object proxy, Method method, Object[] args){
   Serializable[] serializableArgs = new Serializable[args.length];
   System.arraycopy(args, 0, serializableArgs, 0, args.length);
   return invoker.invoke(clazz, method.getName(), args);
 }
};
return Proxy.newProxyInstance(clazz.getClassLoader(),
 new Class[] { clazz }, handler);
}
}

When creating a new test case using the updated implementation, it should pass as is. The Invoker itself can be separated so an interface can be put into the client and the implementation can be put into the EJB tier. The next step would be to change Invoker to an EJB.

Solution Step 6: Creating a sample client

Creating a simple client that invokes the remote service would help in testing. Initially, the local Invoker would be used rather a remote one.

protected void init() {
  getServletContext().setAttribute(ServiceLocator.class.getName(),
  new InvokerServiceLocator());
}

protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
  ServiceLocator locator =
    (ServiceLocator) getServletContext().getAttribute(
      ServiceLocator.class.getName());
  UpperService upperService =
    (UpperService) locator.getService(UpperService.class);
  resp.getWriter().println(upperService.upper("Abcde"));
}

Since this eventually would use an EJB call, it this client should have to be tested on a full J2EE container rather than using something like ServletUnit. It is suggested that an HttpUnit test is created for this one to ensure that it will work in an automated fashion.

Solution Step 7: Converting to an EJB

Converting the Invoker to be an EJB is a purely mechanical process. The constructor is moved to the ejbCreate() method and the boiler plate interfaces, descriptors and manifests are created.

public class InvokerBean implements SessionBean {
private Map map;
public void ejbCreate() throws CreateException {
map = new HashMap();
map.put(UpperService.class, new UpperServiceImpl());
}
public Serializable invoke(Class clazz, String mName, Serializable[] args) {
Object impl = map.get(clazz);
Class[] parameterTypes = new Class[args.length];
for (int i = 0; i < args.length; ++i) {
 parameterTypes[i] = args[i].getClass();
}
Method method = clazz.getMethod(mName, parameterTypes);
return (Serializable)method.invoke(impl, args);
}
}

The interfaces can be implemented as Local or Remote or both. Its recommended that the local interface is used rather than remote, not because of efficiency (some application servers can convert to use pass-by-reference automatically if the EJB is in the same server), but because of security. Local interfaces ensure that the invoker gets used only by the application and not remote clients. Its a choice between not needing to change the code if they want remote or security.

Solution Step 8: Converting InvokerServiceLocator to use the EJB

The final step is to create an implementation of the InvokerServiceLocator to use the EJB instead of a local version.

public Object getService(Class clazz) {
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
 Serializable[] serializableArgs = new Serializable[args.length];
 System.arraycopy(args, 0, serializableArgs, 0, args.length);
 Context context = new InitialContext();
 InvokerLocalHome =
   (InvokerLocalHome) context.lookup("java:comp/env/ejb/Invoker"));
 InvokerLocal invoker = home.create();
 return invoker.invoke(clazz, method.getName(), serializableArgs);
}
};
return Proxy.newProxyInstance(clazz.getClassLoader(),
new Class[] { clazz }, handler);
}

When invoking the test client it should yield the same results.

Passing in additional data

On multi-tier applications, sometimes it is required to pass some contextual data stored in a ThreadLocal or other variable from one tier to another. The pattern allows to make the invoke method pass data that is not inside the method signature and send it over the wire. The EJB implementation class can create a new ThreadLocal to store the data or call a separate interface method that would store the contextual data. Although Dependency Injection would be better, ThreadLocal is an easy way of transmitting contextual data around without having to do any of the leg-work. The invoker method on the EJB tier can wrap the setting and clearing of ThreadLocal data as follows:

public Serializable invoke(Serializable contextData,
  Class clazz, String mName, Serializable[] args) {
  try {
    setThreadLocalData(contextData);
    Object impl = map.get(clazz);
    Class[] parameterTypes = new Class[args.length];
    for (int i = 0; i < args.length; ++i) {
      parameterTypes[i] = args[i].getClass();
    }
    Method method = clazz.getMethod(mName, parameterTypes);
    return (Serializable)method.invoke(impl, args);
  } finally {
    clearThreadLocalData();
  }
}

Please note that if dependency injection is used or the services contain attributes, you might not use the map to store the services but instantiate them as needed.

Further Work

The implementation provided in this article has not been optimized for performance. There are several ways of improving this such as creating a cache of methods and caching the JNDI lookup. The list of implementation classes are also hard coded into the Maps and those can be made property file driven. All these techniques would be described in the next article (which I apologize that it had never materialized).