How to run workflow from Scheduled Task | Community
Skip to main content
Level 2
August 5, 2022
Solved

How to run workflow from Scheduled Task

  • August 5, 2022
  • 4 replies
  • 2771 views

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.

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by DEBAL_DAS

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.

4 replies

Jagadeesh_Prakash
Community Advisor
Community Advisor
August 6, 2022

@jam2021  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-concurrent-execution-1), so that only 1 of these scheduled jobs runs at once, and not many.

 

 

arunpatidar
Community Advisor
Community Advisor
August 6, 2022

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);

 

Arun Patidar
DEBAL_DAS
DEBAL_DASAccepted solution
New Member
August 6, 2022

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.

Debal Das, Senior AEM Consultant
Jam2021Author
Level 2
August 8, 2022

Thanks for all inputs. After I changed input library from com.day.cq.workflow.* to com.adobe.granite.workflow.*, it works prefect now.