Popular Posts

Saturday, May 26, 2012

Switching between implemenations of an interface at runtime in java

Do you remember that class that you would love put an "if" before each method? Or, that in witch you would love to catch all exceptions in a unique stretch of code? Some way to change the application flow without any change in existing class? If you are thinking in aspects, you're wrong! There is a way to make the same thing without add any class, api, framework, special compiler or virtual machine. It's pure and basic Java. In this step I would like to present the Proxy class at Reflection API that comes together with Java since version 1.3. 

The Proxy is a special class that allows intercept a set of methods identified by an interface. You can, for example, intercept all methods of a class to check if an attribute is not null or to catch possible exceptions in methods. E.g.: Let's say that the class below is present in our commercial systems: 

public class Sum {
public Integer value1;
public Integer value2;
 
public Sum(Integer val1, Integer val2) {
value1 = val1;
value2 = val2;
}
 
public Integer sum() {
return new Integer(value1.intValue() + value2.intValue());
}
 
}


This class will run? It'll find the correct result? Well, being optimistical, yes, it will do. But, if a null value was passed as a parameter to the constructor, the method sum() will throw a pretty NullPointerException. Who is catching that? Anyone. Who will be using the class never will remember (and wouldn't have) that the method sum() can launch a exception. What we can do for solve? We can use reflection to help the user. Here we go. 

First, we need an interface with the method to intercept. In our case, the sum(). 

public interface ISum {
public Integer sum();
}


After, we have to create a class that will handle the method call. This class will try to run the true method sum() in Sum class, if it fails, the NullPointerException must be catched and a zero value must return. The handle must implement the InvocationHandler interface and obviously implements the public Object invoke(Object proxy, Method method, Object[] args) method. This method will be called when the user invoke the method sum() in a Proxy class. The "try catch" will evict the NullPointerException. See above: 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class Handler implements InvocationHandler {
 
public Sum trueSum;
 
public Handler(Sum sum) {
this.trueSum = sum;
}
 
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(trueSum, args);
} catch ( Exception e ) {
return new Integer(0);
}
}
}


And now, to be beautiful, we must implement a Sum factory. An object that will create the true Sum and the proxy class. Anyone can create a Sum class without this factory class. You can block this action with a private or protected constructor. 

import java.lang.reflect.Proxy;
 
public class SumFactory {
public ISum createSum(Integer val1, Integer val2) {
Sum sum = new Sum(val1, val2);
Handler handler = new Handler(sum);
Class[] interfacesArray = new Class[] {ISum.class};
 
return (ISum) Proxy.newProxyInstance(Sum.class.getClassLoader(), interfacesArray, handler);
}
}


As you can see, this class creates a Sum instance with the params to sum() and a handler to intercept the calls in a Proxy class. The var sum is passed into a Handler constructor to be the responsable to right result. The method newProxyInstance creates the Proxy class that will change the application flow, repassing responsibilities to handler. The var interfacesArray is an array of interfaces that have the methods to handler intercept. 

To finish, it's necessary declare Sum class implementing the ISum interface. If you don't do this the Proxy class will throw an Exception like:"Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class" on constructor. 

After, all this work, you can test with a simple class, like: 
public class Test {
public static void main(String[] args) {
ISum s = new SumFactory().createSum(null, new Integer(2));
System.out.println("Returns (null+2): "+s.sum());

s = new SumFactory().createSum(new Integer(3), new Integer(2));
System.out.println("Returns (3+2): "+s.sum());
}
}


Easy? Yes, the proxy class calls the handler that invokes que true sum() method and catch possible exceptions. 

No comments:

Post a Comment