The OracleAS Job Scheduler provides scheduled asynchronous processing to J2EE applications. By using the OracleAS Job Scheduler J2EE applications can:
This guide focuses on how to design, develop, and implement job's using the OracleAS Job Scheduler.
This section is intended as an in-depth discussion designing and implementing jobs. A job provides an implementation of the oracle.ias.scheduler.Executable interface. This interface is defined as follows:
public interface Executable {
public void execute (JobContext context) throws JobExecutionException, JobCancellationException;
}
The execute method is invoked by the scheduler when the associated job trigger fires. The input parameter of type oracle.ias.scheduler.JobContext provides a context for job execution. With this context a job can examine and evaluate all associated metadata related to the job definition as well as providing access to the logging subsystem.
The oracle.ias.scheduler.JobContext object provides the following methods:
| Method | Description |
|---|---|
| getLogger() | Returns a JDK 1.4-compliant logger object of type java.util.logging.Logger that references the application's log. |
| getJob() | Returns the job data object of type oracle.ias.scheduler.Job. This object is used to access the job's configuration information. |
The exceptions that may be thrown by the execute method, and associated usage, are as follows:
In order for a job to be executed it must first be submitted. This is done using the Scheduler's addJob() API. As part of the submission input parameters may be specified as name/value pairs using a java.util.Properties object. To improve reusabilty job parameters should be used whenever possible.
A legacy application has been migrated to the J2EE environment,
part of which includes data that is stored in the filesystem. As
part of the J2EE application a job is required to backup the data
on a regular basis. The job requires two input parameters as
follows:
This information could have been hard-coded into the job implementation itself. However, by specifying these as parameters to the job properties the job can be reused later without modification. Refer to listing 2 for an example of how properties are specified.
import java.io.File;
import java.io.IOException;
import oracle.ias.scheduler.Job;
import oracle.ias.scheduler.Executable;
import oracle.ias.scheduler.JobContext;
import oracle.ias.scheduler.JobExecutionException;
public class BackupJob implements Executable {
public void execute(JobContext context) throws JobExecutionException {
// retrieve the source/destination directories
Job job = context.getJob();
String source = job.getProperties().getProperty("SourceDirectory");
String destination = job.getProperties().getProperty("DestinationDirectory");
// get the list of files to copy
File directory = new File(source);
File[] files = directory.listFiles();
// copy the files
Runtime runtime = Runtime.getRuntime();
Process process;
for (int x = 0; x < files.length; x++) {
try {
process = runtime.exec("/bin/cp " + files[x].toString() +
" " + destination);
process.waitFor();
} catch(IOException e) {
throw new RuntimeException("copy failed: "+files[x],e);
} catch(InterruptedException e) {
throw new RuntimeException("copy failed: "+files[x],e);
}
}
}
}
Notice the implementation provides a single method execute(). Implementation of this method fulfills the contract for implementing the oracle.ias.scheduler.Executable interface. This method is invoked every time the job is executed.
Once a job has been created it must be submitted to the scheduler for execution. A job is submitted using the add method provided by the scheduler. The following code fragment demonstrates how the job is submitted with properties.
// setup the properties
java.util.Properties properties = new Properties();
properties.put("SourceDirectory","/mnt/data");
properties.put("DestinationDirectory","/mnt/backup");
// submit the job
scheduler.add("file backup job, runs every week",
new BackupJob().getClass().getName(),
new Schedule(),
properties);
Notice that the job properties and schedule are specified when the job is submitted to the scheduler.
The Scheduler supports the notion of non-destructive cancellation. This involves notifying the job implementation, through a pre-defined interface, that an administrator wishes to cancel the job's execution (e.g., in order to shutdown the container). This provides the job with the opportunity to perform any house cleaning (e.g., release resources, etc..) prior to aborting execution. In order to participate in cancellation a job must provide an implementation of the oracle.ias.scheduler.Cancellable.
During testing the developer realized the job may run for long periods of time. Realizing this it became apparent there may be need for cancelling the job.
The following is the modified implementation that provides an implementation of both oracle.ias.scheduler.Executable and oracle.ias.scheduler.Cancellable. By providing an implementation of this interface the job is eligible to be cancelled during execution.
import java.io.File;
import java.io.IOException;
import oracle.ias.scheduler.Job;
import oracle.ias.scheduler.Executable;
import oracle.ias.scheduler.Cancellable;
import oracle.ias.scheduler.JobContext;
import oracle.ias.scheduler.JobCancellationException;
import oracle.ias.scheduler.JobExecutionException;
public class CancellableBackupJob implements Executable, Cancellable {
boolean m_cancelled = false;
public void cancel() {
m_cancelled = true;
}
public void execute(JobContext context) throws
JobExecutionException, JobCancellationException {
// retrieve the source/destination directories
Job job = context.getJob();
String source = job.getProperties().getProperty("SourceDirectory");
String destination = job.getProperties().getProperty("DestinationDirectory");
// get the list of files to copy
File directory = new File(source);
File[] files = directory.listFiles();
// copy the files
Runtime runtime = Runtime.getRuntime();
Process process;
for (int x = 0; x < files.length; x++) {
// cancelled?
if (m_cancelled) {
throw new JobCancellationException();
}
try {
process = runtime.exec("/bin/cp " + files[x].toString() +
" " + destination);
process.waitFor();
} catch(IOException e) {
throw new RuntimeException("copy failed: "+files[x],e);
} catch(InterruptedException e) {
throw new RuntimeException("copy failed: "+files[x],e);
}
}
}
}
The following modifications have been made to the implementation:
This method of cancellation is intentionally passive. Thus it is the application programmer's responsibility to react to the cancellation appropriately. At a minimum this means the following:
When designing and implementing a job keep the following in mind:
A schedule-driven job is a job that is associated with a schedule. Moreover, this indicates the job's execution is largely time-based. In contrast a job associated with a trigger is largely event-based and typically driven by events initiated by the application.
A single-action schedule refers to the class of schedules that have a single expiration. This kind of schedule should be used when a job requires a single timeout.
The class oracle.ias.scheduler.Schedule is used to specify this kind of schedule. This schedule provides the following attributes:
| Attribute | Description |
|---|---|
| expiration | The initial expiration of the schedule |
During testing the developer and administrator realized the need to execute the backup job on an as-needed basis. To do this a single-action schedule will be used. The following code fragment demonstrates how the job is submitted with a single-action schedule.
// setup the properties
java.util.Properties properties = new Properties();
properties.put("SourceDirectory","/mnt/data");
properties.put("DestinationDirectory","/mnt/backup");
// setup the schedule, single-action at midnight
Schedule schedule = new Schedule();
Calendar midnight = Calendar.getInstance();
midnight.set(Calendar.HOUR_OF_DAY,24);
midnight.set(Calendar.MINUTE,0);
midnight.set(Calendar.SECOND,0);
schedule.setExpiration(midnight);
// submit the job
scheduler.add("file backup job, runs at midnight",
new BackupJob().getClass().getName(),
schedule,
properties);
A repeating schedule refers to the class of schedules that are associated with multiple expirations. This kind of schedule should be used when a job requires repeating timeouts.
The class oracle.ias.scheduler.IntervalSchedule is used to specify this kind of schedule. This schedule provides the following attributes:
| Attribute | Description |
|---|---|
| expiration | The initial expiration of the schedule |
| interval | The interval (specified in milliseconds) between expirations |
| end date | The date and time at which the schedule ends |
// setup the properties
java.util.Properties properties = new Properties();
properties.put("SourceDirectory","/mnt/data");
properties.put("DestinationDirectory","/mnt/backup");
// setup the schedule, repeats every week
IntervalSchedule schedule = new IntervalSchedule();
schedule.setInterval(IntervalSchedule.EVERY_WEEK);
// submit the job
scheduler.add("file backup job, runs at midnight",
new BackupJob().getClass().getName(),
schedule,
properties);
This section introduces two new concepts: trigger and
notification. Notifications are messages sent from one
object to another notifying the recipient something interesting has
happened. The recipient of a notification is always a trigger. A
trigger describes a condition based on the receipt of one or more
notifications which, when evaluates to true, causes a job to
execute. Triggers are described by a logical expression where the
operands are job notification assertions. Notifications may be
generated in either of the following ways:
Likewise notifications may either be sent to a specific trigger or to a specified set of triggers. On receipt, however, triggers in turn do not generate notifications. By employing the use of triggers job's can be enabled to respond to application conditions. For example triggering a job's execution based on revenue exceeding a threshold.
When a job is defined a trigger is associated with the definition. A trigger determines when the job will execute. When a job's definition does not include a trigger definition a default trigger is provided. This default trigger fires when the associated schedule expires.
A trigger is used to specify the conditions by which the associated job is executed. A condition is expressed as a logical combination of operands. The following logical operators are supported:
Precedence may be specified using the parentheses characters ‘(‘ and ‘)’. Example expressions:
The operand in a condition is the name associated with the notifications sent using the Scheduler notify() method.
For example to send the notification DataHasArrived to all triggers the application would use the following code fragment:
Scheduler.notify(new Notification(“DataHasArrived”));
The scheduler evaluates triggers when the notification is sent. The result of a trigger evaluation is boolean. When the evaluation of the trigger is true the trigger fires resulting in the execution of the associated job. Once the trigger fires it is immediately reset before the job is executed. When a trigger is reset the record of all previously received notifications by the trigger are erased. If the trigger does not fire the notification is recorded by the trigger for later use.
For example a trigger has the following condition:
N1 && N2
Assume the trigger receives notification N1. Using the notification the trigger evaluates to false and the notification is recorded. Next the trigger receives notification N2. Using the notification the trigger evaluates to true and the trigger fires and the job is executed. After the job completes execution the trigger is reset.
Jobs may be associated with a schedule, trigger, or both a schedule and trigger. When a job is associated with a schedule only, an implicit trigger is associated with the job.
When the schedule expires a timeout notification is always sent to the associated trigger for processing. In the implicit case this results in the trigger firing, execution of the job, and finally the trigger is reset. More generally the timeout notification may also be used in a trigger expression along with other notifications.
Example expressions using the timeout notification:
The timeout notification may only be used in cases where the job is associated with both a schedule and a trigger. The notification name timeout is likewise reserved and may not be used or sent by an application to the scheduler. Additionally the timeout notification must be referenced in the condition expression of the trigger.
When employing the use of the not operator in trigger expressions
the following guidelines should be followed.
The job scheduler provides several kinds of logging semantics. They
are as follows:
Execution logging is specified as part of the job's definition. Because the scheduler uses the Java logging APIs log levels are specified using the log levels provided by the class java.util.logging.Level.
If the logging level is set to a value of Level.WARNING, log entries are written under the following conditions:
If the logging level is set to a value of Level.FINE the following additional information is written to the log:
If the log level is set to a value of Level.FINER the following additional information is written to the log:
If the log level is set to a value of Level.FINEST the following additional information is written to the log:
The same logging facilities used by execution logging is also available to the job implementation when it is executed. The logging context is available via the context that is passed to the job when it is executed via the JobContext object:
public interface JobContext extends Serializable {
public Job getJob();
public java.util.logging.Logger getLogger();
public java.util.logging.Logger getLogger(String resourceBundleName);
}
Either of the getLogger() methods can be used, the latter of which allows a resource bundle to be specified.
Our developer has decided to add logging capabilities to her job. In this example a message is logged for every file backed up.
Continuing from Example 4 an information log entry is written before every file is copied. This is done by retrieving the logger from the passed in job context and writing an informational log message prior to performing the copy command.
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import oracle.ias.scheduler.Job;
import oracle.ias.scheduler.Executable;
import oracle.ias.scheduler.Cancellable;
import oracle.ias.scheduler.JobContext;
import oracle.ias.scheduler.JobCancellationException;
import oracle.ias.scheduler.JobExecutionException;
public class CancellableBackupJobLogged implements Executable, Cancellable {
boolean m_cancelled = false;
public void cancel() {
m_cancelled = true;
}
public void execute(JobContext context) throws JobExecutionException, JobCancellationException {
// retrieve the source/destination directories
Job job = context.getJob();
Logger log = context.getLogger();
String source = job.getProperties().getProperty("SourceDirectory");
String destination = job.getProperties().getProperty("DestinationDirectory");
// get the list of files to copy
File directory = new File(source);
File[] files = directory.listFiles();
// copy the files
Runtime runtime = Runtime.getRuntime();
Process process;
for (int x = 0; x < files.length; x++) {
// cancelled?
if (m_cancelled) {
throw new JobCancellationException();
}
log.info("copying file "+files[x]);
try {
process = runtime.exec("/bin/cp " + files[x].toString() +
" " + destination);
process.waitFor();
} catch(IOException e) {
throw new RuntimeException("copy failed: "+files[x],e);
} catch(InterruptedException e) {
throw new RuntimeException("copy failed: "+files[x],e);
}
}
}
}
Execution audit records provide audit information for the execution of every submitted job. This information can be used to determine, for example, when the job execution occurred. An execution audit record, as defined by the class oracle.ias.scheduler.ExecuteRecord provides the following information:
| Attribute | Java Type | Description |
|---|---|---|
| Execution Start Time | java.util.Calendar | Time at which job execution started. |
| Execution End Time | java.util.Calendar | Time at which job execution ended. This value is null until the job execution has completed, signified by a status value of EXECUTION_INCOMPLETE. |
| Execution Type | int | The type associated with this job execution, one of TRIGGER_TYPE, REPLAY_TYPE, RETRY_TYPE as defined in the class oracle.ias.scheduler.ExecuteRecord. |
| Execution Status | int | Status of the job execution, one of EXECUTION_INCOMPLETE, EXECUTION_SUCCEEDED, EXECUTION_FAILED, EXECUTION_CANCELLED, EXECUTION_BLACKOUT, EXECUTION_THRESHOLD_EXCEEDED as defined in the class oracle.ias.scheduler.ExecuteRecord. |
| Exeception | java.lang.Throwable | If the status is EXECUTION_FAILED the associated exception which was the cause of the failure. |
Typically a job executes as a result of the associated trigger
firing either due to the associated schedule expiring or as a
result of a notification. The reason for execution is summarized by
the execution type field in the execute audit record. The
following table details the difference between the types of job
execution:
| Execution Type | Description |
|---|---|
| TRIGGER_TYPE | Execution occurred as a result of the trigger firing either through a) the associated schedule expiring or b) via a notification. |
| REPLAY_TYPE | A previously paused job was resumed with the replay flag set to true. Execution will result only if the trigger fired during the time the job was paused. |
| RETRY_TYPE | Execution occurred as a result of the immediately prior execution failing. Retry will occur if a non-zero retry value was specified when the job was submitted. |
The following table summarizes the difference between the status
types for a job execution:
| Status Type | Description |
|---|---|
| EXECUTION_INCOMPLETE | Execution has started but not yet completed. |
| EXECUTION_SUCCEEDED | Execution has completed and the result was success. |
| EXECUTION_FAILED | Execution has completed and resulted in a failure. |
| EXECUTION_CANCELLED | Execution had started and, prior to completing, was cancelled. |
| EXECUTION_BLACKOUT | Execution was supressed due to an active blackout window when execution was attempted. |
| EXECUTION_THRESHOLD_EXCEEDED | The time at which execution occurred exceeded the threshold specified when the job was submitted. |
As previously mentioned an audit record is created every time a job
executes and audit records are always stored in the job store. The
following Scheduler methods are used to manage audit records:
| Method Name | Description |
|---|---|
| getAuditRecords | Returns a collection view of all audit records for the job specified. |
| removeAuditRecords | Removes all audit records for the job specified. |