Embracing Aspect Oriented programming with Interceptors

Interceptors serve as an Aspect Oriented programming toolkit for the Java EE Platform. They allow you to easily incorporate cross cutting business concerns without polluting the core business logic. Let's begin with a quick overview of the history of Interceptors specification

History

Debut

Interceptors (v 1.0) were first introduced in EJB 3.0 (they did not have a specification dedicated them). They bought basic AOP related features to managed beans (POJOs) via simple annotations like @Interceptors, @AroundInvoke etc.

Version 1.1

Along came Java EE 6 with EJB 3.1 and Interceptors 1.1. They were still included as a part of the EJB specification. New annotations such as @InterceptorBinding, @Interceptor, @AroundTimeout were introduced in this version

Version 1.2: a dedicated specification

Interceptors were split off into an individual specification in Java EE 7 and thus Interceptors 1.2 came into being. The @AroundConstruct annotation was introduced as a part of this particular release. Interceptors 1.2 was a maintenance release on top of 1.1 and hence the JSR number still remained the same as EJB 3.1 (JSR 318)

EJBs and Interceptors - how are they related ?

They are not coupled to EJBs at all. From a functionality perspective, you do not need to do anything special to reap the benefits of aspect oriented programming offered by Interceptors. Thanks to the introduction of Managed Beans specification in Java EE 6, Interceptors' capabilities are applicable to any POJO (which qualifies as a Managed Bean) within your Java EE application. As EJBs are nothing but a specialized/advanced form of Managed Beans, one can utilize the Interceptor annotations on EJBs in order to implement generic cross cutting functionality like logging, auditing etc.

Let us look at the Interceptor annotations in detail along with some code examples demonstrating their usage

@AroundInvoke

This annotation is used to decorate the method of a class which contains the interception logic for target class methods

package ejbap.interceptors;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class LoggerInterceptor {

   @AroundInvoke 
   public Object log(InvocationContext ic) throws Exception{
       Object result = null;
       final String className = ic.getTarget().getClass().getSimpleName();
       final String methodName = ic.getMethod().getName();
       try {
           System.out.println(String.format(">> %s#%s", className, methodName));
           result = ic.proceed();
       } catch (Exception ex) {
           System.err.println(ex.getMessage());
       }finally{
           System.out.println(String.format("<< %s#%s", className, methodName));
       }
       return result;
   } 
}

@AroundConstruct

Use this annotation to intercept the constructor of a class itself i.e. a method decorated with @AroundConstruct is invoked by the container before an instance of the class (to which the interceptor is bound) is about to be created. The instance creation is deemed to be complete only when the interceptor method returns successfully

package ejbap.interceptors;

import javax.interceptor.AroundConstruct;
import javax.interceptor.InvocationContext;

public class CreationTracker {

    @AroundConstruct
    public void logObjectCreation(InvocationContext ic){
        final String className = ic.getTarget().getClass().getName();
        System.out.println("Created an instance of class "+ className);
    }
}

Do not get confused between @PostConstruct and @AroundConstruct. The former is triggered by the container after instance creation

@Interceptors

Use @Interceptors annotation to define a one or more interceptor classes. This can be used on a class or method.

package ejbap.interceptors;

import java.util.Date;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;

@Stateless
@Interceptors({LoggerInterceptor.class, CreationTracker.class})
public class StatelessBeanWithInterceptor {

    public String getTime(){
        return new Date().toString();
    }


}

The interceptor declaration forces you to provide the exact type of the target class - hence introduces tight coupling between the interceptor and intercepted class. This was improved with the help of the CDI 1.0 specification

@InterceptorBinding

CDI 1.0 introduced a loosely coupled and type safe way of specifying interceptors of a class or a method.

package ejbap.interceptors.cdi;

import javax.interceptor.InterceptorBinding;
import java.lang.annotation.*;

@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PerformanceMonitorInterceptorBinding {

}

@Interceptor

Before the CDI specification came into being (i.e. prior to Java EE 6), there was no notion of explicitly declaring a class (containing interception methods) as an interceptor. The @Interceptor annotation was used to explicitly declare a class containing an interception logic in a specific method (annotated with @AroundInvoke etc) as an interceptor along with an appropriate Interceptor Binding (discussed above).

package ejbap.interceptors.cdi;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@PerformanceMonitorInterceptorBinding
public class PerformanceMonitor {

   @AroundInvoke 
   public Object measure(InvocationContext ic) throws Exception{
       long start = System.currentTimeMillis();
       Object result = null;
       final String className = ic.getTarget().getClass().getSimpleName();
       final String methodName = ic.getMethod().getName();
       try {
           result = ic.proceed();
       } catch (Exception ex) {
           System.err.println(ex.getMessage());
       }finally{
           long elapsed = System.currentTimeMillis() - start;
           System.out.println(String.format("Time spent in %s#%s - %s", className, 
                   methodName, elapsed));
       }
       return result;
   }
}
package ejbap.interceptors.cdi;

import javax.ejb.Stateless;

@Stateless
public class StatelessBeanWithInterceptorBinding {

    @PerformanceMonitorInterceptorBinding
    public void timeTakingOp(){
        try {
            //not allowed inside EJB container... just for simulation
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            //ignore...
        }
    }
}

Memory Aid

It might be helpful to think of Interceptors as components which can interpose on beans throughout their life cycle

  • @AroundConstruct: before they are even constructed
  • @PostConstruct: after they are constructed
  • @AroundInvoke: during their life time (method invocation)
  • @PreDestroy: prior to destruction
  • @AroundTimeout: EJB time outs

What's next ??

The next chapter will explore ways of using EJBs to execute business logic in an asynchronous manner.

results matching ""

    No results matching ""