Design and Implementation of a Lightweight Java Workflow Engine
This article explains why a custom lightweight workflow engine was built, outlines its micro‑kernel architecture, demonstrates incremental development from a Hello‑World example to a simple approval process with branching, and discusses future enhancements such as exception handling, persistence, and dynamic graph modification.
1. What is a workflow engine? A workflow engine is a set of code that drives the execution of a workflow. The article assumes readers are familiar with the concept of workflows and their use cases.
2. Why reinvent the wheel? Existing open‑source engines (Activiti, Flowable, Camunda) do not meet specific business requirements, have high learning curves, and introduce maintenance risks. Building a custom engine allows full control, minimal dependencies (only JDK 8), and strategic ownership of core technology.
3. How to build the engine Three development approaches are suggested: fixed‑scope delivery, iterative development, and divide‑and‑conquer. The design focuses on being lightweight, micro‑kernel based, and only implements a subset of BPMN needed for the target scenarios.
4. Hello ProcessEngine (first iteration) The goal is to print "Hello ProcessEngine" using an XML definition of the process. The XML representation is:
<definitions>
<process id="process_1" name="hello">
<startEvent id="startEvent_1">
<outgoing>flow_1</outgoing>
</startEvent>
<sequenceFlow id="flow_1" sourceRef="startEvent_1" targetRef="printHello_1" />
<printHello id="printHello_1" name="hello">
<incoming>flow_1</incoming>
<outgoing>flow_2</outgoing>
</printHello>
<sequenceFlow id="flow_2" sourceRef="printHello_1" targetRef="printProcessEngine_1" />
<printProcessEngine id="printProcessEngine_1" name="processEngine">
<incoming>flow_2</incoming>
<outgoing>flow_3</outgoing>
</printProcessEngine>
<sequenceFlow id="flow_3" sourceRef="printProcessEngine_1" targetRef="endEvent_1"/>
<endEvent id="endEvent_1">
<incoming>flow_3</incoming>
</endEvent>
</process>
</definitions>The corresponding Java model classes are:
public class PeProcess {
public String id;
public PeNode start;
public PeProcess(String id, PeNode start) { this.id = id; this.start = start; }
}
public class PeEdge {
private String id;
public PeNode from;
public PeNode to;
public PeEdge(String id) { this.id = id; }
}
public class PeNode {
private String id;
public String type;
public PeEdge in;
public PeEdge out;
public PeNode(String id) { this.id = id; }
}The engine logic iterates from the start node, follows outgoing edges, executes node‑specific logic, and stops at the end event.
5. Simple approval (second iteration) A linear approval flow is modeled with XML and three operator implementations (apply, approve, notify). The XML adds nodes for approvalApply , approval , and notify . The engine registers each operator via registNodeProcessor and runs the process.
6. General approval with branching (third iteration) To support decision logic, a simpleGateway node is introduced. Its XML includes an expr element and a trueOutGoing edge. The gateway operator evaluates the expression using a JavaScript engine:
public class OperatorOfSimpleGateway implements IOperator {
@Override public String getType() { return "simpleGateway"; }
@Override public void doTask(ProcessEngine engine, PeNode node, PeContext ctx) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine js = mgr.getEngineByName("js");
js.put("approvalResult", ctx.getValue("approvalResult"));
String expr = XmlUtil.childTextByName(node.xmlNode, "expr");
String trueEdge = XmlUtil.childTextByName(node.xmlNode, "trueOutGoing");
PeEdge out = (Boolean) js.eval(expr) ? node.outWithID(trueEdge) : node.outWithOutID(trueEdge);
engine.nodeFinished(out);
}
}The updated PeNode now holds lists of incoming and outgoing edges, enabling multiple branches.
7. Summary and outlook The engine now has a stable core, extensible operator layer, context for global variables, and supports sequential, branching, and loop‑like structures. Future work includes exception handling, persistence, DAG validation, dynamic graph updates, concurrency control, and richer rule engines.
JD Tech Talk
Official JD Tech public account delivering best practices and technology innovation.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.