Wednesday, September 24, 2008

Usage of Interceptors and Timer Service in EJB3

Interceptors are used to intercept business method invocations og EJB3 bean class. The interceptors can be defined either class level or method level.
Also the interceptors can be in a seperate class using @Interceptor annotation or a method annotated with @AroundInvoke with signature Object methodname (InvocationContext ic)in the same class.
More than one interceptor classes can also be defined for a bean, using @Interceptor annotation. Interceptors have same life cycle as of that of associated bean and can have dependency injection in the same context.

Invocation Context

InvocationContext allows you to propagate through a chain of interceptors, along with its get/set methods, which even provides reference to the bean. Following are the methods that can be accessed by the handle provided.

Object getBean(): Provides the reference to the bean for which the Interceptor is defined.
Method getMethod():Provides information of about which method in the bean is called
Object[] getParameters():Provides infomation of the parameters passed to the bean.
void setParameters(): Set the parameter values
Map getContextData(): Provides context data that can be shared by the chain.
Object proceed(): Proceeds to the next interceptor in the chain or to the business method of the bean.

A simple example of using the Interceptor is illustrated below

package com.test;


import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TimedObject;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
import javax.ejb.TimerService;


@Stateless
@RemoteBinding(jndiBinding="Welcome")
@Remote(UpdateEJB.class)
@Interceptors(TestInterceptor.class)
public class UpdateEJBBean implements UpdateEJB {

private String message;

@Resource
private SessionContext ctx;

@Interceptors(TestInterceptor1.class)
public String sayHello(String name) {
System.out.println("Inside EJB method");
return "Hello " + name + message;
}


@AroundInvoke
public Object log(InvocationContext invocationContext) throws Exception {
System.err.println(invocationContext.getMethod().getName() + " called from interceptor 3");
return invocationContext.proceed();
}

}
The @RemoteBinding(jndiBinding="Welcome") is used to bind the EJB Bean to the JNDI name which we specify, instead using default JNDI name, provided by appserver vendors
The interceptor class will be like the one shown below.

public class TestInterceptor{

@AroundInvoke
public Object log(InvocationContext invocationContext) throws Exception {
System.err.println(invocationContext.getMethod().getName() + " called from interceptor 1");
return invocationContext.proceed();
}

}

public class TestInterceptor1{

@AroundInvoke
public Object log(InvocationContext invocationContext) throws Exception {
System.err.println(invocationContext.getMethod().getName() + " called from interceptor 1");
return invocationContext.proceed();
}

}

The order of Interceptor Invocation will be like
1.EJB Bean Class level Interceptor
2.EJB Bean Method level interceptor annotated by @Interceptor above the method
3.The method annotated with @AroundInvoke annotation.

Timer Service


Using EJB3 we can create a timely triggers and method invocations using the TimerService, provided by EJB3. You can either implement TimedObject interface, or you can
annotate method that you need to trigger after each timer expiration using @Timeout annotation.

package com.test;

import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TimedObject;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
import javax.ejb.TimerService;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionManagement;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;


@Stateless
@RemoteBinding(jndiBinding="Welcome")
@Remote(UpdateEJB.class)
public class UpdateEJBBean implements UpdateEJB,TimedObject {

private String message;

@Resource
private SessionContext ctx;

public String sayHello(String name) {
ctx.getTimerService().createTimer(6000, "HAI");
ctx.getTimerService().createTimer(6000,6000, "HAI");
return "Hello " + name + message;
}

public void ejbTimeout(Timer handle) {
System.out.println("handle.getTimeRemaining()"+handle.getTimeRemaining());
System.out.println("handle.getNextTimeout()"+handle.getNextTimeout());

}
}

To create the timer ctx.getTimerService().createTimer method is used, the timer timeout can be only once or frequently in a timed manner.
createTimer(6000,6000, "HAI") this says that the timer will expire in a period of 6000ms with initial delay of 6000ms.

The argument, Timer provides the handle to the timer, which has getTimeRemaining and getNextTimeout methods.

Instead of implementing interface you can also use annotation @Timeout above the method that needs to be triggered after each timer expiration.

Hope the article was useful.

1 comment:

Marcos Jara said...

Hello

I have an sales EJB project which controls the inventory, in and out of stock.

I want to add extra functionality to the EJB, such as payment and billing.

For this I want to use Interceptor in Session Bean class. I want to implement the new functionality as a component, decoupled from the current implementation.

But I have not access to the current session bean or Xml (ejb-jar), so I can not put the @ Interceptor in the class or method! how I can solve my problem?

Can I add @ Interceptor from another location, class or session? There is another way of doing?

Thanks for your help.