Task Scheduling in Java EE 6 on GlassFish using the Timer Service

June 22nd, 2012 by

Creating cronjobs or scheduled service executions is made really easy in Java EE 6. Scheduled tasks may be created in a programmatical style or simply by adding some annotations to an EJB.

In the following tutorial we’re creating some simple scheduled tasks and let them run on an embedded GlassFish instance using the Maven Embedded GlassFish plugin..


 

Java EE 6 Maven Project from Archetype

First of all we’re creating a new maven-ized project using one of the appropriate jee6 Maven archetypes

Creating a new Java EE 6 project using Maven archetypes

Creating a new Java EE 6 project using Maven archetypes

Afterwards we’re adding some dependencies for the embedded GlassFish and the corresponding maven plugin and some repositories so that a final pom.xml could look like this one ..

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hascode.tutorial</groupId>
<artifactId>jee6-timerservice-tutorial</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<name>jee6-timerservice-tutorial</name>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
    <dependency>
        <groupId>org.glassfish.main.extras</groupId>
        <artifactId>glassfish-embedded-all</artifactId>
        <version>3.1.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-web-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <finalName>timerapp</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.1.1</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.glassfish</groupId>
            <artifactId>maven-embedded-glassfish-plugin</artifactId>
            <version>3.1.1</version>
            <configuration>
                <app>${project.build.directory}/${project.build.finalName}.war</app>
                <port>8080</port>
                <contextRoot>${project.build.finalName}</contextRoot>
                <name>${project.build.finalName}</name>
            </configuration>
        </plugin>
    </plugins>
</build>
<repositories>
    <repository>
        <id>java.net-public-repository-group</id>
        <name>Java.Net Public Maven Repository Group</name>
        <url>https://maven.java.net/content/groups/public</url>
    </repository>
    <repository>
        <id>java.net-staging-repository-group</id>
        <name>Java.Net Staging Maven Repository Group</name>
        <url>https://maven.java.net/content/groups/staging</url>
    </repository>
    <repository>
        <id>java.net-releases-repository-group</id>
        <name>Java.Net Releases Maven Repository Group</name>
        <url>https://maven.java.net/content/repositories/releases</url>
    </repository>
    <repository>
        <id>java.net-snapshots-repository-group</id>
        <name>Java.Net Snapshots Maven Repository Group</name>
        <url>https://maven.java.net/content/repositories/snapshots</url>
    </repository>
    <repository>
        <id>apache-snapshot</id>
        <url>https://repository.apache.org/content/repositories/snapshots/</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>apache-snapshot</id>
        <url>https://repository.apache.org/content/repositories/snapshots/</url>
    </pluginRepository>
    <pluginRepository>
        <id>maven-central</id>
        <url>http://repo1.maven.org/maven2/</url>
    </pluginRepository>
</pluginRepositories>
</project>

Calendar Based Expressions

Calendar expressions may be defined using a syntax that is similar to the *nix cron syntax – a detailed overview is available in the following JEE documentation at Oracle.com.

You’re able to specify values, wildcard expressions, lists, ranges or increments on the following accepted attributes: second, minute, hour, dayOfMonth, month, dayOfWeek, year, timezone. Second, minute and hour have a default value of 0, the other attributes a default value of “*”.

Some examples:

  • Every weekday at 20:15: minute=”15″, hour=”20″, dayOfWeek=”Mon-Fri”
  • Every friday at midnight: dayOfWeek=”Fri”
  • Every five minutes: minute=”*/5″, hour=”*”
  • Every twenty seconds starting at second 10: second=”10/20″, minute = “*”, hour = “*”

Creating Timers with Annotations

Using annotations to create a scheduled task is really easy .. take an EJB, add one @Schedule annotation or multiple @Schedule annotations as parameters of an @Schedules annotation to a method

We’re creating a stateless session bean here and we’re using the @Schedule annotation to execute runEveryMinute - every minute ;)

package com.hascode.tutorial;
 
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.ejb.Schedule;
import javax.ejb.Stateless;
 
@Stateless
public class TimerExampleEJB {
 private final Logger log = Logger
 .getLogger(TimerExampleEJB.class.getName());
 
 @Schedule(minute = "*/1", hour = "*")
 public void runEveryMinute() {
 log.log(Level.INFO,
 "running every minute .. now it's: " + new Date().toString());
 }
}

Deploying the application the timer creates the following output in the console

INFO: running every minute .. now it's: Thu Jun 21 19:31:00 CEST 2012
Jun 21, 2012 7:32:00 PM com.hascode.tutorial.TimerExampleEJB runEveryMinute
INFO: running every minute .. now it's: Thu Jun 21 19:32:00 CEST 2012
Jun 21, 2012 7:33:00 PM com.hascode.tutorial.TimerExampleEJB runEveryMinute
INFO: running every minute .. now it's: Thu Jun 21 19:33:00 CEST 2012
Jun 21, 2012 7:34:00 PM com.hascode.tutorial.TimerExampleEJB runEveryMinute

Programmatic Timer Creation

The other choice to create a timer is by doing it the programmatical way – first inject the TimerService into your EJB using @Resource. Afterwards configure your desired execution interval/time using ScheduleExpressions.

Finally you may pass information that is needed at the time of execution as a Serializable into the TimerConfig.

In the following example we’re creating a new timer at deployment time that is going to be executed every ten seconds and passes a string to the target execution context.

package com.hascode.tutorial;
 
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
 
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
 
@Startup
@Singleton
public class ProgrammaticalTimerEJB {
	private final Logger log = Logger.getLogger(getClass().getName());
 
	@Resource
	private TimerService timerService;
 
	@PostConstruct
	public void createProgrammaticalTimer() {
		log.log(Level.INFO, "ProgrammaticalTimerEJB initialized");
		ScheduleExpression everyTenSeconds = new ScheduleExpression()
				.second("*/10").minute("*").hour("*");
		timerService.createCalendarTimer(everyTenSeconds, new TimerConfig(
				"passed message " + new Date(), false));
	}
 
	@Timeout
	public void handleTimer(final Timer timer) {
		log.info("timer received - contained message is: " + timer.getInfo());
	}
}

The following output is produced – as you can see, the Serializable object passed in the the TimerConfig is the same for every execution the date printed stays the same

Jun 22, 2012 6:46:30 PM com.hascode.tutorial.ProgrammaticalTimerEJB handleTimer
INFO: timer received - contained message is: passed message Fri Jun 22 18:46:22 CEST 2012
Jun 22, 2012 6:46:40 PM com.hascode.tutorial.ProgrammaticalTimerEJB handleTimer
INFO: timer received - contained message is: passed message Fri Jun 22 18:46:22 CEST 2012
Jun 22, 2012 6:46:50 PM com.hascode.tutorial.ProgrammaticalTimerEJB handleTimer
The programmatical created timer's output in Eclipse IDE

The programmatical created timer's output in Eclipse IDE

Persistent and non-persistent Timers

Per default timers are persistent – that means that they survive a shutdown of the application server and when the server is started again they are executed as if no shutdown had happened.

To create non-persistent timers we’re able to use one of the following options:

  • Annotation-based declaration: Add persistent=false to the annotation .. e.g.: @Schedule(minute=”*/1″ hour=”*”, persistent=false)
  • Programmatical declaration: Add false as second parameter to the TimerConfig .. e.g.: new TimerConfig(info, false);

Running on embedded GlassFish

The embedded GlassFish plugins really eases running the examples in a Java EE 6 environment here .. just run the following command:

mvn clean package embedded-glassfish:run

If you’re using the Eclipse Maven Plugin and start the GlassFish there you might see a similar output

Embedded GlassFish and Eclipse IDE

Embedded GlassFish and Eclipse IDE

Tutorial Sources

I have put the source from this tutorial on my Bitbucket repository – download it there or check it out using Mercurial:

hg clone https://bitbucket.org/hascode/jee6-timer-tutorial

Resources

Attribute Description Default Value Allowable Values and Examples
second One or more seconds within a minute 0 0 to 59. For example: second="30".
minute One or more minutes within an hour 0 0 to 59. For example: minute="15".
hour One or more hours within a day 0 0 to 23. For example: hour="13".
dayOfWeek One or more days within a week * 0 to 7 (both 0 and 7 refer to Sunday). For example: dayOfWeek="3".

Sun, Mon, Tue, Wed, Thu, Fri, Sat. For example: dayOfWeek="Mon".

dayOfMonth One or more days within a month * 1 to 31. For example: dayOfMonth="15".

–7 to –1 (a negative number means the nth day or days before the end of the month). For example: dayOfMonth="–3".

Last. For example: dayOfMonth="Last".

[1st, 2nd, 3rd, 4th, 5th, Last] [Sun, Mon, Tue, Wed, Thu, Fri, Sat]. For example: dayOfMonth="2nd Fri".

month One or more months within a year * 1 to 12. For example: month="7".

Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec. For example: month="July".

year A particular calendar year * A four–digit calendar year. For example: year="2011".

Tags: , , , , , , , , , , , , ,

2 Responses to “Task Scheduling in Java EE 6 on GlassFish using the Timer Service”

  1. Rhys Evans Says:

    Great tutorial Micha thank you. I’ve been using the embedded GlassFish plugin (v 3.1.1) in a project of mine for a while now to deploy an application during the integration-test phase of the build process to the embedded container. I found that as soon as I included a Bean that makes use of the timer functionality, I’ve annotated a method with @Schedule(dayOfWeek=”Sun”, hour=”0″) to be precise, I get an odd directory created in the root directory of the module where the tests were run.

    The directory created is named “null” and doesn’t contain anything. I’ve noticed that this tends to happen when the TimerBeanContainer Shutdown() request is executed by the embedded container. Have you ever experienced behaviour like this? As mentioned I am using the 3.1.1 version of the embedded container via Maven 2, the container is launched by the failsafe plugin.

  2. Rhys Evans Says:

    Apologies just to be clear on the last statement of the previous post, it is the tests that launch and make use of the embedded container and they are launched by the failsafe plugin.

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 80,968 bad guys.

Search
Categories