Java EE EJB Interceptors tutorial and example

01 July 2013
By Gonçalo Marques
In this tutorial you will learn how to configure EJB Interceptors used to - as the name states - intercept calls to EJB methods.

Introduction

Interceptors - as the name states - are components that intercepts calls to EJB methods. They may be used - for example - to implement an auditing / logging system around calls to EJB.

Another usage example may be to check if the client is authorized to call a given EJB method. Interceptors may be configured to intercept specific EJB method calls or to intercept calls to any method from a given EJB.

We may also have multiple interceptors to intercept a single EJB method call - resembling a chain of Interceptors - each one being executed in a predefined sequence.

In this tutorial we will see how to configure EJB Interceptors in detail.

This tutorial considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Glassfish 4.0

A simple EJB

We start by defining a simple Stateless EJB:

SimpleBean.java

package com.byteslounge.ejb;

import javax.ejb.Stateless;
import javax.interceptor.Interceptors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.byteslounge.ejb.interceptor.LoggingInterceptor;

@Stateless
@Interceptors(LoggingInterceptor.class)
public class SimpleBean {

  private static final Logger logger = 
    LoggerFactory.getLogger(SimpleBean.class.getName());
  
  public String sayHello(String name){
    logger.debug("Executing EJB method");
    return "Hello " + name;
  }
  
}

This is a very simple Stateless EJB containing a single method - sayHello() - that returns an echo of the input String parameter. We also log the method execution so we may record the behaviour of this EJB in conjunction with interceptors.

The EJB is annotated with @Interceptors where the value is a single interceptor: LoggingInterceptor. We defined the interceptor at the type (or class) level so every single call to any EJB method will be intercepted by the LoggingInterceptor interceptor.

We will define the interceptor in the next section.

Defining an Interceptor

Now we define the interceptor that is referenced in the EJB we defined in the previous section:

LoggingInterceptor.java

package com.byteslounge.ejb.interceptor;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingInterceptor {

  private static final Logger logger = 
   LoggerFactory.getLogger(LoggingInterceptor.class.getName());
  
  @AroundInvoke
  public Object intercept(
      InvocationContext ctx) throws Exception {

     logger.debug("LoggingInterceptor - before EJB method invoke: "
        + ctx.getMethod().getName());

     Object result = ctx.proceed();

     logger.debug("LoggingInterceptor - after EJB method invoke: " 
        + ctx.getMethod().getName());

     return result;
  }
}

An interceptor is defined as a simple class where there is a single method annotated with @AroundInvoke. This method will be called on every method call to the EJB we defined in the previous section. We may have other methods in the interceptor but only a single one with the @AroundInvoke annotation.

We implement any business logic that should be executed prior to the EJB call by writing it before calling ctx.proceed(). When we call ctx.proceed() the execution will be handled to the next interceptor in the interceptor chain, or to the EJB itself if this is the last interceptor in the chain.

The code that is written after ctx.proceed() will be executed after the EJB executes and returns to the interceptor. Finally we return the result to the caller that will be any other interceptor in the interceptor chain (in the reverse order of execution) or the client, if no other interceptor remains in the interceptor chain.

We are basically logging all calls to our EJB, before and after method execution. If a client invoked our EJB the following output would be generated to the underlying logging system:

LoggingInterceptor - before EJB method invoke: sayHello
Executing EJB method
LoggingInterceptor - after EJB method invoke: sayHello

We may also extract useful information from the InvocationContext instance that is passed to the interceptor method. In this case we are simply logging the EJB method name that is currently being invoked.

Multiple interceptors

Let's define a second interceptor in order to intercept calls to the same EJB we defined previously:

AuthorizationInterceptor.java

package com.byteslounge.ejb.interceptor;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AuthorizationInterceptor {

  private static final Logger logger = 
   LoggerFactory.getLogger(AuthorizationInterceptor.class.getName());
  
  @AroundInvoke
  public Object intercept(InvocationContext ctx) throws Exception
  {
     logger.debug("AuthorizationInterceptor - before EJB method invoke: "
       + ctx.getMethod().getName());

     try{
       authorizationCheck();
     } catch(Exception e){
       logger.debug("Authorization check failed");
       throw e;
     }
     
     return ctx.proceed();
  }
  
  private void authorizationCheck() throws Exception{
    // Do some kind of authorization check.
    // Throw exception if user is not authorized
  }
}

This interceptor represents an authorization check we may need to do against the calling client before allowing the call to proceed to the EJB itself.

Now we need to go back to our EJB and add this new interceptor to the interceptor chain:

SimpleBean.java

@Stateless
@Interceptors({
	AuthorizationInterceptor.class, 
	LoggingInterceptor.class
})
public class SimpleBean {

  // The remaining EJB class was not
  // changed so we ommit it for clarity.

}

The order in which we define the interceptors is the same order they will be executed, so AuthorizationInterceptor will be executed first. Then the execution will be passed to LoggingInterceptor. Finally the EJB method will be called.

If a client now invokes our EJB method the following output will be produced in the underlying logging system:

AuthorizationInterceptor - before EJB method invoke: sayHello
LoggingInterceptor - before EJB method invoke: sayHello
Executing EJB method
LoggingInterceptor - after EJB method invoke: sayHello

If an exception is thrown by an Interceptor the execution will stop at that same interceptor.

Method level interceptors

We may also have method level interceptors. The declaration of this kind of interceptors is done at the method level:

Method level interceptor

@Interceptors(
  MethodLevelInterceptor.class 
)
public String sayHello(String name){
  logger.debug("Executing EJB method");
  return "Hello " + name;
}



Note: Method level interceptors are invoked after type - or class - level interceptors

Default interceptors

We may also have default interceptors, ie. interceptors that are defined globally across the application and intercept every call to any EJB. This kind of interceptors must be defined in ejb-jar.xml:

Default interceptor defined in ejb-jar.xml

<assembly-descriptor>
  <interceptor-binding>
    <ejb-name>*</ejb-name>
    <interceptor-class>com.byteslounge.ejb.interceptor.DefaultInterceptor</interceptor-class>
  </interceptor-binding>
</assembly-descriptor>

In this case we would have to implement DefaultInterceptor as any other interceptor. As we have said before, this interceptor would be called on every call to any EJB method. Note the * wildcard in ejb-name element.

Note: Default interceptors are invoked before any other interceptors

Interceptor exclusion

Finally we will see how we may exclude interceptor execution. If we want to exclude default interceptor do be executed when we call our EJB we use the @ExcludeDefaultInterceptors annotation:

Default interceptor exclusion

@Stateless
@ExcludeDefaultInterceptors
public class SimpleBean {

}

We may also exclude interceptor execution at method level:

Method level interceptor exclusion

@Stateless
public class SimpleBean {

  @ExcludeClassInterceptors
  public String sayHello(String name){
    return "Hello " + name;
  }

}

In this case the sayHello() method invocation would not trigger type - or class - level interceptors.

Downloadable sample

You may find a downloadable sample at the end of this page. The sample is a Maven project containing all the required dependencies to be run in an enterprise container.

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with 8+ years of experience in software development and architecture design. During this period his main focus was delivering software solutions both in banking and telecommunications area. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

He is also the author of the WiFi File Browser Android application: