Thursday, January 18, 2007

EJB 3 Timer Service

Applications with business processes that are dependent on periodic notifications (for state transitions etc.) need a timer/scheduler service. In the past I posted how to use the Quartz Scheduler for scheduling jobs. In this post, I will describe the use of the EJB 3 Timer Service for scheduling, or temporal event driven processing.

The EJB Timer Service is a container-managed service that provides a way to allow methods to be invoked at specific times or time intervals. The EJB Timer may be set to invoke certain EJB methods at a specific time, after a specific duration, or at specific recurring intervals. The Timer Service is implemented by the EJB container and injected into the EJB through the EJBContext interface, or the EJB can access it through a JNDI lookup. In the example presented below, the EJB timer is set when the client invokes the startTimer method. The checkStatus() method is used to get the time left for the next timeout. The example was implemented on Glassfish server, with Eclipse 3.2 used for development.
Follow these steps to implement the example.

The EJB
  1. The EJB interface: The interface for the EJB is shown below.
    package ejb;

    public interface ITimedBean {
    public String checkStatus();
    public void startTimer();
    }
    ITimedBean.java
  2. The Bean: The following is the code for the EJB followed by a brief explanation
    package ejb;

    import java.util.Collection;
    import java.util.Iterator;

    import javax.annotation.Resource;
    import javax.ejb.Remote;
    import javax.ejb.SessionContext;
    import javax.ejb.Stateless;
    import javax.ejb.Timeout;
    import javax.ejb.Timer;

    @Stateless(mappedName="ejb/timedBean")
    @Remote
    public class TimedBean implements ITimedBean {

    @Resource
    private SessionContext ctx;

    public void startTimer() {
    ctx.getTimerService().createTimer(1000, 1000, null);
    System.out.println("Timers set");

    }

    public String checkStatus() {
    Timer timer = null;
    Collection timers = ctx.getTimerService().getTimers();
    Iterator iter = timers.iterator();
    while (iter.hasNext()) {
    timer = (Timer) iter.next();

    return ("Timer will expire after " + timer.getTimeRemaining() + " milliseconds.");
    }
    return ("No timer found");

    }

    @Timeout
    public void handleTimeout(Timer timer) {
    System.out.println("HandleTimeout called.");

    }
    }
    TimedBean.java
    • The @Stateless(mappedName="ejb/timedBean") annotation declares the bean as a stateless EJB. The mappedName attribute defines the global JNDI name to which this EJB will be bound.
    • In the startTimer method, the ctx.getTimerService().createTimer(1000, 1000, null); call is used to create the timer, this call will create a timer that invokes the methods at specific intervals. Another way to create a timer would be to use the call ctx.getTimerService().createTimer(1000, null);, in which case, the timer will invoke the EJB method just once.
    • The EJB 3 Timer Service also allows you to send client-specific information at timer creation, throught the public Timer createTimer(long initialDuration, long intervalDuration, java.io.Serializable info); and some overloaded methods as shown below in the TimerService interface.
      public interface javax.ejb.TimerService {

      public Timer createTimer(long duration,java.io.Serializable info);

      public Timer createTimer(long initialDuration,long intervalDuration, java.io.Serializable info);

      public Timer createTimer(java.util.Date expiration,java.io.Serializable info);

      public Timer createTimer(java.util.Date initialExpiration,long intervalDuration, java.io.Serializable info);

      public Collection getTimers();
      }
    • A timer may be cancelled at any time, by using the cancel() method in the Timer interface.
    • The @Timeout annotation declares the handleTimeout() method to be a callback method for the timer.
  3. Deploying: When deploying on Glassfish using the Admin console, check the "Generate RMI Stubs in a Jar File" option. The Jar file will be created in the GLASSFISH_HOME/domains/DOMAIN_NAME/generated/xml/j2ee-modules/APPLICATION_NAME directory.

The Timer Client

For this example, I used a web client. The client has a context listener which loads the timer when the application is deployed. A single servlet is used to invoke the checkStatus() method on the client.
  1. Start with a Dynamic Web Application in Eclipse. Include the generated client Jar file as a dependency.
  2. The Context Listener: The code for the context listener is shown below.
    package servlets;

    import javax.ejb.EJB;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;

    import ejb.ITimedBean;

    public class LoadTimer implements ServletContextListener {
    @EJB(name="timerBean", mappedName="corbaname:iiop:localhost:3700#ejb/timedBean")
    private ITimedBean timerBean;

    public void contextInitialized(ServletContextEvent servletcontextevent) {
    System.out.println("Starting timer");
    timerBean.startTimer();
    }

    public void contextDestroyed(ServletContextEvent servletcontextevent) {

    }

    public ITimedBean getTimerBean() {
    return timerBean;
    }

    public void setTimerBean(ITimedBean timerBean) {
    this.timerBean = timerBean;
    }

    }
    LoadTimer.java

    You will notice that the Context listener has a timerBean field with @EJB annotation. The mapped name for the EJB is defined to be the full JNDI name.
    @EJB(name="timerBean", mappedName="corbaname:iiop:localhost:3700#ejb/timedBean")
    This is only needed in case of remote deployments on different clusters. In the same cluster you could simply use "ejb/timedBean".
  3. The Servlet: The servlet is has a similar @EJB annotation.
    package servlets;

    import java.io.IOException;

    import javax.ejb.EJB;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import ejb.ITimedBean;

    public class TimerClient extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    @EJB(name="timerBean", mappedName="corbaname:iiop:localhost:3700#ejb/timedBean")
    private ITimedBean timerBean;

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.getWriter().println(timerBean.checkStatus());
    response.getWriter().println(timerBean.checkStatus());
    response.getWriter().println(timerBean.checkStatus());

    }

    public ITimedBean getTimerBean() {
    return timerBean;
    }

    public void setTimerBean(ITimedBean timerBean) {
    this.timerBean = timerBean;
    }
    }
    TimerClient.java
  4. The Deployment descriptor: The deployment descriptor is listed below.
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>TimerClientWeb</display-name>
    <listener>
    <listener-class>servlets.LoadTimer</listener-class>
    </listener>
    <servlet>
    <description></description>
    <display-name>TimerClient</display-name>
    <servlet-name>TimerClient</servlet-name>
    <servlet-class>servlets.TimerClient</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>TimerClient</servlet-name>
    <url-pattern>/timerClient</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    web.xml

Timer Service Additional Info (From the EJB 3.0 Specification)
  • Invocations of the methods to create and cancel timers and of the timeout callback method are typically made within a transaction.
  • If the transaction is rolled back, the timer creation is rolled back and so is the case with timer cancellation.
  • If container-managed transaction demarcation is used and the REQUIRED or REQUIRES_NEW transaction attribute is specified or defaulted (Required or RequiresNew if the deployment descriptor is used), the container must begin a new transaction prior to invoking the timeout callback method.
  • If the transaction fails or is rolled back, the container must retry the timeout at least once.
  • Timers survive container crashes, server shutdown, and the activation/passivation and load/store cycles of the enterprise beans that are registered with them.
  • Since the timeout callback method is an internal method of the bean class, it has no client security context. When getCallerPrincipal is called from within the timeout callback method, it returns the container's representation of the unauthenticated identity.

7 comments:

  1. Hi abhi,

    i am trying to use javabeans through dwr (remoting library), and i am using simple java class to interact with javabeans.

    your timer example is goog but i don't know how to use with simple java class, can u please explain it to me, if it is possible

    ReplyDelete
  2. You can try quartz scheduler for this instead of using the EJB 3 timer service. See Scheduling with quartz

    ReplyDelete
  3. Hi,
    I have a one Bussiness Method name
    goodMorning();. I want to repeat this method every morning. How should I do in J2EE??

    ReplyDelete
  4. Hi,
    I have a one Bussiness Method name
    goodMorning();. I want to repeat this method every morning. How should I do in J2EE

    ReplyDelete
  5. Hi Abhi,

    I have seen in some places that developers usually use the ServletContextListener to initialize the EJB timers.

    However, if you use EJB probably you are in a distributable environment, so probably you also want to set up front end (web app) in a cluster.

    The Servlet specification says that we shouldn't rely on the ServletContextListener in a distributed environment since the ServletContextEvent may not be triggered. I guess that setting the listener in the default web application will initialize the timers but we are loosing some transparency since we have to know whcih one is the main node.

    Do you know any other way to initialize the timers which may be a better option?

    Thanks.

    ReplyDelete
  6. Hi Abhi,

    I would like to limit the times of re-try timeout method at EJB 3 time service. Could you please instruct how to specify it?

    Thanks.

    ReplyDelete
  7. Hello, I used your example but the annotation @ Resource on SessionContext ctx does not work always have one when I call the method NullPointer ctx.getTimerService (). CreateTimer (1000, 1000, null);
    ctx always this void, I tried several types of injection, but always this void.
    You know what can that be?
    Thank you.

    ReplyDelete

Popular Posts