Here is my sample code -
/**
*
*/
package com.aem.demo.core.services;
import org.apache.sling.api.resource.ResourceResolver;
/**
* @author debal
*
* This service will be used as a utility and it will help us to get
* resource resolver object , JCR session and close resource resolver
*
*/
public interface JcrUtility {
public ResourceResolver getResourceResolver();
public void closeResourceResolver(ResourceResolver resourceResolver);
}
Implementation class [OSGi component] -
/**
*
*/
package com.aem.demo.core.services.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.demo.core.services.JcrUtility;
/**
* @author debal
*
*/
@Component(service = JcrUtility.class, immediate = true)
public class JcrUtilityImpl implements JcrUtility {
private final Logger logger = LoggerFactory.getLogger(JcrUtilityImpl.class);
@Reference
ResourceResolverFactory resourceResolverFactory;
@Override
public ResourceResolver getResourceResolver() {
Map<String, Object> map = new HashMap<String, Object>();
map.put(resourceResolverFactory.SUBSERVICE, "readWriteService");
ResourceResolver serviceResourceResolver = null;
try {
serviceResourceResolver = resourceResolverFactory.getServiceResourceResolver(map);
} catch (LoginException e) {
logger.error("Could not get service user [ {} ]", "demoSystemUser", e.getMessage());
}
return serviceResourceResolver;
}
@Override
public void closeResourceResolver(ResourceResolver resourceResolver) {
if (Objects.nonNull(resourceResolver)) {
resourceResolver.close();
}
}
}
Bundle symbolic name , subservice name and system user mapping as shown below -

OSGi configuration associates with Workflow Scheduler -
package com.aem.demo.core.configurations;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@ObjectClassDefinition(name = "Workflow Scheduler Configuration", description = "Workflow Scheduler Configuration")
public @interface WorkflowSchedulerConfiguration {
/**
* 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 "Workflow Scheduler configuration";
/**
* This method will set flag to enable the scheduler
*
* @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 the Webpage Path
*
*
* @return {@link String}
*/
@AttributeDefinition(name = "Webpage Path", description = "Webpage Path", type = AttributeType.STRING)
public String pagePath() default "/content/we-retail/language-masters/en";
@AttributeDefinition(name = "Workflow Model", description = "Workflow Model", type = AttributeType.STRING)
public String model() default "";
}
Scheduler code -
package com.aem.demo.core.schedulers;
import org.apache.sling.api.resource.ResourceResolver;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.model.WorkflowModel;
import com.aem.demo.core.configurations.WorkflowSchedulerConfiguration;
import com.aem.demo.core.services.JcrUtility;
import com.google.common.base.Strings;
@Component(service = Runnable.class, immediate = true)
@Designate(ocd = WorkflowSchedulerConfiguration.class)
public class WorkflowScheduler implements Runnable {
private final Logger logger = LoggerFactory.getLogger(WorkflowScheduler.class);
@Reference
JcrUtility jcrUtility;
@Reference
Scheduler scheduler;
private String webpagePath;
private String schedulerName;
private String model;
@Activate
private void activate(WorkflowSchedulerConfiguration configuration) {
this.webpagePath = configuration.pagePath();
this.schedulerName = configuration.schdulerName();
logger.info("**** Workflow Scheduler ****");
// This scheduler will continue to run automatically even after the server
// reboot, otherwise the scheduled tasks will stop running after the server
// reboot.
addScheduler(configuration);
}
@Modified
protected void modified(WorkflowSchedulerConfiguration configuration) {
// Remove the scheduler registered with old configuration
removeScheduler(configuration);
webpagePath = configuration.pagePath();
model = configuration.model();
// Add the scheduler registered with new configuration
addScheduler(configuration);
}
private void addScheduler(WorkflowSchedulerConfiguration configuration) {
boolean enabled = configuration.enabled();
if (enabled) {
ScheduleOptions scheduleOptions = scheduler.EXPR(configuration.cronExpression());
if (!Strings.isNullOrEmpty(schedulerName)) {
scheduleOptions.name(schedulerName);
scheduleOptions.canRunConcurrently(false);
scheduler.schedule(this, scheduleOptions);
logger.info("****** Workflow Scheduler has been added successfully ******");
}
} else {
logger.info("****** Workflow Scheduler is in disable state ******");
}
}
@Deactivate
protected void deactivated(WorkflowSchedulerConfiguration configuration) {
logger.info("**** Removing Workflow Scheduler Successfully on deactivation ****");
removeScheduler(configuration);
}
private void removeScheduler(WorkflowSchedulerConfiguration configuration) {
logger.info("**** Removing Workflow Scheduler Successfully **** {}", schedulerName);
scheduler.unschedule(schedulerName);
}
@Override
public void run() {
ResourceResolver resourceResolver = jcrUtility.getResourceResolver();
WorkflowSession workflowSession = resourceResolver.adaptTo(WorkflowSession.class);
try {
WorkflowModel workflowModel = workflowSession.getModel(model);
WorkflowData workflowData = workflowSession.newWorkflowData("JCR_PATH", webpagePath);
workflowSession.startWorkflow(workflowModel, workflowData);
logger.info("******Workflow Scheduler has been started ******{}", workflowModel.getTitle());
} catch (WorkflowException e) {
e.printStackTrace();
}
finally {
jcrUtility.closeResourceResolver(resourceResolver);
}
}
}
Scheduler OSGi configuration at /system/console/configMgr -

Scheduler has been trigerred at configured time [captured in log file]-
06.08.2022 17:37:00.225 *INFO* [sling-default-4-Workflow Scheduler configuration] com.aem.demo.core.schedulers.WorkflowScheduler ******Workflow Scheduler has been started ******AEM Page Lock
Hope this will help.