@nanobpm/nano-ide-app-embedded-graalvm-native 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/nano-ide.ext.json CHANGED
@@ -14,7 +14,8 @@
14
14
  "templates": [
15
15
  {
16
16
  "id": "embedded-graalvm-starter",
17
- "label": "KYC microservice (GraalVM native) — same demo, ~30 MB standalone binary (ADR 0005)"
17
+ "label": "KYC microservice (GraalVM native)",
18
+ "description": "Same demo, ~30 MB standalone binary (ADR 0005)"
18
19
  }
19
20
  ],
20
21
  "toolchain": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanobpm/nano-ide-app-embedded-graalvm-native",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "GraalVM Native Image variant of the embedded Nano JVM template: same Bernd-in-JVM code, compiled to a single standalone binary (~30 MB, no JDK required at runtime).",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -31,8 +31,8 @@ and run.
31
31
 
32
32
  ```sh
33
33
  ./microservice/target/kyc-microservice
34
- # defaults to ws://localhost:8080/falcon; override:
35
- FALCON_URL=ws://gateway.example.com/falcon ./microservice/target/kyc-microservice
34
+ # defaults to http://localhost:8080 (Camunda REST); override:
35
+ CAMUNDA_REST_ADDRESS=http://gateway.example.com:8080 ./microservice/target/kyc-microservice
36
36
  ```
37
37
 
38
38
  ## What's inside the binary
@@ -43,15 +43,14 @@ FALCON_URL=ws://gateway.example.com/falcon ./microservice/target/kyc-microservic
43
43
  - The Nano engine's wasm blob shipped inside `nano-bernd` (kept by
44
44
  `-H:IncludeResources=nano-bernd/.*`)
45
45
  - Chicory (pure-Java wasm interpreter — AOT-compiles cleanly)
46
- - Jackson + java.net.http WebSocket for the outer Falcon transport
46
+ - Stock `io.camunda:camunda-client-java` for the outer REST job worker
47
47
 
48
48
  ## Reflection config
49
49
 
50
50
  `microservice/src/main/resources/META-INF/native-image/reflect-config.json`
51
51
  flags the `EmbeddedEngine`, `ActivatedJob`, and `WasmManifest` types
52
- because `EmbeddedNanoTransport` (in `camunda-client-java-falcon`) uses
53
- reflection to talk to them — Graal's closed-world compilation needs this
54
- so the methods aren't stripped.
52
+ because `nano-bernd` uses reflection to talk to them — Graal's
53
+ closed-world compilation needs this so the methods aren't stripped.
55
54
 
56
55
  ## Notes
57
56
 
@@ -22,15 +22,15 @@
22
22
  <maven.compiler.source>21</maven.compiler.source>
23
23
  <maven.compiler.target>21</maven.compiler.target>
24
24
 
25
- <camunda-client.version>1.1.0</camunda-client.version>
25
+ <camunda-client.version>8.9.11</camunda-client.version>
26
26
  <nano-bernd.version>0.2.0</nano-bernd.version>
27
27
  <native.maven.plugin.version>0.10.3</native.maven.plugin.version>
28
28
  </properties>
29
29
 
30
30
  <dependencies>
31
31
  <dependency>
32
- <groupId>io.github.jwulf</groupId>
33
- <artifactId>camunda-client-java-falcon</artifactId>
32
+ <groupId>io.camunda</groupId>
33
+ <artifactId>camunda-client-java</artifactId>
34
34
  <version>${camunda-client.version}</version>
35
35
  </dependency>
36
36
  <dependency>
@@ -1,15 +1,15 @@
1
1
  /*
2
2
  * KYC Microservice — embedded-Nano demo (ADR 0005).
3
3
  *
4
- * [ Nano IDE gateway ] [ This JVM process ]
5
- * │
4
+ * [ Outer BPMN engine ] [ This JVM process ]
5
+ * │
6
6
  * models/onboarding.bpmn ┌─ OuterKycService ─┐
7
- * (deployed by IDE) Falcon WebSocket
7
+ * (deployed to the engine) Camunda REST
8
8
  * │ │ verify_kyc worker │
9
- * │ Falcon WebSocket (NanoTransport.falcon)│
10
- * │◄──────────────── verify_kyc ───────────┤
11
- * │ │ │
12
- * │ │ ▼
9
+ * │ stock Camunda REST (activateJobs)
10
+ * │◄──────────────── verify_kyc ─────────┤
11
+ * │ │ │
12
+ * │ │ ▼
13
13
  * │ │ ┌─ InnerKycService ─┐
14
14
  * │ │ │ Bernd embedded │
15
15
  * │ │ │ engine + kyc.bpmn │
@@ -23,10 +23,14 @@
23
23
  * │ │ └───────────────────┘
24
24
  * │◄──── completeJob(decision) ──────────┤
25
25
  *
26
+ * The outer engine can be Camunda 8, Camunda Self-Managed, or a Nano
27
+ * gateway — we only speak stock Camunda REST on the outer path.
28
+ *
26
29
  * Two bounded contexts, two files:
27
- * - OuterKycService owns the Falcon wire-up. Onboarding team's boundary.
28
- * - InnerKycService owns the embedded engine + inner flow. Compliance team's
29
- * boundary. Trivially extractable into its own process.
30
+ * - OuterKycService owns the REST job worker wire-up. Onboarding team's
31
+ * boundary.
32
+ * - InnerKycService owns the embedded engine + inner flow. Compliance
33
+ * team's boundary. Trivially extractable into its own process.
30
34
  *
31
35
  * This class only wires them together and keeps the JVM alive.
32
36
  */
@@ -37,15 +41,17 @@ import java.util.concurrent.CountDownLatch;
37
41
 
38
42
  public final class KycMicroservice {
39
43
 
40
- // Set FALCON_URL to override; defaults to the Nano IDE gateway's Falcon endpoint.
41
- private static final String DEFAULT_FALCON_URL = "ws://localhost:8080/falcon";
44
+ // Set CAMUNDA_REST_ADDRESS to override; defaults to the Nano IDE gateway's
45
+ // REST endpoint. A stock Camunda 8 gateway on :8080 also works (same REST
46
+ // API).
47
+ private static final String DEFAULT_REST_ADDRESS = "http://localhost:8080";
42
48
 
43
49
  public static void main(final String[] args) throws Exception {
44
- final URI falconUrl = URI.create(
45
- System.getenv().getOrDefault("FALCON_URL", DEFAULT_FALCON_URL));
50
+ final URI restAddress = URI.create(
51
+ System.getenv().getOrDefault("CAMUNDA_REST_ADDRESS", DEFAULT_REST_ADDRESS));
46
52
 
47
53
  final InnerKycService inner = InnerKycService.boot();
48
- final OuterKycService outer = OuterKycService.boot(falconUrl, inner);
54
+ final OuterKycService outer = OuterKycService.boot(restAddress, inner);
49
55
 
50
56
  Runtime.getRuntime().addShutdownHook(new Thread(() -> {
51
57
  outer.close();
@@ -1,70 +1,78 @@
1
1
  /*
2
- * Outer KYC service — wires the onboarding gateway's `verify_kyc` job type to
3
- * the InnerKycService over Falcon.
2
+ * Outer KYC service — wires the outer engine's `verify_kyc` job type to the
3
+ * InnerKycService over stock Camunda REST.
4
4
  *
5
- * Bounded context: onboarding owns the outer gateway (the deployed
6
- * onboarding.bpmn, this is the service task that satisfies it). This class
5
+ * Bounded context: onboarding owns the outer engine (the deployed
6
+ * onboarding.bpmn; this is the service task that satisfies it). This class
7
7
  * knows nothing about how the KYC decision is produced — it just delegates
8
8
  * to InnerKycService.verify(customerId) and completes the outer job with the
9
9
  * returned decision.
10
10
  *
11
- * If InnerKycService moved out to its own JVM/process, the only thing that
12
- * changes here is the field type + how it's injected the verify call site
13
- * stays intact.
11
+ * The outer engine can be Camunda 8, Camunda Self-Managed, or a Nano
12
+ * gateway we only speak stock Camunda REST here. If InnerKycService moved
13
+ * out to its own JVM/process, the only thing that changes here is the field
14
+ * type + how it's injected — the verify call site stays intact.
14
15
  *
15
- * Concurrency: maxJobs=1 (serial). The InnerKycService's embedded engine is
16
- * driver-bound; handling multiple concurrent outer verify_kyc activations
17
- * would let inner activateJobs polls race across KycContexts. Scale
18
- * horizontally with more microservice instances.
16
+ * Concurrency: maxJobsActive=1 (serial). The InnerKycService's embedded
17
+ * engine is driver-bound; handling multiple concurrent outer verify_kyc
18
+ * activations would let inner activateJobs polls race across KycContexts.
19
+ * Scale horizontally with more microservice instances.
19
20
  */
20
21
  package com.example;
21
22
 
22
- import com.fasterxml.jackson.databind.JsonNode;
23
- import com.nanobpm.camunda.falcon.FalconTransport;
24
- import com.nanobpm.camunda.transport.NanoTransport;
23
+ import io.camunda.client.CamundaClient;
24
+ import io.camunda.client.api.response.ActivatedJob;
25
+ import io.camunda.client.api.worker.JobWorker;
25
26
  import java.net.URI;
27
+ import java.time.Duration;
26
28
  import java.util.Map;
27
- import java.util.concurrent.TimeUnit;
28
29
 
29
30
  public final class OuterKycService implements AutoCloseable {
30
31
 
31
32
  private static final String OUTER_JOB_TYPE = "verify_kyc";
32
33
  private static final String OUTER_WORKER = "kyc-microservice";
33
- private static final long JOB_LEASE_MS = 30_000L;
34
- private static final long CALL_TIMEOUT_SECONDS = 5L;
34
+ private static final Duration JOB_LEASE = Duration.ofSeconds(30);
35
+ private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(30);
35
36
 
36
- private final NanoTransport transport;
37
+ private final CamundaClient client;
37
38
  private final InnerKycService inner;
39
+ private JobWorker worker;
38
40
 
39
- private OuterKycService(final NanoTransport transport, final InnerKycService inner) {
40
- this.transport = transport;
41
+ private OuterKycService(final CamundaClient client, final InnerKycService inner) {
42
+ this.client = client;
41
43
  this.inner = inner;
42
44
  }
43
45
 
44
46
  /**
45
- * Open a Falcon connection to the onboarding gateway and subscribe to
46
- * verify_kyc. Delegates every activation to {@code inner}.
47
+ * Open a stock Camunda REST connection to the outer engine and subscribe
48
+ * to verify_kyc. Delegates every activation to {@code inner}.
47
49
  */
48
- public static OuterKycService boot(final URI falconUrl, final InnerKycService inner)
49
- throws Exception {
50
- final NanoTransport transport = NanoTransport.falcon(falconUrl);
51
- transport.connect().get(CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
52
- System.out.println("[outer] connected to onboarding gateway at " + falconUrl);
50
+ public static OuterKycService boot(final URI restAddress, final InnerKycService inner) {
51
+ final CamundaClient client = CamundaClient.newClientBuilder()
52
+ .restAddress(restAddress)
53
+ .preferRestOverGrpc(true)
54
+ .defaultRequestTimeout(REQUEST_TIMEOUT)
55
+ .build();
56
+ System.out.println("[outer] connected to onboarding engine at " + restAddress);
53
57
 
54
- final OuterKycService service = new OuterKycService(transport, inner);
55
- transport.subscribe(new FalconTransport.Subscription(
56
- OUTER_JOB_TYPE, OUTER_WORKER, 1, JOB_LEASE_MS, null, service::handleVerifyKyc))
57
- .get(CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
58
+ final OuterKycService service = new OuterKycService(client, inner);
59
+ service.worker = client.newWorker()
60
+ .jobType(OUTER_JOB_TYPE)
61
+ .handler((jobClient, job) -> service.handleVerifyKyc(job))
62
+ .name(OUTER_WORKER)
63
+ .maxJobsActive(1)
64
+ .timeout(JOB_LEASE)
65
+ .open();
58
66
  System.out.println(
59
67
  "[outer] subscribed to " + OUTER_JOB_TYPE
60
- + " — start an onboarding instance in the IDE");
68
+ + " — start an onboarding instance");
61
69
  return service;
62
70
  }
63
71
 
64
- private void handleVerifyKyc(final JsonNode job) {
65
- final String outerJobKey = job.get("jobKey").asText();
66
- final JsonNode vars = job.path("variables");
67
- final String customerId = vars.path("customerId").asText("cust-unknown");
72
+ private void handleVerifyKyc(final ActivatedJob job) {
73
+ final long outerJobKey = job.getKey();
74
+ final Map<String, Object> vars = job.getVariablesAsMap();
75
+ final String customerId = String.valueOf(vars.getOrDefault("customerId", "cust-unknown"));
68
76
 
69
77
  System.out.println();
70
78
  System.out.println("═══════════════════════════════════════════════════════════════");
@@ -76,7 +84,7 @@ public final class OuterKycService implements AutoCloseable {
76
84
  try {
77
85
  decision = inner.verify(customerId);
78
86
  } catch (final Exception e) {
79
- // Do NOT complete the outer job — let Nano's job timeout drive a retry.
87
+ // Do NOT complete the outer job — let the engine's job timeout drive a retry.
80
88
  System.err.println("[inner] flow failed for customer=" + customerId + ": " + e);
81
89
  e.printStackTrace(System.err);
82
90
  System.err.println("[outer] NOT completing " + OUTER_JOB_TYPE
@@ -88,12 +96,10 @@ public final class OuterKycService implements AutoCloseable {
88
96
  System.out.println("═══════════════════════════════════════════════════════════════");
89
97
 
90
98
  try {
91
- // ABI v2 embedded engine ignores completeJob variables; but the OUTER
92
- // gateway is the full Nano engine and does accept them, so the
93
- // exclusive gateway in onboarding.bpmn can branch on `decision`.
94
- transport
95
- .completeJob(outerJobKey, Map.of("decision", decision, "customerId", customerId))
96
- .get(CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
99
+ client.newCompleteCommand(job)
100
+ .variables(Map.of("decision", decision, "customerId", customerId))
101
+ .send()
102
+ .join();
97
103
  } catch (final Exception e) {
98
104
  System.err.println("[outer] complete failed: " + e.getMessage());
99
105
  }
@@ -101,10 +107,9 @@ public final class OuterKycService implements AutoCloseable {
101
107
 
102
108
  @Override
103
109
  public void close() {
104
- try {
105
- transport.close();
106
- } catch (final Exception ignored) {
107
- // best effort
110
+ if (worker != null) {
111
+ try { worker.close(); } catch (final Exception ignored) { /* best effort */ }
108
112
  }
113
+ try { client.close(); } catch (final Exception ignored) { /* best effort */ }
109
114
  }
110
115
  }