From 820e297e7f54d57add6631d80bcb7a82e711d280 Mon Sep 17 00:00:00 2001 From: triskill Date: Tue, 11 Aug 2026 17:10:44 +0200 Subject: [PATCH] docs(#747): update java client documentation --- README.md | 254 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 150 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index ffbc688..f9ff59c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ # ZenBPM Java Client -> Spring Boot starter and core REST/gRPC client to interact with the ZenBPM process engine. Includes auto-configuration, OpenAPI-generated REST client, optional gRPC job workers, logging and OpenTelemetry hooks. +> Spring Boot starter for ZenBPM, plus a core artifact containing the generated REST APIs and gRPC stubs. Spring auto-configuration, `@JobWorker`, logging, and OpenTelemetry hooks are provided by the starter; gRPC workers also require a `ManagedChannel` provider. ## Features -* Project uses JDK 8+ and Spring Boot 2.7+ to support legacy systems, but can be easily adjusted to the newest JDK and Spring versions (see pom.xml). * Spring Boot auto-configuration (drop-in starter) -* REST client (`ApiClient` + typed APIs generated from OpenAPI) -* gRPC job workers via `@JobWorker` and ZenbpmJobWorkerManager +* REST client (`ApiClient` plus typed APIs generated from OpenAPI) +* gRPC job workers via `@JobWorker` and `ZenbpmJobWorkerManager` * OpenTelemetry interceptors for REST and spans for gRPC * Configurable HTTP/gRPC logging ## Build this project -This repo uses [mise ](https://mise.jdx.dev/) (`mise.toml`) to pin the local Java and Maven versions used by CI. +This repo uses [mise](https://mise.jdx.dev/) (`mise.toml`) to pin the local Java and Maven versions used by CI. First-time setup: @@ -33,7 +32,7 @@ This runs `mvn -B clean package` with pinned Temurin 17 and Maven, without requi For release validation, set the Maven artifact version from a release tag before building: ```bash -RELEASE_TAG=v1.4.0 mise run set-release-version +RELEASE_TAG=v1.5.0 mise run set-release-version mise run verify-release ``` @@ -41,7 +40,7 @@ The release workflow downloads backend OpenAPI/proto sources before running thes ## Releasing -Releases are triggered by the ZenBPM release orchestrator with `workflow_dispatch` input `version` set to the backend release tag, for example `v1.4.0`. +Releases are triggered by the ZenBPM release orchestrator with `workflow_dispatch` input `version` set to the backend release tag, for example `v1.5.0`. The workflow downloads `openapi/api.yaml` and `pkg/zenclient/proto/zenbpm.proto` from the matching `pbinitiative/zenbpm` tag, sets Maven artifact versions from that tag without the `v` prefix, commits the generated release inputs, tags this repository, creates a GitHub Release, and publishes artifacts to Maven Central under `org.pbinitiative.zenbpm`. @@ -49,136 +48,183 @@ Required repository secrets: `APP_ID_ZENBPM_RELEASE`, `APP_PRIVATE_KEY_ZENBPM_RE ## Getting started -Add the starter to your application and the core client as needed. +The Java client uses Maven group and Java package prefix `org.pbinitiative.zenbpm`. + +### Compatibility + +The ZenBPM Java client has been tested on: + +- Java 17 and Spring Boot 3 +- Java 8 and Spring Boot 2.7 + +The examples below target Java 17 and Spring Boot 3 and intentionally use modern Java syntax. + +### Dependencies + +For a minimal Spring Boot application, use the standard Boot starter together with the ZenBPM starter and a gRPC channel provider: -Maven: ```xml - - org.pbinitiative.zenbpm - zenbpm-spring-boot-starter - ${project.version} - - - org.pbinitiative.zenbpm - zenbpm-client-core - ${project.version} - + + org.springframework.boot + spring-boot-starter-parent + 3.5.16 + + + + + 17 + + + + + org.springframework.boot + spring-boot-starter + + + org.pbinitiative.zenbpm + zenbpm-spring-boot-starter + 1.5.0 + + + io.grpc + grpc-netty-shaded + 1.78.0 + + ``` -Configure connection settings in application.yml +Do **not** directly declare `org.pbinitiative.zenbpm:zenbpm-client-core` in a Spring application: `zenbpm-spring-boot-starter` brings `zenbpm-client-core` transitively. Client 1.5.0 supplies the gRPC APIs and stubs but no `ManagedChannel` provider, so `grpc-netty-shaded` is required when using `@JobWorker`; it is not an optional worker transport. -values shown in `zenbpm` section are defaults. +A non-Spring application may instead declare `org.pbinitiative.zenbpm:zenbpm-client-core:1.5.0` to use the generated REST APIs or gRPC stubs, but it must construct and configure the clients itself. gRPC use still requires a channel provider. `@JobWorker` and its auto-configuration are provided by the Spring Boot starter. + +### Configuration + +This tested `application.yml` uses kebab-case Spring properties and environment overrides for the engine endpoints: -`logging` section configures logging for rest and grpc clients separately. - - `DEBUG` levels expose headers of calls and responses. - - `TRACE` level exposes full request and response bodies. Never use this in production! ```yaml zenbpm: - restUrl: http://localhost:8080/v1 - restLoggingEnabled: true - grpcHost: localhost - grpcPort: 9090 - grpcPlaintext: true - grpcLoggingEnabled: true - jobWorkerEnabled: true - -otel.sdk.disabled: true - -logging: - level: - root: INFO - org.pbinitiative.zenbpm.rest: TRACE - org.pbinitiative.zenbpm.grpc: DEBUG - + rest-url: ${ZENBPM_REST_URL:http://localhost:8080/v1} + rest-logging-enabled: false + grpc-host: ${ZENBPM_GRPC_HOST:localhost} + grpc-port: ${ZENBPM_GRPC_PORT:9090} + # Local development only. Use gRPC TLS and an HTTPS REST URL in other environments. + grpc-plaintext: true + grpc-logging-enabled: false + job-worker-enabled: true + +otel: + sdk: + disabled: true ``` -## Working examples +This sample explicitly disables client logging. Set both logging flags to `false` in production, especially when process variables may contain sensitive data. If REST logging is enabled, OkHttp `BASIC` logging at DEBUG emits request/response lines, not headers or bodies. At TRACE, REST `BODY` logging can expose full HTTP data, and gRPC logging can expose job variables and results. + +### REST: deploy and start + +Place the example classes in the same package as, or a child package of, your `@SpringBootApplication` class so Spring discovers them. Replace the example `package` declaration with your application's package when needed. -### 1) Use REST APIs -Inject the provided ZenbpmClientService to obtain the ApiClient, then create a typed API. +`ZenbpmClientService` supplies the configured `ApiClient`. Deploy a `java.io.File` with `ProcessDefinitionApi`, then start with a business key and variables in `CreateProcessInstanceRequest`: ```java -import org.springframework.stereotype.Service; -import org.springframework.beans.factory.annotation.Autowired; -import org.pbinitiative.zenbpm.rest.ZenbpmClientService; -import org.pbinitiative.zenbpm.client.ApiException; +package com.example.zenbpmverification; + +import java.io.File; +import java.util.Map; + import org.pbinitiative.zenbpm.client.ApiClient; +import org.pbinitiative.zenbpm.client.ApiException; import org.pbinitiative.zenbpm.client.api.ProcessDefinitionApi; import org.pbinitiative.zenbpm.client.api.ProcessInstanceApi; import org.pbinitiative.zenbpm.client.api.dto.CreateProcessInstanceRequest; - -import java.util.HashMap; -import java.util.Map; +import org.pbinitiative.zenbpm.client.api.dto.ProcessInstance; +import org.pbinitiative.zenbpm.rest.ZenbpmClientService; +import org.springframework.stereotype.Service; @Service -public class MyService { - @Autowired - private ZenbpmClientService zenbpm; +public class ZenbpmRestService { + + private final ApiClient apiClient; + private final ProcessDefinitionApi processDefinitionApi; + private final ProcessInstanceApi processInstanceApi; + + public ZenbpmRestService(ZenbpmClientService zenbpmClientService) { + this.apiClient = zenbpmClientService.getApiClient(); + this.processDefinitionApi = new ProcessDefinitionApi(apiClient); + this.processInstanceApi = new ProcessInstanceApi(apiClient); + } + + public long deploy(File bpmnFile) throws ApiException { + return processDefinitionApi.createProcessDefinition(bpmnFile).getProcessDefinitionKey(); + } + + public ProcessInstance start( + long processDefinitionKey, + String businessKey, + Map variables) throws ApiException { + CreateProcessInstanceRequest request = new CreateProcessInstanceRequest() + .processDefinitionKey(processDefinitionKey) + .businessKey(businessKey) + .variables(variables); + + return processInstanceApi.createProcessInstance(request); + } + + public ProcessInstance get(long processInstanceKey) throws ApiException { + return processInstanceApi.getProcessInstance(processInstanceKey); + } +} +``` - public Long deployExampleProcess() throws ApiException { - ApiClient apiClient = zenbpm.getApiClient(); - ProcessDefinitionApi defApi = new ProcessDefinitionApi(apiClient); +### gRPC worker - // Example: create a process definition from a BPMN string (adjust to your endpoint contract) - String bpmnXml = "..."; - Long definitionKey = defApi.createProcessDefinition(bpmnXml).getProcessDefinitionKey(); - return definitionKey; - } +The worker must be a discovered Spring bean. This example validates that `email` is a nonblank `String` and returns a mock completion: - public void startMyProcess() throws ApiException { - ApiClient apiClient = zenbpm.getApiClient(); - ProcessInstanceApi piApi = new ProcessInstanceApi(apiClient); +```java +package com.example.zenbpmverification; - Map vars = new HashMap<>(); - vars.put("orderId", 12345L); +import java.util.Map; - CreateProcessInstanceRequest req = new CreateProcessInstanceRequest() - .processDefinitionKey(123456L) - .variables(vars); +import org.pbinitiative.zenbpm.grpc.JobContext; +import org.pbinitiative.zenbpm.grpc.JobWorker; +import org.springframework.stereotype.Component; + +@Component +public class EmailWorker { - piApi.createProcessInstance(req); - } + /** Returns a mock confirmation; this worker does not send an actual email. */ + @JobWorker("send-email") + public Map sendEmail(JobContext context) { + Object emailValue = context.getVariables().get("email"); + if (!(emailValue instanceof String email) || email.isBlank()) { + throw new IllegalArgumentException("Job variable 'email' must be a nonblank String"); + } + + return Map.of( + "emailSent", true, + "confirmation", "Mock email confirmation for " + email); + } } ``` -Notes: -- Available typed APIs include ProcessDefinitionApi, ProcessInstanceApi, JobApi, MessageApi, etc. Construct them with the provided ApiClient. -- Methods and DTOs come from the generated package `org.pbinitiative.zenbpm.client.api` and `org.pbinitiative.zenbpm.client.api.dto`. +An exception from the method fails the job. The returned map is serialized as job-completion output variables. The worker manager connects only when `zenbpm.job-worker-enabled` is true **and** at least one annotated worker has been discovered. -### 2) Register a gRPC job worker -Create a Spring bean with a method annotated by `@JobWorker`. Accepted method signatures: -- no parameters -- one parameter of type `org.pbinitiative.zenbpm.proto.Zenbpm.WaitingJob` -- one parameter of type `org.pbinitiative.zenbpm.grpc.JobContext` -- one parameter of type `Map` +### Tested flow -Return value can be any object and will be serialized as variables for job completion. Throwing an exception fails the job. +The BPMN service task's job type must match the annotation. Declare `xmlns:zenbpm="http://zenbpm.pbinitiative.org/1.0"` on the BPMN definitions and place the task definition and output mappings inside the service task's extension elements: -```java -import org.springframework.stereotype.Component; -import org.pbinitiative.zenbpm.grpc.JobWorker; -import org.pbinitiative.zenbpm.grpc.JobContext; -import java.util.Map; -import java.util.HashMap; - -@Component -public class EmailWorker { - @JobWorker("send-email") - public Map handleJob(JobContext ctx) { - Map vars = ctx.getVariables(); - String to = (String) vars.get("email"); - - // send email ... - - Map result = new HashMap<>(); - result.put("success", true); - result.put("message", "Email to " + to + " mocked successfully"); - return result; - } -} +```xml + + + + + + + + + ``` -The gRPC worker manager connects on application start if `zenbpm.jobWorkerEnabled` is true. +Deploy the BPMN `File` and start the instance through `ZenbpmRestService`, passing `Map.of("email", "customer@example.com")` and a business key. The `EmailWorker` then receives and completes the `send-email` job over gRPC; the mappings copy its `emailSent` and `confirmation` outputs into process variables. ---