Sunday, January 14, 2018

How to define factory of OSGI schedulers(Dynamic Schedulers) in Adobe Experience Manager(AEM)

Defining Factory of OSGI schedulers(Dynamic Schedulers) in Adobe Experience Manager(AEM)

This post explains the approach to define factory of OSGI schedulers(Dynamic Schedulers) in Adobe Experience Manager(AEM)

Define a factory class to capture the configurations:

Define a component and enable as configuration factory(configurationFactory=true)
Configure all the required properties run the scheduler job.

Retrieve the properties through activate method and assign the values to class variable, define the getters to retrieve the property values in Job Class.

Specify the label to identify the configuration in OSGI console.

@Component(configurationFactory = true, policy = ConfigurationPolicy.OPTIONAL, metatype = true, immediate = true, label = "Scheduled Package Generator")
@Service(value = GeneratePackageSchedulerRequest.class)
public class GeneratePackageSchedulerRequest {

final Logger logger = LoggerFactory.getLogger(this.getClass());

@Property(unbounded=PropertyUnbounded.DEFAULT, label="Scheduler Expression", description="Scheduler Expression", value="")
private static final String SCHEDULER_EXPRESSION = "sheduleExpression";
private String sheduleExpression;

@Property(unbounded = PropertyUnbounded.ARRAY, label = "Packages Filter String", cardinality = 50, description = "Packages Filter String")
public static final String PACKAGE_FILTERS = "packageFilters";
private String[] packageFilters;

@Property(unbounded=PropertyUnbounded.DEFAULT, label="Package Name", description="Package Name")
private static final String PACKAGE_NAME = "packgeName";
private String packageName;

@Property(unbounded=PropertyUnbounded.DEFAULT, label="Root Path to store the package", description="Root Path")
private static final String ROOT_PATH = "rootpath";
private String rootPath;

protected void activate(final ComponentContext ctx) {

Dictionary<?, ?> props = ctx.getProperties();
sheduleExpression = PropertiesUtil.toString(props.get(SCHEDULER_EXPRESSION), "");
packageFilters = PropertiesUtil.toStringArray(props.get(PACKAGE_FILTERS), null);
packageName = PropertiesUtil.toString(props.get(PACKAGE_NAME), null);
rootPath=PropertiesUtil.toString(props.get(ROOT_PATH), null);

public String[] getPackageFilters() {
return packageFilters;

public String getJobname() {
return packageName;

public String getSheduleExpression() {
return sheduleExpression;

public String getRootPath() {
return rootPath;


Define a Job class to perform the required task with provided data:

@Component(immediate = true, metatype = true)
public class GeneratePackageScheduledTask {

private SlingRepository repository;

private SlingSettingsService settingsService;

private Scheduler  scheduler;

protected final Logger logger = LoggerFactory.getLogger(this.getClass());

@Reference(cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, referenceInterface = GeneratePackageSchedulerRequest.class, policy = ReferencePolicy.DYNAMIC)
private final List<GeneratePackageSchedulerRequest> providers = new LinkedList<GeneratePackageSchedulerRequest>();

protected void bindProviders(final GeneratePackageSchedulerRequest config) throws Exception {

final String schedulingExpression=config.getSheduleExpression();
final String jobname= config.getJobname();

final Runnable job = new Runnable() {

public void run() {

logger.debug("run() STARTS"+jobname);

if (isRunMode("author")&& isMasterRepository()) {// this can be removed if the Job can be executed in all nodes

try {

//define the Job code here

} catch (Exception rex) {
logger.error("Error occurred in Job execution..", rex.getMessage());

logger.debug("run() END"+jobname);


ScheduleOptions so = scheduler.EXPR(schedulingExpression);;
                so.canRunConcurrently(true);// change based on the Job configuration
this.scheduler.schedule(job, so);
logger.debug("Scheduled Job: " + config.getJobname()+" "+schedulingExpression);

protected void unbindProviders(final GeneratePackageSchedulerRequest config) {
logger.debug("Removed Job: " + config.getJobname());

private Boolean isRunMode(String mode) {
Set<String> runModes = settingsService.getRunModes();
for (String runMode : runModes) {
if (runMode.equalsIgnoreCase(mode)) {
logger.debug("Current Runmode is : " + runMode);
return true;
return false;

public boolean isMasterRepository(){
    final String isMaster = repository.getDescriptor("crx.cluster.master");
    return isMaster!=null && !isMaster.equals("") && Boolean.parseBoolean(isMaster);


Define bindProviders and unbindProviders methods

bindProviders  - this method will be invoked whenever new Factory configuration is created/modified with the input config object. Schedule the Job with input configurations, the Job will be scheduled based on the schedulingExpression.

unbindProviders  - this method will be invoked whenever the existing factory configuration is removed. unschedule the existing Job with the same Job name.

After deploying the bundle, login to OSGI configuration and search for Factory configuration - search through the label specified in the factory - Scheduled Package Generator


Define the individual configurations - the Job will be scheduled based on the configurations

The Jobs will be running based on the scheduled time, refer the log file for more details - better define a separate log file to capture scheduler logs

No comments:

Post a Comment