Chapter 2. Scheduling Tasks

Table of Contents

2.1. Overview
2.2. JEUS Scheduler Class
2.3. Defining Tasks
2.3.1. Implementing ScheduleListener Interface
2.3.2. Schedule Class Inheritance
2.3.3. Inheriting RemoteSchedule Class
2.4. Obtaining Scheduler Object
2.4.1. Obtaining Scheduler Object in Local Environment
2.4.2. Obtaining a Scheduler Object in Remote Environment
2.5. Registering Tasks
2.5.1. Defining One-Time Tasks
2.5.2. Registering Recurring Tasks
2.5.3. Registering Schedule Task Objects
2.6. Controlling Tasks
2.7. Using Scheduler
2.7.1. Using In a Standalone Environment
2.7.2. Using on the JEUS Server
2.7.3. Using in Java EE Components
2.8. Using Job-list

This chapter outlines basic concepts related to JEUS Scheduler and instructions for JEUS Scheduler programming.

2.1. Overview

For JEUS 5 and later, the following features have been added or changed. JEUS 8 Scheduler is compatible with its earlier versions, and programs written with previous versions can still be used without modification.

  • Add SchedulerListener Interface

    The ScheduleListener interface defines a task.

    This is the top level interface that must be implemented by all tasks. It also implements the existing schedule task classes. Therefore, tasks can be defined by implementing ScheduleListener without inheriting a schedule task class.

  • Add SchedulerFactory Class

    The existing SchedulerManager class for client and server is replaced by the SchedulerFactory class. You can use SchedulerFactory to get Scheduler objects, and register tasks regardless of environment (client, server, and remote client environments).

  • Add various methods

    Various methods for adding a task have been added. You can specify a start time, cycle, termination time, and maximum execution count properties when adding a task.

  • Modify into a multi-thread execution method that uses a thread pool

    While the existing JEUS Scheduler used a single thread to execute tasks, the new JEUS Scheduler executes each task with a separate thread using a thread pool. This prevents a blocked task from affecting other tasks.

  • Add Job-list feature

    Job-list feature allows tasks to be registered on JEUS server configuration instead of through programming.

The next section describes the classes that make up the JEUS Scheduler, and how to define, register, and control a job schedule. All examples explained in this chapter can be found in the JEUS_HOME/samples/scheduler directory.

2.2. JEUS Scheduler Class

JEUS Scheduler is conceptually identical to and contains similar interfaces of J2SE Timer. The java.util.TimerTask class that represents a task corresponds to the jeus.schedule.ScheduleListener interface, and the java.util.Timer class that registers a task corresponds to the jeus.schedule.Scheduler interface. So if you are familiar with J2SE Timer interface, you can use JEUS Scheduler without difficulty.

The following figure shows each Scheduler API classes.

[Figure 2.1] Scheduler API Classes

Scheduler API Classes

API ClassDescription
package jeus.scheduleAll classes and interfaces of JEUS Scheduler belong to jeus.schedule package and its sub-packages.
interface ScheduleListenerA task that needs to execute at a set time is defined as a class by implementing the ScheduleListener interface. ScheduleListener contains a callback method called onTime() that is invoked at the scheduled time.
abstract class ScheduleAbstract class that implements ScheduleListener which was used in versions earlier than JEUS 5. This class contains the nextTime() and onTime() callback methods. The nextTime() method is used dynamically to get the time to execute the task after it is scheduled
abstract class RemoteScheduleSpecial abstract class that contains the initialize() callback method and receives initialization parameters when creating an object.
class SchedulerFactoryCalls a concrete Scheduler object.
interface SchedulerCore interface for registering tasks in JEUS Scheduler. It defines various registerSchedule() methods.
interface ScheduleControllerWhen a task is registered in the Scheduler, the object that implements the ScheduleController interface is returned. This handler object is used to get information about or cancel the task.
exception JeusSchedulerExceptionJeusSchedulerException may be thrown if an internal error occurs while registering or canceling a task in JEUS Scheduler.

Note

For more information about each interface or class, refer to the JEUS Scheduler Javadoc API.

2.3. Defining Tasks

To execute a task, you must first define its class and then the task itself by specifying its execution time and cycle.

2.3.1. Implementing ScheduleListener Interface

A task that executes at a set time is defined by first implementing the ScheduleListener interface.

ScheduleListener contains the onTime() callback method that is invoked at the set time. A task class must implement the onTime() method that contains the task implementation.

[Example 2.1] Task object example

public class SimpleTask implements ScheduleListener {
    private String name;
    private int count;

    // no-arg constructor is required if classname is used for task registration
    public SimpleTask() {
    }
    
    public SimpleTask(String name) {
        this.name = name;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }
...
}


2.3.2. Schedule Class Inheritance

Instead of directly implementing a ScheduleListener, you can define it by inheriting the Schedule class or RemoteSchedule class. The Schedule and RemoteSchedule classes are used in previous versions of JEUS. In JEUS 5, ScheduleListener must be implemented to define tasks in general, but the previous task classes are also provided for backward compatibility.

The Schedule abstract class contains the nextTime() callback method in addition to the onTime() method. The nextTime() method does not schedule the call time when registering tasks, but instead the next call time is scheduled in the task class. Therefore, it is more efficient to use the method for tasks with a variable time cycle than a fixed time cycle..

After registering a task object, JEUS Scheduler determines the initial execution time by calling nextTime(). At the set time, onTime() is called and the task is executed when onTime() call terminates. The nextTime() method must return the next scheduled execution interval in milliseconds. If it returns 0, the task will no longer be executed.

Because onTime() must be finished before the Schedule task object calls nextTime(), while onTime() is executing, the nextTime() call is delayed. But it is difficult to write programs to execute the task at exact time intervals. Thus, when you are registering a task, it is recommended to set the execution cycle instead of using the nextTime() method.

[Example 2.2] Schedule object example

public class SimpleSchedule extends Schedule {
    private String name;
    private int count;
    private long period = 2000; // 2 seconds

    // no-arg constructor is required if classname is used for task registration
    public SimpleSchedule() {
    }

    public SimpleSchedule(String name) {
        this.name = name;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }

    public long nextTime(long currentTime) {
        return currentTime + period;
    }
...
}


2.3.3. Inheriting RemoteSchedule Class

The RemoteSchedule class is a Schedule object that can set the initialization parameters when registering tasks remotely. You can register a remote task object with the class name. The class contains the initialize() Callback method which is called just once after a task is registered. You can use this method to assign initialization values when using the class name to register a remote task.

Note that the initialize() callback can be invoked only when RemoteSchedule task object is registered by using the Scheduler.registerSchedule(classname, hashtable, daemon_flag) method.

[Example 2.3] RemoteSchedule object example

public class SimpleRemoteSchedule extends RemoteSchedule {
    private String name;
    private int count;
    private long period;

    // no-arg constructor is required if classname is used for task registration
    public SimpleRemoteSchedule() {
    }

    // this is called by scheduler after creation
    public void initialize(Hashtable parameters) {
        name = (String) parameters.get("name");
        Long interval = (Long) parameters.get("interval");
        if (interval != null)
            period = interval.longValue();
        else
            period = 2000;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }

    public long nextTime(long currentTime) {
        return currentTime + period;
    }
...
}


Note

When registering a task by using the Job-list or an API that uses the class name, a no-arg (default) constructor is required because the corresponding class is initialized by the container.

2.4. Obtaining Scheduler Object

This section describes how to obtain a Scheduler object. JEUS Scheduler operates in both local and remote environments.

2.4.1. Obtaining Scheduler Object in Local Environment

When JEUS Scheduler runs in a local environment, a Scheduler instance is created on a local Java Virtual Machine (JVM) and all registered tasks run on the same JVM.

In a local environment, a single instance of JEUS Scheduler is created. This instance is the default scheduler that is shared by all clients on the JVM. JEUS Scheduler in a local environment is used by regular J2SE applications, Java EE application clients, and Java EE components. Use the SchedulerFactory to use JEUS Scheduler in a local environment.

The default Scheduler instance can be easily obtained in the following way.

// Get the default scheduler
Scheduler scheduler = SchedulerFactory.getDefaultScheduler();

2.4.2. Obtaining a Scheduler Object in Remote Environment

When JEUS Scheduler runs in a remote environment, a Scheduler instance is created on a remote JVM, and all registered tasks run on the remote JVM.

Because the remote Scheduler is in the form of an RMI object, JEUS Scheduler can be used through an RMI call. In a JEUS environment, the remote Scheduler service runs on the JEUS server. A remote JEUS Scheduler is used when a remote client registers a task on a JEUS node.

To access the remote JEUS Scheduler on a JEUS node, a JNDI lookup is used.

A JEUS node Scheduler instance (Stub) can be obtained in the following way.

// Get the remote scheduler
InitialContext ic = new InitialContext();
Scheduler scheduler = (Scheduler)ic.lookup(Scheduler.SERVER_SCHEDULER_NAME);

Note

jeus.schedule.server.SchedulerManager and jeus.schedule.client.SchedulerManager used in JEUS 5 and earlier are no longer used (deprecated). Instead, it is recommended to use Scheduler objects generated by the SchedulerFactory. The deprecated classes, however, are provided for backward compatibility.

2.5. Registering Tasks

This section describes how to register tasks using the Scheduler interface. The methods for registering tasks are the same in both local and remote Schedulers. The only difference is that for a remote JEUS Scheduler, the task objects are serialized and run remotely.

2.5.1. Defining One-Time Tasks

You can register a task to execute only once by specifying the execution time. The execution time can be set as an absolute time by using the java.util.Date object or as a relative time based on the current time in milliseconds.

The following shows how to register a task.

registerSchedule(ScheduleListener task, Date time, boolean isDaemon)
registerSchedule(ScheduleListener task, long delay, boolean isDaemon)

The following shows how to use the method. For more information on method parameters, refer to "2.5.2. Registering Recurring Tasks".

SimpleTask task1 = new SimpleTask("task1");
Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
ScheduleController handle1 = scheduler.registerSchedule(task1, firstTime1, false);

2.5.2. Registering Recurring Tasks

You can register a recurring task by specifying the initial execution time, cycle, termination time, and the maximum number of executions. Select either the fixed-delay or fixed-rate method according to the task.

  • Fixed-delay Type

    The execution interval is fixed. The next execution time is set according to the previous execution time and cycle. If a task execution is delayed (due to long execution time or external reasons such as garbage collection) and the next execution time passes, the next task is executed immediately, but the following tasks are all delayed. As a result, all task executions are delayed.

  • Fixed-rate Type

    The ratio of task execution is fixed. The next execution time is determined according to the initial execution time and cycle. Even though a task execution is delayed, the next task is executed immediately after the current task to maintain the rate of executions per hour. In the long term, the task execution times are maintained according to the specified cycle.

Note

When executing a fixed-rate task is delayed, JEUS Scheduler calls the task using another thread to maintain the execution time. So even if executing a task is delayed, the task may be executed concurrently by another thread. To ensure this process, you should check if the task objects are thread safe.

The following methods show how to register a task.

registerSchedule(ScheduleListener task, Date firstTime, long period, Date endTime, 
                 long maxcount, boolean isDaemon)

registerSchedule(ScheduleListener task, long delay, long period, Date endTime, 
                 long maxcount, boolean isDaemon)

registerScheduleAtFixedRate(ScheduleListener task, Date firstTime, long period, 
                 Date endTime, long maxcount, boolean isDaemon)

registerScheduleAtFixedRate(ScheduleListener task, long delay, long period, 
                 Date endTime, long maxcount, boolean isDaemon)

The following describes each parameter.

ParameterDescription
Date firstTimeInitial execution time.
long delayNext execution time relative to the current time (Unit: milliseconds).
long periodRecurring execution cycle (Unit: milliseconds).
Date endTimeTermination time after which a task is no longer executed. If set to null, the recurring task executes indefinitely.
long maxcountMaximum number of executions. If set to Scheduler.UNLIMITED, there is no constraint.
boolean isDaemon

Used only if the Schedule is registered remotely. If set to true, the task terminates when connection to a client is lost.

A client decides whether a connection is lost according to the distributed garbage collection (DGC) policy of RMI Runtime. Schedule is canceled after 15 minutes from the time the client is disconnected.

boolean isThreadedDeprecated.

The following example shows how to register recurring tasks.

[Example 2.4] Defining Recurring Tasks

SimpleTask task2 = new SimpleTask("task2");
ScheduleController handle2 = scheduler.registerSchedule(task2, 2000, 2000, null, 
                         Scheduler.UNLIMITED, false);

SimpleTask task3 = new SimpleTask("task3");
Date firstTime3 = new Date(System.currentTimeMillis() + 2000);
Date endTime3 = new Date(System.currentTimeMillis() + 10 * 1000);
ScheduleController handle3 = scheduler.registerScheduleAtFixedRate(task3, 
                             firstTime3, 2000, endTime3, 10, false);


2.5.3. Registering Schedule Task Objects

The function for registering Schedule or RemoteSchedule task objects is provided for backward compatibility.

Because you can specify the initial and recurring execution times of a task with the nextTime() method of the Schedule task object, additional parameters are not required. The following method show how to define a task.

registerSchedule(Schedule task, boolean isDaemon)
registerSchedule(String classname, Hashtable params, boolean isDaemon)

The following example shows how to register tasks.

[Example 2.5] Registering Schedule Task Objects

Hashtable params = new Hashtable();
params.put("name", "task3");
params.put("interval", new Long(3000));
ScheduleController handle3 = scheduler.registerSchedule(
    "samples.scheduler.SimpleRemoteSchedule", params, true);

SimpleSchedule task4 = new SimpleSchedule("task4");
ScheduleController handle4 = scheduler.registerSchedule(task4, true);


Caution

Scheduler must be configured correctly since it is a main service provided by a Managed Server (MS). If an MS fails to create the Scheduler, it displays an error and fails to start.

2.6. Controlling Tasks

When you register a task in JEUS Scheduler, the ScheduleController handler object is returned. This object is created for each defined task for you to control the task. You can use the handler to get information about a task or to cancel it.

The following example shows how to cancel a task by calling the ScheduleController.cancel() method.

[Example 2.6] Calling the ScheduleController.cancel Method

SimpleTask task2 = new SimpleTask("task2");
ScheduleController handle2 = scheduler.registerSchedule(task2, 2000, 2000, null, 
                              Scheduler.UNLIMITED, false);
Thread.sleep(10 * 1000);
handle2.cancel();


2.7. Using Scheduler

You can use JEUS Scheduler after defining a task, obtaining the Scheduler object, registering a task, and setting the task control handler.

This section explains how to use Scheduler in various environments.

2.7.1. Using In a Standalone Environment

You can use JEUS Scheduler within regular J2SE applications or Java EE application clients. In these cases, JEUS Scheduler and J2SE Timer can be used as libraries. The SchedulerFactory class is used to obtain the Scheduler object. A daemon flag can be set to any value because it is not used when tasks are registered in a local environment

The following is an example of a standalone client:

[Example 2.7] Using Scheduler on a Standalone Client

public class StandAloneClient {
    public static void main(String args[]) {
        try {
            // Get the default scheduler
            Scheduler scheduler = SchedulerFactory.getDefaultScheduler();

            // Register SimpleTask which runs just one time
            echo("Register task1 which runs just one time...");
            SimpleTask task1 = new SimpleTask("task1");
            Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
            ScheduleController handle1 = scheduler.registerSchedule(task1, 
                            firstTime1, false);

            Thread.sleep(5 * 1000);
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-delay
            echo("Register task2 which is repeated " + "until it is canceled...");
            SimpleTask task2 = new SimpleTask("task2");
            ScheduleController handle2 = scheduler.registerSchedule(task2, 2000, 
                          2000, null, Scheduler.UNLIMITED, false);

            Thread.sleep(10 * 1000);
            handle2.cancel();
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-rate
            echo("Register task3 which is repeated " + "for 10 seconds...");
            SimpleTask task3 = new SimpleTask("task3");
            Date firstTime3 = new Date(System.currentTimeMillis() + 2000);
            Date endTime3 = new Date(System.currentTimeMillis() + 10 * 1000);
            ScheduleController handle3 = scheduler.registerScheduleAtFixedRate(
                    task3, firstTime3, 2000, endTime3, 10, false);

            Thread.sleep(12 * 1000);
            echo("");

            // Register SimpleSchedule which is repeated
            // every 2 seconds
            echo("Register task4 which is repeated " + "every 2 seconds...");
            SimpleSchedule task4 = new SimpleSchedule("task4");
            ScheduleController handle4 = scheduler.registerSchedule(task4, false);

            Thread.sleep(10 * 1000);
            echo("");

            // Cancel all tasks
            echo("Cancel all tasks registerd on the scheduler...");
            scheduler.cancel();
            Thread.sleep(5 * 1000);

            System.out.println("Program terminated.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void echo(String s) {
        System.out.println(s);
    }

}


If JEUS Scheduler is used within Java EE application clients, JEUS Scheduler's thread pool can be configured in the deployment descriptor (jeus-client-dd.xml).

Note

1. For information about how to configure a Scheduler's thread pool, refer to JEUS Reference Book. "4.2.5. Thread Management Commands".

2. To compile or execute source code that uses Scheduler, JEUS related classes such as jeus.jar must be specified in the class path.

2.7.2. Using on the JEUS Server

If the Scheduler Service startup is configured on the JEUS server, Scheduler Service running on the server can be accessed by remote clients.

Scheduler Service on JEUS server registers the RMI Scheduler object with JNDI. Therefore, clients can obtain the remote scheduler objects (stub object) using a JNDI lookup. The name of the object is “jeus_service/Scheduler”, and it uses the Scheduler.SERVER_SCHEDULER_NAME constant..

After obtaining the Scheduler object, you can register it with Scheduler Service. Note the registered objects are serialized and run from remote Schedulers, which means they run on JEUS servers.

When you are registering a task, if you set the daemon flag to true, the remote tasks terminate when the remote clients terminate.

Note

For more information about how to configure Scheduler service to execute on JEUS server, refer to JEUS Reference Book. "4.2.5. Thread Management Commands".

The following is an example of a remote client:

[Example 2.8] Using Scheduler on a Remote Client

public class RemoteClient {
    public static void main(String args[]) {
        try {
            // Get the remote scheduler
            InitialContext ic = new InitialContext();
            Scheduler scheduler = (Scheduler)ic.lookup(
                Scheduler.SERVER_SCHEDULER_NAME);

            // Register SimpleTask which runs just one time
            echo("Register task1 which runs just one time...");
            SimpleTask task1 = new SimpleTask("task1");
            Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
            ScheduleController handle1 = scheduler.registerSchedule(task1, 
                             firstTime1, true);

            Thread.sleep(5 * 1000);
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-delay
            echo("Register task2 which is repeated " + "until it is canceled...");
            SimpleTask task2 = new SimpleTask("task2");
            ScheduleController handle2 = scheduler.registerSchedule(task2, 
                    2000, 2000, null, Scheduler.UNLIMITED, true);

            Thread.sleep(10 * 1000);
            handle2.cancel();
            echo("");

            // Register SimpleRemoteSchedule which is repeated
            // every 3 seconds
            echo("Register task3 which is repeated " + "every 3 seconds...");
            Hashtable params = new Hashtable();
            params.put("name", "task3");
            params.put("interval", new Long(3000));
            ScheduleController handle3 = scheduler.registerSchedule(
                    "samples.scheduler.SimpleRemoteSchedule", params, true);

            Thread.sleep(10 * 1000);
            echo("");

            // Cancel all tasks
            echo("Cancel all tasks registerd on the scheduler...");
            scheduler.cancel();
            Thread.sleep(5 * 1000);

            System.out.println("Program terminated.");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static void echo(String s) {
        System.out.println(s);
    }
}

Caution

To execute the previous example, you must compress relevant task class files into a JAR file and save them in the 'DOMAIN_HOME/lib/application' or 'SERVER_HOME/lib/application' directory. The JEUS server loads the class files to execute the task, and if JEUS is running, it must be restarted.

2.7.3. Using in Java EE Components

JEUS Scheduler can be used in Java EE components such as EJB and servlet. In these environments, JEUS Scheduler runs on the JEUS server.

The EJB 2.1 standard specifications include the EJB Timer Service. Therefore, to comply with the Java EE standard, it is recommended to use the EJB Timer Service for EJB components instead of JEUS Scheduler. But for Java EE components other than EJB components, you must use JEUS Scheduler because EJB Timer Service is not supported.

You can use JEUS Scheduler in Java EE components with the same methods for using Standalone JEUS Scheduler. First, you use the SchedulerFactory class to obtain a Scheduler object running on the server, and then use a method to register the task.

Note

You can configure a thread pool for JEUS Scheduler that runs on JEUS server. For information about the configuration, refer to "3.4. Configuring a Thread Pool".

The following example shows how to use JEUS Scheduler in EJB.

[Example 2.9] Using JEUS Scheduler in EJB

public class HelloEJB implements SessionBean {
    private SimpleTask task;
    private ScheduleController taskHandler;
    private boolean isStarted;

    public HelloEJB() {
    }

    public void ejbCreate() {
        task = new SimpleTask("HelloTask");
        isStarted = false;
    }

    public void trigger() throws RemoteException {
        if (!isStarted) {
            Scheduler scheduler = SchedulerFactory.getDefaultScheduler();
            taskHandler = scheduler.registerSchedule(task, 
                          2000, 2000, null, Scheduler.UNLIMITED, false);
            isStarted = true;
        }
    }

    public void ejbRemove() throws RemoteException {
        if (isStarted) {
            taskHandler.cancel();
            isStarted = false;
        }
    }

    public void setSessionContext(SessionContext sc) {
    }

    public void ejbActivate() {
    }

    public void ejbPassivate() {
    }
}
public class HelloClient {
    public static void main(String args[]) {
        try {
            InitialContext ctx = new InitialContext();

            HelloHome home = (HelloHome) ctx.lookup("helloApp");
            Hello hello = (Hello) home.create();
            hello.trigger();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


2.8. Using Job-list

You can register a task on the JEUS server by using the Job-list setting in the configuration file instead of coding. The Job-list setting can be configured for JEUS Scheduler. Tasks you register with Job-list are executed at a fixed rate.

Note

The Job-list setting can be configured for JEUS Scheduler that runs on JEUS server. For more information about how to configure the Job-list, refer to "3.2. Configuring Job-list".