Table of Contents

Scheduled task with parameters

CzechIdM supports three types of the so-called Scheduled tasks - business logic execution tasks running asynchronously outside of the main thread. There are following types:

You can find description of each tasks type in the Task scheduler chapter.

Scheduled task

Simple scheduled tasks are the mostly used type of tasks. These are automatically registered in CzechIdM's scheduler implementation and are easily configurable from GUI. Following is an example of such task with parameters, which is supposed to read CSV from path passed as parameter and process it (not implemented for brevity purposes). Notice the `IMPORTANT` comments:

package eu.bcvsolutions.idm.tutorial.scheduler;
 
import ...;
 
// IMPORTANT 1
@Component
@Description("Parses input CSV (path as parameter) and assigns users roles from CSV. Only role assignment is allowed.")
// IMPORTANT 2
public class ImportCSVUserRolesTask extends AbstractSchedulableTaskExecutor<Boolean> {
 
	private static final Logger LOG = LoggerFactory.getLogger(ImportCSVUserRolesTask.class);
	private static final String PARAM_CSV_FILE_NAME = "rolesCSV";
	private static final String CSV_USERNAME_HEADER = "username";
	private static final String CSV_ROLE_HEADER = "role";
 
	private String csvPath;
 
	@Autowired private IdmIdentityService identityService;
	@Autowired private IdmRoleService roleService;
	@Autowired private IdmIdentityRoleService identityRoleService;
	@Autowired private IdmRoleRequestService roleRequestService;
	@Autowired private IdmConceptRoleRequestService conceptRoleRequestService;
	@Autowired private IdmIdentityContractService identityContractService;
 
        // IMPORTANT 3
	@Override
	public Boolean process() {
                // path to CSV is required, also check file exists and is readable
		if (csvPath == null) {
			throw new IllegalArgumentException("CSV path must be defined.");
		}
		//
                // TODO processing :)
                //
                return Boolean.TRUE;
	}
 
        // IMPORTANT 4
	@Override
	public List<String> getPropertyNames() {
		List<String> params = super.getPropertyNames();
		params.add(PARAM_CSV_FILE_NAME);
		return params;
	}
 
        // IMPORTANT 5
	@Override
	public void init(Map<String, Object> properties) {
		super.init(properties);
		csvPath = getParameterConverter().toString(properties, PARAM_CSV_FILE_NAME);
	}
}

While implementing scheduled tasks, don't forget those 5 important pieces marked as comments in the code above:

Stateful tasks

It is also easily possible to execute workflow from a stateful task. This is for example heavily used for personal processes, where workflows offer great flexibility for customer specific customization. Following is an example of such task, which executes a workflow.

package eu.bcvsolutions.idm.tutorial.scheduler;
 
import ...;
 
// IMPORTANT 1
@Service
@Description("My workflow process")
@DisallowConcurrentExecution
// IMPORTANT 2
public class MyWorkflowProcess extends AbstractWorkflowStatefulExecutor<IdmIdentityContractDto> {
 
	private static final String PROCESS_NAME = "myWorkflowProcess";
 
	@Autowired
	private IdmIdentityContractService identityContractService;
 
	/**
	 * Find all identity contracts, that are both valid and enabled.
	 */
        // IMPORTANT 3
	@Override
	public Page<IdmIdentityContractDto> getItemsToProcess(Pageable pageable) {
		IdentityContractFilter filter = new IdentityContractFilter();
		filter.setValid(Boolean.TRUE);
		filter.setDisabled(Boolean.FALSE);
		return identityContractService.find(filter, pageable);
	}
 
        // IMPORTANT 4
	@Override
	public String getWorkflowName() {
		return PROCESS_NAME;
	}
}

And following is the content of the Activiti workflow, which handles the business logic. There are always 3 input variables of the workflow:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.bcvsolutions.eu/testDeployAndRun">
  <process id="myWorkflowProcess" name="My workflow process - does something awesome!" isExecutable="true">
    <documentation>My workflow process
 
// IMPORTANT 5
Input:
    longRunningTaskId - UUID
    scheduledTaskId - UUID
    dto - IdmIdentityContractDto</documentation>
    <startEvent id="startevent1" name="Start"></startEvent>
    <endEvent id="endevent" name="End"></endEvent>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="scripttask1"></sequenceFlow>
    <scriptTask id="scripttask1" name="enable contract script" scriptFormat="groovy" activiti:autoStoreVariables="false">
      <script>//
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.bcvsolutions.idm.core.api.domain.OperationState;
import eu.bcvsolutions.idm.core.api.entity.OperationResult;
import eu.bcvsolutions.idm.core.scheduler.api.service.SchedulableStatefulExecutor;
 
Logger LOG = LoggerFactory.getLogger("myWorkflowProcess")
LOG.info("long running tID: [{}], scheduled tID: [{}], dto: [{}]", longRunningTaskId, scheduledTaskId, dto)
 
// TODO processing ... :)
 
def result = new OperationResult.Builder(OperationState.EXECUTED).build()
 
// IMPORTANT 6
execution.setVariable("success", OperationState.isSuccessful(result.getState()))
execution.setVariable(SchedulableStatefulExecutor.OPERATION_RESULT_VAR, result)
//
// process end
</script>
    </scriptTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow3" sourceRef="scripttask1" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow4" name="[FAILURE]" sourceRef="exclusivegateway1" targetRef="scripttask2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${success == false}]]></conditionExpression>
    </sequenceFlow>
    <scriptTask id="scripttask2" name="handle failure script" scriptFormat="groovy" activiti:autoStoreVariables="false">
      <script>println "FAILURE!"</script>
    </scriptTask>
    <sequenceFlow id="flow5" name="[SUCCESS]" sourceRef="exclusivegateway1" targetRef="scripttask3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${success == true}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="scripttask2" targetRef="endevent"></sequenceFlow>
    <sequenceFlow id="flow7" sourceRef="scripttask3" targetRef="endevent"></sequenceFlow>
    <scriptTask id="scripttask3" name="handle success script" scriptFormat="groovy" activiti:autoStoreVariables="false">
      <script>println "SUCCESS!"</script>
    </scriptTask>
  </process>
  <!-- BPMN 2.0 diagram elements -->
</definitions>

While implementing stateful tasks with workflows, don't forget those important pieces marked as comments in the code above:

Scheduled and stateful tasks are a large topic in CzechIdM, definitely have a look at the documentation of Task scheduler and also see few implemented tasks in the `core` module, for example: