> ## 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 the Java backend logic that executes custom workflow task actions in Atoti Sign-Off, including the delegator pattern and appending to the default task handler.

## Overview

Once the task has been submitted on the Atoti Sign-Off UI, the details are sent to the Atoti Sign-Off 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"]}}
import java.io.Serializable;

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 DEFINITION or TASK
    protected String workflowType;
    // The object keys (process definition ID or task ID) for which to process the task action.
    protected List<String> keys = new ArrayList<>();
    // Optional workflow variables to provide in the DTO
    protected Map<String, Serializable> taskVariables = new HashMap<>();
}
```

where:

* the `taskKey` is used by the delegator to determine which task to execute.
* the `workflowType` indicates the type of workflow the action belongs to.
* the `keys` indicate the objects upon which the action 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 one method:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
/**
 * <b>IWorkflowTaskActionDelegator</b>
 *
 * <p>A delegator which accepts a {@link WorkflowTaskActionExecutionDTO}, which contains a {@code
 * taskKey}, and resolves that key to a task action.
 *
 * @author ActiveViam
 */
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 the payload to execute an action
     */
    void executeTaskAction(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) throws ValidationException;
}
```

The `executeTaskAction()` method accepts the `WorkflowTaskActionExecutionDTO` and delegates it to a task action based on its `taskKey`.

## 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 implemented in `DefaultWorkflowTaskActionDelegator`, which is imported in the starter.
Take a look to see how the tasks are delegated, but note that the methods of delegation are completely up to you.

#### Override the default delegator

To override the delegator, implement your own `IWorkflowTaskActionDelegator`.

Here’s an example of a simple delegator that 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 {

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

        if (workflowTaskActionExecutionDTO.getWorkflowType() == "MY_WORKFLOW_TYPE") {
            switch (workflowTaskActionExecutionDTO.getTaskKey()) {
                case "MY_ACTION_1":
                    // Execution for action 1 ...
                    break;
            }
        }
    }
}
```

You then may implement different logic for different combinations of workflow types and task keys retrieved from the `WorkflowTaskActionExecutionDTO` object.

#### Append to the default delegator

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

The `ICustomWorkflowTaskActionService` has one method that 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 currently 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 DefaultCustomWorkflowTaskActionService implements ICustomWorkflowTaskActionService {

      private final static String ERROR_MSG =
              "No workflow action found for taskKey %s. "
                      + "If this is a custom workflow action then please implement your own ICustomWorkflowTaskActionService to handle it.";

      @Override
      public UserTaskRecordDTO executeCustomTask(WorkflowTaskActionExecutionDTO workflowTaskActionExecutionDTO) {
          String message =
                  String.format(ERROR_MSG, workflowTaskActionExecutionDTO.getTaskKey());
          throw new ActiveViamRuntimeException(message);
      }
  }
  ```
</Note>

Here’s an example of a custom comment action:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
@Service
@RequiredArgsConstructor
@Slf4j
public class ExampleCustomWorkflowTaskActionService implements ICustomWorkflowTaskActionService{

    public static final String COMMENT_KEY = "CUSTOM COMMENT";

    protected ISignOffProcessInstanceWorkflowService signOffProcessInstanceWorkflowService;
    protected ISignOffProcessDefinitionWorkflowService signOffProcessDefinitionWorkflowService;

    @Override
    public List<AHistoryRecordDTO> executeCustomTask(WorkflowTaskActionExecutionDTO executionDTO) {
        List<AHistoryRecordDTO> historyRecordDTOS = new ArrayList<>();
        Map<String, String> variables = executionDTO.getTaskVariables();

        if (executionDTO.getTaskKey().equals(COMMENT_KEY)) {
            try {
                for (String key: executionDTO.getKeys()) {
                    historyRecordDTOS.add(executeCommentTask(key, variables, executionDTO.getWorkflowType()));
                }
            } catch (ValidationException e) {
                throw new ActiveViamRuntimeException(e);
            }
        } else {
            log.error("No custom task action configured for key {}", executionDTO.getTaskKey());
        }
        return historyRecordDTOS;
    }

    private UserTaskRecordDTO executeCommentTask(String key, Map<String, String> variables, String workflowType) throws ValidationException {
        UserTaskRecordDTO userTaskRecordDTO = null;
        switch (workflowType) {
            case DEFINITION_WORKFLOW_TYPE -> userTaskRecordDTO = commentOnDefinition(key, variables);
            case TASK_WORKFLOW_TYPE -> userTaskRecordDTO = commentOnTask(key, variables);
            default -> log.error("Unknown workflow type {}", workflowType);
        }
        return userTaskRecordDTO;
    }

    private UserTaskRecordDTO commentOnDefinition(String key, Map<String, String> workflowVariables) throws ValidationException {
        SignOffProcessDefinitionDTO definitionDTO = signOffProcessDefinitionWorkflowService.get(key);
        Map<String, Object> variables = new HashMap<>();
        variables.put("action", "comment");
        variables.putAll(workflowVariables);
        return signOffProcessDefinitionWorkflowService.taskStateTransition(key, definitionDTO, COMMENT_KEY, "Commenting", variables);
    }

    private UserTaskRecordDTO commentOnTask(String key, Map<String, String> workflowVariables) throws ValidationException {
        SignOffProcessInstanceDTO taskDTO = signOffProcessInstanceWorkflowService.get(key);
        Map<String, Object> variables = new HashMap<>();
        variables.put("action", "comment");
        variables.putAll(workflowVariables);
        return signOffProcessInstanceWorkflowService.taskStateTransition(key, taskDTO, COMMENT_KEY, "Commenting", variables);
    }
}
```

Note that unlike actions defined in the `DefaultWorkflowTaskActionDelegator.java` this implements a logic that fits both workflow types.

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