Tuesday, December 21, 2004

Aspect oriented programming with CGLIB


1 Introduction and background

2 Purpose of this article
3 Prerequisites
4 Topic 1: Measure the time taken by method calls
4.1 SimpleClass class
4.2 TimeInterceptor class 4
4.3 MethodTimeGenerator class 4
4.4 Main class 5
4.5 Topic Conclusion 6
5 Topic 2: Dynamic connection allocation 6
5.1 DaoTagInterface 6
5.2 DAO class 6
5.3 DaoInterceptor class 7
5.4 DaoGenerator class 9
5.5 DaoFactory class 9
5.6 Main class 9
5.7 Topic Conclusion 10
6 Conclusion. 11
7 Drawbacks 11

1 Introduction and background

Aspect oriented programming has been talked about greatly recently. There are developers who think that it is a great idea and also an equal number who think that it is not as it breaks the principles of Object oriented programming.

2 Purpose of this article

During the last few years I faced some issues that were present across the entire code base.
I thought must have a clean/fast way of solving but I did not have an answer until I read what aspect oriented programming was.
I will present two such problems and their solutions.
I investigated CGLIB after reading this article http://www.onjava.com/lpt/a/5250 by Eugene Kuleshov at Onjava.com.
This paper is a result of that investigation.

3 Prerequisites

Cglib 2 jar file in the class path. It can be downloaded from here: http://sourceforge.net/project/showfiles.php?group_id=56933

4 Topic 1: Measure the time taken by method calls

One of the issues was measuring the exact time taken by each of my method calls, so as to better solve performance issues.
I wanted to do this in a way, so as to be able to switch it on/off as and when required.

Here are the classes

4.1 SimpleClass class

package com.test.methodtimetest;
/**
* @author suchakj
*/
public class SimpleClass {
public void simpleMethod() {
System.out.println("Hello In Simple Method");
}
}
This is just a class with a single method that prints hello.

4.2 TimeInterceptor class

package com.test.methodtimetest;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @author suchakj
*/
public class TimeInterceptor implements MethodInterceptor {
/* This is the method that intecepts a method, it automatically adds the start and end time
* for a method.
* @see net.sf.cglib.proxy.MethodInterceptor#intercept
* (java.lang.Object, java.lang.reflect.Method, java.lang.Object[],
* net.sf.cglib.proxy.MethodProxy)
*/
public Object intercept(
Object obj,
Method method,
Object[] args,
MethodProxy proxy)
throws Throwable {
System.out.println("Before method : " + method + " Time : " + new Timestamp(System.currentTimeMillis()));
Object o = proxy.invokeSuper(obj,args);
System.out.println("After method : " + method + " Time : " + new Timestamp(System.currentTimeMillis()));
return o;
}
}
}

This is a class that implements the Cglib MethodInterceptor interface. The MethodInterceptor interface implements a single method called intercept(). The cglib framework calls this method when a particular Object method is intercepted.
As we can see that the intercept method , adds a Timestamp, before and after the method call. The method is called using proxy.invokeSuper(obj,args); . This takes care of the method timing in one central place.

4.3 MethodTimeGenerator class

package com.test.methodtimetest;
import net.sf.cglib.proxy.Enhancer;
/**
* @author suchakj
*/
public class MethodTimeGenerator {
public static Object getTimeEnhancedObject(Class clazz){
Enhancer e = new Enhancer();
e.setSuperclass(clazz);
e.setCallback(new TimeInterceptor());
return e.create();
}
}

This is the main generator. This class uses the cglib Enhancer class to join the method interceptor class with the any class(in our case it is SimpleClass).

4.4 Main class

package com.test;
import com.test.daoconntest.DAO;
import com.test.daoconntest.DaoFactory;
import com.test.daoconntest.DaoInterceptor;
import com.test.methodtimetest.MethodTimeGenerator;
import com.test.methodtimetest.SimpleClass;
/**
* @author suchakj
*/

public class Main {

public static void testMethodTimeTest(){
//this could come from a factory.
SimpleClass s = (SimpleClass)MethodTimeGenerator.getTimeEnhancedObject(SimpleClass.class);
s.simpleMethod();
}

public static void main(String[] args) {
testMethodTimeTest();
}

}

This the test class. This class is testing our implementation.

Here is the output.
---------------------------------------------------------------------------------------------------------------
Before method : public void com.test.methodtimetest.SimpleClass.simpleMethod() Time : 2004-12-17 14:20:36.046
Hello In Simple Method
After method : public void com.test.methodtimetest.SimpleClass.simpleMethod() Time : 2004-12-17 14:20:36.14
---------------------------------------------------------------------------------------------------------------

4.5 Topic Conclusion

The above code shows how simple it is to add time delay interceptor to a class. It is not at all intrusive on the class that is being intercepted for recording the time delay.

5 Topic 2: Dynamic connection allocation

The importance of connection pools cannot be underestimated.
The main issue I faced in an environment was whether a method returned the connection back to the pool after it had finished using the same.
I wanted to do this in a way, so as to do this in a central place also.
This example will also modify the arguments passed to the method.

Here are the classes

5.1 DaoTagInterface

package com.test.daoconntest;
/**
* @author suchakj
*
* This is just a tag inteface
*/

public interface DaoTagInterface {
}

This is just a tagging interface for DAO classes.

5.2 DAO class

package com.test.daoconntest;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author suchakj
*/

public class DAO implements DaoTagInterface{

public void daoMethod(String someParam,Object connection){
System.out.println("In Dao Method");
if(connection!=null){
System.out.println("Got a Valid connection that i can use");
}else{
System.out.println("No connection");
}

/* try {
Statement s = ((Connection)connection).createStatement();
ResultSet r = s.executeQuery("Select * from SomeTable");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
}

This is the Data access class. It does the call to the database.

5.3 DaoInterceptor class

package com.test.daoconntest;
import java.lang.reflect.Method;
import java.sql.Connection;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* @author suchakj
*/

public class DaoInterceptor implements MethodInterceptor {

/* This is the method that intecepts dao methods, it automatically adds connection objects,
* to the method from a pool and also returns them back to the pool
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
*/

public Object intercept(Object obj, Method method, Object[] args,

MethodProxy proxy) throws Throwable {

// Connection connection = getConnection();
Object connection = getConnection();
args[args.length -1] = connection;
proxy.invokeSuper(obj,args);
returnConnection(connection);
return null;
}

/**
* @return Connection
*/

private Object getConnection() {
// get the connection from some Connection pool
System.out.println("Getting connection from pool ...");
return new Object();// this is just for testing this should be a java.sql.connection object
}

/**
* @param Connection
*/

private void returnConnection(Object c) {
System.out.println("returning connection to pool ...");
// Return the connection back to the pool..
}
}

This is a class that implements the Cglib MethodInterceptor interface. The MethodInterceptor interface implements a single method called intercept(). The cglib framework calls this method when a particular Object method is intercepted.
The getConnection() and returnConnection() methods can get and return connections from a pool.
As we can see this class changes the arguments passed to the method as
“Object connection = getConnection();”
“args[args.length -1] = connection;”
“proxy.invokeSuper(obj,args);”
Thus a “connection object” should be passed to the method daoMethod(String someParam,Object connection). We shall see this in the test.

5.4 DaoGenerator class

package com.test.daoconntest;
import net.sf.cglib.proxy.Enhancer;
/**
* @author suchakj
*/

public class DaoGenerator {
public static Object getTimeEnhancedObject(Class clazz){
Enhancer e = new Enhancer();
e.setSuperclass(clazz);
e.setCallback(new DaoInterceptor());
return e.create();
}
}

This is the main generator for the DAO interceptor. This class uses the cglib Enhancer class to join the interceptor class DaoInterceptor with the any dao class(in our case it is DAO class).

5.5 DaoFactory class

package com.test.daoconntest;

/**
* @author suchakj
*/

public class DaoFactory {
public static DaoTagInterface getDao(Class clazz) throws InstantiationException, IllegalAccessException {
return (DaoTagInterface) DaoGenerator.getTimeEnhancedObject(clazz);
}
}

This is a factory for generating DAO classes. It uses the DaoGenerator for generating Dao classes.

5.6 Main class


This the same test class as in the previous topic. This class is testing our implementation.

package com.test;
import com.test.daoconntest.DAO;
import com.test.daoconntest.DaoFactory;
import com.test.daoconntest.DaoInterceptor;
import com.test.methodtimetest.MethodTimeGenerator;
import com.test.methodtimetest.SimpleClass;

/**
* @author suchakj
*/

public class Main {

public static void testMethodTimeTest(){
//this could come from a factory.
SimpleClass s = (SimpleClass)MethodTimeGenerator.getTimeEnhancedObject(SimpleClass.class);
s.simpleMethod();
}

public static void testDaoConn(){
DAO d;
try {
d = (DAO)DaoFactory.getDao(DAO.class);
d.daoMethod("test",null);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void main(String[] args) {
//testMethodTimeTest();
testDaoConn();
}


}

Here is the output.
---------------------------------------------------------------------------------------------------------------
Getting connection from pool ...
In Dao Method
Got a Valid connection that I can use
Returning connection to pool ...
---------------------------------------------------------------------------------------------------------------

5.7 Topic Conclusion

The above code shows how simple it is for a dao to give and return a connection from/to the connection pool, without writing such code in all the dao methods.Also the code for this is in a central place and can be modified as needed.

6 Conclusion.

Aspects of a code base , a framework can be cleanly coded using Aspect oriented programming. It can be coded in a non intrusive way using Cglib. Thus the result is Aspect code of a similar type in one central place and still applied to the objects that need the Aspect. The two aspects we talked about were getting connection and returning them to a connection pool, and timing methods for querying performance.

7 Drawbacks

Some of the drawbacks of what is explained above are.

1) There could be two or more dao method calls that want to use the same connection object for the reason of transactions. For this the solution could be that we check if the user has sent a connection and if he has then do not apply the interception

2) For both the topics a new Enhancer is created at each method call, it would be better to have a cache of a Hashmap or a WeakHashMap so that the impact of new Enhancer creation at every method call is reduced.

3) The daointerceptor assumes that the method will have a Connection param, which will be the last param, and also which will be ignored even if sent by the user.

0 Comments:

Post a Comment

<< Home