> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Execute the Java task

> How to implement Java logic for custom legacy workflow tasks in Atoti Limits, covering IWorkflowTaskActionDelegator, WorkflowTaskActionExecutionDTO, and overriding or extending the default delegator via ICustomWorkflowTaskActionService

## 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:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
public class WorkflowTaskActionExecutionDTO implements Serializable {

  private static final long serialVersionUID = 6548671478152737730L;

  /*
   *The name of the task for the custom action. This should map directly to the user task name in the BPMN process.
   * */
  @JsonProperty("taskKey")
  protected String taskName;

  /** The name of the action to execute. */
  protected String actionName;

  /**
   * The type of the objects to be processed when executing the action. Expected "INCIDENT" or
   * "LIMIT".
   */
  protected String workflowType;

  /**
   * The IDs of the objects to be processed when executing the action. We use an ArrayList to avoid
   * code smells on serialization.
   */
  @Builder.Default
  protected ArrayList<String> keys = new ArrayList<>();

  /** Variables to be set when executing the action. */
  @Builder.Default
  protected Map<String, String> taskVariables = new HashMap<>();

  /** Attachments to be uploaded when executing the action. */
  @Builder.Default
  protected List<MultipartFile> attachments = new ArrayList<>();
}
```

### The delegator

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

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
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 `taskName`.
The `retrieveWorkflowKey()` method is used to retrieve a map of all workflows by their name. The default implementation maps to the [limit workflow properties](../../../../user-ref/properties/property-files/application-yml#section-limit-workflow).

## Customize 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.

#### Override 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:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Component
@Slf4j
public class LogWorkflowTaskActionDelegator implements IWorkflowTaskActionDelegator {

    @Autowired
    protected LimitsProperties limitsProperties;

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

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

#### Append 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.

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
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:

  ```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
  @Slf4j
  @Service
  public class DefaultCustomWorkflowService implements ICustomWorkflowTaskActionService {
  <span style="color:#a6e22e">@Override</span>
  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">executeCustomTask</span><span style="color:#f92672">(</span>WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
      String message <span style="color:#f92672">=</span> String<span style="color:#f92672">.</span><span style="color:#a6e22e">format</span><span style="color:#f92672">(</span>
              <span style="color:#e6db74">&#34;No workflow found for taskName %s. If this is a custom workflow then please implement your own ICustomWorkflowService to handle it.&#34;</span><span style="color:#f92672">,</span>
              workflowTaskActionExecutionDTO<span style="color:#f92672">.</span><span style="color:#a6e22e">getTaskName</span><span style="color:#f92672">());</span>
      <span style="color:#66d9ef">throw</span> <span style="color:#66d9ef">new</span> ActiveViamRuntimeException<span style="color:#f92672">(</span>message<span style="color:#f92672">);</span>
  <span style="color:#f92672">}</span>

  }
  ```
</Note>

Here’s an example of a custom comment action:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@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 taskName = workflowTaskActionExecutionDTO.getTaskName();
            List<LimitsProcessInstance> limitsProcessInstanceS = getLimitsProcessInstances(workflowTaskActionExecutionDTO);
            if (taskName.equals(CUSTOM_KEY)) {
                executeCommentTask(limitsProcessInstanceS, workflowTaskActionExecutionDTO.getTaskVariables());
            } else {
                log.error("No custom task action configured for key {}", taskName);
            }
        } 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);

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

## Import the services

Once the appropriate components/services have been defined, [import them to the project](../..#importing-spring-beans-into-the-project) using a Spring configuration class.
