Human Tasks in Detail¶
Human tasks represent activities that require human judgment, approval, or interaction. They are a core component in business processes that need human involvement. The Aletyx Enterprise Build of Kogito and Drools provides robust human task management capabilities through its task service APIs.
Human Task Lifecycle¶
Human tasks in the Aletyx Enterprise Build of Kogito and Drools follow a defined lifecycle with well-established states and transitions.
Core Task States¶
When a process execution reaches a user task node, the task enters the following states as it progresses:
- Created: Initial state when the task is instantiated
- Ready: The task is available to be worked on by potential owners
- Reserved/Claimed: The task has been claimed by a specific user
- In Progress: Work on the task has begun
- Completed: The task has been successfully finished
- Failed: The task could not be completed successfully
- Error: The task encountered an error during execution
- Exited: The task was ended before normal completion
- Obsolete: The task is no longer relevant
Task Phase Transitions¶
Tasks transition between phases through specific operations:
- Claim: Assigns the task to a specific user, restricting access to others
- Start: Indicates that work on the task has begun
- Complete: Finishes the task successfully with result data
- Fail: Marks the task as failed with fault data
- Skip: Bypasses the task if it's optional
- Release: Unassigns the task from a user, making it available to others
- Delegate: Temporarily transfers ownership to another user
- Forward: Completely changes ownership to another user
- Suspend: Temporarily prevents work on the task
- Resume: Allows a suspended task to be worked on again
Task Assignment¶
The Aletyx Enterprise Build of Kogito and Drools provides multiple ways to assign tasks to users or groups.
Assignment Methods¶
-
Direct Assignment: Assigning to a specific user
-
Group Assignment: Assigning to a group of users
-
Expression-Based Assignment: Using expressions to calculate assignment at runtime
Assignment Parameters¶
The Aletyx Enterprise Build of Kogito and Drools supports the following assignment parameters:
Parameter Name | Description | Example Value |
---|---|---|
ActorId | Comma-separated list of authorized users | john,mary,#{actor} |
GroupId | Comma-separated list of authorized groups | managers,#{mygroup} |
BusinessAdministratorId | Comma-separated list of administrators | administrator,#{adminuser} |
BusinessAdministratorGroupId | Comma-separated list of administrator groups | admins,#{admingroup} |
ExcludedOwnerId | Comma-separated list of unauthorized users | paul,#{lastactor} |
Assignment Best Practices¶
- Prefer Group Assignment: Assign to groups rather than individuals to avoid bottlenecks
- Use Claim Pattern: Have users claim tasks rather than directly assigning them
- Define Escalation Paths: Configure what happens if tasks aren't completed in time
- Use Dynamic Assignment: Calculate assignments at runtime based on workload, skills, etc.
- Consider Business Admins: Designate administrators who can intervene if needed
Task Data Management¶
Tasks consume and produce data through a well-defined data mapping system.
Input and Output Data Handling¶
- Task Input: Data passed to the task when it is created
- Task Output: Data produced when the task is completed
- Content Storage: Task data is stored separately from process data
Data Mapping Example¶
<userTask id="approveExpense" name="Approve Expense Report">
<ioSpecification>
<dataInput id="_DataInput_1" name="expense"/>
<dataInput id="_DataInput_2" name="requester"/>
<dataOutput id="_DataOutput_1" name="approved"/>
<dataOutput id="_DataOutput_2" name="comments"/>
<inputSet>
<dataInputRefs>_DataInput_1</dataInputRefs>
<dataInputRefs>_DataInput_2</dataInputRefs>
</inputSet>
<outputSet>
<dataOutputRefs>_DataOutput_1</dataOutputRefs>
<dataOutputRefs>_DataOutput_2</dataOutputRefs>
</outputSet>
</ioSpecification>
<dataInputAssociation>
<sourceRef>expenseReport</sourceRef>
<targetRef>_DataInput_1</targetRef>
</dataInputAssociation>
<dataInputAssociation>
<sourceRef>initiator</sourceRef>
<targetRef>_DataInput_2</targetRef>
</dataInputAssociation>
<dataOutputAssociation>
<sourceRef>_DataOutput_1</sourceRef>
<targetRef>expenseApproved</targetRef>
</dataOutputAssociation>
<dataOutputAssociation>
<sourceRef>_DataOutput_2</sourceRef>
<targetRef>approvalComments</targetRef>
</dataOutputAssociation>
</userTask>
Data Access Patterns¶
Tasks provide several ways to access their data:
- Through the Task Service API: Programmatically access task content
- Through Task Forms: Render forms based on task data
- Through Task Listeners: React to task lifecycle events
Task API Interaction¶
The Aletyx Enterprise Build of Kogito and Drools provides comprehensive APIs for interacting with human tasks.
Core Task Services¶
TaskService API¶
The TaskService
is the main API for interacting with human tasks:
@Inject
private TaskService taskService;
// Key methods
long taskId = taskService.addTask(task, params);
taskService.claim(taskId, userId);
taskService.start(taskId, userId);
taskService.complete(taskId, userId, results);
taskService.fail(taskId, userId, faultData);
taskService.forward(taskId, userId, targetUserId);
taskService.release(taskId, userId);
taskService.delegate(taskId, userId, targetUserId);
UserTaskService API¶
The UserTaskService
provides methods specific to user task management:
@Inject
private UserTaskService userTaskService;
// Key methods
List<TaskSummary> tasks = userTaskService.getTasksAssignedAsPotentialOwner(userId, language);
List<TaskSummary> tasks = userTaskService.getTasksOwned(userId, language);
Task task = userTaskService.getTask(taskId);
Authentication and Authorization¶
Task operations are protected by authentication and authorization:
// Set up security policy with identity information
StaticIdentityProvider identity = new StaticIdentityProvider("admin", Collections.singletonList("managers"));
SecurityPolicy policy = SecurityPolicy.of(identity);
// Get list of work items, taking security restrictions into account
List<WorkItem> workItems = processInstance.workItems(policy);
// Work on a task with security context
processInstance.transitionWorkItem(wiId,
new HumanTaskTransition(Claim.ID, null, policy));
REST API Endpoints¶
Tasks can also be managed through REST endpoints:
Task Lifecycle Management¶
Example:
http://localhost:8080/approval/ec44f890-d21d-444f-a4ec-cb88589bd79/firstLineApproval/7a520588-d5ab-464a-8378-d34fda3ff7e9/phase/complete
Collaborative Features¶
Tasks support comments and attachments through REST:
# Comments
[GET] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/comments
[POST] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/comments
[PUT] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/comments/{commentId}
[DELETE] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/comments/{commentId}
# Attachments
[GET] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/attachments
[POST] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/attachments
[PUT] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/attachments/{attachmentId}
[DELETE] /{processId}/{processInstanceId}/{taskName}/{taskInstanceId}/attachments/{attachmentId}
Task Forms¶
Human tasks typically include a form for collecting user input.
Form Types¶
- Generated Forms: Automatically generated based on task data
- Form Modeler Forms: Designed using the Form Modeler
- Custom Forms: Custom HTML/JavaScript forms for maximum flexibility
Form Integration¶
Forms are associated with tasks through form names that follow specific conventions:
{processId}.{taskName}
: Task-specific form{taskName}
: Generic form for a task name{processId}.{taskName}-taskform
: Alternative naming convention
Displaying Forms¶
Forms can be displayed in several ways:
- Via Tasklist UI: Default user interface for task management
- Via Embedded Form Renderer: Embedding forms in custom UIs
- Via Custom UI Integration: Building custom task interfaces
Task Notifications and Deadlines¶
Tasks can have notifications and deadlines to ensure timely processing.
Deadline Configuration¶
<userTask id="approveExpense">
<extensionElements>
<tns:deadlines>
<tns:startDeadline>
<tns:deadline date="2023-12-31T23:59:59Z">
<tns:escalations>
<tns:escalation name="Start Deadline Escalation">
<tns:notifications>
<tns:notification>
<tns:emailNotification>
<tns:to>#{manager}</tns:to>
<tns:subject>Task Start Deadline Approaching</tns:subject>
<tns:body>The task "Approve Expense" needs to be started soon.</tns:body>
</tns:emailNotification>
</tns:notification>
</tns:notifications>
</tns:escalation>
</tns:escalations>
</tns:deadline>
</tns:startDeadline>
<tns:endDeadline>
<tns:deadline period="2d">
<tns:escalations>
<tns:escalation name="End Deadline Escalation">
<tns:reassignments>
<tns:reassignment>
<tns:users>
<tns:user>manager</tns:user>
</tns:users>
</tns:reassignment>
</tns:reassignments>
</tns:escalation>
</tns:escalations>
</tns:deadline>
</tns:endDeadline>
</tns:deadlines>
</extensionElements>
</userTask>
Notification Types¶
- Email Notifications: Send emails when deadlines approach
- Reassignments: Automatically reassign tasks when deadlines are missed
- Custom Notifications: Implement custom notification handlers
Task Event Listeners¶
Task event listeners allow you to react to task state changes.
public class MyTaskEventListener implements TaskLifeCycleEventListener {
@Override
public void beforeTaskActivatedEvent(TaskEvent event) {
// Code executed before task is activated
}
@Override
public void afterTaskCompletedEvent(TaskEvent event) {
// Code executed after task is completed
Task task = event.getTask();
long processInstanceId = task.getTaskData().getProcessInstanceId();
// Custom logic here
}
// Implement other event methods as needed
}
Custom Task Lifecycle Implementation¶
The Aletyx Enterprise Build of Kogito and Drools allows for custom task lifecycle definitions:
public class CustomTaskLifecycleManager extends DefaultUserTaskManager {
@Override
public void beforeTaskTransition(TaskContext context, Task task, TaskTransition transition) {
// Custom logic before task transition
super.beforeTaskTransition(context, task, transition);
}
@Override
public void afterTaskTransition(TaskContext context, Task task, TaskTransition transition) {
// Custom logic after task transition
super.afterTaskTransition(context, task, transition);
}
}
Integration with External Systems¶
Task management can be integrated with external systems:
@ApplicationScoped
public class ExternalTicketSystemIntegration implements TaskLifeCycleEventListener {
@Inject
private TicketSystemClient ticketClient;
@Override
public void afterTaskAddedEvent(TaskEvent event) {
Task task = event.getTask();
// Only create tickets for certain task types
if ("CustomerSupport".equals(task.getNames().get(0).getText())) {
// Extract relevant data
Map<String, Object> inputData = getTaskContent(task.getId());
String customerId = (String) inputData.get("customerId");
String issue = (String) inputData.get("issue");
// Create ticket in external system
String ticketId = ticketClient.createTicket(customerId, issue, "Created from task " + task.getId());
// Store ticket ID with task
Map<String, Object> outputData = new HashMap<>();
outputData.put("ticketId", ticketId);
taskService.addContent(task.getId(), outputData);
}
}
@Override
public void afterTaskCompletedEvent(TaskEvent event) {
Task task = event.getTask();
// Only process tickets for certain task types
if ("CustomerSupport".equals(task.getNames().get(0).getText())) {
// Get ticket ID
Map<String, Object> taskData = getTaskContent(task.getId());
String ticketId = (String) taskData.get("ticketId");
if (ticketId != null) {
// Get completion data
String resolution = (String) taskData.get("resolution");
// Update external ticket
ticketClient.closeTicket(ticketId, resolution);
}
}
}
private Map<String, Object> getTaskContent(long taskId) {
try {
return taskService.getTaskContent(taskId);
} catch (Exception e) {
return new HashMap<>();
}
}
}
Human Task Best Practices¶
Task Design Principles¶
- Clear Ownership: Make it obvious who should work on the task
- Sufficient Context: Provide enough information to perform the task
- Appropriate Granularity: Neither too large nor too small
- Realistic Timeframes: Set reasonable deadlines and notifications
- Error Recovery: Design for handling exceptions and error conditions
Common Anti-Patterns¶
- Orphaned Tasks: Tasks without clear owners or groups
- Overburdened Actors: Too many tasks assigned to the same person
- Bottleneck Users: Critical processes dependent on specific individuals
- Unclear Task Purpose: Tasks without clear instructions or outcomes
- Missing Escalation Paths: No handling for missed deadlines
Performance Considerations¶
- Task Indexing: Use appropriate indexing for task queries
- Task Archiving: Move completed tasks to archives
- Task Data Size: Keep task data reasonably sized
- Batch Operations: Use batch operations for bulk task management
- Monitoring: Track task performance and bottlenecks
Complete Task Lifecycle Example¶
// Complete example showing task lifecycle management
try {
// Get task service
TaskService taskService = runtimeEngine.getTaskService();
// Find tasks for potential owner
List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner("john", "en-UK");
if (!tasks.isEmpty()) {
TaskSummary taskSummary = tasks.get(0);
long taskId = taskSummary.getId();
// Task details
System.out.println("Task: " + taskSummary.getName());
System.out.println("Status: " + taskSummary.getStatus());
System.out.println("Created: " + taskSummary.getCreatedOn());
// Claim the task
taskService.claim(taskId, "john");
System.out.println("Task claimed");
// Start the task
taskService.start(taskId, "john");
System.out.println("Task started");
// Get task input data
Map<String, Object> inputContent = taskService.getTaskContent(taskId);
System.out.println("Task input data: " + inputContent);
// Prepare task output data
Map<String, Object> results = new HashMap<>();
results.put("approved", true);
results.put("comments", "Looks good to me");
// Complete the task
taskService.complete(taskId, "john", results);
System.out.println("Task completed");
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}