Generically chain dynamic proxies

Add AOP concepts to your application

Programming with plain-old Java objects (POJOs) is rather popular these days. When we program with POJOs, we can apply object-oriented programming (OOP) pretty easily. But sometimes implementing cross-cutting aspects throughout an application using OOP proves difficult. For example, generally implementing logging or security in our POJO business objects throughout an application is difficult to achieve with OOP. Dynamic proxies, which were introduced in J2SE 1.3, offer an easier solution.

The idea behind dynamic proxies is to include dynamic behavior around an object, yet neither change the object's existing code nor its interface. The famous Gang of Four (GoF) Decorator pattern provides a way to decorate an object (change its behavior) without changing the object code and allows us to add cross-cutting aspects to our business objects. Many existing frameworks and tools use this pattern. But when implementing static decorating, the pattern imposes some problems, which I discuss later in the article.

Prior to the introduction of dynamic proxies, there was no direct way to decorate objects dynamically. So vendors came up with tools to generate code automatically and decorate objects. Though a code-generation tool can help us generate static decorators, it requires extra steps and also introduces overhead for maintaining the generated code. By using dynamic proxies, we can greatly reduce this auto-generated code (perhaps to zero).

To see how dynamic proxies work, let's take an example of the classic Decorator pattern as a method interceptor and see what dynamic proxies can offer in its place. If we use dynamic proxies as is, we may face some coding complexities. Later in the article, you will see how to wrap these complexities involved with dynamic proxies and provide an abstraction over them. Most of the source code used in this article can be downloaded from Resources.

Static decorating and chaining without dynamic proxies

Suppose we have a simple business interface:

 public interface IMyBusinessObject {
   public String doExecute(String in);
}

And this implementation of a business object class:

 public class MyBusinessObject implements IMyBusinessObject {
   public String doExecute(String in) {
      System.out.println("Here in MyBusinessObject doExecute: input :" + in);
      return in;
   }
}

Now we want to add some behavior (e.g., logging) before and after the doExecute() method. The Decorator pattern helps us easily add this functionality.

We define an abstract decorator class like the following:

 public abstract class ADecorator implements IMyBusinessObject {
   protected IMyBusinessObject target;
   public void setTarget(IMyBusinessObject target_) {
      this.target = target_;
   }
   public ADecorator(){}
   public ADecorator(IMyBusinessObject target_) {
      setTarget(target_);
   }
}

Now we define a concrete DebugConcreteDecorator, which extends ADecorator. Our intention is to add debug messages before and after our business object's method invocation:

 

public class DebugConcreteDecorator extends ADecorator { public String doExecute(String in) { System.out.println("DebugConcreteDecorator: before method : doExecute ");

String ret = target.doExecute(in); System.out.println("DebugConcreteDecorator: after method : doExecute "); return ret; } }

Now from the client code, we call our business object:

 IMyBusinessObject aIMyBusinessObject = new MyBusinessObject();
IMyBusinessObject wrappedObject = 
   new DebugConcreteDecorator(aIMyBusinessObject);
wrappedObject.doExecute("Hello World");

In the above code snippet, we wrap our business object with the DebugConcreteDecorator class instance. Since DebugConcreteDecorator extends ADecorator and ADecorator implements the business object interface IMyBusinessObject, the DebugConcreteDecorator class is itself an instance of IMyBusinessObject. First, we create an instance of MyBusinessObject. Next, we create an instance of DebugConcreteDecorator and pass the business object in the constructor. Since no constructor is in DebugConcreteDecorator, the constructor of its superclass (ADecorator) is called and the target object is set with the MyBusinessObject instance. So when the doExecute() method is called on the DebugConcreteDecorator instance, it first logs some debug message (using System.out) and then calls the business method on the actual business object (MyBusinessObject).

The above invocation's output is the following:

 DebugConcreteDecorator: before method : doExecute
Here in MyBusinessObject doExecute: input :Hello World
DebugConcreteDecorator: after method : doExecute

From this output, we observe that we are able to add the debug messages before and after the business method invocation.

We can also chain the decorators—call one decorator after another decorator—before invoking actual business methods. Let's define another decorator to show this approach:

 public class AnotherConcreteDecorator extends ADecorator {
   public String doExecute(String in) {
      System.out.println("AnotherConcreteDecorator: Going to execute method : doExecute");
      in = in + " Modified by AnotherConcreteDecorator";
      String ret = target.doExecute(in);
      System.out.println("AnotherConcreteDecorator: After execute method : doExecute");
      return ret;
   }
}

The above code snippet modifies the business method's input string parameter by adding an extra string (" Modified by AnotherConcreteDecorator") to it.

If we want to chain the decorators, we write the following code snippet in the client:

 IMyBusinessObject aIMyBusinessObject = new MyBusinessObject();
IMyBusinessObject wrappedObject =
   new AnotherConcreteDecorator (new DebugConcreteDecorator(aIMyBusinessObject));
wrappedObject.doExecute("Hello World");

In the above code snippet, we create a DebugConcreteDecorator instance by passing the actual target business object instance into it. Then we wrap the DebugConcreteDecorator instance with our new AnotherConcreteDecorator instance. So when the doExecute() method is called in the AnotherConcreteDecorator instance, AnotherConcreteDecorator first modifies the input parameter by adding the extra string. Then the call forwards to the DebugConcreteDecorator instance's doExecute() method. There, DebugConcreteDecorator logs the entry of the doExecute() method and calls the doExecute() method of the actual business object's doExecute() method.

The return path is the exact opposite sequence. After returning from the actual business object's (MyBusinessObject) doExecute() method, the rest of the code in DebugConcreteDecorator executes. Then the call returns to the AnotherConcreteDecorator instance and executes the rest of its part.

The output of the above invocation is the following:

 AnotherConcreteDecorator: Going to execute method : doExecute
DebugConcreteDecorator: before method : doExecute
Here in MyBusinessObject doExecute: input :Hello World Modified by AnotherConcreteDecorator
DebugConcreteDecorator: after method : doExecute
AnotherConcreteDecorator: After execute method : doExecute 

The class diagram of the above approach appears in Figure 1.

Figure 1. Class diagram of decorating the business object with static decorator

Now let's look into the issues with static decorating.

Look inside the doExecute() method of either of the two decorators (DebugConcreteDecorator or AnotherConcreteDecorator). It makes a hard-coded call to the target object's doExecute() method. Also, if we define another method in the IMyBusinessObject interface, we must override that in all the concrete decorators and provide an implementation for the method. So in practicality, we may end up with many decorators and a lot of code inside each decorator. A dynamic proxy helps us remove those hard-coded calls. In addition, we don't need to override and implement each method of the business interface inside the decorators.

I do not go into detail about dynamic proxies in this article. Instead, I take a small example and show how a dynamic proxy works. Then we jump into the chaining of dynamic proxies to intercept method calls in a generic way.

J2SE 1.3 dynamic proxies: An example

A dynamic proxy class is a class that implements a list of interfaces specified at runtime when the class is created. A proxy interface is an interface implemented by a proxy class and an instance of the java.lang.reflect.Proxy class. Each proxy instance has an associated invocation handler object, which implements the interface java.lang.reflect.InvocationHandler. A method invocation on a proxy instance through one of its proxy interfaces is dispatched to the invoke method of the instance's invocation handler, passing the proxy instance, a java.lang.reflect.Method object identifying the method that was invoked, and an array of type java.lang.Object that contains the arguments. The invocation handler processes the encoded method invocation as appropriate and the result it returns is returned as the result of the method invocation on the proxy instance.

For example, say we have the same interface IMyBusinessObject and the business class MyBusinessObject used in the earlier example. Now while using the dynamic proxy, we must write an invocation handler since the java.lang.reflect.Proxy class will use this handler.

The DebugInvocationHandler class looks like this:

 

public class MyDebugInvocationHandler implements java.lang.reflect.InvocationHandler {

private Object target = null; public void setTarget(Object target_) { this.target = target_; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { System.out.println("Going to execute method : " + method.getName); Object retObject = method.invoke(target, args); System.out.println("After execute method : " + method.getName()); return retObject; } catch(InvocationTargetException e) { throw e.getTargetException(); } catch(Exception e) { throw e; } } }

In the above example, the invoke() method is important and is invoked through the java.lang.reflect.Proxy class. Inside this method, we can do our extra processing and then forward this processing to the real target object (in our case, an instance of MyBusinessObject).

So our client side code can be the following:

 

IMyBusinessObject bo = new MyBusinessObject(); MyDebugInvocationHandler aMyDebugInvocationHandler = new MyDebugInvocationHandler(); aMyDebugInvocationHandler.setTarget(bo);

IMyBusinessObject proxyObject = (IMyBusinessObject) Proxy.newProxyInstance (IMyBusinessObject.class.getClassLoader(), new Class[] { IMyBusinessObject.class }, aMyDebugInvocationHandler);

System.out.println(proxyObject.doExecute("Hello World"));

In the code above, we create an instance of MyBusinessObject and an instance of MyDebugInvocationHandler. We set the target as MyBusinessObject in MyDebugInvocationHandler such that when invoke() is called, it can pass the request to the proper target. Then we create a proxy object of the IMyBusinessObject interface using java.lang.reflect.Proxy. After that, we invoke a method on the proxy. It is important to note that since the invoke() method deals with the generic java.lang.reflect.Method class and no business-interface-specific methods, the need to write separate invocation handlers for separate business interfaces is not required. Also, if we want to implement some cross-cutting aspect through all the business methods, we do not have to implement all the business methods defined in the business interface. For example, to implement security in all our business methods, we must implement the security logic in only one place and within one method (SecurityInvocationHandler's invoke() method, which we will write in a generic way).

If we want to add multiple handlers in the chain, we must create another invocation handler. Then in the new handler's setTarget() method, instead of setting the instance of MyBusinessObject, we set the previous proxy object of the chain. Thus the code would appear as follows:

 

MyAnotherInvocationHandler aMyAnotherInvocationHandler = new MyAnotherInvocationHandler (); //Here we will set the proxyObject, which we get through earlier //code snippet, instead of the business object instance aMyAnotherInvocationHandler.setTarget(proxyObject);

IMyBusinessObject nextProxyObject = (IMyBusinessObject) Proxy.newProxyInstance (IMyBusinessObject.class.getClassLoader(), new Class[] { IMyBusinessObject.class }, aMyAnotherInvocationHandler);

System.out.println(nextProxyObject.doExecute("Hello World"));

From the above example, we can see how using a dynamic proxy adds the supplementary behavior dynamically, with less code than static decorator chaining. However, if we use the dynamic proxy as shown above, some problems remain: You still must write a lot of code while creating and chaining the dynamic proxies. And you have to deal with the proxy interface API, which is not as user-friendly as normal object creation or object creation from a factory class. Also, when we need to proxy our business classes, repeating the same code in multiple places is not a good idea.

The rest of this article tries to solve these problems. We will write a generic proxy factory that can expose simple APIs (hiding the proxy object creation and chaining within it), but can still provide the flexibility of the dynamic proxy.

1 2 3 4 Page 1
Page 1 of 4