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

temporalio / sdk-java / #347

31 Jul 2026 06:59PM UTC coverage: 68.148% (-0.03%) from 68.181%
#347

push

github

web-flow
NEXUS-485: Support Workflow Update as a Nexus Operation (#2945)

* NEXUS-485: Support Workflow Update as a Nexus Operation

* address comments, change signatures to newer

* address comments 2: add all workflow exec overloads

* address comments: log failed

7166 of 12554 branches covered (57.08%)

Branch coverage included in aggregate %.

118 of 296 new or added lines in 11 files covered. (39.86%)

28 existing lines in 7 files now uncovered.

29722 of 41575 relevant lines covered (71.49%)

0.71 hits per line

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

45.09
/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.WorkflowExecution;
11
import io.temporal.client.UpdateOptions;
12
import io.temporal.client.WorkflowClient;
13
import io.temporal.client.WorkflowOptions;
14
import io.temporal.client.WorkflowStub;
15
import io.temporal.client.WorkflowTargetOptions;
16
import io.temporal.client.WorkflowUpdateException;
17
import io.temporal.client.WorkflowUpdateHandle;
18
import io.temporal.client.WorkflowUpdateStage;
19
import io.temporal.common.Experimental;
20
import io.temporal.internal.client.NexusStartWorkflowResponse;
21
import io.temporal.internal.nexus.CurrentNexusOperationContext;
22
import io.temporal.internal.nexus.InternalNexusOperationContext;
23
import io.temporal.internal.nexus.NexusOperationMetadata;
24
import io.temporal.internal.nexus.NexusStartWorkflowHelper;
25
import io.temporal.internal.nexus.OperationToken;
26
import io.temporal.internal.nexus.OperationTokenUtil;
27
import io.temporal.workflow.Functions;
28
import java.lang.reflect.Type;
29
import java.util.Objects;
30
import java.util.concurrent.atomic.AtomicBoolean;
31

32
/** Package-private implementation of {@link TemporalNexusClient}. */
33
@Experimental
34
final class TemporalNexusClientImpl implements TemporalNexusClient {
35

36
  private final WorkflowClient client;
37
  private final OperationContext operationContext;
38
  private final OperationStartDetails operationStartDetails;
39
  private final AtomicBoolean asyncOperationStarted = new AtomicBoolean(false);
1✔
40

41
  TemporalNexusClientImpl(
42
      WorkflowClient client,
43
      OperationContext operationContext,
44
      OperationStartDetails operationStartDetails) {
1✔
45
    this.client = Objects.requireNonNull(client);
1✔
46
    this.operationContext = Objects.requireNonNull(operationContext);
1✔
47
    this.operationStartDetails = Objects.requireNonNull(operationStartDetails);
1✔
48
  }
1✔
49

50
  @Override
51
  public WorkflowClient getWorkflowClient() {
52
    return client;
×
53
  }
54

55
  // ---------- Returning (Func) overloads ----------
56

57
  @Override
58
  public <T, R> TemporalOperationResult<R> startWorkflow(
59
      Class<T> workflowClass, Functions.Func1<T, R> workflowMethod, WorkflowOptions options) {
60
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
61
    return invokeAndReturn(WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub)));
1✔
62
  }
63

64
  @Override
65
  public <T, A1, R> TemporalOperationResult<R> startWorkflow(
66
      Class<T> workflowClass,
67
      Functions.Func2<T, A1, R> workflowMethod,
68
      A1 arg1,
69
      WorkflowOptions options) {
70
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
71
    return invokeAndReturn(
1✔
72
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1)));
1✔
73
  }
74

75
  @Override
76
  public <T, A1, A2, R> TemporalOperationResult<R> startWorkflow(
77
      Class<T> workflowClass,
78
      Functions.Func3<T, A1, A2, R> workflowMethod,
79
      A1 arg1,
80
      A2 arg2,
81
      WorkflowOptions options) {
82
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
83
    return invokeAndReturn(
1✔
84
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2)));
1✔
85
  }
86

87
  @Override
88
  public <T, A1, A2, A3, R> TemporalOperationResult<R> startWorkflow(
89
      Class<T> workflowClass,
90
      Functions.Func4<T, A1, A2, A3, R> workflowMethod,
91
      A1 arg1,
92
      A2 arg2,
93
      A3 arg3,
94
      WorkflowOptions options) {
95
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
96
    return invokeAndReturn(
1✔
97
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2, arg3)));
1✔
98
  }
99

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

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

131
  @Override
132
  public <T, A1, A2, A3, A4, A5, A6, R> TemporalOperationResult<R> startWorkflow(
133
      Class<T> workflowClass,
134
      Functions.Func7<T, A1, A2, A3, A4, A5, A6, R> workflowMethod,
135
      A1 arg1,
136
      A2 arg2,
137
      A3 arg3,
138
      A4 arg4,
139
      A5 arg5,
140
      A6 arg6,
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, arg6)));
1✔
146
  }
147

148
  // ---------- Void (Proc) overloads ----------
149

150
  @Override
151
  public <T> TemporalOperationResult<Void> startWorkflow(
152
      Class<T> workflowClass, Functions.Proc1<T> workflowMethod, WorkflowOptions options) {
153
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
154
    return invokeAndReturn(WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub)));
1✔
155
  }
156

157
  @Override
158
  public <T, A1> TemporalOperationResult<Void> startWorkflow(
159
      Class<T> workflowClass,
160
      Functions.Proc2<T, A1> workflowMethod,
161
      A1 arg1,
162
      WorkflowOptions options) {
163
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
164
    return invokeAndReturn(
1✔
165
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1)));
1✔
166
  }
167

168
  @Override
169
  public <T, A1, A2> TemporalOperationResult<Void> startWorkflow(
170
      Class<T> workflowClass,
171
      Functions.Proc3<T, A1, A2> workflowMethod,
172
      A1 arg1,
173
      A2 arg2,
174
      WorkflowOptions options) {
175
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
176
    return invokeAndReturn(
1✔
177
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2)));
1✔
178
  }
179

180
  @Override
181
  public <T, A1, A2, A3> TemporalOperationResult<Void> startWorkflow(
182
      Class<T> workflowClass,
183
      Functions.Proc4<T, A1, A2, A3> workflowMethod,
184
      A1 arg1,
185
      A2 arg2,
186
      A3 arg3,
187
      WorkflowOptions options) {
188
    T stub = client.newWorkflowStub(workflowClass, options);
1✔
189
    return invokeAndReturn(
1✔
190
        WorkflowHandle.fromWorkflowMethod(() -> workflowMethod.apply(stub, arg1, arg2, arg3)));
1✔
191
  }
192

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

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

224
  @Override
225
  public <T, A1, A2, A3, A4, A5, A6> TemporalOperationResult<Void> startWorkflow(
226
      Class<T> workflowClass,
227
      Functions.Proc7<T, A1, A2, A3, A4, A5, A6> workflowMethod,
228
      A1 arg1,
229
      A2 arg2,
230
      A3 arg3,
231
      A4 arg4,
232
      A5 arg5,
233
      A6 arg6,
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, arg6)));
1✔
239
  }
240

241
  // ---------- Untyped ----------
242

243
  @Override
244
  public <R> TemporalOperationResult<R> startWorkflow(
245
      String workflowType, Class<R> resultClass, WorkflowOptions options, Object... args) {
246
    return startWorkflow(workflowType, resultClass, null, options, args);
1✔
247
  }
248

249
  @Override
250
  public <R> TemporalOperationResult<R> startWorkflow(
251
      String workflowType,
252
      Class<R> resultClass,
253
      Type resultType,
254
      WorkflowOptions options,
255
      Object... args) {
256
    WorkflowStub stub = client.newUntypedWorkflowStub(workflowType, options);
1✔
257
    WorkflowHandle<R> handle = WorkflowHandle.fromWorkflowStub(stub, resultClass, args);
1✔
258
    return invokeAndReturn(handle);
1✔
259
  }
260

261
  private <R> TemporalOperationResult<R> invokeAndReturn(WorkflowHandle<R> handle) {
262
    markAsyncOperationStarted();
1✔
263
    try {
264
      NexusStartWorkflowResponse response =
1✔
265
          NexusStartWorkflowHelper.startWorkflowAndAttachLinks(
1✔
266
              operationContext,
267
              operationStartDetails,
268
              request -> handle.getInvoker().invoke(request));
1✔
269
      return TemporalOperationResult.async(response.getOperationToken());
1✔
270
    } catch (Throwable t) {
×
271
      // Reset on failure so that if startWorkflowAndAttachLinks throws,
272
      // the handler can retry without being blocked by the guard.
273
      asyncOperationStarted.set(false);
×
274
      throw t;
×
275
    }
276
  }
277

278
  // ---------- Update Workflow overloads ----------
279

280
  @Override
281
  public <T, R> TemporalOperationResult<R> startWorkflowUpdate(
282
      Class<T> workflowClass,
283
      String workflowId,
284
      Functions.Func1<T, R> updateMethod,
285
      UpdateOptions<R> options)
286
      throws OperationException {
NEW
287
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
NEW
288
    return executeUpdate(
×
289
        options,
NEW
290
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
291
  }
292

293
  @Override
294
  public <T, A1, R> TemporalOperationResult<R> startWorkflowUpdate(
295
      Class<T> workflowClass,
296
      String workflowId,
297
      Functions.Func2<T, A1, R> updateMethod,
298
      A1 arg1,
299
      UpdateOptions<R> options)
300
      throws OperationException {
301
    T stub = client.newWorkflowStub(workflowClass, workflowId);
1✔
302
    return executeUpdate(
1✔
303
        options,
304
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
1✔
305
  }
306

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

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

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

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

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

401
  @Override
402
  public <T> TemporalOperationResult<Void> startWorkflowUpdate(
403
      Class<T> workflowClass,
404
      String workflowId,
405
      Functions.Proc1<T> updateMethod,
406
      UpdateOptions<Void> options)
407
      throws OperationException {
NEW
408
    T stub = client.newWorkflowStub(workflowClass, workflowId);
×
NEW
409
    return executeUpdate(
×
410
        options,
NEW
411
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
412
  }
413

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

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

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

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

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

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

522
  /** Function that will trigger {@code startUpdate} on overloads */
523
  @FunctionalInterface
524
  private interface UpdateCommand<R> {
525
    WorkflowUpdateHandle<R> triggerUpdate(UpdateOptions<R> options);
526
  }
527

528
  /** Common code for all {@code startWorkflowUpdate} overloads. */
529
  private <R> TemporalOperationResult<R> executeUpdate(
530
      UpdateOptions<R> options, UpdateCommand<R> updateWrapper) throws OperationException {
531

532
    UpdateOptions.Builder<R> effectiveOptsBuilder = UpdateOptions.newBuilder(options);
1✔
533
    String requestId = operationStartDetails.getRequestId();
1✔
534
    if (Strings.isNullOrEmpty(options.getUpdateId())) {
1✔
535
      // if updateId is unset, use requestId - consistent with other SDKs
536
      effectiveOptsBuilder.setUpdateId(requestId);
1✔
537
    }
538
    options = effectiveOptsBuilder.build();
1✔
539
    checkNexusUpdateOptionsValid(options);
1✔
540
    markAsyncOperationStarted();
1✔
541

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

595
  /**
596
   * @throws OperationException if the options provided are invalid like missing
597
   *     UpdateName/WorkflowID/etc
598
   */
599
  private <R> void checkNexusUpdateOptionsValid(UpdateOptions<R> options)
600
      throws OperationException {
601
    if (options.getWaitForStage() != WorkflowUpdateStage.ACCEPTED) {
1!
NEW
602
      throw new HandlerException(
×
603
          HandlerException.ErrorType.INTERNAL,
604
          "invalid update request",
605
          new IllegalArgumentException(
606
              "nexus op workflow updates only support WorkflowUpdateStageAccepted for async updates"),
607
          RetryBehavior.RETRYABLE);
608
    }
609
    try {
610
      options.validate();
1✔
NEW
611
    } catch (IllegalStateException e) {
×
NEW
612
      throw new HandlerException(
×
613
          HandlerException.ErrorType.INTERNAL,
614
          "invalid update request",
615
          e,
616
          RetryBehavior.RETRYABLE);
617
    }
1✔
618
  }
1✔
619

620
  private void markAsyncOperationStarted() {
621
    if (!asyncOperationStarted.compareAndSet(false, true)) {
1✔
622
      throw new HandlerException(
1✔
623
          HandlerException.ErrorType.BAD_REQUEST,
624
          new IllegalStateException(
625
              "Only one async operation can be started per operation handler invocation. "
626
                  + "Use getWorkflowClient() for additional workflow interactions."));
627
    }
628
  }
1✔
629

630
  // ---------- Update Workflow overloads for WorkflowExecution ----------
631

632
  @Override
633
  public <T, R> TemporalOperationResult<R> startWorkflowUpdate(
634
      Class<T> workflowClass,
635
      WorkflowExecution execution,
636
      Functions.Func1<T, R> updateMethod,
637
      UpdateOptions<R> options)
638
      throws OperationException {
NEW
639
    T stub = newWorkflowStub(workflowClass, execution);
×
NEW
640
    return executeUpdate(
×
641
        options,
NEW
642
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
643
  }
644

645
  @Override
646
  public <T, A1, R> TemporalOperationResult<R> startWorkflowUpdate(
647
      Class<T> workflowClass,
648
      WorkflowExecution execution,
649
      Functions.Func2<T, A1, R> updateMethod,
650
      A1 arg1,
651
      UpdateOptions<R> options)
652
      throws OperationException {
NEW
653
    T stub = newWorkflowStub(workflowClass, execution);
×
NEW
654
    return executeUpdate(
×
655
        options,
NEW
656
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
×
657
  }
658

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

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

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

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

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

753
  @Override
754
  public <T> TemporalOperationResult<Void> startWorkflowUpdate(
755
      Class<T> workflowClass,
756
      WorkflowExecution execution,
757
      Functions.Proc1<T> updateMethod,
758
      UpdateOptions<Void> options)
759
      throws OperationException {
NEW
760
    T stub = newWorkflowStub(workflowClass, execution);
×
NEW
761
    return executeUpdate(
×
762
        options,
NEW
763
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub), effective));
×
764
  }
765

766
  @Override
767
  public <T, A1> TemporalOperationResult<Void> startWorkflowUpdate(
768
      Class<T> workflowClass,
769
      WorkflowExecution execution,
770
      Functions.Proc2<T, A1> updateMethod,
771
      A1 arg1,
772
      UpdateOptions<Void> options)
773
      throws OperationException {
NEW
774
    T stub = newWorkflowStub(workflowClass, execution);
×
NEW
775
    return executeUpdate(
×
776
        options,
NEW
777
        effective -> WorkflowClient.startUpdate(() -> updateMethod.apply(stub, arg1), effective));
×
778
  }
779

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

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

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

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

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

874
  private <T> T newWorkflowStub(Class<T> workflowClass, WorkflowExecution execution) {
NEW
875
    return client.newWorkflowStub(
×
NEW
876
        workflowClass, WorkflowTargetOptions.newBuilder().setWorkflowExecution(execution).build());
×
877
  }
878
}
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