Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions getting-started/01-run-the-engine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 01 · Run the engine

Everything else in Getting Started builds on a running engine. This chapter starts one — no BPMN, no code.

The engine is defined in the shared [`compose.yaml`](../compose.yaml) at the root of the Getting Started track. Run these commands **from the `getting-started/` folder**.

## Start

```bash
cd .. # into getting-started/, where compose.yaml lives
docker compose up -d
```

The engine exposes two APIs:

| Address | Protocol | Used for |
|---|---|---|
| `http://localhost:8080` | REST | Deploy processes, start instances, query state |
| `localhost:9090` | gRPC | Connect workers |

## Verify

```bash
curl http://localhost:8080/v1/process-definitions
```

An empty list is the healthy response: the engine is up with nothing deployed yet. You're ready for [02-first-bpmn-process](../02-first-bpmn-process/).

## Optional: web UI

```bash
docker compose --profile ui up -d
```

Then open http://localhost:9000.

## Stop

```bash
docker compose down
```

Engine state is kept in a named volume, so it survives a restart. Use `docker compose down -v` to wipe it.
80 changes: 80 additions & 0 deletions getting-started/02-first-bpmn-process/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 02 · First BPMN process

Deploy the **first-bpmn-process**, run a **worker** (Java or Go) that handles its one service task, and watch an instance execute end to end.

```
[Start] --> [Log Greeting] --> [Done]
|
log-worker <- the worker (java/ or go/) in this folder
```

Deploying is done **manually** with `curl` (the tutorial teaches it). The worker is provided in **both Java and Go** — pick one.

## Files

| Path | What it is |
|---|---|
| `first-bpmn-process.bpmn` | The process definition you deploy (shared by both workers) |
| `java/` | A Java (Spring Boot) worker that handles the `log-worker` job |
| `go/` | A Go worker that handles the `log-worker` job |

## Prerequisites

- The engine running — from the [`getting-started/`](../) folder run `docker compose up -d` (see [01-run-the-engine](../01-run-the-engine/))
- For the Java worker: Java 17+ and Maven — **or** for the Go worker: Go 1.22+

## 1. Deploy the process (manual)

```bash
curl -X POST http://localhost:8080/v1/process-definitions \
-F "resource=@first-bpmn-process.bpmn"
```

Note the returned `processDefinitionKey`.

## 2. Run the worker

Pick your language and leave it running.

**Java**

The ZenBPM Java client is on Maven Central, so no extra setup is needed:

```bash
cd java
mvn spring-boot:run
```

> `java/pom.xml` uses `org.pbinitiative.zenbpm:zenbpm-spring-boot-starter:1.4.0` (the client version tracks the engine version) plus `grpc-netty-shaded` for the worker transport.

**Go**

```bash
cd go
go mod tidy
go run .
```

Either one connects to the engine and registers the `log-worker` handler.

## 3. Start an instance

In another terminal, using the key from step 1:

```bash
curl -X POST http://localhost:8080/v1/process-instances \
-H "Content-Type: application/json" \
-d '{"processDefinitionKey": <PROCESS_DEFINITION_KEY>, "variables": {}}'
```

The worker logs:

```
[log-worker] Hello, World!
```

That greeting came out of your instance. Try starting an instance **before** the worker is running to see it park at the service task and wait — then start the worker and watch it complete.

## Clean up

Stop the worker (Ctrl+C), then stop the engine from the [`getting-started/`](../) folder with `docker compose down`.
58 changes: 58 additions & 0 deletions getting-started/02-first-bpmn-process/first-bpmn-process.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:zeebe="http://camunda.org/schema/zeebe/1.0"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"
exporter="ZenBPM Modeler" exporterVersion="1.0.0">
<bpmn:process id="first-bpmn-process" name="First BPMN Process" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" name="Start">
<bpmn:outgoing>Flow_1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:serviceTask id="Task_Log" name="Log Greeting">
<bpmn:extensionElements>
<zeebe:taskDefinition type="log-worker" />
<zeebe:ioMapping>
<zeebe:input source="Hello, World!" target="log" />
</zeebe:ioMapping>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1</bpmn:incoming>
<bpmn:outgoing>Flow_2</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:endEvent id="EndEvent_1" name="Done">
<bpmn:incoming>Flow_2</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="Task_Log" />
<bpmn:sequenceFlow id="Flow_2" sourceRef="Task_Log" targetRef="EndEvent_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="first-bpmn-process">
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
<dc:Bounds x="182" y="102" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="188" y="145" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_Log_di" bpmnElement="Task_Log">
<dc:Bounds x="270" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
<dc:Bounds x="422" y="102" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="430" y="145" width="21" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
<di:waypoint x="218" y="120" />
<di:waypoint x="270" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
<di:waypoint x="370" y="120" />
<di:waypoint x="422" y="120" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
7 changes: 7 additions & 0 deletions getting-started/02-first-bpmn-process/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module first-bpmn-process-worker

go 1.22

// Dependencies are intentionally omitted here. Run `go mod tidy` to resolve them
// from the imports in main.go (pin github.com/pbinitiative/zenbpm to the version
// matching your engine).
37 changes: 37 additions & 0 deletions getting-started/02-first-bpmn-process/go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"context"
"fmt"

"github.com/pbinitiative/zenbpm/pkg/zenclient"
"github.com/pbinitiative/zenbpm/pkg/zenclient/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

// Handles the "log-worker" service task of the first-bpmn-process: reads the
// "log" variable, prints it, and completes the job so the instance can finish.
func main() {
conn, err := grpc.NewClient(
"127.0.0.1:9090",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
panic(err)
}
defer conn.Close()

zen := zenclient.NewGrpc(conn)

// Subscribe to "log-worker" jobs.
zen.RegisterWorker(context.Background(), "first-bpmn-process-worker",
func(ctx context.Context, job *proto.WaitingJob) (map[string]any, *zenclient.WorkerError) {
fmt.Printf("[log-worker] %v\n", job.GetVariables()["log"])
return map[string]any{}, nil // no output variables; job complete
},
"log-worker",
)

select {} // keep the worker running
}
69 changes: 69 additions & 0 deletions getting-started/02-first-bpmn-process/java/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- Spring Boot parent gives us dependency management and the spring-boot:run goal. -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/>
</parent>

<groupId>org.pbinitiative.examples</groupId>
<artifactId>first-bpmn-process-worker</artifactId>
<version>1.0.0</version>

<properties>
<java.version>17</java.version>
<!-- ZenBPM Java client (on Maven Central); version tracks the engine version. -->
<zenbpm.version>1.4.0</zenbpm.version>
<opentelemetry.version>1.58.0</opentelemetry.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- The ZenBPM starter needs a newer OpenTelemetry than Spring Boot 3.3.x manages. -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-bom</artifactId>
<version>${opentelemetry.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Spring Boot core. The ZenBPM starter declares spring-boot-autoconfigure as
optional (not transitive), so the application must provide Spring Boot itself. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- ZenBPM Spring Boot starter: provides @JobWorker and auto-connects on startup.
Pulls in zenbpm-client-core transitively. On Maven Central — no extra repo needed. -->
<dependency>
<groupId>org.pbinitiative.zenbpm</groupId>
<artifactId>zenbpm-spring-boot-starter</artifactId>
<version>${zenbpm.version}</version>
</dependency>
<!-- Required for gRPC job workers (the @JobWorker transport). -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.80.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.pbinitiative.examples.gettingstarted;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Boots the worker. The ZenBPM Spring Boot starter connects to the engine over
* gRPC on startup (see application.yml) and registers every @JobWorker bean.
*/
@SpringBootApplication
public class FirstBpmnProcessWorkerApplication {
public static void main(String[] args) {
SpringApplication.run(FirstBpmnProcessWorkerApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.pbinitiative.examples.gettingstarted;

import org.springframework.stereotype.Component;
import org.pbinitiative.zenbpm.grpc.JobContext;
import org.pbinitiative.zenbpm.grpc.JobWorker;

import java.util.Map;

/**
* Handles the "log-worker" service task of the first-bpmn-process.
*
* When a process instance reaches the "Log Greeting" task, the engine creates a
* job of type "log-worker" and waits. This method picks it up, reads the "log"
* variable, prints it, and completes the job so the instance can finish.
*/
@Component
public class LogWorker {

@JobWorker("log-worker")
public Map<String, Object> handleJob(JobContext ctx) {
System.out.println("[log-worker] " + ctx.getVariables().get("log"));
return Map.of(); // no output variables; job complete
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Where the worker finds the engine. These match the ports from 01-run-the-engine.
zenbpm:
grpcHost: localhost
grpcPort: 9090
grpcPlaintext: true # local/dev only; use TLS in production
jobWorkerEnabled: true # connect and register @JobWorker beans on startup
Loading