JavaEJBUnderstanding Interceptors for Java EE

Understanding Interceptors for Java EE

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

In most application development, we come across certain cross-cutting concerns that need to be merged somehow with the flow of the design and implementation specification. These concerns can neither be decomposed from the rest of the system nor they can be streamlined with the system flow: A system outcast who is too important to be ignored yet too unbiased to be coalesced with the system counterpart. The Interceptor class is the object-oriented way to handle such situations. In Java, they are nothing more than components, colored to implement cross-cutting concerns, that are mainly used to intercept EJB method calls or invocation of life-cycle events on an associated target class.

Interceptor Interposes

Interceptors are commonly used when a business method on a target class is invoked on life cycle events to create/destroy or timeout, such as, found when implementing profiling, auditing, logging, and so forth. For example, when writing an application for a medical records, indexing diagnosis reports may be a core concern but logging the history of record changes to the database, or checking whether a client has the authority to execute a transaction on a particular object in the database are some of the unavoidable cross-cutting concerns that should be taken into account while designing/implementating of the system. A cohesive attempt to assimilate these concerns into the mainstream flow of the system would result in the effect of scattering and tangling. However, this disruptive bifurcation can be streamlined with the help of interceptors. Interceptors are perfect callback methods in a situation where, for example, we may want a certain method to be called after the bean has been instantiated but before it has been transferred to the client. The instantiation method, meanwhile, can do anything such as look up resources using JNDI, configure state information… pretty much anything provided it does not require a transactional context. Interceptors are good fit for this type of situation. The advantage is obvious; once defined, they can be applied across EJBs and methods without scattering repetitive code at multiple location.

Defining Interceptors

Interceptors can be defined in Java as an method interceptor or a class interceptor. The preferred way to define in Java code is by using meta-data annotations. They can be defined in the application descriptor as well, but, in that case they are not portable across Java EE servers. Some of the meta-data annotations found in the javax.interceptor package are: @AroundInvoke, @AroundTimeout, @PostConstruct, and @PreDestroy. Refer to the Java Docs (Java EE 7 APIs doc) for detailed information.

Method Interceptors

Suppose we want to define an interceptor that is available only for a particular class. Method interceptor makes perfect sense in such a situation. For example, to add an interceptor of a log method to the bean itself, we can write a function as follows:

@Transactional
public class BookLibraryService {

   @Inject
   private EntityManager entityManager;
   @Inject
   private Logger logger;

   public void createPatron(Patron patron) {
      entityManager.persist(patron);
    }
 
   @AroundInvoke
   private Object doLog(InvocationContext ic) {
      Object obj = null;
      try {
         logger.entering(ic.getTarget().toString(),
            ic.getMethod().getName());
         obj = ic.proceed();
      } catch (Exception ex) {
      } finally {
         logger.exiting(ic.getTarget().toString(),
            ic.getMethod().getName());
      }
      return obj;
   }
}

Here, the annotation @AroundInvoke is used to log a message whenever any method within the bean is entered or exited. On invocation, createPatron() or any other function would be intercepted by the doLog() method. However, the scope of this interceptor is limited to the bean itself, which is the target class here. To use the @AroundInvoke annotation, an interceptor method must adhere to the specific signature pattern such as:

  • The method must have a return type Object to return the result of invoked target method.
  • A parameter InvocationContext, to control the behavior of the invocation chain.
  • Static or final methods are not allowed.

Class Interceptors

Class interceptors are perfect for isolating cross-cutting concerns into separate classes so that the container can reuse the intercept on separate bean calls. Let’s rewrite the preceding logging example with class interceptors. the basic idea remains the same, but the interception now become reusable. The @AroundConstruct annotation signifies that the method will be invoked on bean construction.

public class LoggingIntercept {
   @Inject
   private Logger logger;

   @AroundConstruct
   private void onInit(InvocationContext ic) {
      try {
         logger.fine("Logging initialized");
         ic.proceed();
      } catch (Exception ex) {

      } finally {
         logger.fine("Initialization done");
      }
   }

   @AroundInvoke
   private Object doLog(InvocationContext ic) {
      Object obj = null;
      try {
         logger.entering(ic.getTarget().toString(),
            ic.getMethod().getName());
         obj = ic.proceed();
      } catch (Exception ex) {

      } finally {
         logger.exiting(ic.getTarget().toString(),
            ic.getMethod().getName());
      }
      return obj;
   }

}

Any specific method interested in the interceptor can be wrapped with the @Interceptors annotation.

@Transactional
public class BookLibraryService {
   @Inject
   private EntityManager entityManager;
   @Inject
   private Logger logger;

   @Interceptors(LoggingIntercept.class)
   public void createPatron(Patron patron) {
      entityManager.persist(patron);
   }
}

If we want that all methods within the bean intercept logging, we can use the annotation to wrap around the class. Also, in this scenario, if we want to exclude a specific method from interception, we can use @ExcludeClassInterceptors.

@Transactional
@Interceptors(LoggingIntercept.class)
public class BookLibraryService {
   @Inject
   private EntityManager entityManager;
   @Inject
   private Logger logger;

   public void createPatron(Patron patron) {
      entityManager.persist(patron);
   }

   @ExcludeClassInterceptors
   public Patron searchPatron(Long patronId) {
      return entityManager.find(Patron.class, patronId);
   }
}

Intercepting a Bean’s Life Cycle

@PostConstruct and @PreDestroy annotations are called life cycle interceptors in the sense that we can inform the container to invoke a method at a specific life cycle phase. Suppose we want to log an entry each time a bean is instantiated; we can use @PostConstruct. In a similar manner, if we want a log entry before a bean object is destroyed, we can use @PreDestroy.

public class LoggingIntercept {
   ...
   @PostConstruct
   private Object doLog(InvocationContext ic) {... }
}

public class LoggingIntercept {
   ...
   @PreDestroy
   private Object doLog(InvocationContext ic) {... }
}

Interceptor Chaining

We can chain several interceptors calls within a single bean with the help of the @Interceptors annotation. The order of the calls is the order they are defined, from left to right.

@Transactional
@Interceptors({Interceptor1.class, Interceptor2.class})
public class BookLibraryService {

   public void createPatron(Patron patron) {... }

   @Interceptors({Interceptor3.class, Interceptor4.class})
   public void removePatron(Patron patron) {... }

   @ExcludeClassInterceptors
   public Patron searchPatron(Long patronId) {...}
}

Conclusion

Interception was restricted only to EJBs in EE5. With the complexity of the enterprise requirement, it grew into a full fledged specification from EE6. EE7 improved it by introducing a new annotation, @Transactional (from JTA). Interception got a wider horizon to be more generically applied to container managed transaction even outside EJB. This annotation helps in binding interceptors by Java EE at run time as well. To dive deeper, it would be interesting to compare Java EE interceptors with the Spring AOP and get an essence of what it’s like to be in aspect-oriented programming in Java.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories