Problem Statement
We all know we need to execute some common code at the beginning and at the end of a business service (a Java API in this case). These are:- Diagnostic log messages at the beginning and at the end of the service
- Open and close transaction at the beginning and at the end
- Security check (authorization, or checking user's role/permission) at the beginning
Rant
Couple of years back I purchased and read the book "Aspectj in Action: Enterprise AOP with Spring Applications". There is lot of AOP evangelizing in it, and the authors managed to write more than 500 pages on this simple topic. Well they didn't keep it simple, they made it complex and solved problems that do not exist.AOP evangelists came up with lots of silly and superfluous jargons: cross-cutting, weaving, join points, point cuts, advice. But they could not come up with a simple solution that I could just close my eyes and use to solve that basic problem I stated above. If anyone in my team uses any of those esoteric features, I will fire her. I sold off the AOP book on Amazon.
World's Simplest Java AOP
I implemented a simple proxy-based solution to my problem. I entirely followed Implement Your Own Proxy-Based AOP Framework, but simplified even further: I got rid of the factory classes, coded diagnostic logging and transaction handling in the proxy class itself. Only for security check, I created an interceptor class, because each method will have different security checks. Here is the code:Annotation class:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SecurityCheck {
String value();
}
Interceptor Interface:Interceptor Class:import java.lang.reflect.Method;public interface SecurityCheckInterceptor {
void before(Method method, Object[] args);
}
Proxy class that will put everything together:import java.lang.reflect.Method;public class SecurityCheckInterceptorImpl implements
SecurityCheckInterceptor {@Override
public void before(Method method, Object[] args) {
System.out.println("Before " + method.getName());
SecurityCheck securityCheck =
method.getAnnotation(SecurityCheck.class);
if (securityCheck == null)
return;
String role = securityCheck.value();
System.out.println("Security check : " + role);
// authorize here
}
}
That's it. Two interfaces and two classes. How to use it? We will create an example.import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
public class DynamicProxyInvocationHandler implements InvocationHandler {
private Object target;
private SecurityCheckInterceptor interceptor;
public DynamicProxyInvocationHandler(Object target,
SecurityCheckInterceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
logBefore(method);
interceptor.before(method, args);
startTransaction();
Object returnValue = method.invoke(target, args);
// add interceptor.after(method, args) here, if defined
return returnValue;
} catch (Throwable t) {
rollbackTransaction();
// add interceptor.inThrowing(method, args) here,
// if defined throw t;
} finally {
commitTransaction();
// add interceptor.inFinally(method, args) here, if defined
logAfter(method);
}
}
private void logBefore(Method method) {
System.out.println("Start " + method + " at "
+ new Date(System.currentTimeMillis()));
}private void logAfter(Method method) {
System.out.println("End " + method + " at "
+ new Date(System.currentTimeMillis()));
}private void startTransaction() {
// start db transaction
}private void rollbackTransaction() {
// rollback db transaction
}private void commitTransaction() {
// commit db transaction
}
}
Service interface. This is the signature of the Java API that is being exposed as a service.
public interface MyService {
@SecurityCheck("SOME_PERMISSION")
void doSomething(String data);
}
Service implementation class. This is the implementation of the Java API that is being exposed as a service.
public class MyServiceImpl implements MyService {
@Override
public void doSomething(String data) {
System.out.println(data);
}
}
Main method that will call the above service.import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MyMain {
public static void main(String[] args) {
InvocationHandler handler = new DynamicProxyInvocationHandler(
new MyServiceImpl(),
new SecurityCheckInterceptorImpl());
MyService proxy = (MyService) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { MyService.class }, handler);
proxy.doSomething("Goodbye World!");
}
}
No comments:
Post a Comment