ATG Scheduler

Many of the tasks we wanted to perform periodically. Scheduler is way to schedule all the task we wanted to run after certain period or at given time. For example : Remove cache every each hour or Send the promotional emails at each day.


To create a ATG Scheduler : 

   Component must define two properties - 

      scheduler=/atg/dynamo/service/Scheduler
      schedule= RelativeSchedule/PeriodicSchedule/CalendarSchedule

  • RelativeSchedule : in integer time-unit (e.g- in 5 min)
  • PeriodicSchedule : every integer time-unit (e.g- every 5 min) [ with catch up]
By Default Periodic Schedulers are without catch up. If we define with catch up. Scheduler will catch up all the missing job. 
For example - If job is schedule to run every 5 min. Job first run at 12:00. So next job execution will be 12:05, 12:10, 12:15. Lets assume job schedule at 12:10 took time of 7 min. So if scheduler has configured with catch up, 12:15 job will start at 12:17. If job is schedule without catch up, scheduler will escape 12:15 job and job will run at 12:20 next time. 
  • CalendarSchedule : calendar months dates days mo-occurs hrs mins                                                                             (calendar (0..11)(1..31)(1..7)(1..last)(0..23)(0..59))
           *  means all values for the parameter
           .   means no value for that parameter
           ,   means specific value 
           
           For example : calendar * . 2 1,last 14 5
           *            - for every month
           .             - for no specific date
           2            - for Monday
           1,last     - for 1st and last of the month
           14          - for 2PM
            5           - for 5th minute
            >> 1st and last Monday of every month, 2:05pm

   Java

  • Classes should implement atg.service.scheduler.Schedulable interface. 
  • Should extends GenericService to get notify of start and stop conditions.
  • Override doStartService and doStopService
  • Override performScheduledTask of Schedulable interface. 
   While creating schedule job we need to define ScheduleJob Thread Method

There are three Thread methods. Complete details from oracle doc - 
SCHEDULER_THREAD
The job is run in the Scheduler’s own thread. This is most efficient, but it blocks the scheduler for that job.Use only if jobs do not take long time to run. 
SEPARATE_THREAD
New thread is created each time to run the schedule job. Thread will be destroyed after completion. Useful when new job creating before finishing existing one.
REUSED_THREAD
A separate thread is created to handle the job.Thread will not destroy after completion, waiting for the next time the job is triggered. If the job is still running when it is scheduled to begin to run again, it does not start again until the next time around. 
           

Example to create a scheduler :



ATG ComponentTestJob.properties

         $class=com.test.TestJob
        $scope=global
        clientLockManager=/atg/dynamo/service/ClientLockManager
        lockTimeOut=2000
        scheduler=/atg/dynamo/service/Scheduler
         schedule=every 2 hours

ATG Class : TestJob.java


import atg.nucleus.GenericService;
import atg.service.scheduler.*;

public class TestJob extends GenericService implements Schedulable {

        String JOB_NAME = "TestJob";
        String JOB_DESCRIPTION = " Exaple to create a scheduler"
        private Scheduler scheduler;
        private Schedule schedule;
        private int jobId;

public void doStartService() throws ServiceException {
             ScheduledJob job = new ScheduledJob(JOB_NAME, JOB_DESCRIPTION, getAbsoluteName(), getSchedule(), this, ScheduledJob.SCHEDULER_THREAD);
             jobId = getScheduler().addScheduledJob(job);
 }

public void doStopService() throws ServiceException {
             getScheduler().removeScheduledJob(jobId);
 }

 public synchronized void performScheduledTask(Scheduler scheduler, ScheduledJob job) {
         // Add task that we want scheduler to perform.
         System.out.println("Test scheduler.");
 }

 public Schedule getSchedule() {  return schedule; }
 public Scheduler getScheduler() {  return scheduler; }
 public void setSchedule(Schedule schedule) {  this.schedule = schedule; }
 public void setScheduler(Scheduler scheduler) {  this.scheduler = scheduler; }

}

Same Schedulable Service on Multiple Servers


There are certain recurring tasks, that should be performed no more than once under any circumstances.
Example: scheduled order be placed once and only once, Periodic (batch) order fulfillment,pre-scheduled email delivery etc.

The typical approach is to configure Schedulable Service on only one Dynamo server within the site.
Example : By adding schedulers in only one server initial.properties

Disadvantage : Introducing a single point of failure into the system.
Like, If the server handling order placement goes down, orders are not placed.

SingletonSchedulableService,
is an abstract class that extends SchedulableService and implements the performScheduledTask, works in conjunction with a client lock manager to guarantee that only one instance of a scheduled service is running at any given time throughout a cluster of cooperating Dynamo servers.

SingletonSchedulableService is designed to work in situations where the job has some status indicator that tells whether or not it currently requires processing.

The code in performScheduledTask contacts the client lock manager and requests a write lock using the specified lock name and timeout. If a lock is obtained successfully, the SingletonSchedulableService checks to see whether it is a global write lock or a local write lock.

If a global write lock is returned the service can assume that it is the only instance running anywhere on the Dynamo cluster, at least if all instances of the service point to client lock managers than in turn point to the same server lock manager. In this case SingletonSchedulableService calls the doScheduledTask method to do whatever work the service does, then releases the global lock.

If a local write lock is returned it means that the client lock manager was unable to contact the server lock manager. The implementation then makes the pessimistic assumption that another instance of the service might be running, so it logs a debugging message, releases the local lock without doing any work, and goes back to sleep until the scheduler calls it again.

If the request to obtain a lock times out, the implementation assumes that another instance of the service is running and taking a long time about it, so it logs a debugging message and goes back to sleep until the scheduler calls it again.

Finally, if a DeadlockException is thrown, the implementation logs an error message and makes the pessimistic assumption that another instance of the service might be running, so it logs an error message and goes back to sleep.

Note: If the client lock manager’s useLockServer property is set to false, it means that global locking is disabled for that lock manager. In this case, SingletonSchedulableService accepts a local lock in place of the global lock, which at least ensures that only one instance of the service runs at a time on the current Dynamo server.