Scheduler


What is a Scheduler?

Apache Sling Scheduler enables you to easily schedule jobs within your application. Jobs can be executed at a specific time, regularly at a given period or at the time given by a cron expression by leveraging the Sling scheduler service.

Scheduling in CQ uses the open source Quartz library, part Sling Commons: ‘org.apache.sling.commons.scheduler.Scheduler’. It is used to execute jobs at predefined times either periodically or at a set time. It is based on the Quartz scheduling library and can therefore make use of its syntax for defining the execution times of jobs, making it possible to precisely indicate times .


How to Write a Scheduler?

The scheduler can be used in two ways,

  • by registering the job through the scheduler API and
  • by leveraging the whiteboard pattern that is supported by the scheduler.

In most cases the whiteboard pattern is preferred. 

I would be giving examples for both the scenarios which will help you give more clarity on Schedulers.


Sample Scheduler Templates:

1. WHITEBOARD PATTERN

@Component
@Service(value = Runnable.class)
@Property( name = "scheduler.expression", value = "0 * * * * ?")
public class ScheduledCronJob implements Runnable {
    /** Default log. */
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    public void run() {
        log.info("Executing a cron job (job#1) through the whiteboard pattern");
    }

}

  •  Fixed Periodic Scheduler
@Component
@Service(value = Runnable.class)
@Property( name = "scheduler.period", longValue = 10)
public class ScheduledPeriodicJob implements Runnable {

    /** Default log. */
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    public void run() {
        log.info("Executing a perodic job (job#2) through the whiteboard pattern");
    }
//
}

Sample Scheduler Template Example:

@Component(
        label = "Sample Sling Scheduler",
        description = "Sample Description",
        immediate = true,
        metatype = true
)

@Properties({
        @Property(
                label = "Enabled",
                description = "Enable/Disable the Scheduled Service",
                name = "service.enabled",
                boolValue = true
        ),
        @Property(
                label = "Cron expression defining when this Scheduled Service will run",
                description = "[every minute = 0 * * * * ?], [12:01am daily = 0 1 0 ? * *]",
                name = "scheduler.expression",
                value = "0 1 0 ? * *"
        ),
        @Property(
                label = "Allow concurrent executions",
                description = "Allow concurrent executions of this Scheduled Service",
                name = "scheduler.concurrent",
                boolValue = false
        ),
        @Property(
                label = "Vendor",
                name = "service.vendor",
                value = "SampleVendor",
                propertyPrivate = true
        )
})

@Service
public class SampleScheduledService implements Runnable {

   
    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    private final Logger log = LoggerFactory.getLogger(this.getClass());
   
    @Override
    public void run() {
      
        ResourceResolver adminResourceResolver = null;
        try {
            adminResourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);

            // Write your code logic here.
        } catch (LoginException ex) {
            java.util.logging.Logger.getLogger(SampleScheduledService.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            // ALWAYS close resolvers you open
            if (adminResourceResolver != null) {
                adminResourceResolver.close();
            }
        }
    }

    @Activate
    protected void activate(final ComponentContext componentContext) throws Exception {
        final Map<String, String> properties = (Map<String, String>) componentContext.getProperties();
    }

    @Deactivate
    protected void deactivate(ComponentContext ctx) {

    }
}

2. SCHEDULER API

A scheduler to schedule time/cron based jobs. A job is an object which is executed/fired by the scheduler. You will understand the difference when you see the code.

The below mentioned code implements a service that simultaneously executes the job based on 3 different kinds of scheduling can look as follows:

@Component
public class HelloWorldScheduledService {

    /** Default log. */
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    /** The scheduler for rescheduling jobs. */
    @Reference
    private Scheduler scheduler;


    protected void activate(ComponentContext componentContext) throws Exception {
        //case 1: with addJob() method: executes the job every minute
				String schedulingExpression = "0 * * * * ?";
				String jobName1 = "case1";
				Map<String, Serializable> config1 = new HashMap<String, Serializable>();
				boolean canRunConcurrently = true;
				final Runnable job1 = new Runnable() {
					public void run() {
						log.info("Executing job1");
					}
				};
				try {
					this.scheduler.addJob(jobName1, job1, config1, schedulingExpression, canRunConcurrently);
				} catch (Exception e) {
					job1.run();
				}

        //case 2: with addPeriodicJob(): executes the job every 3 minutes
				String jobName2 = "case2";
				long period = 180;
				Map<String, Serializable> config2 = new HashMap<String, Serializable>();
				final Runnable job2 = new Runnable() {
					public void run() {
						log.info("Executing job2");
					}
				};
				try {
					this.scheduler.addPeriodicJob(jobName2, job2, config2, period, canRunConcurrently);
				} catch (Exception e) {
					job2.run();
				}

        //case 3: with fireJobAt(): executes the job at a specific date (date of deployment + delay of 30 seconds)
				String jobName3 = "case3";
				final long delay = 30*1000;
				final Date fireDate = new Date();
				fireDate.setTime(System.currentTimeMillis() + delay);
				Map<String, Serializable> config3 = new HashMap<String, Serializable>();
				final Runnable job3 = new Runnable() {
					public void run() {
						log.info("Executing job3 at date: {} with a delay of: {} seconds", fireDate, delay/1000);
					}
				};
				try {
					this.scheduler.fireJobAt(jobName3, job3, config3, fireDate);
				} catch (Exception e) {
					job3.run();
				}
    }

    protected void deactivate(ComponentContext componentContext) {
        log.info("Deactivated, goodbye!");
    }

}

Here we are leveraging the underlying CQ scheduler instance which is using addJob(), addPeriodicJob() or fireJobAt() methods .

I would suggest to use approach 1 for creating your schedulers.  Happy Scheduling :)

Advertisements

14 thoughts on “Scheduler

  1. Hi,

    I have tried both approaches (method 1 and method 2). I didn’t see the log info in error.log file.

    I am using the bundle project to build the jar file to install in OSGI bundle.

    May i know is it automatically print out the
    log.info(“Executing a cron job (job#1) through the whiteboard pattern”); once the bundle is active?

    Else how to trigger this run()?

    Thank you. Appreciate your help.

    Like

      • Hi Hashim,

        Sorry for the late reply. Got stuck with project work. The issue was not with Scheduler configuration you mentioned but the dependencies weren’t getting injected and there were no error in log files. Fixed the issue. Thanks a lot for the artcle.

        Like

  2. when i am changing the scheduler cron expression from osgi console .The scheduler is not reading the new property value.i have to restart the component.is this a mandatory thing or do i need to make some changes.

    Like

  3. Hi,
    I have git source for qa and stage I have created scheduler and make it run in QA code then I promoted the code to Stage branch there I see my scheduler is not working. I am getting exception like Failure loooking up method bindRrFactory(org.osgi.framework.ServiceReference) in class class org.acs.cancer.core.scheduler.LocalAddressDetailUpdateScheduler. Assuming no such method. (java.lang.NoClassDefFoundError: javax/jcr/version/VersionException)
    java.lang.NoClassDefFoundError: javax/jcr/version/VersionException

    any help appreciated

    Like

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s