Table of Contents
This chapter outlines basic concepts related to JEUS Scheduler and instructions for JEUS Scheduler programming.
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.
Added the 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.
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).
Added 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.
Changed to 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.
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.
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.
For more information about each interface or class, refer to the JEUS Scheduler Javadoc API.
To execute a task, you must first define its class and then the task itself by specifying its execution time and cycle.
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()); } ... }
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; } ... }
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; } ... }
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.
This section describes how to obtain a Scheduler object. JEUS Scheduler operates in both local and remote environments.
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();
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);
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.
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.
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.
SimpleTask task1 = new SimpleTask("task1"); Date firstTime1 = new Date(System.currentTimeMillis() + 2000); ScheduleController handle1 = scheduler.registerSchedule(task1, firstTime1, false);
For information about method parameters, refer to "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.
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.
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.
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.
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);
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);
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.
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();
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.
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).
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.
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.
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); } }
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.
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.
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(); } } }
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.
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".