I am trying to run an existing workflow from a Scheduled Task, it wouldn't start workflow and throws an exception:
java.lang.NullPointerException: null
...............
at org.apache.sling.commons.scheduler.impl.QuartzJobExecutor.execute(QuartzJobExecutor.java:349) [org.apache.sling.commons.scheduler:2.7.12]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [org.apache.sling.commons.scheduler:2.7.12]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
But if I copy codes to servlet with the same system user which has permission to run workflow, it works fine without any problems. Is there any trick to run workflow from Scheduled Task?
Thanks for your help.
Solved! Go to Solution.
Views
Replies
Total Likes
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.
@jamesc25111500 There could be multiple issues for this error . Out of which one could be that the the default thread pool the scheduler is using has just 5 threads. That means if all of these 5 threads are busy, no new thread is scheduled. On top: Other parts of the product are using this threadpool too.
Option 1: You create a dedicated Sling Pool (by OSGI configuration) of appropriate size, this at least separates your operation from the rest of the system
Option 2: disable concurrent execution (https://sling.apache.org/documentation/bundles/scheduler-service-commons-scheduler.html#preventing-c...), so that only 1 of these scheduled jobs runs at once, and not many.
Hi,
You need to get the resource resolver first in Schedule task using subservice . I think resourceresolver is null in your case.
paramMap.put(ResourceResolverFactory.SUBSERVICE, "readService"); resourceResolver = resourceFactory.getServiceResourceResolver(paramMap); WorkflowSession wfSession = (WorkflowSession) resourceResolver.adaptTo(WorkflowSession.class);
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.
Thanks for all inputs. After I changed input library from com.day.cq.workflow.* to com.adobe.granite.workflow.*, it works prefect now.
Views
Likes
Replies
Views
Likes
Replies