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
- Leveraging Cron Expression . (Read how to write your Cron script )
@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 :)
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.
LikeLike
Hi, Is your bundle in Active state ? What is the cron expression which you are using ?
LikeLike
Hi Hashim,
Thanks for your comment.
I’ve fixed the problem to re-create the maven project and run it again. The root cause might be the dependency issues in pom.xml.
LikeLike
Hi Hashim,
I followed the above steps and it worked, but if i do any code changes and do the deployment again, scheduler stops working.
LikeLike
Hi, What are the changes which you are making ? Is your bundle resolved after you deploy your changes ?
LikeLike
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.
LikeLike
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.
LikeLike
Do you have immediate=true defined in your schedular definition ? Check the logs if the schedular is restarting on edit of property.
LikeLike
Thanks for this. Are there any advantage on using the 2nd option? We keep using the 1st one.
LikeLike
Hi, The first method is the standard way to do these days. As declaring it as a Service is always beneficial wrt Component declaration. I just wanted to tell that both ways are possible.
LikeLiked by 1 person
Hi Hashim,
Can we do a scheduling without coding (implementing runnable service).
Example: Activate set of content every day night 9PM.
Thanks for all your comments.
LikeLike
Hi, Yes you can do so. Get a curl command of Tree Activation for replication and then write a shell script or a Jenkins jobs to schedule the curl command everyday at 9PM.
LikeLike
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
LikeLike
Hi,
Its difficult to debug the issue just by the description. Try to open AEM in debugging mode and check where is the issue. Check if qa and stage have same configurations for Schedular.
It could also be a bundle related issue https://forums.adobe.com/thread/2328066
Uninstall the bundle and deploy the package again on Stage to test. Add proper debugging logs.
LikeLike
Thanks Hashim for the article.If we use the first method and the server is restarted at the same time when the scheduler is about to run,would it queue it and run it post restart or will have to wait for the next scheduled time to run it?
( I assume the latter since these are not persisted but would request you to comment in case of any pointers on this.)Also what is the basis for preferring approach 1 over the other options?
LikeLike
Yes you are correct. It should be scheduled for the next time slot as per the Cron expression. The first is more preferred and widely used as it gives a clearer picture of the time slots of various cron jobs. And thereby giving us more control to distribute the jobs throughout the clock so that there is not much load on the system at one given time.
LikeLike
That’s really useful article Hashim.
We have scheduled the job to run at 00 hrs and we stopped the server while the job was already running as there was too much data to be processed. When we restated the server, the job started running again. Is there a way to stop it explicitly?
LikeLike
Hi Shelly,
Thanks. You should be able to stop the scheduler by Disabling the Component/Service from /system/console/components.
Other approach could be to use https://sling.apache.org/apidocs/sling7/org/apache/sling/commons/scheduler/Scheduler.html#unschedule-java.lang.String- , but I guess the first one would work.
LikeLike
Hi Hashim,
why do we write value=Runnable.Class or Servlet.class in Service annotation
@Service(value = Runnable.class)
what difference will it make if we dont write the value parameter.
LikeLike
Hi Harsha,
That’s a good question. It’s nice to understand why we write code in such a way.
The value parameter is used to define the name of the service interface provided by the component. If this property is not set explicitly then it will be generated for all the interfaces which that class implements. Generally, we want it to limit to 1 interface, hence it’s a norm to define it in value parameter.
Although things have changed now with the introduction of OSGi Declarative Services. You should read more about it http://www.nateyolles.com/blog/2017/05/osgi-declarative-services-annotations-in-aem
LikeLike
Hi Hashim,
Thanks for your article, i was trying 2nd approach where i am getting below warning for addJob(), addPeriodicJob() or fireJobAt() methods –
The method addPeriodicJob(String, Object, Map, long, boolean) from the type Scheduler is deprecated
any help on this would be appreciated
Thanks!
LikeLike
Hi, As per this document use the alternate methods for the deprecated ones
https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/org/apache/sling/commons/scheduler/Scheduler.html
LikeLike
Hi Hashim,
I need to call scheduler from another servlet.
here you have written runnable in same class. how can we call run method from another class file.
LikeLike
Hi,
If you want to use the methods defined in scheduler Class you can either create an instance of that Class. Or better approach would be to move the common methods used in Servlet and Scheduler to a Util Class and let both of them use the methods from the Util class by passing parameters. The second approach is cleaner.
LikeLike
Hi Hashim,
I have few question.
1)difference b/w felix annotation and osgi apache annotation
2)what are the way for scheduler configuration in system/console/configMgr like
@ property and @AttributeDefinition
i need more details about the above question.
3)Kindly provide some sample projects for convert html file to aem component.
LikeLike
Hi,
I think you mean to ask about the new OSGi R6 specifications. These links would give you a better picture. http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin.html
http://www.nateyolles.com/blog/2017/05/osgi-declarative-services-annotations-in-aem
For second question read this – http://blogs.adobe.com/experiencedelivers/experience-management/using-osgi-annotations-aem6-2/
Didn’t actually get what is your question.
LikeLike
Hi Hashim, I am new to AEM and wanted to understand few things about scheduler.
1. Is scheduler and workflow are same or there any difference. Can workflow can be developed without scheduler.
Regards
Shah
LikeLike
Scheduler is created for doing a process in a scheduled manner after a particular time period repeatedly. While workflow can be designed to get triggered on an event or manually.
Yes you should be able to create scheduler / workflow using the same Code logic Service class. Use that service class from both scheduler and workflow.
LikeLike