• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #349

03 Aug 2026 05:42AM UTC coverage: 68.221% (+0.08%) from 68.143%
#349

push

github

web-flow
Add Standalone Activities to Temporal Nexus Operation Handler (#2918)

* Enable Nexus activity operations without regressing workflow updates

Compose standalone activity support with the workflow-update Nexus model already present on master. Shared token, callback, link, client, and cancellation paths retain both operation families.

Constraint: Preserve the workflow-update token and API contracts from master
Rejected: Choose one conflict side | each side would drop a supported Nexus operation family
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep activity execution at token type 2 and workflow update at token type 3
Tested: Focused temporal-sdk token, link, invoker, client, async activity, and cancellation tests
Not-tested: Full repository suite and real-server-only standalone activity cases

* Make sure we test attaching

* Make sure to handle ActivityAlreadyStartedException

* Add test with SANO

* Bump test server

7220 of 12610 branches covered (57.26%)

Branch coverage included in aggregate %.

182 of 283 new or added lines in 15 files covered. (64.31%)

13 existing lines in 3 files now uncovered.

29918 of 41828 relevant lines covered (71.53%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.2
/temporal-sdk/src/main/java/io/temporal/nexus/TemporalNexusClientImpl.java
1
package io.temporal.nexus;
2

3
import com.fasterxml.jackson.core.JsonProcessingException;
4
import com.google.common.base.Strings;
5
import io.nexusrpc.OperationException;
6
import io.nexusrpc.handler.HandlerException;
7
import io.nexusrpc.handler.HandlerException.RetryBehavior;
8
import io.nexusrpc.handler.OperationContext;
9
import io.nexusrpc.handler.OperationStartDetails;
10
import io.temporal.api.common.v1.Payload;
11
import io.temporal.api.common.v1.WorkflowExecution;
12
import io.temporal.client.ActivityClient;
13
import io.temporal.client.ActivityClientOptions;
14
import io.temporal.client.StartActivityOptions;
15
import io.temporal.client.UpdateOptions;
16
import io.temporal.client.WorkflowClient;
17
import io.temporal.client.WorkflowOptions;
18
import io.temporal.client.WorkflowStub;
19
import io.temporal.client.WorkflowTargetOptions;
20
import io.temporal.client.WorkflowUpdateException;
21
import io.temporal.client.WorkflowUpdateHandle;
22
import io.temporal.client.WorkflowUpdateStage;
23
import io.temporal.common.Experimental;
24
import io.temporal.common.context.ContextPropagator;
25
import io.temporal.common.interceptors.ActivityClientCallsInterceptor;
26
import io.temporal.common.interceptors.Header;
27
import io.temporal.internal.client.ActivityClientInternal;
28
import io.temporal.internal.client.NexusStartActivityResponse;
29
import io.temporal.internal.client.NexusStartWorkflowResponse;
30
import io.temporal.internal.nexus.CurrentNexusOperationContext;
31
import io.temporal.internal.nexus.InternalNexusOperationContext;
32
import io.temporal.internal.nexus.NexusOperationMetadata;
33
import io.temporal.internal.nexus.NexusStartActivityHelper;
34
import io.temporal.internal.nexus.NexusStartWorkflowHelper;
35
import io.temporal.internal.nexus.OperationToken;
36
import io.temporal.internal.nexus.OperationTokenUtil;
37
import io.temporal.internal.util.MethodExtractor;
38
import io.temporal.workflow.Functions;
39
import java.lang.reflect.Method;
40
import java.lang.reflect.Type;
41
import java.util.Arrays;
42
import java.util.Collections;
43
import java.util.HashMap;
44
import java.util.List;
45
import java.util.Map;
46
import java.util.Objects;
47
import java.util.concurrent.atomic.AtomicBoolean;
48

49
/** Package-private implementation of {@link TemporalNexusClient}. */
50
@Experimental
51
final class TemporalNexusClientImpl implements TemporalNexusClient {
52

53
  private final WorkflowClient client;
54
  private final OperationContext operationContext;
55
  private final OperationStartDetails operationStartDetails;
56
  private final AtomicBoolean asyncOperationStarted = new AtomicBoolean(false);
1✔
57

58
  TemporalNexusClientImpl(
59
      WorkflowClient client,
60
      OperationContext operationContext,
61
      OperationStartDetails operationStartDetails) {
1✔
62
    this.client = Objects.requireNonNull(client);
1✔
63
    this.operationContext = Objects.requireNonNull(operationContext);
1✔
64
    this.operationStartDetails = Objects.requireNonNull(operationStartDetails);
1✔
65
  }
1✔
66

67
  @Override
68
  public WorkflowClient getWorkflowClient() {
69
    return client;
×
70
  }
71

72
  // ---------- Returning (Func) overloads ----------
73

74
  @Override
75
  public <T, R> TemporalOperationResult<R> startWorkflow(
76
      Class<T> workflowClass, Functions.Func1<T, R> workflowMethod, WorkflowOptions options) {
77
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
78
    return invokeAndReturn(WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub)));
1✔
79
  }
80

81
  @Override
82
  public <T, A1, R> TemporalOperationResult<R> startWorkflow(
83
      Class<T> workflowClass,
84
      Functions.Func2<T, A1, R> workflowMethod,
85
      A1 arg1,
86
      WorkflowOptions options) {
87
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
88
    return invokeAndReturn(
1✔
89
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1)));
1✔
90
  }
91

92
  @Override
93
  public <T, A1, A2, R> TemporalOperationResult<R> startWorkflow(
94
      Class<T> workflowClass,
95
      Functions.Func3<T, A1, A2, R> workflowMethod,
96
      A1 arg1,
97
      A2 arg2,
98
      WorkflowOptions options) {
99
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
100
    return invokeAndReturn(
1✔
101
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2)));
1✔
102
  }
103

104
  @Override
105
  public <T, A1, A2, A3, R> TemporalOperationResult<R> startWorkflow(
106
      Class<T> workflowClass,
107
      Functions.Func4<T, A1, A2, A3, R> workflowMethod,
108
      A1 arg1,
109
      A2 arg2,
110
      A3 arg3,
111
      WorkflowOptions options) {
112
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
113
    return invokeAndReturn(
1✔
114
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2, arg3)));
1✔
115
  }
116

117
  @Override
118
  public <T, A1, A2, A3, A4, R> TemporalOperationResult<R> startWorkflow(
119
      Class<T> workflowClass,
120
      Functions.Func5<T, A1, A2, A3, A4, R> workflowMethod,
121
      A1 arg1,
122
      A2 arg2,
123
      A3 arg3,
124
      A4 arg4,
125
      WorkflowOptions options) {
126
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
127
    return invokeAndReturn(
1✔
128
        WorkflowHandle.fromWorkflowMethod(
1✔
129
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4)));
1✔
130
  }
131

132
  @Override
133
  public <T, A1, A2, A3, A4, A5, R> TemporalOperationResult<R> startWorkflow(
134
      Class<T> workflowClass,
135
      Functions.Func6<T, A1, A2, A3, A4, A5, R> workflowMethod,
136
      A1 arg1,
137
      A2 arg2,
138
      A3 arg3,
139
      A4 arg4,
140
      A5 arg5,
141
      WorkflowOptions options) {
142
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
143
    return invokeAndReturn(
1✔
144
        WorkflowHandle.fromWorkflowMethod(
1✔
145
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4, arg5)));
1✔
146
  }
147

148
  @Override
149
  public <T, A1, A2, A3, A4, A5, A6, R> TemporalOperationResult<R> startWorkflow(
150
      Class<T> workflowClass,
151
      Functions.Func7<T, A1, A2, A3, A4, A5, A6, R> workflowMethod,
152
      A1 arg1,
153
      A2 arg2,
154
      A3 arg3,
155
      A4 arg4,
156
      A5 arg5,
157
      A6 arg6,
158
      WorkflowOptions options) {
159
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
160
    return invokeAndReturn(
1✔
161
        WorkflowHandle.fromWorkflowMethod(
1✔
162
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6)));
1✔
163
  }
164

165
  // ---------- Void (Proc) overloads ----------
166

167
  @Override
168
  public <T> TemporalOperationResult<Void> startWorkflow(
169
      Class<T> workflowClass, Functions.Proc1<T> workflowMethod, WorkflowOptions options) {
170
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
171
    return invokeAndReturn(WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub)));
1✔
172
  }
173

174
  @Override
175
  public <T, A1> TemporalOperationResult<Void> startWorkflow(
176
      Class<T> workflowClass,
177
      Functions.Proc2<T, A1> workflowMethod,
178
      A1 arg1,
179
      WorkflowOptions options) {
180
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
181
    return invokeAndReturn(
1✔
182
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1)));
1✔
183
  }
184

185
  @Override
186
  public <T, A1, A2> TemporalOperationResult<Void> startWorkflow(
187
      Class<T> workflowClass,
188
      Functions.Proc3<T, A1, A2> workflowMethod,
189
      A1 arg1,
190
      A2 arg2,
191
      WorkflowOptions options) {
192
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
193
    return invokeAndReturn(
1✔
194
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2)));
1✔
195
  }
196

197
  @Override
198
  public <T, A1, A2, A3> TemporalOperationResult<Void> startWorkflow(
199
      Class<T> workflowClass,
200
      Functions.Proc4<T, A1, A2, A3> workflowMethod,
201
      A1 arg1,
202
      A2 arg2,
203
      A3 arg3,
204
      WorkflowOptions options) {
205
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
206
    return invokeAndReturn(
1✔
207
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2, arg3)));
1✔
208
  }
209

210
  @Override
211
  public <T, A1, A2, A3, A4> TemporalOperationResult<Void> startWorkflow(
212
      Class<T> workflowClass,
213
      Functions.Proc5<T, A1, A2, A3, A4> workflowMethod,
214
      A1 arg1,
215
      A2 arg2,
216
      A3 arg3,
217
      A4 arg4,
218
      WorkflowOptions options) {
219
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
220
    return invokeAndReturn(
1✔
221
        WorkflowHandle.fromWorkflowMethod(
1✔
222
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4)));
1✔
223
  }
224

225
  @Override
226
  public <T, A1, A2, A3, A4, A5> TemporalOperationResult<Void> startWorkflow(
227
      Class<T> workflowClass,
228
      Functions.Proc6<T, A1, A2, A3, A4, A5> workflowMethod,
229
      A1 arg1,
230
      A2 arg2,
231
      A3 arg3,
232
      A4 arg4,
233
      A5 arg5,
234
      WorkflowOptions options) {
235
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
236
    return invokeAndReturn(
1✔
237
        WorkflowHandle.fromWorkflowMethod(
1✔
238
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4, arg5)));
1✔
239
  }
240

241
  @Override
242
  public <T, A1, A2, A3, A4, A5, A6> TemporalOperationResult<Void> startWorkflow(
243
      Class<T> workflowClass,
244
      Functions.Proc7<T, A1, A2, A3, A4, A5, A6> workflowMethod,
245
      A1 arg1,
246
      A2 arg2,
247
      A3 arg3,
248
      A4 arg4,
249
      A5 arg5,
250
      A6 arg6,
251
      WorkflowOptions options) {
252
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
253
    return invokeAndReturn(
1✔
254
        WorkflowHandle.fromWorkflowMethod(
1✔
255
            () -> workflowMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6)));
1✔
256
  }
257

258
  // ---------- Untyped ----------
259

260
  @Override
261
  public <R> TemporalOperationResult<R> startWorkflow(
262
      String workflowType, Class<R> resultClass, WorkflowOptions options, Object... args) {
263
    return startWorkflow(workflowType, resultClass, null, options, args);
1✔
264
  }
265

266
  @Override
267
  public <R> TemporalOperationResult<R> startWorkflow(
268
      String workflowType,
269
      Class<R> resultClass,
270
      Type resultType,
271
      WorkflowOptions options,
272
      Object... args) {
273
    WorkflowStub stub = client.newUntypedWorkflowStub(workflowType, options);
1✔
274
    WorkflowHandle<R> handle = WorkflowHandle.fromWorkflowStub(stub, resultClass, args);
1✔
275
    return invokeAndReturn(handle);
1✔
276
  }
277

278
  private <R> TemporalOperationResult<R> invokeAndReturn(WorkflowHandle<R> handle) {
279
    markAsyncOperationStarted();
1✔
280
    try {
281
      NexusStartWorkflowResponse response =
1✔
282
          NexusStartWorkflowHelper.startWorkflowAndAttachLinks(
1✔
283
              operationContext,
284
              operationStartDetails,
285
              request -> handle.getInvoker().invoke(request));
1✔
286
      return TemporalOperationResult.async(response.getOperationToken());
1✔
287
    } catch (Throwable t) {
×
288
      // Reset on failure so that if startWorkflowAndAttachLinks throws,
289
      // the handler can retry without being blocked by the guard.
290
      asyncOperationStarted.set(false);
×
291
      throw t;
×
292
    }
293
  }
294

295
  // ---------- Update Workflow overloads ----------
296

297
  @Override
298
  public <T, R> TemporalOperationResult<R> startWorkflowUpdate(
299
      Class<T> workflowClass,
300
      String workflowId,
301
      Functions.Func1<T, R> updateMethod,
302
      UpdateOptions<R> options)
303
      throws OperationException {
304
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
305
    return executeUpdate(
×
306
        options,
307
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
308
  }
309

310
  @Override
311
  public <T, A1, R> TemporalOperationResult<R> startWorkflowUpdate(
312
      Class<T> workflowClass,
313
      String workflowId,
314
      Functions.Func2<T, A1, R> updateMethod,
315
      A1 arg1,
316
      UpdateOptions<R> options)
317
      throws OperationException {
318
    T stub = client.newWorkflowStub(workflowClass, workflowId);
1✔
319
    return executeUpdate(
1✔
320
        options,
321
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
1✔
322
  }
323

324
  @Override
325
  public <T, A1, A2, R> TemporalOperationResult<R> startWorkflowUpdate(
326
      Class<T> workflowClass,
327
      String workflowId,
328
      Functions.Func3<T, A1, A2, R> updateMethod,
329
      A1 arg1,
330
      A2 arg2,
331
      UpdateOptions<R> options)
332
      throws OperationException {
333
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
334
    return executeUpdate(
×
335
        options,
336
        effective ->
337
            WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1, arg2), effective));
×
338
  }
339

340
  @Override
341
  public <T, A1, A2, A3, R> TemporalOperationResult<R> startWorkflowUpdate(
342
      Class<T> workflowClass,
343
      String workflowId,
344
      Functions.Func4<T, A1, A2, A3, R> updateMethod,
345
      A1 arg1,
346
      A2 arg2,
347
      A3 arg3,
348
      UpdateOptions<R> options)
349
      throws OperationException {
350
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
351
    return executeUpdate(
×
352
        options,
353
        effective ->
354
            WorkflowClient.startUpdate(
×
355
                () -> updateMethod.apply(stub, arg1, arg2, arg3), effective));
×
356
  }
357

358
  @Override
359
  public <T, A1, A2, A3, A4, R> TemporalOperationResult<R> startWorkflowUpdate(
360
      Class<T> workflowClass,
361
      String workflowId,
362
      Functions.Func5<T, A1, A2, A3, A4, R> updateMethod,
363
      A1 arg1,
364
      A2 arg2,
365
      A3 arg3,
366
      A4 arg4,
367
      UpdateOptions<R> options)
368
      throws OperationException {
369
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
370
    return executeUpdate(
×
371
        options,
372
        effective ->
373
            WorkflowClient.startUpdate(
×
374
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4), effective));
×
375
  }
376

377
  @Override
378
  public <T, A1, A2, A3, A4, A5, R> TemporalOperationResult<R> startWorkflowUpdate(
379
      Class<T> workflowClass,
380
      String workflowId,
381
      Functions.Func6<T, A1, A2, A3, A4, A5, R> updateMethod,
382
      A1 arg1,
383
      A2 arg2,
384
      A3 arg3,
385
      A4 arg4,
386
      A5 arg5,
387
      UpdateOptions<R> options)
388
      throws OperationException {
389
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
390
    return executeUpdate(
×
391
        options,
392
        effective ->
393
            WorkflowClient.startUpdate(
×
394
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5), effective));
×
395
  }
396

397
  @Override
398
  public <T, A1, A2, A3, A4, A5, A6, R> TemporalOperationResult<R> startWorkflowUpdate(
399
      Class<T> workflowClass,
400
      String workflowId,
401
      Functions.Func7<T, A1, A2, A3, A4, A5, A6, R> updateMethod,
402
      A1 arg1,
403
      A2 arg2,
404
      A3 arg3,
405
      A4 arg4,
406
      A5 arg5,
407
      A6 arg6,
408
      UpdateOptions<R> options)
409
      throws OperationException {
410
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
411
    return executeUpdate(
×
412
        options,
413
        effective ->
414
            WorkflowClient.startUpdate(
×
415
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6), effective));
×
416
  }
417

418
  @Override
419
  public <T> TemporalOperationResult<Void> startWorkflowUpdate(
420
      Class<T> workflowClass,
421
      String workflowId,
422
      Functions.Proc1<T> updateMethod,
423
      UpdateOptions<Void> options)
424
      throws OperationException {
425
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
426
    return executeUpdate(
×
427
        options,
428
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
429
  }
430

431
  @Override
432
  public <T, A1> TemporalOperationResult<Void> startWorkflowUpdate(
433
      Class<T> workflowClass,
434
      String workflowId,
435
      Functions.Proc2<T, A1> updateMethod,
436
      A1 arg1,
437
      UpdateOptions<Void> options)
438
      throws OperationException {
439
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
440
    return executeUpdate(
×
441
        options,
442
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
×
443
  }
444

445
  @Override
446
  public <T, A1, A2> TemporalOperationResult<Void> startWorkflowUpdate(
447
      Class<T> workflowClass,
448
      String workflowId,
449
      Functions.Proc3<T, A1, A2> updateMethod,
450
      A1 arg1,
451
      A2 arg2,
452
      UpdateOptions<Void> options)
453
      throws OperationException {
454
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
455
    return executeUpdate(
×
456
        options,
457
        effective ->
458
            WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1, arg2), effective));
×
459
  }
460

461
  @Override
462
  public <T, A1, A2, A3> TemporalOperationResult<Void> startWorkflowUpdate(
463
      Class<T> workflowClass,
464
      String workflowId,
465
      Functions.Proc4<T, A1, A2, A3> updateMethod,
466
      A1 arg1,
467
      A2 arg2,
468
      A3 arg3,
469
      UpdateOptions<Void> options)
470
      throws OperationException {
471
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
472
    return executeUpdate(
×
473
        options,
474
        effective ->
475
            WorkflowClient.startUpdate(
×
476
                () -> updateMethod.apply(stub, arg1, arg2, arg3), effective));
×
477
  }
478

479
  @Override
480
  public <T, A1, A2, A3, A4> TemporalOperationResult<Void> startWorkflowUpdate(
481
      Class<T> workflowClass,
482
      String workflowId,
483
      Functions.Proc5<T, A1, A2, A3, A4> updateMethod,
484
      A1 arg1,
485
      A2 arg2,
486
      A3 arg3,
487
      A4 arg4,
488
      UpdateOptions<Void> options)
489
      throws OperationException {
490
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
491
    return executeUpdate(
×
492
        options,
493
        effective ->
494
            WorkflowClient.startUpdate(
×
495
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4), effective));
×
496
  }
497

498
  @Override
499
  public <T, A1, A2, A3, A4, A5> TemporalOperationResult<Void> startWorkflowUpdate(
500
      Class<T> workflowClass,
501
      String workflowId,
502
      Functions.Proc6<T, A1, A2, A3, A4, A5> updateMethod,
503
      A1 arg1,
504
      A2 arg2,
505
      A3 arg3,
506
      A4 arg4,
507
      A5 arg5,
508
      UpdateOptions<Void> options)
509
      throws OperationException {
510
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
511
    return executeUpdate(
×
512
        options,
513
        effective ->
514
            WorkflowClient.startUpdate(
×
515
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5), effective));
×
516
  }
517

518
  @Override
519
  public <T, A1, A2, A3, A4, A5, A6> TemporalOperationResult<Void> startWorkflowUpdate(
520
      Class<T> workflowClass,
521
      String workflowId,
522
      Functions.Proc7<T, A1, A2, A3, A4, A5, A6> updateMethod,
523
      A1 arg1,
524
      A2 arg2,
525
      A3 arg3,
526
      A4 arg4,
527
      A5 arg5,
528
      A6 arg6,
529
      UpdateOptions<Void> options)
530
      throws OperationException {
531
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
532
    return executeUpdate(
×
533
        options,
534
        effective ->
535
            WorkflowClient.startUpdate(
×
536
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6), effective));
×
537
  }
538

539
  /** Function that will trigger {@code startUpdate} on overloads */
540
  @FunctionalInterface
541
  private interface UpdateCommand<R> {
542
    WorkflowUpdateHandle<R> triggerUpdate(UpdateOptions<R> options);
543
  }
544

545
  /** Common code for all {@code startWorkflowUpdate} overloads. */
546
  private <R> TemporalOperationResult<R> executeUpdate(
547
      UpdateOptions<R> options, UpdateCommand<R> updateWrapper) throws OperationException {
548

549
    UpdateOptions.Builder<R> effectiveOptsBuilder = UpdateOptions.newBuilder(options);
1✔
550
    String requestId = operationStartDetails.getRequestId();
1✔
551
    if (Strings.isNullOrEmpty(options.getUpdateId())) {
1✔
552
      // if updateId is unset, use requestId - consistent with other SDKs
553
      effectiveOptsBuilder.setUpdateId(requestId);
1✔
554
    }
555
    options = effectiveOptsBuilder.build();
1✔
556
    checkNexusUpdateOptionsValid(options);
1✔
557
    markAsyncOperationStarted();
1✔
558

559
    InternalNexusOperationContext nexusContext = CurrentNexusOperationContext.get();
1✔
560
    try {
561
      String callbackUrl = operationStartDetails.getCallbackUrl();
1✔
562
      if (Strings.isNullOrEmpty(callbackUrl)) {
1!
563
        throw new HandlerException(
×
564
            HandlerException.ErrorType.BAD_REQUEST,
565
            new IllegalArgumentException("callback URL is required for a Nexus operation"));
566
      }
567
      NexusOperationMetadata nexusOperationMetadata =
1✔
568
          new NexusOperationMetadata(
569
              requestId, callbackUrl, operationStartDetails.getCallbackHeaders());
1✔
570
      // set the nexusOperationMetadata and capture operationCompleted
571
      nexusContext.setNexusOperationMetadata(nexusOperationMetadata);
1✔
572
      WorkflowUpdateHandle<R> handle = updateWrapper.triggerUpdate(options);
1✔
573
      if (nexusOperationMetadata.operationCompleted) {
1!
574
        try {
575
          R value = handle.getResult();
1✔
576
          return TemporalOperationResult.sync(value);
1✔
577
        } catch (WorkflowUpdateException e) {
1✔
578
          // Only case where operation is completed but getResult fails is if the update
579
          // fails non-retriably - validation failure - so fail the operation immediately
580
          throw OperationException.failed(e);
1✔
581
        }
582
      }
583
      // regenerate token so it has the actual run ID that update is running on
584
      // previous generation is only to handle completion before handle is returned
585
      String token = "";
×
586
      try {
587
        OperationToken ot =
×
588
            OperationTokenUtil.loadWorkflowUpdateOperationToken(
×
589
                nexusOperationMetadata.operationToken);
590
        token =
×
591
            OperationTokenUtil.generateWorkflowUpdateOperationToken(
×
592
                ot.getNamespace(),
×
593
                ot.getWorkflowId(),
×
594
                handle.getExecution().getRunId(),
×
595
                ot.getUpdateId());
×
596
      } catch (IllegalArgumentException | JsonProcessingException e) {
×
597
        // should not happen, this is all in SDK
598
        throw new HandlerException(
×
599
            HandlerException.ErrorType.INTERNAL, "unexpected error reconstructing token", e);
600
      }
×
601
      return TemporalOperationResult.async(token);
×
602
    } catch (Throwable t) {
1✔
603
      // Reset on failure so that if the update RPC throws, the handler can retry without being
604
      // blocked by the guard.
605
      asyncOperationStarted.set(false);
1✔
606
      throw t;
1✔
607
    } finally {
608
      nexusContext.setNexusOperationMetadata(null);
1✔
609
    }
610
  }
611

612
  /**
613
   * @throws OperationException if the options provided are invalid like missing
614
   *     UpdateName/WorkflowID/etc
615
   */
616
  private <R> void checkNexusUpdateOptionsValid(UpdateOptions<R> options)
617
      throws OperationException {
618
    if (options.getWaitForStage() != WorkflowUpdateStage.ACCEPTED) {
1!
619
      throw new HandlerException(
×
620
          HandlerException.ErrorType.INTERNAL,
621
          "invalid update request",
622
          new IllegalArgumentException(
623
              "nexus op workflow updates only support WorkflowUpdateStageAccepted for async updates"),
624
          RetryBehavior.RETRYABLE);
625
    }
626
    try {
627
      options.validate();
1✔
628
    } catch (IllegalStateException e) {
×
629
      throw new HandlerException(
×
630
          HandlerException.ErrorType.INTERNAL,
631
          "invalid update request",
632
          e,
633
          RetryBehavior.RETRYABLE);
634
    }
1✔
635
  }
1✔
636

637
  private void markAsyncOperationStarted() {
638
    if (!asyncOperationStarted.compareAndSet(false, true)) {
1✔
639
      throw new HandlerException(
1✔
640
          HandlerException.ErrorType.BAD_REQUEST,
641
          new IllegalStateException(
642
              "Only one async operation can be started per operation handler invocation. "
643
                  + "Use getWorkflowClient() for additional workflow interactions."));
644
    }
645
  }
1✔
646

647
  // ---------- Update Workflow overloads for WorkflowExecution ----------
648

649
  @Override
650
  public <T, R> TemporalOperationResult<R> startWorkflowUpdate(
651
      Class<T> workflowClass,
652
      WorkflowExecution execution,
653
      Functions.Func1<T, R> updateMethod,
654
      UpdateOptions<R> options)
655
      throws OperationException {
656
    T stub = newWorkflowStub(workflowClass, execution);
×
657
    return executeUpdate(
×
658
        options,
659
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
660
  }
661

662
  @Override
663
  public <T, A1, R> TemporalOperationResult<R> startWorkflowUpdate(
664
      Class<T> workflowClass,
665
      WorkflowExecution execution,
666
      Functions.Func2<T, A1, R> updateMethod,
667
      A1 arg1,
668
      UpdateOptions<R> options)
669
      throws OperationException {
670
    T stub = newWorkflowStub(workflowClass, execution);
×
671
    return executeUpdate(
×
672
        options,
673
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
×
674
  }
675

676
  @Override
677
  public <T, A1, A2, R> TemporalOperationResult<R> startWorkflowUpdate(
678
      Class<T> workflowClass,
679
      WorkflowExecution execution,
680
      Functions.Func3<T, A1, A2, R> updateMethod,
681
      A1 arg1,
682
      A2 arg2,
683
      UpdateOptions<R> options)
684
      throws OperationException {
685
    T stub = newWorkflowStub(workflowClass, execution);
×
686
    return executeUpdate(
×
687
        options,
688
        effective ->
689
            WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1, arg2), effective));
×
690
  }
691

692
  @Override
693
  public <T, A1, A2, A3, R> TemporalOperationResult<R> startWorkflowUpdate(
694
      Class<T> workflowClass,
695
      WorkflowExecution execution,
696
      Functions.Func4<T, A1, A2, A3, R> updateMethod,
697
      A1 arg1,
698
      A2 arg2,
699
      A3 arg3,
700
      UpdateOptions<R> options)
701
      throws OperationException {
702
    T stub = newWorkflowStub(workflowClass, execution);
×
703
    return executeUpdate(
×
704
        options,
705
        effective ->
706
            WorkflowClient.startUpdate(
×
707
                () -> updateMethod.apply(stub, arg1, arg2, arg3), effective));
×
708
  }
709

710
  @Override
711
  public <T, A1, A2, A3, A4, R> TemporalOperationResult<R> startWorkflowUpdate(
712
      Class<T> workflowClass,
713
      WorkflowExecution execution,
714
      Functions.Func5<T, A1, A2, A3, A4, R> updateMethod,
715
      A1 arg1,
716
      A2 arg2,
717
      A3 arg3,
718
      A4 arg4,
719
      UpdateOptions<R> options)
720
      throws OperationException {
721
    T stub = newWorkflowStub(workflowClass, execution);
×
722
    return executeUpdate(
×
723
        options,
724
        effective ->
725
            WorkflowClient.startUpdate(
×
726
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4), effective));
×
727
  }
728

729
  @Override
730
  public <T, A1, A2, A3, A4, A5, R> TemporalOperationResult<R> startWorkflowUpdate(
731
      Class<T> workflowClass,
732
      WorkflowExecution execution,
733
      Functions.Func6<T, A1, A2, A3, A4, A5, R> updateMethod,
734
      A1 arg1,
735
      A2 arg2,
736
      A3 arg3,
737
      A4 arg4,
738
      A5 arg5,
739
      UpdateOptions<R> options)
740
      throws OperationException {
741
    T stub = newWorkflowStub(workflowClass, execution);
×
742
    return executeUpdate(
×
743
        options,
744
        effective ->
745
            WorkflowClient.startUpdate(
×
746
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5), effective));
×
747
  }
748

749
  @Override
750
  public <T, A1, A2, A3, A4, A5, A6, R> TemporalOperationResult<R> startWorkflowUpdate(
751
      Class<T> workflowClass,
752
      WorkflowExecution execution,
753
      Functions.Func7<T, A1, A2, A3, A4, A5, A6, R> updateMethod,
754
      A1 arg1,
755
      A2 arg2,
756
      A3 arg3,
757
      A4 arg4,
758
      A5 arg5,
759
      A6 arg6,
760
      UpdateOptions<R> options)
761
      throws OperationException {
762
    T stub = newWorkflowStub(workflowClass, execution);
×
763
    return executeUpdate(
×
764
        options,
765
        effective ->
766
            WorkflowClient.startUpdate(
×
767
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6), effective));
×
768
  }
769

770
  @Override
771
  public <T> TemporalOperationResult<Void> startWorkflowUpdate(
772
      Class<T> workflowClass,
773
      WorkflowExecution execution,
774
      Functions.Proc1<T> updateMethod,
775
      UpdateOptions<Void> options)
776
      throws OperationException {
777
    T stub = newWorkflowStub(workflowClass, execution);
×
778
    return executeUpdate(
×
779
        options,
780
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
781
  }
782

783
  @Override
784
  public <T, A1> TemporalOperationResult<Void> startWorkflowUpdate(
785
      Class<T> workflowClass,
786
      WorkflowExecution execution,
787
      Functions.Proc2<T, A1> updateMethod,
788
      A1 arg1,
789
      UpdateOptions<Void> options)
790
      throws OperationException {
791
    T stub = newWorkflowStub(workflowClass, execution);
×
792
    return executeUpdate(
×
793
        options,
794
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
×
795
  }
796

797
  @Override
798
  public <T, A1, A2> TemporalOperationResult<Void> startWorkflowUpdate(
799
      Class<T> workflowClass,
800
      WorkflowExecution execution,
801
      Functions.Proc3<T, A1, A2> updateMethod,
802
      A1 arg1,
803
      A2 arg2,
804
      UpdateOptions<Void> options)
805
      throws OperationException {
806
    T stub = newWorkflowStub(workflowClass, execution);
×
807
    return executeUpdate(
×
808
        options,
809
        effective ->
810
            WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1, arg2), effective));
×
811
  }
812

813
  @Override
814
  public <T, A1, A2, A3> TemporalOperationResult<Void> startWorkflowUpdate(
815
      Class<T> workflowClass,
816
      WorkflowExecution execution,
817
      Functions.Proc4<T, A1, A2, A3> updateMethod,
818
      A1 arg1,
819
      A2 arg2,
820
      A3 arg3,
821
      UpdateOptions<Void> options)
822
      throws OperationException {
823
    T stub = newWorkflowStub(workflowClass, execution);
×
824
    return executeUpdate(
×
825
        options,
826
        effective ->
827
            WorkflowClient.startUpdate(
×
828
                () -> updateMethod.apply(stub, arg1, arg2, arg3), effective));
×
829
  }
830

831
  @Override
832
  public <T, A1, A2, A3, A4> TemporalOperationResult<Void> startWorkflowUpdate(
833
      Class<T> workflowClass,
834
      WorkflowExecution execution,
835
      Functions.Proc5<T, A1, A2, A3, A4> updateMethod,
836
      A1 arg1,
837
      A2 arg2,
838
      A3 arg3,
839
      A4 arg4,
840
      UpdateOptions<Void> options)
841
      throws OperationException {
842
    T stub = newWorkflowStub(workflowClass, execution);
×
843
    return executeUpdate(
×
844
        options,
845
        effective ->
846
            WorkflowClient.startUpdate(
×
847
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4), effective));
×
848
  }
849

850
  @Override
851
  public <T, A1, A2, A3, A4, A5> TemporalOperationResult<Void> startWorkflowUpdate(
852
      Class<T> workflowClass,
853
      WorkflowExecution execution,
854
      Functions.Proc6<T, A1, A2, A3, A4, A5> updateMethod,
855
      A1 arg1,
856
      A2 arg2,
857
      A3 arg3,
858
      A4 arg4,
859
      A5 arg5,
860
      UpdateOptions<Void> options)
861
      throws OperationException {
862
    T stub = newWorkflowStub(workflowClass, execution);
×
863
    return executeUpdate(
×
864
        options,
865
        effective ->
866
            WorkflowClient.startUpdate(
×
867
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5), effective));
×
868
  }
869

870
  @Override
871
  public <T, A1, A2, A3, A4, A5, A6> TemporalOperationResult<Void> startWorkflowUpdate(
872
      Class<T> workflowClass,
873
      WorkflowExecution execution,
874
      Functions.Proc7<T, A1, A2, A3, A4, A5, A6> updateMethod,
875
      A1 arg1,
876
      A2 arg2,
877
      A3 arg3,
878
      A4 arg4,
879
      A5 arg5,
880
      A6 arg6,
881
      UpdateOptions<Void> options)
882
      throws OperationException {
883
    T stub = newWorkflowStub(workflowClass, execution);
×
884
    return executeUpdate(
×
885
        options,
886
        effective ->
887
            WorkflowClient.startUpdate(
×
888
                () -> updateMethod.apply(stub, arg1, arg2, arg3, arg4, arg5, arg6), effective));
×
889
  }
890

891
  private <T> T newWorkflowStub(Class<T> workflowClass, WorkflowExecution execution) {
892
    return client.newWorkflowStub(
×
893
        workflowClass, WorkflowTargetOptions.newBuilder().setWorkflowExecution(execution).build());
×
894
  }
895

896
  // ---------- Activity overloads (Func returning) ----------
897

898
  @Override
899
  public <I, R> TemporalOperationResult<R> startActivity(
900
      Class<I> activityInterface,
901
      Functions.Func1<I, R> activityMethod,
902
      StartActivityOptions options) {
NEW
903
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
904
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
905
    return startActivityImpl(activityType, Collections.emptyList(), options);
×
906
  }
907

908
  @Override
909
  public <I, A1, R> TemporalOperationResult<R> startActivity(
910
      Class<I> activityInterface,
911
      Functions.Func2<I, A1, R> activityMethod,
912
      A1 arg1,
913
      StartActivityOptions options) {
NEW
914
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
915
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
916
    return startActivityImpl(activityType, Collections.singletonList(arg1), options);
×
917
  }
918

919
  @Override
920
  public <I, A1, A2, R> TemporalOperationResult<R> startActivity(
921
      Class<I> activityInterface,
922
      Functions.Func3<I, A1, A2, R> activityMethod,
923
      A1 arg1,
924
      A2 arg2,
925
      StartActivityOptions options) {
NEW
926
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
927
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
928
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2), options);
×
929
  }
930

931
  @Override
932
  public <I, A1, A2, A3, R> TemporalOperationResult<R> startActivity(
933
      Class<I> activityInterface,
934
      Functions.Func4<I, A1, A2, A3, R> activityMethod,
935
      A1 arg1,
936
      A2 arg2,
937
      A3 arg3,
938
      StartActivityOptions options) {
NEW
939
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
940
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
941
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3), options);
×
942
  }
943

944
  @Override
945
  public <I, A1, A2, A3, A4, R> TemporalOperationResult<R> startActivity(
946
      Class<I> activityInterface,
947
      Functions.Func5<I, A1, A2, A3, A4, R> activityMethod,
948
      A1 arg1,
949
      A2 arg2,
950
      A3 arg3,
951
      A4 arg4,
952
      StartActivityOptions options) {
NEW
953
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
954
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
955
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3, arg4), options);
×
956
  }
957

958
  @Override
959
  public <I, A1, A2, A3, A4, A5, R> TemporalOperationResult<R> startActivity(
960
      Class<I> activityInterface,
961
      Functions.Func6<I, A1, A2, A3, A4, A5, R> activityMethod,
962
      A1 arg1,
963
      A2 arg2,
964
      A3 arg3,
965
      A4 arg4,
966
      A5 arg5,
967
      StartActivityOptions options) {
NEW
968
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
969
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
970
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3, arg4, arg5), options);
×
971
  }
972

973
  @Override
974
  public <I, A1, A2, A3, A4, A5, A6, R> TemporalOperationResult<R> startActivity(
975
      Class<I> activityInterface,
976
      Functions.Func7<I, A1, A2, A3, A4, A5, A6, R> activityMethod,
977
      A1 arg1,
978
      A2 arg2,
979
      A3 arg3,
980
      A4 arg4,
981
      A5 arg5,
982
      A6 arg6,
983
      StartActivityOptions options) {
NEW
984
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
985
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
986
    return startActivityImpl(
×
NEW
987
        activityType, Arrays.asList(arg1, arg2, arg3, arg4, arg5, arg6), options);
×
988
  }
989

990
  // ---------- Activity overloads (Proc void) ----------
991

992
  @Override
993
  public <I> TemporalOperationResult<Void> startActivity(
994
      Class<I> activityInterface, Functions.Proc1<I> activityMethod, StartActivityOptions options) {
995
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
1✔
996
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
1✔
997
    return startActivityImpl(activityType, Collections.emptyList(), options);
1✔
998
  }
999

1000
  @Override
1001
  public <I, A1> TemporalOperationResult<Void> startActivity(
1002
      Class<I> activityInterface,
1003
      Functions.Proc2<I, A1> activityMethod,
1004
      A1 arg1,
1005
      StartActivityOptions options) {
NEW
1006
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1007
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1008
    return startActivityImpl(activityType, Collections.singletonList(arg1), options);
×
1009
  }
1010

1011
  @Override
1012
  public <I, A1, A2> TemporalOperationResult<Void> startActivity(
1013
      Class<I> activityInterface,
1014
      Functions.Proc3<I, A1, A2> activityMethod,
1015
      A1 arg1,
1016
      A2 arg2,
1017
      StartActivityOptions options) {
NEW
1018
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1019
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1020
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2), options);
×
1021
  }
1022

1023
  @Override
1024
  public <I, A1, A2, A3> TemporalOperationResult<Void> startActivity(
1025
      Class<I> activityInterface,
1026
      Functions.Proc4<I, A1, A2, A3> activityMethod,
1027
      A1 arg1,
1028
      A2 arg2,
1029
      A3 arg3,
1030
      StartActivityOptions options) {
NEW
1031
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1032
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1033
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3), options);
×
1034
  }
1035

1036
  @Override
1037
  public <I, A1, A2, A3, A4> TemporalOperationResult<Void> startActivity(
1038
      Class<I> activityInterface,
1039
      Functions.Proc5<I, A1, A2, A3, A4> activityMethod,
1040
      A1 arg1,
1041
      A2 arg2,
1042
      A3 arg3,
1043
      A4 arg4,
1044
      StartActivityOptions options) {
NEW
1045
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1046
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1047
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3, arg4), options);
×
1048
  }
1049

1050
  @Override
1051
  public <I, A1, A2, A3, A4, A5> TemporalOperationResult<Void> startActivity(
1052
      Class<I> activityInterface,
1053
      Functions.Proc6<I, A1, A2, A3, A4, A5> activityMethod,
1054
      A1 arg1,
1055
      A2 arg2,
1056
      A3 arg3,
1057
      A4 arg4,
1058
      A5 arg5,
1059
      StartActivityOptions options) {
NEW
1060
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1061
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1062
    return startActivityImpl(activityType, Arrays.asList(arg1, arg2, arg3, arg4, arg5), options);
×
1063
  }
1064

1065
  @Override
1066
  public <I, A1, A2, A3, A4, A5, A6> TemporalOperationResult<Void> startActivity(
1067
      Class<I> activityInterface,
1068
      Functions.Proc7<I, A1, A2, A3, A4, A5, A6> activityMethod,
1069
      A1 arg1,
1070
      A2 arg2,
1071
      A3 arg3,
1072
      A4 arg4,
1073
      A5 arg5,
1074
      A6 arg6,
1075
      StartActivityOptions options) {
NEW
1076
    Method method = MethodExtractor.extract(activityInterface, activityMethod);
×
NEW
1077
    String activityType = MethodExtractor.activityTypeName(activityInterface, method);
×
NEW
1078
    return startActivityImpl(
×
NEW
1079
        activityType, Arrays.asList(arg1, arg2, arg3, arg4, arg5, arg6), options);
×
1080
  }
1081

1082
  // ---------- Activity untyped ----------
1083

1084
  @Override
1085
  public <R> TemporalOperationResult<R> startActivity(
1086
      String activityType, Class<R> resultClass, StartActivityOptions options, Object... args) {
NEW
1087
    List<Object> argList = args == null ? Collections.emptyList() : Arrays.asList(args);
×
NEW
1088
    return startActivityImpl(activityType, argList, options);
×
1089
  }
1090

1091
  private <R> TemporalOperationResult<R> startActivityImpl(
1092
      String activityType, List<Object> args, StartActivityOptions options) {
1093
    markAsyncOperationStarted();
1✔
1094
    InternalNexusOperationContext nexusContext = CurrentNexusOperationContext.get();
1✔
1095
    try {
1096
      NexusOperationMetadata nexusOperationMetadata =
1✔
1097
          new NexusOperationMetadata(
1098
              operationStartDetails.getRequestId(),
1✔
1099
              operationStartDetails.getCallbackUrl(),
1✔
1100
              operationStartDetails.getCallbackHeaders());
1✔
1101
      nexusContext.setNexusOperationMetadata(nexusOperationMetadata);
1✔
1102
      NexusStartActivityResponse response =
1✔
1103
          NexusStartActivityHelper.startActivityAndAttachLinks(
1✔
1104
              operationContext,
1105
              operationStartDetails,
1106
              activityType,
1107
              args,
1108
              options,
1109
              propagatedHeader(),
1✔
1110
              request -> {
1111
                ActivityClientCallsInterceptor.StartActivityInput input =
1✔
1112
                    new ActivityClientCallsInterceptor.StartActivityInput(
1113
                        request.getActivityType(),
1✔
1114
                        request.getArgs(),
1✔
1115
                        request.getOptions(),
1✔
1116
                        request.getHeader());
1✔
1117
                // Build an internal ActivityClient aligned with the surrounding WorkflowClient.
1118
                // User-configured standalone ActivityClient interceptors are not available in the
1119
                // Nexus operation-handler lifecycle.
1120
                ActivityClient activityClient =
1✔
1121
                    ActivityClient.newInstance(
1✔
1122
                        client.getWorkflowServiceStubs(),
1✔
1123
                        ActivityClientOptions.newBuilder()
1✔
1124
                            .setNamespace(client.getOptions().getNamespace())
1✔
1125
                            .setDataConverter(client.getOptions().getDataConverter())
1✔
1126
                            .setIdentity(client.getOptions().getIdentity())
1✔
1127
                            .build());
1✔
1128
                ActivityClientCallsInterceptor.StartActivityOutput out =
1✔
1129
                    ((ActivityClientInternal) activityClient).getInvoker().startActivity(input);
1✔
1130
                // The invoker generated the runId-free token before the start RPC and injected it
1131
                // into the callback headers when a callback URL was supplied. That token cannot
1132
                // include a run ID because the run ID isn't known until after the start RPC
1133
                // returns. The operation token returned to the Nexus caller can — and should —
1134
                // include it, so it's regenerated here from the same activity ID + the run ID the
1135
                // start RPC produced.
1136
                String headerToken = nexusOperationMetadata.operationToken;
1✔
1137
                if (headerToken == null) {
1!
NEW
1138
                  throw new HandlerException(
×
1139
                      HandlerException.ErrorType.INTERNAL,
1140
                      "invoker did not generate a Nexus operation token for activity start",
1141
                      new IllegalStateException(
1142
                          "operationToken is null on NexusOperationMetadata after activity start"));
1143
                }
1144
                String returnToken;
1145
                try {
1146
                  returnToken =
1✔
1147
                      OperationTokenUtil.generateActivityExecutionOperationToken(
1✔
1148
                          out.getActivityId(),
1✔
1149
                          out.getActivityRunId(),
1✔
1150
                          client.getOptions().getNamespace());
1✔
NEW
1151
                } catch (JsonProcessingException e) {
×
NEW
1152
                  throw new HandlerException(
×
1153
                      HandlerException.ErrorType.INTERNAL,
1154
                      "failed to generate activity operation token",
1155
                      e);
1156
                }
1✔
1157
                return new NexusStartActivityResponse(
1✔
1158
                    out.getActivityId(), out.getActivityRunId(), returnToken);
1✔
1159
              });
1160
      return TemporalOperationResult.async(response.getOperationToken());
1✔
NEW
1161
    } catch (Throwable t) {
×
1162
      // Reset on failure so that if the activity start throws, the handler can retry without
1163
      // being blocked by the guard.
NEW
1164
      asyncOperationStarted.set(false);
×
NEW
1165
      throw t;
×
1166
    } finally {
1167
      nexusContext.setNexusOperationMetadata(null);
1✔
1168
    }
1169
  }
1170

1171
  private Header propagatedHeader() {
1172
    List<ContextPropagator> propagators = client.getOptions().getContextPropagators();
1✔
1173
    if (propagators.isEmpty()) {
1!
NEW
1174
      return Header.empty();
×
1175
    }
1176
    Map<String, Payload> result = new HashMap<>();
1✔
1177
    for (ContextPropagator propagator : propagators) {
1✔
1178
      result.putAll(propagator.serializeContext(propagator.getCurrentContext()));
1✔
1179
    }
1✔
1180
    return new Header(result);
1✔
1181
  }
1182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc