EJB 3.0: Interceptors and Callbacks Made Easy – Part II
In the EJB 3.0: Interceptors and Callbacks Made Easy – Part I of these two-part blog series I gave a basic introduction to EJB 3.0 interceptors and discussed their main features. Now, we’ll have a detailed look into the
InvocationContext
interface and see some examples of interceptors use cases.
The <code>InvocationContext</code> interface
The method returns the map with contextual data that can be used to pass information between interceptors. If they are invoked as a result of the invocation on a web service endpoint, this map will be the JAX-WS
MessageContext
object that has been passed to JAX-WS handlers.
The
proceed
method causes the invocation of the next interceptor method in the chain, or, when called from the last AroundInvoke method, the business method. If
InvocationContext.proceed()
is not called, no subsequent interceptor methods or bean business method or lifecycle callback method will be invoked. The
proceed
method returns the result of the next method invoked or
null
if a method returns
void
.
Interceptors use cases
Now, that we’ve layed out the fundamentals of EJB interceptors, let’s have a look at some examples of how they can be used to serve typical tasks in enterprise applications.
Profiling
Interceptors provide the possibility to add profiling logic to applications, which can be easily reused, extended, and enabled or disabled. Below is the code of a simple profiling interceptor class:
public class ProfilingInterceptor { @AroundInvoke public Object profile(InvocationContext ctx) throws Exception { long start = System.currentTimeMillis(); try { return ctx.proceed(); } finally { long time = System.currentTimeMillis() – start; Method method = ctx.getMethod(); System.out.println(method + ” took ” + time + ” (ms)”); } }}
In order to enable profiling for all EJBs in an application, for a single EJB, or only for a single business method, we need to apply this interceptor at the appropriate level. For example:
@Stateless
@Interceptors(ProfilingInterceptor.class)
public class EmployeeServicesBean implements EmployeeServices {
}
enables profiling for all methods of the EmployeeServices bean.
Printing the profiling information to the default system output is not very smart, though. You can possibly think of various ways to extend this profiling logic. For instance, the JPA EntityManager can be injected and used in the interceptor class to store the information in a relational database:
public class ProfilingInterceptor { @PersistenceContext EntityManager em; @AroundInvoke public Object profile(InvocationContext ctx) throws Exception { long start = System.currentTimeMillis(); try { return ctx.proceed(); } finally { long time = System.currentTimeMillis() – start; Method method = ctx.getMethod(); ProfilingInfo info = new ProfilingInfo(method, time); em.persist(info); } }}
Logging/tracing
Interceptors can also be used to trace method invocations with input parameters and return values:
public class LoggingInterceptor { static final Logger logger = Logger.getLogger(“LoggerName”); @AroundInvoke public Object trace(InvocationContext ctx) throws Exception { Method method = ctx.getMethod(); logger.entering(method, ctx.getParameters()); Object result = null; try { result = ctx.proceed(); return result; } catch (Exception e) { logger.throwing(method, e); throw e; } finally { logger.exiting(method, result); } }}
Code-based security
Another application of interceptors could be for the implementation of custom security permission checks. In the next example the interceptor delegates to a specially designed SecurityServices bean to carry out the checks for calling a business method.
public class SecurityInterceptor { @EJB SecurityServices secSvc; @AroundInvoke public Object checkPermission(InvocationContext ctx) throws Exception { if (!secSvc.authorized(ctx.getMethod(), ctx.getContextData()) { throw new EJBAccessException(“User not authorized”); } return ctx.proceed(); }}
Exception handling
Interceptor methods are allowed to abort business method invocation, to catch and suppress exceptions and retry an invocation, or to throw a different exception. The example below shows a simple bank account withdrawal validation interceptor:
public class WithdrawValidation { @Resource double maxWithdraw = 1000.0; @AroundInvoke public Object validate(InvocationContext ctx) throws Exception { double amount = (Double) ctx.getParameters()[0]; if (amount > maxWithdraw) { throw new AccountException(“Max withdraw is ” + maxWithdraw); } return ctx.proceed(); }}
Conclusion
Interceptors are reusable framework components (“a framework for frameworks”) that can be seamlessly plugged in and extended as necessary. They bring new power to the EJB specification, they are easy to use and fit very well in certain use cases.
References:
EJB 3.0: Interceptors and Callbacks Made Easy – Part I
Nice blog, I feel reading Head First EJB book once and EJB spec once is a must.
Regards,
Vivek Nidhi.
I guess, there are a lot of books, articles, tutorials, etc. on EJB out there - and also here on SDN. I personally would not judge which one is better or worse 🙂 However, reading the whole EJB spec as a book, if you just want to build EJB apps - and not an EJB container implementation, might be an overkill.
BR,
Vladimir