Saturday, February 28, 2015

Understanding JAVA EE Interceptors

Interceptors are used to implement orthogonal or cross-cutting concerns such as logging, auditing, profiling etc in Java EE applications. In this aspect, it is similar to aspect-oriented programming (AOP).

In Java EE 5, Interceptors were part of EBJs, but in later versions, Interceptors have evolved into a new specification of its own. Interceptors were split into an individual spec in Java EE 7 (Interceptors 1.2) and are part of Context and Dependency Injection(CDI) specification.  They can be applied to any managed classes/beans like EJBs, Servlets, SOAP and RESTful web services. Managed Beans are container-managed objects (unlike Java Beans or POJOs which run inside JVM).  In Java EE 7, Dependency Injection (JSR 330) and  CDI (JSR 299) are merged. You can refer to managed beans as CDI bean as well. These managed beans are injectable and interceptable.

In this post, I will be using terms CDI bean and managed bean interchangeably.

Interceptors

Interceptor is a class whose methods are invoked when methods on a target class are invoked. And this invocation of interceptors methods is performed by the container. Obviously, this would be possible if the target class and the interceptors both are managed by the container.  This is possible because beans managed by containers (servlet or EJB) provides the ability to intercept method invocation through interceptors. 


Interceptors are powerful means to decouple technical concerns from business logic. And it uses strongly typed annotations to achieve it instead of String-based identifiers. This way usage of XML descriptors is minimized. Interceptors use a mandatory deployment descriptor, bean.xml. This is required so that CDI is able to discover the bean from the classpath.

Below are interceptor metadata annotations (from javax.interceptor ):
  • @AroundConstruct associates with the constructor of the target class
  • @AroundInvoke associates with a business method and gets called while entering and exiting
  • @AroundTimeout associates with timeout methods
  • @PostConstruct & @PreDestroy on corresponding lifecycle events of the target class

Using Interceptors

Interceptors, intercept a particular target class so they have the same life cycle as that of the target class. Target class can have any number of associated interceptors.

Intercepting a REST Service

Interceptors can intercept any managed bean; so it can intercept servlet, REST entry point, EJB etc. I will be showing an example of intercepting a REST service.

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

/**
 * Interface for logging/auditing JAX-RS requests
 *
 */
@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Auditor {
}



import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
/**
 * CDI/Managed bean which intecepts REST API
 * Add @Interceptors(RestAuditor.class) to the target class/method
 *
 */
@Interceptor
@Auditor
public class RestAuditor {
 @AroundInvoke
 public Object intercept(InvocationContext context) throws Exception {
  //context.getMethod().getName())
               // audit/log the request
  return context.proceed();
  //control comes back after target method execution 
 }
}


<beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
        <class>RestAuditor</class>  <!--fully qualified class name -->
    </interceptors>
</beans>

So interceptor definition and the declaration of the interceptor in bean.xml completes the interceptor part. Now next step is to use above defined interceptor on a REST service. Please note that the intercept method will be called before actually calling the REST method and then again once REST method is complete, the control comes back. So the request as well response both can be logged in the intercept(..) method. Using the interceptor is quite trivial, we just need to annotate the REST class as shown below :

@Path("/sample")
@ApplicationScoped
@Interceptors(RestAuditor.class)
public class SampleRestService implements Serializable {  
 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public boolean getLogs() {
  boolean resp = true;
  return resp;
 }
}

That's it!

Important Points

  • If the application contains several jar files and you want to enable CDI across the application then you need to have beans.xml in each jar. Only then CDI will trigger bean discovery for each jar. 
  • Interceptors can be applied to method as well as class level. If you want to intercept a specific method then put the annotation at method level only (NOT at class level as shown above).

1 comment: