Sunday 8 June 2014

Examples of EJB 3.1 TimerService - Programmatic Timers

A programmatic timer is typically required when there is a user interface that controls the scheduling activity.
User would be able to start or stop a scheduler which do certain processing using the specified timer schedule. Here is a simple example of a programmatic EJB Timer.

In this example, it is assumed that the application have a SessionBean facade(s) which have all the business methods. A SessionBean facade is required for making use of the container managed persistence context in the business methods. The timer just invokes the relevant business method based on the timer schedule expiration.

package com.prasune.ejb.timer;

import java.util.Date;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.ScheduleExpression;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

import com.prasune.ejb.persistence.JPASessionFacade;

@Stateless(name = "ProgramaticTimer", mappedName = "ProgramaticTimer")
public class ProgramaticTimerBean implements ProgramaticTimer {
   
    @Resource
    TimerService timerService;
   
    @EJB
    private JPASessionFacade jpaSessionFacade;

    public ProgramaticTimerBean() {
    }

    @Timeout
    public void doProcess(Timer timer) {
        System.out.println("Programatic Timer got invoked at " + new Date());
        jpaSessionFacade.dmlOperation();
    }
   
    public void startScheduler(){
        ScheduleExpression scheduleExpression = new ScheduleExpression();
        scheduleExpression.hour("*").minute("*").second("*/5");
        timerService.createCalendarTimer(scheduleExpression, new TimerConfig("TestTimer", true));
        System.out.println("Programatic Timer got created succesfully");
    }
   
    public void stopScheduler(String schedulerId) {
        if (timerService.getTimers() != null) {
            for (Timer timer : timerService.getTimers()) {
                if (timer.getInfo().equals(schedulerId)) {
                    timer.cancel();
                    System.out.println("Programatic Timer got stopped succesfully");
                }
            }
        }
    }
}


startScheduler() method creates a timer using the calendar based ejb ScheduleExpression.
The ScheduleExpression is very similar to chrone job schedule expression. Detailed usage of the ScheduleExpression for creating a timer can be found at Understanding EJB Timer Service 3.1.
In the example, we are creating a timer schedule to be executed every 5 seconds from now.

TimerConfig: There is an optional TimerConfig that can be passed while creating the calendar based timer. In the TimerConfig, we can specify an info like an Id using which we can identify the Timer. The Timer needs to be identified for stopping it as and when the user requires to do the same.The second parameter of the TimerConfig decides whether the Timer Schedule needs to be persisted or not. This is by default true and it means that the schedule of the timer is persisted and is not specific to JVM. Hence, it can survive a server crash.

@Resource annotation is used by the JEE server to inject the TimerService to the EJB.

@Timeout annotation is used to mark the method to be executed on timer schedule expiration. A programmatic Timer can have only one method with this annotation per EJB. The doProcess() method executes the relevant business method specified from the SessionBean facade.

@EJB annotation is used by the JEE server for injecting the SessionBean facade for calling the relevant business method.

stopScheduler() method fetches the Timers registered with TimerService and uses the TimerConfig info to identify the relevant timer and stops it.


ProgramaticTimer is the Local and Remote interface of the EJB. Although it is not mandatory to have a separate interface and the EJB can be annotated with Local/Remote annotations, it is better to program to interface. This will help while multiple applications are involved as the integrator needs to be given only the interface. 

package com.prasune.ejb.timer;

import javax.ejb.Local;
import javax.ejb.Remote;

@Local
@Remote
public interface ProgramaticTimer {
    public void startScheduler();
    public void stopScheduler(String schedulerId);
}


ProgramaticTimerRemoteClient is a sample remote client for invoking the timer application to see if it is working fine. The example client uses Weblogic server initial context. The ProgramaticTimerBean APIs can be called for starting/stopping the scheduler and to verify whether it is working fine.

package com.prasune.ejb.timer.client;

import com.prasune.ejb.timer.ProgramaticTimer;

import java.util.Hashtable;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class ProgramaticTimerRemoteClient {
    public static void main(String[] args) {
        try {
            final Context context = getInitialContext();
            ProgramaticTimer programaticTimer =
                (ProgramaticTimer) context.lookup("ProgramaticTimer#com.prasune.ejb.timer.ProgramaticTimer");
            programaticTimer.startScheduler();
            //programaticTimer.stopScheduler("TestTimer");
            System.out.println("Invoked Timer");
        } catch (CommunicationException ex) {
            System.out.println(ex.getClass().getName());
            System.out.println(ex.getRootCause().getLocalizedMessage());
            System.out.println("\n*** A CommunicationException was raised.  This typically\n*** occurs when the target WebLogic server is not running.\n");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static Context getInitialContext() throws NamingException {
        Hashtable env = new Hashtable();
        // WebLogic Server 10.x/12.x connection details
        env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        env.put(Context.PROVIDER_URL, "t3://127.0.0.1:7101");
        return new InitialContext(env);
    }
}


Packaging and Deployment

Packaging and deployment of an EJB 3.1 Timer is very simple. The class file needs to be in a jar file with the correct directory structure of the package and the jar file can be directly deployed on any JEE server supporting EJB 3.1. The vendor specific ejb-jar.xml are optional and annotations are sufficient for the JEE server to identify the class as EJB Timer

No comments:

Post a Comment