A modern, embeddable workflow orchestration library for Java applications. Built on event-driven architecture with NATS, virtual threads, and structured concurrency.
- Event-Driven Architecture: Workflows emerge from nodes reacting to events
- Pull-Based Work Distribution: Nodes pull work via atomic CAS operations
- Elastic Scaling: Automatic node pool management with min/max instances
- Fault Tolerance: Automatic orphan detection, retry with exponential backoff, checkpointing
- Observability: REST API, metrics (Micrometer), and comprehensive logging
- Modern Java: Built with Java 21, virtual threads, and structured concurrency
- Java 21 JDK
- Gradle 8.0+
- NATS Server 2.10+ (optional - can use in-memory implementations for development)
./gradlew build./gradlew runOr with custom NATS URL:
java -jar build/libs/orchestrator-poc-1.0-SNAPSHOT.jarThe orchestrator can be configured via:
- Programmatic Configuration (current default)
- YAML Configuration (coming soon)
- Environment Variables:
ORCHESTRATOR_API_PORT: REST API port (default: 8080)
When running, the orchestrator exposes a REST API on port 8080 (configurable):
GET /health- Health checkGET /api/nodes- List all nodesGET /api/nodes/{nodeId}- Get node detailsGET /api/tasks/{taskId}- Get task detailsGET /api/controller/status- Controller statusPOST /api/controller/shutdown- Graceful shutdown
package io.orchestrator.examples.ci.nodes;
import io.orchestrator.node.*;
import io.orchestrator.event.Event;
import java.util.List;
public class MyNode implements Node {
@Override
public String getType() {
return "my-node";
}
@Override
public List<String> getSubscriptions() {
return List.of("my.event");
}
@Override
public void initialize(NodeContext context) throws NodeInitializationException {
// Initialize your node
}
@Override
public void run() {
// Subscribe to events and process tasks
context.getEventBus().subscribe("my.event", this::handleEvent);
}
private void handleEvent(Event event) {
// Process event and claim associated task
String taskId = event.getPayloadString("task_id");
Optional<Task> task = context.claimTask(taskId);
if (task.isPresent()) {
// Execute task
// ...
}
}
@Override
public void shutdown() {
// Handle shutdown
}
@Override
public void close() {
// Cleanup resources
}
@Override
public NodeStatus getStatus() {
return NodeStatus.IDLE;
}
}Nodes are discovered via Java ServiceLoader. Create a file:
META-INF/services/io.orchestrator.node.Node
With the content:
io.orchestrator.examples.ci.nodes.MyNode
Place your node JAR in the plugins/ directory.
- WorkflowController: Manages node pools and reconciliation
- Node: Long-lived workers that execute tasks
- EventBus: Event publishing and subscription (NATS or in-memory)
- KeyValueStore: Metadata storage (NATS KV or in-memory)
- Events are published to NATS JetStream
- Nodes subscribe to relevant events
- Nodes claim tasks via atomic CAS operations
- Tasks execute and publish completion events
- Downstream nodes react to completion events
The controller runs a reconciliation loop every 5 seconds that:
- Checks node health (heartbeat monitoring)
- Detects orphaned tasks
- Scales node pools (spawn/terminate nodes)
- Maintains desired state
./gradlew testorchestrator-poc/
├── src/main/java/io/orchestrator/
│ ├── controller/ # WorkflowController
│ ├── node/ # Node interfaces and models
│ ├── event/ # EventBus implementations
│ ├── storage/ # KeyValueStore implementations
│ ├── config/ # Configuration classes
│ ├── api/ # REST API
│ └── metrics/ # Metrics collection
├── plugins/
│ └── example-nodes/ # Example node implementations
└── docs/ # Documentation
- ✅ Core interfaces and models
- ✅ NATS integration (basic)
- ✅ Controller with reconciliation loop
- ✅ Node lifecycle management
- ✅ Retry logic
- ✅ Visitor pattern framework
- ✅ Example BuildNode
- ✅ Checkpointing infrastructure
- ✅ Orphan detection and recovery
- ✅ Graceful shutdown with timeout
- ✅ REST API
- ✅ Metrics infrastructure
- 🟡 Structured concurrency (in progress)
- ⏳ Comprehensive test suite
See docs/5-Roadmap.md for detailed implementation roadmap.
[To be determined]
[To be determined]