April 10, 2020
Estimated Post Reading Time ~

Schedulers in AEM

In simple terms, a scheduler is a service to schedule jobs. Thus if we want some work to happen at a particular time or at a regular interval, then Schedulers are the way to go.

In AEM, we can create scheduler in two ways -

  1. Whiteboard Pattern - In this, we create a Runnable thread to perform our task. This is similar to the Java Thread concept.
  2. Scheduler API - In this, we use Apache Commons' Scheduler API to perform our task. It uses an open-source Quartz library.
Both of these methods can be seen here. However, from AEM 6.2 onwards Felix SCR annotations are deprecated and are replaced with OSGi R6 annotations. Therefore, in this post, we will be discussing the clean way of creating schedulers that OSGi R6 annotations provide.

Scheduler in AEM

To create a scheduler in AEM, we will follow the below steps - 
  • Create an OSGi configuration to read the scheduler specific values from the user i.e. cron expression, the name of the scheduler, custom parameter etc.
  • Create a sling scheduler which displays the custom parameter at an interval specified by the cron expression.
Without making a further delay, let's dive into our code

Create an OSGi configuration

package org.redquark.demo.core.services;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

/**
 * @author Anirudh Sharma
 * 
 * This is the configuration class that takes properties for a scheduler to run
 */
@ObjectClassDefinition(name = "SlingSchedulerConfiguration", description = "Sling scheduler configuration")
public @interface SlingSchedulerConfiguration {

 /**
  * This method will return the name of the Scheduler
  * 
  * @return {@link String}
  */
 @AttributeDefinition(
  name = "Scheduler name",
  description = "Name of the scheduler",
  type = AttributeType.STRING)
 public String schdulerName() default "Custom Sling Scheduler Configuration";

 /**
  * This method will check if the scheduler is concurrent or not
  * 
  * @return {@link Boolean}
  */
 @AttributeDefinition(
  name = "Enabled",
  description = "True, if scheduler service is enabled",
  type = AttributeType.BOOLEAN)
 public boolean enabled() default false;

 /**
  * This method returns the Cron expression which will decide how the scheduler will run
  * 
  * @return {@link String}
  */
 @AttributeDefinition(
  name = "Cron Expression",
  description = "Cron expression used by the scheduler",
  type = AttributeType.STRING)
 public String cronExpression() default "0 * * * * ?";

 /**
  * This method returns a custom parameter just to show case the functionality
  * 
  * @return {@link String}
  */
 @AttributeDefinition(
  name = "Custom Parameter",
  description = "Custom parameter to be displayed by the scheduler",
  type = AttributeType.STRING)
 public String customParameter() default "AEM Scheduler Demo";
}

Here we are getting configuration properties for the Sling Scheduler.

Create Scheduler

Now we are going to create a sling scheduler. Create a class named CustomScheduler and paste the following code in it.

package org.redquark.demo.core.schedulers;

import org.apache.sling.commons.scheduler.ScheduleOptions;
import org.apache.sling.commons.scheduler.Scheduler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
import org.redquark.demo.core.services.SlingSchedulerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Anirudh Sharma
 * 
 * A Sling Scheduler demo using OSGi R6 annotations
 *
 */
@Component(immediate = true, service = CustomScheduler.class)
@Designate(ocd = SlingSchedulerConfiguration.class)
public class CustomScheduler implements Runnable {

 /**
  * Logger
  */
 private static final Logger log = LoggerFactory.getLogger(CustomScheduler.class);

 /**
  * Custom parameter that is to be read from the configuration
  */
 private String customParameter;

 /**
  * Id of the scheduler based on its name
  */
 private int schedulerId;

 /**
  * Scheduler instance injected
  */
 @Reference
 private Scheduler scheduler;

 /**
  * Activate method to initialize stuff
  * 
  * @param config
  */
 @Activate
 protected void activate(SlingSchedulerConfiguration config) {

  /**
   * Getting the scheduler id
   */
  schedulerId = config.schdulerName().hashCode();

  /**
   * Getting the custom parameter
   */
  customParameter = config.customParameter();
 }

 /**
  * Modifies the scheduler id on modification
  * 
  * @param config
  */
 @Modified
 protected void modified(SlingSchedulerConfiguration config) {

  /**
   * Removing the scheduler
   */
  removeScheduler();

  /**
   * Updating the scheduler id
   */
  schedulerId = config.schdulerName().hashCode();

  /**
   * Again adding the scheduler
   */
  addScheduler(config);
 }

 /**
  * This method deactivates the scheduler and removes it
  * @param config
  */
 @Deactivate
 protected void deactivate(SlingSchedulerConfiguration config) {

  /**
   * Removing the scheduler
   */
  removeScheduler();
 }

 /**
  * This method removes the scheduler
  */
 private void removeScheduler() {

  log.info("Removing scheduler: {}", schedulerId);

  /**
   * Unscheduling/removing the scheduler
   */
  scheduler.unschedule(String.valueOf(schedulerId));
 }

 /**
  * This method adds the scheduler
  * 
  * @param config
  */
 private void addScheduler(SlingSchedulerConfiguration config) {

  /**
   * Check if the scheduler is enabled
   */
  if (config.enabled()) {

   /**
    * Scheduler option takes the cron expression as a parameter and run accordingly
    */
   ScheduleOptions scheduleOptions = scheduler.EXPR(config.cronExpression());

   /**
    * Adding some parameters
    */
   scheduleOptions.name(config.schdulerName());
   scheduleOptions.canRunConcurrently(false);

   /**
    * Scheduling the job
    */
   scheduler.schedule(this, scheduleOptions);

   log.info("Scheduler added");

  } else {

   log.info("Scheduler is disabled");

  }
 }

 /**
  * Overridden run method to execute Job
  */
 @Override
 public void run() {

  log.info("Custom Scheduler is now running using the passed custom paratmeter, customParameter {}", customParameter);

 }

}
  • Let us understand the code step-by-step. First, we are registering the class as a service and implementing the Runnable interface. At the same time, using @Desginate annotation, we are linking the OSGi configuration created in the previous section with this class.
  • Now, we are injecting the org.apache.sling.commons.scheduler.Scheduler dependency.
  • In the activate() method, we are reading the required values. Then we are getting the schedulerId from the scheduler name.
  • The modified() method recalculates the schedulerId in case the OSGi configuration is modified.
  • In the addScheduler() method, we are registering the scheduler using the Scheduler API.
  • The run() method will be defining our task. Here we are just printing the customParameter in the logs.

Configuring OSGi

  • Go to ./system/console/configMgr and search for SlingSchedulerConfiguration and configure it like the below screenshot.



OSGi Configuration
  • Save the configuration.

Result

Now go to your project-specific log (my log file is - project-demoproject.log) and look for the traces, you will find like below

org.redquark.demo.core.schedulers.CustomScheduler Scheduler added

org.redquark.demo.core.schedulers.CustomScheduler Custom Scheduler is now running using the passed custom parameter, customParameter AEM Scheduler Demo

org.redquark.demo.core.schedulers.CustomScheduler Custom Scheduler is now running using the passed custom parameter, customParameter AEM Scheduler Demo

org.redquark.demo.core.schedulers.CustomScheduler Removing scheduler: -1760311957

org.redquark.demo.core.schedulers.CustomScheduler Scheduler added

org.redquark.demo.core.schedulers.CustomScheduler Custom Scheduler is now running using the passed custom parameter, customParameter AEM Scheduler Demo

org.redquark.demo.core.schedulers.CustomScheduler Custom Scheduler is now running using the passed custom parameter, customParameter AEM Scheduler Demo



By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.