A complete, ready-to-run Spring Boot application demonstrating the External Task Pattern with OrqueIO BPM engine. This all-in-one example includes both the OrqueIO engine and an external task worker in a single application.
- Overview
- What are External Tasks?
- Features
- Architecture
- Prerequisites
- Quick Start
- Testing
- Project Structure
- Configuration
- Customization
- API Reference
- Key Components
- Dependencies
This project demonstrates how to implement the External Task Pattern using OrqueIO, a powerful open-source BPM platform. Unlike traditional service tasks that run synchronously within the engine, external tasks enable:
- ✅ Decoupled architecture - Workers run independently from the engine
- ✅ Horizontal scalability - Multiple workers can process the same topic
- ✅ Fault tolerance - Automatic retry and error handling
- ✅ Technology flexibility - Workers can be written in any language
- ✅ Microservices ready - Perfect for distributed systems
External Tasks implement the poll-based service invocation pattern:
┌─────────────────────┐ ┌──────────────────────┐
│ OrqueIO Engine │ │ External Worker │
│ (Port 8080) │ │ (Java Application) │
│ │ │ │
│ 1. Creates task │ │ 1. Polls for tasks │
│ 2. Waits for │◄────────┤ 2. Locks task │
│ completion │ Poll │ 3. Executes logic │
│ 3. Continues │◄────────┤ 4. Completes task │
│ process │ Complete│ │
└─────────────────────┘ └──────────────────────┘
Key differences from traditional Service Tasks:
| Aspect | Service Task | External Task |
|---|---|---|
| Execution | Synchronous, in-engine | Asynchronous, external process |
| Coupling | Tightly coupled | Loosely coupled |
| Scalability | Limited to engine threads | Unlimited workers |
| Fault Tolerance | Engine transaction rollback | Retry mechanism with backoff |
| Technology | Must be Java | Any language with HTTP client |
- Complete OrqueIO BPM Engine with REST API
- External Task Worker with automatic polling
- H2 In-Memory Database (no external DB required)
- Auto-deployment of BPMN processes
- Web UI for manual task completion (
/manual-complete.html) - OrqueIO Cockpit for process monitoring
- Test scripts (Bash and Batch)
- Comprehensive logging and error handling
- Retry mechanism with exponential backoff
- Lock duration management to prevent duplicate processing
┌──────────────────────────────────────────────────────────┐
│ external-task-example (Port 8080) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ OrqueIO BPM Engine │ │
│ │ • H2 In-Memory Database │ │
│ │ • REST API (/engine-rest) │ │
│ │ • Cockpit UI │ │
│ │ • Process Engine (BPMN execution) │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ │
│ │ HTTP REST API │
│ │ │
│ ┌─────────────────────▼──────────────────────────────┐ │
│ │ SampleExternalTaskWorker │ │
│ │ • Polls topic: "process-data" │ │
│ │ • Lock duration: 10 seconds │ │
│ │ • Processes business logic │ │
│ │ • Completes tasks with results │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ALL IN ONE APPLICATION! │
└──────────────────────────────────────────────────────────┘
- Java 21 - Download OpenJDK
- Maven 3.6+ - Download Maven
- Git (for cloning the repository)
No external database or additional services required!
git clone https://github.com/OrqueIO/external-task-example.git
cd external-task-examplemvn clean installmvn spring-boot:runThe application will start and perform the following:
- ✅ Start OrqueIO engine on http://localhost:8080
- ✅ Initialize H2 in-memory database
- ✅ Auto-deploy BPMN process from
src/main/resources/bpmn/ - ✅ Start external task worker listening to topic
"process-data"
Wait for this log message:
External Task Worker is now listening to topic: process-data
Start a process instance with curl:
curl -X POST http://localhost:8080/engine-rest/process-definition/key/ExternalTaskProcess/start \
-H "Content-Type: application/json" \
-d '{
"variables": {
"inputData": {
"value": "Hello OrqueIO",
"type": "String"
}
}
}'Expected output in logs:
INFO : External task received: [task-id]
INFO : Activity ID: ExternalTask_ProcessData
INFO : Variables: {inputData=Hello OrqueIO}
INFO : Input data: Hello OrqueIO
INFO : Processed data: PROCESSED: HELLO ORQUEIO
INFO : External task completed successfully: [task-id]
- Open browser: http://localhost:8080
- Login with credentials:
- Username:
admin - Password:
admin
- Username:
- Navigate to Cockpit
- View the deployed process: "External Task Sample Process"
- Start a new process instance
- Add variable:
inputData="Hello World"(type: String) - Watch the worker process the task in real-time!
For testing without the automatic worker:
- Open: http://localhost:8080/manual-complete.html
- Comment out
@ComponentinSampleExternalTaskWorker.javato disable auto-worker - Use the web interface to manually fetch and complete tasks
Run the provided test scripts:
Windows:
test-api.batLinux/Mac/Git Bash:
chmod +x test-api.sh
./test-api.shexternal-task-example/
├── src/
│ └── main/
│ ├── java/
│ │ └── io/orqueio/externaltask/
│ │ ├── ExternalTaskApplication.java # Spring Boot main class
│ │ └── worker/
│ │ └── SampleExternalTaskWorker.java # External task worker
│ └── resources/
│ ├── application.yml # Application configuration
│ ├── bpmn/
│ │ └── sample_external_task_process.bpmn # Auto-deployed process
│ ├── static/
│ │ └── manual-complete.html # Manual completion UI
│ └── sample_external_task_process.bpmn # BPMN source
├── test-api.sh # Linux test script
├── test-api.bat # Windows test script
├── manual-external-task.sh # Manual task completion script
├── pom.xml # Maven configuration
└── README.md # This file
Configuration file: src/main/resources/application.yml
server:
port: 8080 # Application port
spring:
application:
name: external-task-example
datasource:
url: jdbc:h2:mem:orqueio;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true # H2 console at /h2-console
path: /h2-console
jpa:
hibernate:
ddl-auto: update
show-sql: false
orqueio:
bpm:
rest-api:
enabled: true
basic-auth-enabled: false # Disable auth for development
database:
schema-update: true # Auto-update DB schema
job-execution:
core-pool-size: 3 # Thread pool for job execution
authorization:
enabled: false # Disable authorization for development
admin-user:
id: admin
password: admin
firstName: Admin
filter:
create: All tasks
deployment-resource-pattern: classpath*:bpmn/*.bpmn # Auto-deploy BPMN files
base-url: http://localhost:8080/engine-rest # Worker connection URL
logging:
level:
root: INFO
io.orqueio: DEBUG
io.orqueio.externaltask: DEBUG # Enable debug logging for workerThe external task worker is implemented in SampleExternalTaskWorker.java with the following key configurations:
// Topic subscription
client.subscribe("process-data")
.lockDuration(10000) // 10 seconds lock
.handler((externalTask, externalTaskService) -> {
// Task processing logic
})
.open();Key Parameters:
- Topic Name:
process-data- Must match the topic in your BPMN process - Lock Duration:
10000ms(10 seconds) - Time the worker has to complete the task - Startup Delay:
5000ms(5 seconds) - Wait time for OrqueIO engine initialization
To customize the worker processing logic, modify the processData() method in SampleExternalTaskWorker.java:82:
private String processData(String input) {
if (input == null) {
return "No data provided";
}
// Add your custom processing logic here
return "PROCESSED: " + input.toUpperCase();
}The worker includes built-in error handling with retry mechanism:
externalTaskService.handleFailure(externalTask,
e.getMessage(), // Error message
"Error details: " + e.getClass().getName(), // Error details
3, // Number of retries
5000); // Retry timeout in millisecondsTo switch from H2 to a persistent database (PostgreSQL/MySQL), update application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/orqueio
driver-class-name: org.postgresql.Driver
username: your_username
password: your_passwordAnd add the corresponding JDBC driver to pom.xml.
All REST endpoints are available at http://localhost:8080/engine-rest/
POST /engine-rest/process-definition/key/{key}/start
Content-Type: application/json
{
"variables": {
"variableName": {
"value": "variableValue",
"type": "String"
}
}
}POST /engine-rest/external-task/fetchAndLock
Content-Type: application/json
{
"workerId": "worker-id",
"maxTasks": 10,
"topics": [
{
"topicName": "process-data",
"lockDuration": 10000
}
]
}POST /engine-rest/external-task/{taskId}/complete
Content-Type: application/json
{
"workerId": "worker-id",
"variables": {
"resultVar": {
"value": "result",
"type": "String"
}
}
}POST /engine-rest/external-task/{taskId}/failure
Content-Type: application/json
{
"workerId": "worker-id",
"errorMessage": "Error description",
"retries": 3,
"retryTimeout": 5000
}- Full REST API: http://localhost:8080/engine-rest
- H2 Console: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:orqueio - Username:
sa - Password: (empty)
- JDBC URL:
- Manual Completion UI: http://localhost:8080/manual-complete.html
Solution:
- Check logs for "External Task Worker is now listening"
- Verify the topic name matches in BPMN and worker code
- Ensure the process instance was started successfully
- Check if tasks are locked by another worker
Solution:
- Ensure the engine is fully started before the worker connects
- The worker waits 5 seconds by default - increase if needed
- Check the
orqueio.base-urlconfiguration
Solution:
- Verify BPMN file is in
src/main/resources/bpmn/ - Check for BPMN validation errors in logs
- Ensure
historyTimeToLiveis set in the BPMN process
Solution:
- H2 database is recreated on each startup (in-memory)
- For persistent data, configure MySQL or PostgreSQL in
application.yml
Debug logging is already enabled by default in application.yml:
logging:
level:
root: INFO
io.orqueio: DEBUG
io.orqueio.externaltask: DEBUGTo disable debug logging, change to INFO or WARN.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Simple Spring Boot application entry point:
@SpringBootApplication
public class ExternalTaskApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalTaskApplication.class, args);
}
}The worker implementation that:
- Implements
CommandLineRunnerto start on application startup - Creates an
ExternalTaskClientconnecting to OrqueIO REST API - Subscribes to the
process-datatopic - Processes tasks by transforming input data to uppercase
- Handles errors with automatic retry mechanism
- Properly shuts down on application termination
Key Methods:
run()- Initializes the worker and subscribes to topicsprocessData()- Business logic for processing external tasks
The project uses the following key dependencies (defined in pom.xml):
<!-- OrqueIO BPM Engine -->
<dependency>
<groupId>io.orqueio.bpm.springboot</groupId>
<artifactId>orqueio-bpm-spring-boot-starter-webapp</artifactId>
</dependency>
<!-- OrqueIO REST API -->
<dependency>
<groupId>io.orqueio.bpm.springboot</groupId>
<artifactId>orqueio-bpm-spring-boot-starter-rest</artifactId>
</dependency>
<!-- External Task Client -->
<dependency>
<groupId>io.orqueio.bpm</groupId>
<artifactId>orqueio-external-task-client</artifactId>
</dependency>
<!-- H2 Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>Versions:
- OrqueIO:
1.0.7 - Spring Boot:
3.5.9 - Java:
21
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: OrqueIO Documentation
Made with ❤️ by the OrqueIO Team