Table of Contents
This chapter describes the basic knowledge and instructions for JEUS Scheduler programming.
The following features have been added or changed since JEUS version 5.0. JEUS 5.0 is backward compatible and programs that are written in previous versions can 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. SchedulerFactory is used 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. Start time, cycle, termination time, and maximum execution count properties can be set 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 as 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. Previous users of J2SE Timer interface can easily use JEUS Scheduler.
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, first the task class must be defined, and then the task according to the execution time and cycle.
A task that executes at a set time is defined by 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, it can be defined by inheriting the Schedule class or RemoteSchedule class.
The Schedule and RemoteSchedule classes are used in previous versions of JEUS. In JEUS 5.0, 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.
JEUS Scheduler determines the initial execution time by calling nextTime() after registering a task object. At the set time, onTime() is called and the task executes 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 execute.
Since the Schedule task object calls nextTime() after onTime() finishes executing, the nextTime() call is delayed while onTime() is executing. Therefore, it is difficult to write programs to execute the task at exact time intervals. Thus, it is recommended to set the execution cycle when registering tasks rather than through 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. This is mainly used to register task objects using the class name.
The class contains the initialize() Callback method which is called just once after a task is registered. Therefore, it can be used to assign initialization values when using the class name to register a task remotely.
The initialize() callback is only invoked 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.
Since 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 JEUS server. A remote JEUS Scheduler is used when a remote client registers a task on a JEUS node.
JNDI Lookup is used to access the remote JEUS Scheduler on a JEUS node.
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.0 and earlier versions 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 the task objects are serialized and run remotely for a remote JEUS Scheduler.
Tasks that execute only once can be registered 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.
Tasks can be registered in the following way.
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".
Recurring tasks can be registered 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 will all be delayed. As a result, all task executions are delayed accordingly.
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.
For a task registered as fixed-rate, JEUS Scheduler calls the task using another thread when the task execution is delayed, to guarantee the execution time. If a task execution is delayed, same tasks may be executed concurrently and task objects should be checked for thread-safety.
Tasks can be registered using the following methods.
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 tasks.
[Example 2.4] Registering Recurring Jobs
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.
Since the nextTime() method of the Schedule task object is used for the initial and recurring execution times, separate parameters are not required for registering a task. The following method is used to register 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 Managed Server (MS). If MS fails to create the Scheduler, it displays an error and fails to start.
When a task is registered in JEUS Scheduler, the ScheduleController handler object is returned. This object is created for each registered task and is used to control the task. The handler can be used to get information about the task and to cancel the task.
The following example shows how to cancel tasks 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();
JEUS Scheduler can only be used after the tasks have been defined, obtained, registered, and controlled.
This section explains how to use Scheduler in various environments.
JEUS Scheduler can be used by 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 on Java EE application clients, JEUS Scheduler's thread pool configuration can be set in the deployment descriptor (jeus-client-dd.xml).
1.For information about how to set a Scheduler's thread pool, refer to the 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.
Remote clients can access scheduler services that are running on JEUS server if the scheduler service startup configuration is set on the JEUS server.
The scheduler service on JEUS server registers the RMI Scheduler object with JNDI. Therefore, clients can obtain the remote scheduler objects (stub object) using 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, same method is used to register tasks. However, the registered task objects are serialized and run from remote Schedulers, which means they run on JEUS servers.
During task registration, if the daemon flag is set 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, 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 JEUS must be restarted if it is running.
JEUS Scheduler can be used in Java EE components such as EJB and Servlet. In this case, JEUS Scheduler runs on JEUS server.
JEUS 5.0 provides the EJB Timer Service that is specified in the EJB 2.1 standard. Therefore, it is recommended to use the EJB Timer Service for EJB components instead of JEUS Scheduler to comply with the Java EE standard. However, JEUS Scheduler should be used for other JAVA EE components besides EJB components since EJB Timer Service cannot be used.
Using JEUS Scheduler in Java EE components is same as using a Standalone JEUS Scheduler. First the SchedulerFactory class is used to obtain a Scheduler object running on the server, and then the necessary method is called to register the task.
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(); } } }
A thread pool and Job-list can be configured for JEUS Scheduler that runs on JEUS server. For information about the configuration, refer to "3.4. Configuring Thread Pool".
Job-list registers tasks on JEUS server using the configuration file instead of the programming method. It can be registered on JEUS server Scheduler. If tasks are registered through Job-list, the tasks are executed at fixed-rate.
Job-list can be set 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".