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.
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.
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.
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
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).
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.