In ATG, a scheduled service is a task that executes at a certain time, or on a set interval. ATG applications use many scheduled jobs under the hood. When writing an application, most scheduled services end up in the integration layer for something like:
- Generating a product catalog export on a daily basis,
- Sending orders to the fulfillment system,
- Processing payments,
- Loading data from external systems.
Many jobs end up in the integration layer of the application because they usually deal with the movement of data from one system to another on a scheduled basis.
Hint: To see a list of scheduled jobs in your application, navigate to the /atg/dynamo/service/ Scheduler component in dynamo admin.
Becoming Familiar with the API
The Schedulable Interface
All scheduled jobs implement the atg.service.scheduler.Schedulable interface, which defines one method:
void performScheduledTask(Scheduler pScheduler, ScheduledJob pJob)
Unlesss there’s a good reason, it’s best not to muck with this interface. You have to do some non-intuitive work to get the scheduled job to actually work, so it’s more of a pain.
Out of the Box Implementations of the Schedulable Interface
ScedulableService
Out of the box, ATG provides one generic schedulable implementation called atg.service.scheduler.SchedulableService that implements the Schedulable interface in a coherent way. In their words: “This base class is a simple implementation of Schedulable that is suitable for most services that want to implement Schedulable”. Classes only need to set some configurations in properties files, and also implement:
void performScheduledTask(Scheduler pScheduler, ScheduledJob pJob)
SingletonSchedulableService
This is the other ATG implmementation of Schedulable that’s worth mentioning. This class extends SchedulableService by adding in the ability for multiple servers to run the same job.
Why is that important? It allows the same scheduled job to be run from many servers, but only one server can execute the job at a time. For very important tasks that need to run, you could schedule them on multiple servers using this class. If one server goes down, the other servers can still complete the job. If the job was only an extension of SchedulableService, it couldn’t be scheduled across multiple servers because they’d all execute the job at the same time, rather than communicating with the client lock manager to decide which single instance gets to execute the job.
Writing a Sample Scheduled Service
Now that we’re familiar with the API, we can start writing the a sample scheduled service. For most scheduled services, extending atg.service.scheduler.SchedulableService is a fine choice.
1) Create a new class extending atg.service.scheduler.SchedulableService
2) Implement: performScheduledTask(Scheduler pScheduler, ScheduledJob pJob)
You do not need to deal with the passed in parameters unless you need meta information about the scheduled job (like the next time it’s going to run, or how many times it’s been run). Since SchedulableService extends GenericService, you will have all the functionality of Nucleus at your fingertips. Here’s the sample job I wrote:
package com.sparkred.scheduled; import atg.service.scheduler.SchedulableService; import atg.service.scheduler.ScheduledJob; import atg.service.scheduler.Scheduler;
public class SampleScheduledJob extends SchedulableService {
public void performScheduledTask(Scheduler pScheduler, ScheduledJob pScheduledJob) {
} }
if(isLoggingDebug()) { logDebug("Sample scheduled job is being run.");
}
// do stuff
}
}
3) Create a component with the necessary configuration:
# /com/sparkred/scheduled/SampleScheduledJob
$class=com.sparkred.scheduled.SampleScheduledJob $scope=global
schedule=every 15 seconds # separate thread
threadMethod=2
scheduler=/atg/dynamo/service/Scheduler
loggingDebug=true
jobName=Sample Scheduled Job jobDescription=Logs a message and returns
Summary of the configurable properties:
Property |
Required |
Notes |
jobId |
Y |
Do NOT set this in the properties file, the Scheduler is responsible for setting this. |
jobName |
N |
Used only in user interfaces to describe the job |
jobDescription |
N |
Used only in user interfaces to describe the job |
scheduler |
Y |
This should almost always be /atg/dynamo/service/Scheduler |
schedule |
Y |
See below |
threadMethod |
Y |
See below |
The ScheduledJob javadocs have a great summary of these properties.
Setting the schedule
The schedule is a specially formatted string that tells ATG how often to run the job.
CalendarSchedule
The calendar schedule is used for scheduling a job at a precise time. It is very powerful because you can schedule a job at say, each Monday and Wednesday in November at 2:30 AM. A calendar schedule is more often used for jobs that happen daily or semi-daily, where the clock timing of the job is very important. For example, jobs that run only once a day might have to be run early in the morning when traffic on the site is very low.
PeriodicSchedule & RelativeSchedule
These are used for jobs that are time-insensitive, meaning that it doesn’t matter what specific time the job is run, just as long as it is run at a certain interval. Typically shorter-term jobs will use this type of scheduling. A job that executes every 5 minutes could have a calendar schedule (like at every 0 and 5 minute mark), but it would make more sense not to tie it to a clock and just let it run on a fixed interval.
See the documentation for a good discussion on the format of these properties.
Setting the threadMethod
The thread method is another important property because it has performance implications on the application if set incorrectly. There are three values that it can be set to:
1, SCHEDULER_THREAD
2, SEPARATE_THREAD
3, REUSE_THREAD
The documentation gives a good overview of the implications of different thread methods. For the example scheduled service, I’ve opted for using a separate thread, but it really doesn’t matter for such an elementary job.
Note: be sure to use the integer values for the thread type, rather than the constant values. The documentation is unclear about this detail.
4) Initialize the component at server startup
If the sample scheduled component that we’re created was built and deployed, it would not work because the component has no way to be initialized or started up. I’ve made this mistake too many times.
To fix this, add the component into the /atg/Initial component*:
initialServices+= /com/spakred/scheduled/SampleScheduledJob
*For all initialServices configured in this component, each is started up. /atg/Initial is an out of the box component, but there will be other custom and out of the box Initial components spread throughout your application.
Demoing our Scheduled Service
Build the java code and configurations into an existing ATG application and startup ATG. You’ll notice our job immediately starts running:
**** debug Sat Jun 21 13:06:32 PDT 2014 1403381192631 /com/ sparkred/scheduled/SampleScheduledJob starting scheduled job: Sample Scheduled Job
**** debug Sat Jun 21 13:06:32 PDT 2014 1403381192633 /com/ sparkred/scheduled/SampleScheduledJob Sample scheduled job is being run.
… …
We can also observe the job registered in the /atg/dynamo/service/Scheduler component from dyn/admin:
Closing
Scheduled jobs are a core part of the ATG API and everyone should know how to write and understand them. I’ve given a simple example to get started, however the documentation should be read and understood entirely before commiting anything.
Sebastiano, you’re very correct – I should have included that flag. I’m surprised something like this was never in the original API. I was trying to condense scheduled services down into an intro-level post, and this definitely slipped my mind.
Good overview Peter, but in my opinion you’ve omitted something which should be mandatory in a scheduled service implementation: a flag (let’s call it “enabled”) to be checked at the very beginning of the performScheduledTask() method, to let operations people disable the service execution if it proves problematic.