Executing the Java Task

Overview

Once the task has been submitted on the Atoti Limits UI, the details are sent to the Atoti Limits server, where the action is completed by the IWorkflowTaskActionDelegator. This delegator accepts the submitted WorkflowTaskActionExecutionDTO, then delegates and executes the task as required.

The Submitted DTO

The WorkflowTaskActionExecutionDTO object looks as follows:

public class WorkflowTaskActionExecutionDTO implements Serializable {
	private static final long serialVersionUID = 6548671478152737730L;

    // The key of the task to be executed
	protected String taskKey; 
    // The type of workflow. Either INCIDENT or LIMIT
	protected String workflowType;
	// The object keys (limitIds or incidentIds) for which to process the task action.
	protected ArrayList<Integer> keys;
	// Optional workflow variables to provide in the DTO
	protected Map<String, String> taskVariables;
}

where:

  • the taskKey is used by the delegator to determine which task to execute
  • the workflowType indicates the type of workflow for which the task exists
  • the keys indicate the objects upon which the task is executed
  • the taskVariables store optional task variables to be handled by the backend and are the result of the inputs to the WorkflowTaskActionInputFieldDTO

The Delegator

The IWorkflowTaskActionDelegator is a simple delegator that accepts a WorkflowTaskActionExecutionDTO and delegates the work to a backend service. It contains two methods:

public interface IWorkflowTaskActionDelegator {
		
	/**
	 * This initiates a workflow task action related to the button the user selected. This function will have to be updated for every new button.
	 * 
	 * @param workflowTaskActionExecutionDTO
	 * @throws ValidationException
	 * @throws Exception 
	 * @throws AccessDeniedException 
	 */
	void executeTaskActionForTaskActionKey(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) throws ValidationException, AccessDeniedException, Exception;
	
	/**
	 * A map of workflow processes to a user specified workflow name.
	 * 
	 * Ex. 
	 * 	{ 
	 * 		StraightThrough: "limit-process-instance.straight-through",
	 * 		FourEyes: "limit-process-instance.four-eyes",
	 * 		Exception: "limit-process-instance.exception"
	 *  }
	 * @return
	 */
	Map<String, String> retrieveWorkflowKey();
	
}

The executeTaskActionForTaskActionKey() method accepts the WorkflowTaskActionExecutionDTO and delegates it to a task action based on its taskKey. The retrieveWorkflowKey() method is used to retrieve a map of all workflows by their name. The default implementation maps to the limit workflow properties.

Customizing the Java tasks

The delegator has a custom implementation defined. This can be overridden if you’re not using the out-of-the-box workflows. Alternatively, you can append custom actions to the default implementation by implementing the ICustomWorkflowTaskActionService.

The Default Delegator

The default delegator is defined in the starter’s DefaultWorkflowTaskActionDelegator. Take a look to see how the tasks are delegated, but note that the methods of delegation are completely optional to you.

Overriding the default delegator

To override the delegator, implement your own IWorkflowTaskActionDelegator.

Here’s an example of a simple delegator that only logs the action that is executed:

@Component
@Slf4j
public class LogWorkflowTaskActionDelegator implements IWorkflowTaskActionDelegator {

    @Autowired
    protected LimitsProperties limitsProperties;

    @Override
    public void executeTaskActionForTaskActionKey(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO)
            throws ValidationException, AccessDeniedException, Exception {
        log.info("Executing task for key {}", workflowTaskActionExecutionDTO.getTaskKey());
    }

    @Override
    public Map<String, String> retrieveWorkflowKey() {
        return limitsProperties.getWorkflowTypes();
    }
}

Appending to the default delegator

To append to the default delegator, add an implementation of ICustomWorkflowTaskActionService.

The ICustomWorkflowTaskActionService has one method which needs to be overridden: executeCustomTask(). This method is responsible for managing how the task is executed in the backend.

public interface ICustomWorkflowTaskActionService {

    /**
     * @param workflowTaskActionExecutionDTO - the custom workflow task action to execute
     */
    void executeCustomTask(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO);
}

note

If a task key is provided that is not handled, it will eventually be routed to the ICustomWorkflowTaskActionService and the default implementation in the starter will throw an exception:

@Slf4j
@Service
public class DefaultCustomWorkflowService implements ICustomWorkflowTaskActionService {

    @Override
    public void executeCustomTask(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) {
        String message = String.format(
                "No workflow found for taskKey %s. If this is a custom workflow then please implement your own ICustomWorkflowService to handle it.",
                workflowTaskActionExecutionDTO.getTaskKey());
        throw new ActiveViamRuntimeException(message);
    }
}

Here’s an example of a custom comment action:

@Slf4j
@Service
public class CustomCommentWorkflowTaskActionService implements ICustomWorkflowTaskActionService {

    protected static final String COMMENT_KEY = "CUSTOM_COMMENT";

    @Autowired
    protected ILimitsProcessInstanceWorkflowService limitsProcessInstanceWorkflowService;

    @Override
    public void executeCustomTask(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) {
        try {
            String taskKey = workflowTaskActionExecutionDTO.getTaskKey();
            List<LimitsProcessInstance> limitsProcessInstanceS = getLimitsProcessInstances(workflowTaskActionExecutionDTO);
            if (taskKey.equals(CUSTOM_KEY)) {
                executeCommentTask(limitsProcessInstanceS, workflowTaskActionExecutionDTO.getTaskVariables());
            } else {
                log.error("No custom task action configured for key {}", taskKey);
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    protected List<LimitsProcessInstance> getLimitsProcessInstances(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) {
        List<LimitsProcessInstance> limitsProcessInstances = new ArrayList<>();
        LimitsProcessInstanceType limitsProcessInstanceType = getLimitsProcessInstanceType(workflowTaskActionExecutionDTO);
        for (Integer objectKey : workflowTaskActionExecutionDTO.getKeys()) {
            LimitsProcessInstanceKey limitsProcessInstanceKey = new LimitsProcessInstanceKey(objectKey, limitsProcessInstanceType);
            limitsProcessInstances.add(limitsProcessInstanceWorkflowService.get(limitsProcessInstanceKey.getKeyString()));
        }
        return limitsProcessInstances;
    }

    protected LimitsProcessInstanceType getLimitsProcessInstanceType(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) {
        String workflowType = workflowTaskActionExecutionDTO.getWorkflowType();
        switch (workflowType) {
            // Limit Workflow
            case "LIMIT":
                return CREATION;
            // Incident Workflow
            case "INCIDENT":
                return EXCEPTION;
            default:
                throw new ActiveViamRuntimeException("Workflow type (" + workflowType + ") does not exist!");
        }
    }

    protected void executeCommentTask(List<LimitsProcessInstance> limitsProcessInstances, Map<String, String> workflowVariables) throws ValidationException {
        Map<String, Object> variables = new HashMap<>();
        variables.put("action", "comment");
        variables.putAll(workflowVariables);
        for (LimitsProcessInstance limitsProcessInstance : limitsProcessInstances) {
            // Submit the task to Activiti and retrieve the history record
            UserTaskRecordDTO historyRecordDto = limitsProcessInstanceWorkflowService.taskStateTransition(limitsProcessInstance.getKey(),
                    limitsProcessInstance,
                    COMMENT_KEY,
                    "N/A",
                    variables);

            String status = historyRecordDto.getStatus();
            // Update the incident in the datastore
            limitsProcessInstanceWorkflowService.transitIncidentWorkflowStatus(limitsProcessInstance, status);
        }
    }
}

Importing the Services

Once the appropriate components/services have been defined, import them to the project using a Spring configuration class.