Two different questions hide in there:
Are REST calls from BPM thread safe? Yes - every service execution runs on its own thread with its own BPMRESTRequest / HttpURLConnection objects; nothing is shared between concurrent executions unless you put it into a static Java field or a shared business object. Java integrations must be written thread safe (no mutable statics; connection pools instead of a shared connection), because the same class serves parallel executions.
How is concurrency handled?
- Inbound (clients calling BPM's REST API): concurrent calls are served by the WebContainer thread pool; the engine serialises conflicting work on the same instance with row locks - two clients finishing the same task at the same time: one succeeds, the other gets an error (task already completed / CWTBG0019E optimistic lock) - handle it by re-reading. Idempotency is your job: use a business key (the request id) to detect duplicate starts.
- Outbound (BPM calling REST services): the number of parallel calls equals the number of parallel service executions (thread pools: WebContainer for coach-triggered services, event manager for system tasks / UCAs); cap it with the event manager thread pool and with the external system's rate limits (a retry with back-off in a wrapper service, or an API gateway in front). Connection reuse: HttpURLConnection keeps alive per JVM; long-running or slow endpoints need explicit timeouts, otherwise threads pile up.
- Transactions: an outbound REST call is not transactional; if the BPD step fails after the call, the call is not rolled back - make the target idempotent (PUT with a key, or check-before-create) so that retry of the failed step is safe.
- Same instance, parallel branches: two branches calling REST and writing the same variable - by-value mapping means the last one to complete wins; write to different variables and merge.
// wrapper with timeout and retry with back-off (server-side script)
function callWithRetry(fn, attempts) {
for (var i = 1; ; i++) {
try { return fn(); }
catch (e) { if (i >= attempts || !/timed out|503|429/i.test(String(e))) throw e; java.lang.Thread.sleep(500 * Math.pow(2, i)); }
}
}
tw.local.result = callWithRetry(function () { var c = new java.net.URL(tw.local.url).openConnection(); c.setConnectTimeout(5000); c.setReadTimeout(20000); /* ... */ return read(c); }, 4);References