Tuesday Oct 19, 2010

TOTD #146: Understanding the EJB 3.1 Timer service in Java EE 6 - Programmatic, Deployment Descriptor, @Schedule

EJB 3.1 has greatly simplified the ability to schedule events according to a calendar-based schedule, at a specified time, after a specified elapsed duration, or at a specific recurring intervals.

There are multiple ways events can be scheduled:

  • Programmatically using Timer service
  • Automatic timers based upon the metadata specified using @Schedule
  • Deployment Descriptor

This Tip Of The Day (TOTD) will show code samples on how timer-based events can be created in EJBs.

Lets start with programmatic creation of timers first.

The Timer Service allows for programmatic creation and cancellation of timers. Programmatic timers can be created using createXXX methods on "TimerService". The method to be invoked at the scheduled time can be any of the flavors mentioned below:

  1. EJB implementing "javax.ejb.TimedObject" interface that has a single method "public void ejbTimeout(Timer timer)". For example:
    @Singleton
    @Startup
    public class DummyTimer2 implements TimedObject {
     
     @Resource TimerService timerService;
     
     @PostConstruct
     public void initTimer() {
       if (timerService.getTimers() != null) {
         for (Timer timer : timerService.getTimers()) {
           if (timer.getInfo().equals("dummyTimer2.1") ||
               timer.getInfo().equals("dummyTimer2.2")) 
             timer.cancel();
           }
       }
     
       timerService.createCalendarTimer(
           new ScheduleExpression().
               hour("\*").
               minute("\*").
               second("\*/10"), new TimerConfig("dummyTimer2.1", true));
       timerService.createCalendarTimer(
           new ScheduleExpression().
               hour("\*").
               minute("\*").
               second("\*/45"), new TimerConfig("dummyTimer2.2", true));
     }
    
     @Override
     public void ejbTimeout(Timer timer) {
       System.out.println(getClass().getName() + ": " + new Date());
     }
    }
    
    

    The "initTimer" method is a lifecycle callback method and cleans up any previously created timers and then create new timers that triggers every 10th and 45th second. The "ejbTimeout" method, implemented from "TimedObject" interface is invoked everytime the timeout occurs. The "timer" parameter in the "ejbTimeout" method can be used to cancel the timer, get information on when the next timeout will occur, get information about the timer and other relevant data.

    Notice, there is a @Startup class-level annotation which ensures that bean is eagerly loaded, lifecycle callback methods invoked and thus timers are created before the bean is ready.
  2. At most one method tagged with "@Timeout". Methods annotated with @Timeout must have one of the following signature:
    1. void <METHOD>()
    2. void <METHOD>(Timer timer)

    The second method signature gives you the ability to cancel the timer and obtain metatdata about the timer. These methods may be public, private, protected, or package level access. For example:
    @Singleton
    @Startup
    public class DummyTimer3 {
      @Resource TimerService timerService;
     
      @PostConstruct
      public void initTimer() {
        if (timerService.getTimers() != null) {
          for (Timer timer : timerService.getTimers()) {
            if (timer.getInfo().equals("dummyTimer3.1") ||
                timer.getInfo().equals("dummyTimer3.2")) 
              timer.cancel();
          }
        }
        timerService.createCalendarTimer(
          new ScheduleExpression().
              hour("\*").
              minute("\*").
              second("\*/10"), new TimerConfig("dummyTimer3.1", true));
        timerService.createCalendarTimer(
          new ScheduleExpression().
              hour("\*").
              minute("\*").
              second("\*/45"), new TimerConfig("dummyTimer3.2", true));
      }
      @Timeout
      public void timeout(Timer timer) {
        System.out.println(getClass().getName() + ": " + new Date());
      }
    }
    

    Here again, the "initTimer" method is used for cleaning / initializing timers. The "timeout" method is marked with "@Timeout" annotation is the target method that is invoked when the timer expires.

Read more details about Timer Service in the Java EE Tutorial.

Lets see how this similar functionality can be achieved using deployment descriptor (ejb-jar.xml). Basically a simple method in the POJO class given below:

public class DummyTimer4 {
  public void timeout(Timer timer) {
    System.out.println(getClass().getName() + ": " + new Date());
  }
}

can be converted into a timer method by adding the following "ejb-jar.xml" in WEB-INF directory of WAR file:

<?xml version="1.0" encoding="UTF-8"?>

<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
  version = "3.1"
  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/ejb-jar_3_1.xsd">
  <enterprise-beans>
    <session>
      <ejb-name>DummyTimer4</ejb-name>
      <ejb-class>org.glassfish.samples.DummyTimer4</ejb-class>
      <session-type>Stateless</session-type>
      <timer>
        <schedule>
          <second>\*/10</second>
          <minute>\*</minute>
          <hour>\*</hour>
          <month>\*</month>
          <year>\*</year>
        </schedule>
        <timeout-method>
          <method-name>timeout</method-name>
          <method-params>
            <method-param>javax.ejb.Timer</method-param>
          </method-params>
       </timeout-method>
     </timer>
   </session>
 </enterprise-beans>
</ejb-jar>

Multiple <schedule>s can be added to create multiple timers.

But all this is too much. EJB 3.1 adds @Schedule annotation that automatically creates timers based upon the metadata specified on a method, such as:

@Singleton
public class DummyTimer {

  @Schedules({
    @Schedule(hour="\*", minute="\*", second="\*/10"),
    @Schedule(hour="\*", minute="\*", second="\*/45")
  })
  public void printTime() {
    System.out.println(getClass().getName() + ": " + new Date());
  }
}

EJB 3.1 container reads the @Schedule annotations and automatically create timers. Notice, there is no need for a @Startup annotation here as lifecycle callback methods are not required. Each re-deploy of application will automatically delete and re-create all the schedule-based timers ... really clean and simple! No messing around with deployment descriptors too :-)

Interval timers created using TimerService can be easily created by using "ScheduleExpression.start()" and "end()" methods. The single-action timer can be easily created by specifying fixed values for each field:

@Schedule(year="A", month="B", dayOfMonth="C", hour="D", minute="E", second="F")

Optionally, you can also pass a "Timer" object to the methods annotated with @Schedule such as:

@Schedules({
  @Schedule(hour="\*", minute="\*", second="\*/10", info="every tenth"),
  @Schedule(hour="\*", minute="\*", second="\*/45", info="every 45th")
})
public void printTime(Timer timer) {
  System.out.println(getClass().getName() + ": " +
      new Date() + ":" +
      timer.getInfo());
}

The "timer" object contains information about the timer that just expired. Note that "info" is passed to each @Schedule annotation and "timer.getInfo()" can be used in "printTime" method to find out which of the timers expired.

The complete source code used in this blog can be downloaded here.

Here are some other points to be noted:

  • Timers can be created in stateless session beans, singleton session beans, MDBs but not for stateful session beans. This functionality may be added to a future version of the specification.
  • Timers are persistent by default, need to made non-persistent programmatically (TimerConfig.setPersistent(false)) or automatically (by adding persistent=false on @Schedule)
  • Schedule-based timers may be optionally associated with a timezone.
  • The user code has no control over the timers created using @Schedule and thus cannot be canceled after creation.
  • Timers are not for real time as the container interleaves the calls to a timeout callback method with the calls to the business methods and the lifecycle callback methods of the bean. So the timed out method may not be invoked exactly at the time specified at timer creation.

This blog is derived from the whiteboard discussion with Linda as captured below and numerous emails with Marina for helping me understand the concepts.

Ah, the joys of sitting a few feet away from most of the Java EE 6 spec leads :-)

Here is one tweet that I saw recently on EJB timers ...

Never wrote a Timer faster in #java ...really like it #ejb3.1 #glassfish http://j.mp/d1owSM

I love the simplicity and power provided by @Schedule, how do you create timers in your enterprise applications ?

Technorati: totd javaee6 glassfish ejb timer deploymentdescriptor schedule annotations

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today