Lab - Adding a Custom Interceptor to an EJB3 Bean

Pete Bennett (pbennett@jboss.com)
10 November 2006

This lab will show you how easy it is to implement a custom interceptor class that you can wrap around an existing EJB3 bean and add your own customised behaviour.

The Stateless Session Bean Base

We will use the CalculatorBean Stateless session bean that we produced in the first EJB3 lab and refactor it so that we remove the logging methods from the Session Bean and put them in a more generic LoggingInterceptor class.

Implementing the Interceptor

Writing the Interceptor Class

org.jboss.tutorial.interceptor.LoggingInterceptor contains a POJO scaffold on which we will build our Inteceptor. Note that this class does not have to extend a specific superclass or implement a specific interface to be an interceptor. It does require a method which follows the template public Object METHOD_NAME(InvocationContext ctx) throws Exception but METHOD_NAME can be a name of your chosing.

TASK1: edit LoggingInterceptor.java so that the logging method has the more user friendly name log but keeps the same signature.

The javax.interceptor.AroundInvoke annotation marks a method as being an interceptor method that can be used to wrap method calls to other EJBs. This annotation does not take any arguments and can be added to a class using the syntax @AroundInvoke just before the method definition in the .java file.

TASK2: Import the AroundInvoke annotation class and add the AroundInvoke annotation to LoggingInterceptor.java, specifying log as the interceptor method.

The flow of control within this method is important. The section before the try-catch-finally block will be called before the method that is being intercepted. In this block we make a note of the system time. The section in the try block needs to pass on control to the actual method call we are intercepting (actually, one can have chains of interceptors and this control may be passed to the next interceptor in the chain).

The InvocationContext Object passed into our method has a number of methods controlling the flow of control between interceptors in the stack and the intercepted bean. InvocationContext.proceed() passes control on to the next interceptor in the stack or onto the method call itself

TASK3: use the InvocationContext Object to pass control on to the bean.

The finally block is exectuted after the wrapped bean method has been called and before we return from this method. In the finally block, we make a note of the system time again. Note that we still have access to the startTime variable and hence can calculate the elapsed time it took to execute this method.

The InvocationContext Object passed into our method has a number of methods which give information about the method call that we are intercepting and the object on which the method call has been made. You can see how to obtain the Class of the bean on which the call has been made and the Method which has been called. InvocationContext.getParameters() returns an Object[] array of the actual parameters with which the method was called.

TASK4: use the InvocationContext Object to obtain a list of the parameters passed to the method that is being intercepted.

Finally, we print out all the information we have gathered. Note that this interceptor class is very generic. There is nothing in it which is specific to a particular EJB bean and it could be reused across many components.

Adding the Interceptor to the CalculatorBean Stateless Session Bean

The javax.interceptor.Interceptors annotation is used to add a stack of one or more interceptors to a method in an EJB. This annotation takes one or more Class paramters and can be added to a class using the syntax @Interceptors just before the class definition in the .java file.

TASK5: Import the Interceptors annotation class and add the Interceptors annotation to CalculatorBean.java, specifying org.jboss.tutorial.interceptor.LoggingInterceptor.class as a single interceptor to use for all methods in the class.

We have now refactored this class to remove cross-cutting logging concerns from the bean definition itself into a generic interceptor. This has the benefits of producing cleaner code and allowing better control. For example, turning off logging can be done in a single place and if/when we moved from println statements to a better logging framework changes would only be required in LoggingInterceptor rather than across all of our beans.

TASK6: Remove the System.out.println statements from CalculatorBean.java

Running the Lab

Make sure JBoss is running and then open a command prompt in the src\build directory and run "ant ejbjar". This compiles the code and then JARs it into a file called lab-slsb-demo3.jar (NOTE: this overides the deployment from lab one). If you investigate this file, you will note that it contains no XML config, only the two class files we have just annotated. This command also deploys the file by copying it to the JBoss deploy directory. JBoss scans the file automatically and discovers and deploys our new EJB. If you look at the output from the running JBoss instance you will see confirmation that it has discovered a new EJB in the JAR file and deployed it sucessfully.

Open a command prompt in the src\build directory and run "ant runejb". This compiles the code and runs the client with the EJB option. Here the client uses a JNDI lookup to bind to the remote interface of the CalculatorBean EJB running in the JBoss AS and then accesses it via the Calculator interface. You can see the interactions printed in the different commands prompt by println statements in both the client (outputs to the ant prompt) and the intereceptor (outputs to the JBoss prompt).

Conclusion

In this lab we have seen how easy it is to use EJB3 annotations to implement reusable interceptors and wrap them around method calls in your EJB3 beans.