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

temporalio / sdk-java / #288

30 Jul 2024 05:58PM UTC coverage: 77.6% (+0.04%) from 77.557%
#288

push

github

web-flow
Workflow-friendly concurrency primitives (#2133)

Workflow-friendly concurrency primitives

60 of 64 new or added lines in 4 files covered. (93.75%)

3 existing lines in 1 file now uncovered.

19719 of 25411 relevant lines covered (77.6%)

0.78 hits per line

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

72.63
/temporal-sdk/src/main/java/io/temporal/workflow/Workflow.java
1
/*
2
 * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3
 *
4
 * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
 *
6
 * Modifications copyright (C) 2017 Uber Technologies, Inc.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this material except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *   http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
package io.temporal.workflow;
22

23
import com.uber.m3.tally.Scope;
24
import io.temporal.activity.ActivityOptions;
25
import io.temporal.activity.LocalActivityOptions;
26
import io.temporal.api.common.v1.WorkflowExecution;
27
import io.temporal.common.RetryOptions;
28
import io.temporal.common.SearchAttributeUpdate;
29
import io.temporal.common.SearchAttributes;
30
import io.temporal.common.converter.DataConverter;
31
import io.temporal.failure.ActivityFailure;
32
import io.temporal.failure.CanceledFailure;
33
import io.temporal.failure.ChildWorkflowFailure;
34
import io.temporal.internal.sync.WorkflowInternal;
35
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
36
import io.temporal.worker.WorkerFactoryOptions;
37
import io.temporal.worker.WorkflowImplementationOptions;
38
import io.temporal.workflow.Functions.Func;
39
import io.temporal.workflow.unsafe.WorkflowUnsafe;
40
import java.lang.reflect.Type;
41
import java.time.Duration;
42
import java.time.OffsetDateTime;
43
import java.util.*;
44
import java.util.function.BiPredicate;
45
import java.util.function.Supplier;
46
import javax.annotation.Nonnull;
47
import javax.annotation.Nullable;
48
import org.slf4j.Logger;
49

50
/**
51
 * This class contains methods exposing Temporal API for Workflows, like
52
 *
53
 * <ul>
54
 *   <li>Creation and scheduling of activities, child workflows, external workflows, continue-as-new
55
 *       runs
56
 *   <li>Operations over workflow elements, like Side Effects, Timers, Versions, {@link
57
 *       CancellationScope}
58
 *   <li>Accessing and updating of the workflow data, like {@link WorkflowInfo}, Memos and Search
59
 *       Attributes
60
 *   <li>Deterministic implementation of popular non-deterministic API working with time, logging
61
 *       and generation of random values
62
 * </ul>
63
 *
64
 * Methods of this class are intended to be called from a workflow method only until explicitly
65
 * stated otherwise on the specific method's javadoc.
66
 *
67
 * <p>For an overview of Temporal JavaSDK Workflows, see {@link io.temporal.workflow}
68
 *
69
 * <p>For methods providing Temporal Workflow alternatives to threading and asynchronous
70
 * invocations, see {@link Async}
71
 *
72
 * @see io.temporal.workflow
73
 */
74
public final class Workflow {
75
  public static final int DEFAULT_VERSION = WorkflowInternal.DEFAULT_VERSION;
76

77
  /**
78
   * Creates client stub to activities that implement given interface. `
79
   *
80
   * @param activityInterface interface type implemented by activities
81
   */
82
  public static <T> T newActivityStub(Class<T> activityInterface) {
83
    return WorkflowInternal.newActivityStub(activityInterface, null, null);
1✔
84
  }
85

86
  /**
87
   * Creates client stub to activities that implement given interface
88
   *
89
   * @param activityInterface interface type implemented by activities.
90
   * @param options options that together with the properties of {@link
91
   *     io.temporal.activity.ActivityMethod} specify the activity invocation parameters
92
   */
93
  public static <T> T newActivityStub(Class<T> activityInterface, ActivityOptions options) {
94
    return WorkflowInternal.newActivityStub(activityInterface, options, null);
1✔
95
  }
96

97
  /**
98
   * Creates client stub to activities that implement given interface.
99
   *
100
   * @param activityInterface interface type implemented by activities
101
   * @param options options that together with the properties of {@link
102
   *     io.temporal.activity.ActivityMethod} specify the activity invocation parameters
103
   * @param activityMethodOptions activity method-specific invocation parameters
104
   */
105
  public static <T> T newActivityStub(
106
      Class<T> activityInterface,
107
      ActivityOptions options,
108
      Map<String, ActivityOptions> activityMethodOptions) {
109
    return WorkflowInternal.newActivityStub(activityInterface, options, activityMethodOptions);
×
110
  }
111

112
  /**
113
   * Creates non typed client stub to activities. Allows executing activities by their string name.
114
   *
115
   * @param options specify the activity invocation parameters.
116
   */
117
  public static ActivityStub newUntypedActivityStub(ActivityOptions options) {
118
    return WorkflowInternal.newUntypedActivityStub(options);
1✔
119
  }
120

121
  /**
122
   * Creates client stub to local activities that implement given interface.
123
   *
124
   * @param activityInterface interface type implemented by activities
125
   */
126
  public static <T> T newLocalActivityStub(Class<T> activityInterface) {
127
    return WorkflowInternal.newLocalActivityStub(activityInterface, null, null);
1✔
128
  }
129

130
  /**
131
   * Creates client stub to local activities that implement given interface. A local activity is
132
   * similar to a regular activity, but with some key differences: 1. Local activity is scheduled
133
   * and run by the workflow worker locally. 2. Local activity does not need Temporal server to
134
   * schedule activity task and does not rely on activity worker. 3. Local activity is for short
135
   * living activities (usually finishes within seconds). 4. Local activity cannot heartbeat.
136
   *
137
   * @param activityInterface interface type implemented by activities
138
   * @param options options that together with the properties of {@link
139
   *     io.temporal.activity.ActivityMethod} specify the activity invocation parameters.
140
   */
141
  public static <T> T newLocalActivityStub(
142
      Class<T> activityInterface, LocalActivityOptions options) {
143
    return WorkflowInternal.newLocalActivityStub(activityInterface, options, null);
1✔
144
  }
145

146
  /**
147
   * Creates client stub to local activities that implement given interface.
148
   *
149
   * @param activityInterface interface type implemented by activities
150
   * @param options options that together with the properties of {@link
151
   *     io.temporal.activity.ActivityMethod} specify the activity invocation parameters
152
   * @param activityMethodOptions activity method-specific invocation parameters
153
   */
154
  public static <T> T newLocalActivityStub(
155
      Class<T> activityInterface,
156
      LocalActivityOptions options,
157
      Map<String, LocalActivityOptions> activityMethodOptions) {
158
    return WorkflowInternal.newLocalActivityStub(activityInterface, options, activityMethodOptions);
×
159
  }
160

161
  /**
162
   * Creates non typed client stub to local activities. Allows executing activities by their string
163
   * name.
164
   *
165
   * @param options specify the local activity invocation parameters.
166
   */
167
  public static ActivityStub newUntypedLocalActivityStub(LocalActivityOptions options) {
168
    return WorkflowInternal.newUntypedLocalActivityStub(options);
1✔
169
  }
170

171
  /**
172
   * Creates client stub that can be used to start a child workflow that implements the given
173
   * interface using parent options. Use {@link #newExternalWorkflowStub(Class, String)} to get a
174
   * stub to signal a workflow without starting it.
175
   *
176
   * @param workflowInterface interface type implemented by activities
177
   */
178
  public static <T> T newChildWorkflowStub(Class<T> workflowInterface) {
179
    return WorkflowInternal.newChildWorkflowStub(workflowInterface, null);
1✔
180
  }
181

182
  /**
183
   * Creates client stub that can be used to start a child workflow that implements given interface.
184
   * Use {@link #newExternalWorkflowStub(Class, String)} to get a stub to signal a workflow without
185
   * starting it.
186
   *
187
   * @param workflowInterface interface type implemented by activities
188
   * @param options options passed to the child workflow.
189
   */
190
  public static <T> T newChildWorkflowStub(
191
      Class<T> workflowInterface, ChildWorkflowOptions options) {
192
    return WorkflowInternal.newChildWorkflowStub(workflowInterface, options);
1✔
193
  }
194

195
  /**
196
   * Creates client stub that can be used to communicate to an existing workflow execution.
197
   *
198
   * @param workflowInterface interface type implemented by activities
199
   * @param workflowId id of the workflow to communicate with.
200
   */
201
  public static <R> R newExternalWorkflowStub(
202
      Class<? extends R> workflowInterface, String workflowId) {
203
    WorkflowExecution execution = WorkflowExecution.newBuilder().setWorkflowId(workflowId).build();
1✔
204
    return WorkflowInternal.newExternalWorkflowStub(workflowInterface, execution);
1✔
205
  }
206

207
  /**
208
   * Creates client stub that can be used to communicate to an existing workflow execution.
209
   *
210
   * @param workflowInterface interface type implemented by activities
211
   * @param execution execution of the workflow to communicate with.
212
   */
213
  public static <R> R newExternalWorkflowStub(
214
      Class<? extends R> workflowInterface, WorkflowExecution execution) {
215
    return WorkflowInternal.newExternalWorkflowStub(workflowInterface, execution);
1✔
216
  }
217

218
  /**
219
   * Extracts workflow execution from a stub created through {@link #newChildWorkflowStub(Class,
220
   * ChildWorkflowOptions)} or {@link #newExternalWorkflowStub(Class, String)}. Wrapped in a Promise
221
   * as child workflow start is asynchronous.
222
   */
223
  public static Promise<WorkflowExecution> getWorkflowExecution(Object childWorkflowStub) {
224
    return WorkflowInternal.getWorkflowExecution(childWorkflowStub);
1✔
225
  }
226

227
  /**
228
   * Creates untyped client stub that can be used to start and signal a child workflow.
229
   *
230
   * @param workflowType name of the workflow type to start.
231
   * @param options options passed to the child workflow.
232
   */
233
  public static ChildWorkflowStub newUntypedChildWorkflowStub(
234
      String workflowType, ChildWorkflowOptions options) {
235
    return WorkflowInternal.newUntypedChildWorkflowStub(workflowType, options);
1✔
236
  }
237

238
  /**
239
   * Creates untyped client stub that can be used to start and signal a child workflow. All options
240
   * are inherited from the parent.
241
   *
242
   * @param workflowType name of the workflow type to start.
243
   */
244
  public static ChildWorkflowStub newUntypedChildWorkflowStub(String workflowType) {
245
    return WorkflowInternal.newUntypedChildWorkflowStub(workflowType, null);
1✔
246
  }
247

248
  /**
249
   * Creates untyped client stub that can be used to signal or cancel an existing workflow
250
   * execution.
251
   *
252
   * @param execution execution of the workflow to communicate with.
253
   */
254
  public static ExternalWorkflowStub newUntypedExternalWorkflowStub(WorkflowExecution execution) {
255
    return WorkflowInternal.newUntypedExternalWorkflowStub(execution);
1✔
256
  }
257

258
  /**
259
   * Creates untyped client stub that can be used to signal or cancel an existing workflow
260
   * execution.
261
   *
262
   * @param workflowId id of the workflow to communicate with.
263
   */
264
  public static ExternalWorkflowStub newUntypedExternalWorkflowStub(String workflowId) {
265
    WorkflowExecution execution = WorkflowExecution.newBuilder().setWorkflowId(workflowId).build();
1✔
266
    return Workflow.newUntypedExternalWorkflowStub(execution);
1✔
267
  }
268

269
  /**
270
   * Creates a client stub that can be used to continue this workflow as a new run.
271
   *
272
   * @param workflowInterface an interface type implemented by the next run of the workflow
273
   */
274
  public static <T> T newContinueAsNewStub(
275
      Class<T> workflowInterface, ContinueAsNewOptions options) {
276
    return WorkflowInternal.newContinueAsNewStub(workflowInterface, options);
1✔
277
  }
278

279
  /**
280
   * Creates a client stub that can be used to continue this workflow as a new run.
281
   *
282
   * @param workflowInterface an interface type implemented by the next run of the workflow
283
   */
284
  public static <T> T newContinueAsNewStub(Class<T> workflowInterface) {
285
    return WorkflowInternal.newContinueAsNewStub(workflowInterface, null);
1✔
286
  }
287

288
  /**
289
   * Continues the current workflow execution as a new run with the same options.
290
   *
291
   * @param args arguments of the next run.
292
   * @see #newContinueAsNewStub(Class)
293
   */
294
  public static void continueAsNew(Object... args) {
295
    Workflow.continueAsNew(null, args);
×
296
  }
×
297

298
  /**
299
   * Continues the current workflow execution as a new run with the same workflowType and overridden
300
   * {@code options}.
301
   *
302
   * @param options option overrides for the next run, can contain null if no overrides are needed
303
   * @param args arguments of the next run.
304
   * @see #newContinueAsNewStub(Class, ContinueAsNewOptions)
305
   */
306
  public static void continueAsNew(@Nullable ContinueAsNewOptions options, Object... args) {
307
    Workflow.continueAsNew(null, options, args);
×
308
  }
×
309

310
  /**
311
   * Continues the current workflow execution as a new run possibly overriding the workflow type and
312
   * options.
313
   *
314
   * @param workflowType workflow type override for the next run, can contain null if no override is
315
   *     needed
316
   * @param options option overrides for the next run, can contain null if no overrides are needed
317
   * @param args arguments of the next run.
318
   * @see #newContinueAsNewStub(Class)
319
   * @deprecated use {@link #continueAsNew(String, ContinueAsNewOptions, Object...)}
320
   */
321
  @Deprecated
322
  public static void continueAsNew(
323
      Optional<String> workflowType, Optional<ContinueAsNewOptions> options, Object... args) {
324
    WorkflowInternal.continueAsNew(workflowType.orElse(null), options.orElse(null), args);
×
325
  }
×
326

327
  /**
328
   * Continues the current workflow execution as a new run possibly overriding the workflow type and
329
   * options.
330
   *
331
   * @param workflowType workflow type override for the next run, can be null of no override is
332
   *     needed
333
   * @param options option overrides for the next run, can be null if no overrides are needed
334
   * @param args arguments of the next run.
335
   * @see #newContinueAsNewStub(Class)
336
   */
337
  public static void continueAsNew(
338
      @Nullable String workflowType, @Nullable ContinueAsNewOptions options, Object... args) {
339
    WorkflowInternal.continueAsNew(workflowType, options, args);
×
340
  }
×
341

342
  public static WorkflowInfo getInfo() {
343
    return WorkflowInternal.getWorkflowInfo();
1✔
344
  }
345

346
  /**
347
   * Extract deserialized Memo associated with given key
348
   *
349
   * @param key memo key
350
   * @param valueClass Java class to deserialize into
351
   * @return deserialized Memo or null if the key is not present in the memo
352
   */
353
  public static <T> Object getMemo(String key, Class<T> valueClass) {
354
    return getMemo(key, valueClass, valueClass);
1✔
355
  }
356

357
  /**
358
   * Extract Memo associated with the given key and deserialized into an object of generic type as
359
   * is done here: {@link DataConverter#fromPayloads(int, java.util.Optional, java.lang.Class,
360
   * java.lang.reflect.Type)} Ex: To deserialize into {@code HashMap<String, Integer>} {@code
361
   * Workflow.getMemo(key, Map.class, new TypeToken<HashMap<String, Integer>>() {}.getType())}
362
   *
363
   * @param key memo key
364
   * @param valueClass Java class to deserialize into
365
   * @param genericType type parameter for the generic class
366
   * @return deserialized Memo or null if the key is not present in the memo
367
   */
368
  public static <T> T getMemo(String key, Class<T> valueClass, Type genericType) {
369
    return WorkflowInternal.getMemo(key, valueClass, genericType);
1✔
370
  }
371

372
  /**
373
   * Wraps the Runnable method argument in a {@link CancellationScope}. The {@link
374
   * CancellationScope#run()} calls {@link Runnable#run()} on the wrapped Runnable. The returned
375
   * CancellationScope can be used to cancel the wrapped code. The cancellation semantic depends on
376
   * the operation the code is blocked on. For example activity or child workflow is first canceled
377
   * then throws a {@link CanceledFailure}. The same applies for {@link Workflow#sleep(long)}
378
   * operation. When an activity or a child workflow is invoked asynchronously then they get
379
   * canceled and a {@link Promise} that contains their result will throw {@link CanceledFailure}
380
   * when {@link Promise#get()} is called.
381
   *
382
   * <p>The new cancellation scope {@link CancellationScope#current()} is linked to the parent one.
383
   * If the parent one is canceled then all the children scopes are wrapped within a root
384
   * cancellation scope which gets canceled when a workflow is canceled through the Temporal
385
   * CancelWorkflowExecution API. To perform cleanup operations that require blocking after the
386
   * current scope is canceled use a scope created through {@link
387
   * #newDetachedCancellationScope(Runnable)}.
388
   *
389
   * <p>Example of running activities in parallel and cancelling them after a specified timeout.
390
   *
391
   * <pre><code>
392
   *     List&lt;Promise&lt;String&gt;&gt; results = new ArrayList&lt;&gt;();
393
   *     CancellationScope scope = Workflow.newDetachedCancellationScope(() -&gt; {
394
   *        Async.function(activities::a1);
395
   *        Async.function(activities::a2);
396
   *     });
397
   *     scope.run(); // returns immediately as the activities are invoked asynchronously
398
   *     Workflow.sleep(Duration.ofHours(1));
399
   *     // Cancels any activity in the scope that is still running
400
   *     scope.cancel("one hour passed");
401
   *
402
   * </code></pre>
403
   *
404
   * @param runnable parameter to wrap in a cancellation scope.
405
   * @return wrapped parameter.
406
   */
407
  public static CancellationScope newCancellationScope(Runnable runnable) {
408
    return WorkflowInternal.newCancellationScope(false, runnable);
1✔
409
  }
410

411
  /**
412
   * Wraps a procedure in a CancellationScope. The procedure receives the wrapping CancellationScope
413
   * as a parameter. Useful when cancellation is requested from within the wrapped code. The
414
   * following example cancels the sibling activity on any failure.
415
   *
416
   * <pre><code>
417
   *               Workflow.newCancellationScope(
418
   *                   (scope) -&gt; {
419
   *                     Promise<Void> p1 = Async.proc(activities::a1).exceptionally(ex-&gt;
420
   *                        {
421
   *                           scope.cancel("a1 failed");
422
   *                           return null;
423
   *                        });
424
   *
425
   *                     Promise<Void> p2 = Async.proc(activities::a2).exceptionally(ex-&gt;
426
   *                        {
427
   *                           scope.cancel("a2 failed");
428
   *                           return null;
429
   *                        });
430
   *                     Promise.allOf(p1, p2).get();
431
   *                   })
432
   *               .run();
433
   * </code></pre>
434
   *
435
   * @param proc code to wrap in the cancellation scope
436
   * @return wrapped proc
437
   */
438
  public static CancellationScope newCancellationScope(Functions.Proc1<CancellationScope> proc) {
439
    return WorkflowInternal.newCancellationScope(false, proc);
1✔
440
  }
441

442
  /**
443
   * Creates a CancellationScope {@link CancellationScope#run()} that is not linked to a parent
444
   * scope must be called to execute the code the scope wraps. The detached scope is needed to
445
   * execute cleanup code after a workflow is canceled which cancels the root scope that wraps
446
   * the @WorkflowMethod invocation. Here is an example usage:
447
   *
448
   * <pre><code>
449
   *  try {
450
   *     // workflow logic
451
   *  } catch (CanceledFailure e) {
452
   *     CancellationScope detached = Workflow.newDetachedCancellationScope(() -&gt; {
453
   *         // cleanup logic
454
   *     });
455
   *     detached.run();
456
   *  }
457
   * </code></pre>
458
   *
459
   * @param runnable parameter to wrap in a cancellation scope.
460
   * @return wrapped parameter.
461
   * @see #newCancellationScope(Runnable)
462
   */
463
  public static CancellationScope newDetachedCancellationScope(Runnable runnable) {
464
    return WorkflowInternal.newCancellationScope(true, runnable);
1✔
465
  }
466

467
  /**
468
   * Create new timer. Note that Temporal service time resolution is in seconds. So all durations
469
   * are rounded <b>up</b> to the nearest second.
470
   *
471
   * @return feature that becomes ready when at least specified number of seconds passes. promise is
472
   *     failed with {@link CanceledFailure} if enclosing scope is canceled.
473
   */
474
  public static Promise<Void> newTimer(Duration delay) {
475
    return WorkflowInternal.newTimer(delay);
1✔
476
  }
477

478
  /**
479
   * @deprecated use {@link #newWorkflowQueue(int)} instead. An implementation returned by this
480
   *     method has a bug.
481
   */
482
  @Deprecated
483
  public static <E> WorkflowQueue<E> newQueue(int capacity) {
484
    return WorkflowInternal.newQueue(capacity);
×
485
  }
486

487
  /**
488
   * Create a new instance of a {@link WorkflowQueue} implementation that is adapted to be used from
489
   * a workflow code.
490
   *
491
   * @param capacity the maximum size of the queue
492
   * @return new instance of {@link WorkflowQueue}
493
   */
494
  public static <E> WorkflowQueue<E> newWorkflowQueue(int capacity) {
495
    return WorkflowInternal.newWorkflowQueue(capacity);
×
496
  }
497

498
  public static <E> CompletablePromise<E> newPromise() {
499
    return WorkflowInternal.newCompletablePromise();
1✔
500
  }
501

502
  public static <E> Promise<E> newPromise(E value) {
503
    return WorkflowInternal.newPromise(value);
1✔
504
  }
505

506
  public static <E> Promise<E> newFailedPromise(Exception failure) {
507
    return WorkflowInternal.newFailedPromise(failure);
1✔
508
  }
509

510
  /**
511
   * Creates a {@link WorkflowLock} implementation that can be used from workflow code.
512
   *
513
   * @apiNote The lock returned is not reentrant. If a workflow thread tries to acquire a lock that
514
   *     it already holds, the call will block indefinitely.
515
   * @return new instance of {@link WorkflowLock}
516
   */
517
  public static WorkflowLock newWorkflowLock() {
NEW
518
    return WorkflowInternal.newWorkflowLock();
×
519
  }
520

521
  /**
522
   * Creates a {@link WorkflowSemaphore} implementation that can be used from workflow code.
523
   *
524
   * @param permits the given number of permits for the semaphore.
525
   * @return new instance of {@link WorkflowSemaphore}
526
   */
527
  public static WorkflowSemaphore newWorkflowSemaphore(int permits) {
NEW
528
    return WorkflowInternal.newWorkflowSemaphore(permits);
×
529
  }
530

531
  /**
532
   * Registers an implementation object. The object must implement at least one interface annotated
533
   * with {@link WorkflowInterface}. All its methods annotated with @{@link SignalMethod}
534
   * and @{@link QueryMethod} are registered.
535
   *
536
   * <p>There is no need to register the top level workflow implementation object as it is done
537
   * implicitly by the framework on object startup.
538
   *
539
   * <p>An attempt to register a duplicated query is going to fail with {@link
540
   * IllegalArgumentException}
541
   */
542
  public static void registerListener(Object listener) {
543
    WorkflowInternal.registerListener(listener);
1✔
544
  }
1✔
545

546
  /**
547
   * Must be used to get current time instead of {@link System#currentTimeMillis()} to guarantee
548
   * determinism.
549
   */
550
  public static long currentTimeMillis() {
551
    return WorkflowInternal.currentTimeMillis();
1✔
552
  }
553

554
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
555
  public static void sleep(Duration duration) {
556
    WorkflowInternal.sleep(duration);
1✔
557
  }
1✔
558

559
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
560
  public static void sleep(long millis) {
561
    WorkflowInternal.sleep(Duration.ofMillis(millis));
1✔
562
  }
1✔
563

564
  /**
565
   * Block current thread until unblockCondition is evaluated to true.
566
   *
567
   * @param unblockCondition condition that should return true to indicate that thread should
568
   *     unblock. The condition is called on every state transition, so it should never call any
569
   *     blocking operations or contain code that mutates any workflow state. It should also not
570
   *     contain any time based conditions. Use {@link #await(Duration, Supplier)} for those
571
   *     instead.
572
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
573
   */
574
  public static void await(Supplier<Boolean> unblockCondition) {
575
    WorkflowInternal.await(
1✔
576
        "await",
577
        () -> {
578
          CancellationScope.throwCanceled();
1✔
579
          return unblockCondition.get();
1✔
580
        });
581
  }
1✔
582

583
  /**
584
   * Block current workflow thread until unblockCondition is evaluated to true or timeoutMillis
585
   * passes.
586
   *
587
   * @param timeout time to unblock even if unblockCondition is not satisfied.
588
   * @param unblockCondition condition that should return true to indicate that thread should
589
   *     unblock. The condition is called on every state transition, so it should not contain any
590
   *     code that mutates any workflow state. It should also not contain any time based conditions.
591
   *     Use timeout parameter for those.
592
   * @return false if timed out.
593
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
594
   */
595
  public static boolean await(Duration timeout, Supplier<Boolean> unblockCondition) {
596
    return WorkflowInternal.await(
1✔
597
        timeout,
598
        "await",
599
        () -> {
600
          CancellationScope.throwCanceled();
1✔
601
          return unblockCondition.get();
1✔
602
        });
603
  }
604

605
  /**
606
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
607
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
608
   *
609
   * @param options retry options that specify retry policy
610
   * @param expiration stop retrying after this interval if provided
611
   * @param fn function to invoke and retry
612
   * @return result of the function or the last failure.
613
   */
614
  public static <R> R retry(
615
      RetryOptions options, Optional<Duration> expiration, Functions.Func<R> fn) {
616
    return WorkflowInternal.retry(options, expiration, fn);
×
617
  }
618

619
  /**
620
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
621
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
622
   *
623
   * @param options retry options that specify retry policy
624
   * @param expiration if specified stop retrying after this interval
625
   * @param proc procedure to invoke and retry
626
   */
627
  public static void retry(
628
      RetryOptions options, Optional<Duration> expiration, Functions.Proc proc) {
629
    WorkflowInternal.retry(
×
630
        options,
631
        expiration,
632
        () -> {
633
          proc.apply();
×
634
          return null;
×
635
        });
636
  }
×
637

638
  /**
639
   * If there is a need to return a checked exception from a workflow implementation do not add the
640
   * exception to a method signature but wrap it using this method before rethrowing. The library
641
   * code will unwrap it automatically using when propagating exception to a remote caller. {@link
642
   * RuntimeException} are just returned from this method without modification.
643
   *
644
   * <p>The reason for such design is that returning originally thrown exception from a remote call
645
   * (which child workflow and activity invocations are ) would not allow adding context information
646
   * about a failure, like activity and child workflow id. So stubs always throw a subclass of
647
   * {@link ActivityFailure} from calls to an activity and subclass of {@link ChildWorkflowFailure}
648
   * from calls to a child workflow. The original exception is attached as a cause to these wrapper
649
   * exceptions. So as exceptions are always wrapped adding checked ones to method signature causes
650
   * more pain than benefit.
651
   *
652
   * <p>
653
   *
654
   * <pre>
655
   * try {
656
   *     return someCall();
657
   * } catch (Exception e) {
658
   *     throw Workflow.wrap(e);
659
   * }
660
   * </pre>
661
   *
662
   * @return CheckedExceptionWrapper if e is checked or original exception if e extends
663
   *     RuntimeException.
664
   */
665
  public static RuntimeException wrap(Exception e) {
666
    return WorkflowInternal.wrap(e);
1✔
667
  }
668

669
  /**
670
   * Replay safe way to generate UUID.
671
   *
672
   * <p>Must be used instead of {@link UUID#randomUUID()} which relies on a random generator, thus
673
   * leads to non-deterministic code which is prohibited inside a workflow.
674
   */
675
  public static UUID randomUUID() {
676
    return WorkflowInternal.randomUUID();
1✔
677
  }
678

679
  /** Replay safe random numbers generator. Seeded differently for each workflow instance. */
680
  public static Random newRandom() {
681
    return WorkflowInternal.newRandom();
1✔
682
  }
683

684
  /**
685
   * True if workflow code is being replayed.
686
   *
687
   * <p><b>Warning!</b> Never make workflow logic depend on this flag as it is going to break
688
   * determinism. The only reasonable uses for this flag are deduping external never failing side
689
   * effects like logging or metric reporting.
690
   *
691
   * <p>This method always returns false if called from a non workflow thread.
692
   *
693
   * @deprecated use {@link WorkflowUnsafe#isReplaying()}
694
   */
695
  @Deprecated
696
  public static boolean isReplaying() {
697
    return WorkflowUnsafe.isReplaying();
×
698
  }
699

700
  /**
701
   * Executes the provided function once, records its result into the workflow history. The recorded
702
   * result on history will be returned without executing the provided function during replay. This
703
   * guarantees the deterministic requirement for workflow as the exact same result will be returned
704
   * in replay. Common use case is to run some short non-deterministic code in workflow, like
705
   * getting random number. The only way to fail SideEffect is to panic which causes workflow task
706
   * failure. The workflow task after timeout is rescheduled and re-executed giving SideEffect
707
   * another chance to succeed.
708
   *
709
   * <p>Caution: do not use sideEffect function to modify any workflow state. Only use the
710
   * SideEffect's return value. For example this code is BROKEN:
711
   *
712
   * <pre><code>
713
   *  // Bad example:
714
   *  AtomicInteger random = new AtomicInteger();
715
   *  Workflow.sideEffect(() -&gt; {
716
   *         random.set(random.nextInt(100));
717
   *         return null;
718
   *  });
719
   *  // random will always be 0 in replay, thus this code is non-deterministic
720
   *  if random.get() &lt; 50 {
721
   *         ....
722
   *  } else {
723
   *         ....
724
   *  }
725
   * </code></pre>
726
   *
727
   * On replay the provided function is not executed, the random will always be 0, and the workflow
728
   * could take a different path breaking the determinism.
729
   *
730
   * <p>Here is the correct way to use sideEffect:
731
   *
732
   * <pre><code>
733
   *  // Good example:
734
   *  int random = Workflow.sideEffect(Integer.class, () -&gt; random.nextInt(100));
735
   *  if random &lt; 50 {
736
   *         ....
737
   *  } else {
738
   *         ....
739
   *  }
740
   * </code></pre>
741
   *
742
   * If function throws any exception it is not delivered to the workflow code. It is wrapped in
743
   * {@link Error} causing failure of the current workflow task.
744
   *
745
   * @param resultClass type of the side effect
746
   * @param func function that returns side effect value
747
   * @return value of the side effect
748
   * @see #mutableSideEffect(String, Class, BiPredicate, Functions.Func)
749
   */
750
  public static <R> R sideEffect(Class<R> resultClass, Func<R> func) {
751
    return WorkflowInternal.sideEffect(resultClass, resultClass, func);
1✔
752
  }
753

754
  /**
755
   * Executes the provided function once, records its result into the workflow history. The recorded
756
   * result on history will be returned without executing the provided function during replay. This
757
   * guarantees the deterministic requirement for workflow as the exact same result will be returned
758
   * in replay. Common use case is to run some short non-deterministic code in workflow, like
759
   * getting random number. The only way to fail SideEffect is to panic which causes workflow task
760
   * failure. The workflow task after timeout is rescheduled and re-executed giving SideEffect
761
   * another chance to succeed.
762
   *
763
   * <p>Caution: do not use sideEffect function to modify any workflow state. Only use the
764
   * SideEffect's return value. For example this code is BROKEN:
765
   *
766
   * <pre><code>
767
   *  // Bad example:
768
   *  AtomicInteger random = new AtomicInteger();
769
   *  Workflow.sideEffect(() -&gt; {
770
   *         random.set(random.nextInt(100));
771
   *         return null;
772
   *  });
773
   *  // random will always be 0 in replay, thus this code is non-deterministic
774
   *  if random.get() &lt; 50 {
775
   *         ....
776
   *  } else {
777
   *         ....
778
   *  }
779
   * </code></pre>
780
   *
781
   * On replay the provided function is not executed, the random will always be 0, and the workflow
782
   * could take a different path breaking the determinism.
783
   *
784
   * <p>Here is the correct way to use sideEffect:
785
   *
786
   * <pre><code>
787
   *  // Good example:
788
   *  int random = Workflow.sideEffect(Integer.class, () -&gt; random.nextInt(100));
789
   *  if random &lt; 50 {
790
   *         ....
791
   *  } else {
792
   *         ....
793
   *  }
794
   * </code></pre>
795
   *
796
   * If function throws any exception it is not delivered to the workflow code. It is wrapped in
797
   * {@link Error} causing failure of the current workflow task.
798
   *
799
   * @param resultClass class of the side effect
800
   * @param resultType type of the side effect. Differs from resultClass for generic types.
801
   * @param func function that returns side effect value
802
   * @return value of the side effect
803
   * @see #mutableSideEffect(String, Class, BiPredicate, Functions.Func)
804
   */
805
  public static <R> R sideEffect(Class<R> resultClass, Type resultType, Func<R> func) {
806
    return WorkflowInternal.sideEffect(resultClass, resultType, func);
×
807
  }
808

809
  /**
810
   * {@code mutableSideEffect} is similar to {@link #sideEffect(Class, Functions.Func)} in allowing
811
   * calls of non-deterministic functions from workflow code.
812
   *
813
   * <p>The difference between {@code mutableSideEffect} and {@link #sideEffect(Class,
814
   * Functions.Func)} is that every new {@code sideEffect} call in non-replay mode results in a new
815
   * marker event recorded into the history. However, {@code mutableSideEffect} only records a new
816
   * marker if a value has changed. During the replay, {@code mutableSideEffect} will not execute
817
   * the function again, but it will return the exact same value as it was returning during the
818
   * non-replay run.
819
   *
820
   * <p>One good use case of {@code mutableSideEffect} is to access a dynamically changing config
821
   * without breaking determinism. Even if called very frequently the config value is recorded only
822
   * when it changes not causing any performance degradation due to a large history size.
823
   *
824
   * <p>Caution: do not use {@code mutableSideEffect} function to modify any workflow state. Only
825
   * use the mutableSideEffect's return value.
826
   *
827
   * <p>If function throws any exception it is not delivered to the workflow code. It is wrapped in
828
   * {@link Error} causing failure of the current workflow task.
829
   *
830
   * @param id unique identifier of this side effect
831
   * @param updated used to decide if a new value should be recorded. A func result is recorded only
832
   *     if call to updated with stored and a new value as arguments returns true. It is not called
833
   *     for the first value.
834
   * @param resultClass class of the side effect
835
   * @param func function that produces a value. This function can contain non-deterministic code.
836
   * @see #sideEffect(Class, Functions.Func)
837
   */
838
  public static <R> R mutableSideEffect(
839
      String id, Class<R> resultClass, BiPredicate<R, R> updated, Func<R> func) {
840
    return WorkflowInternal.mutableSideEffect(id, resultClass, resultClass, updated, func);
1✔
841
  }
842

843
  /**
844
   * {@code mutableSideEffect} is similar to {@link #sideEffect(Class, Functions.Func)} in allowing
845
   * calls of non-deterministic functions from workflow code.
846
   *
847
   * <p>The difference between {@code mutableSideEffect} and {@link #sideEffect(Class,
848
   * Functions.Func)} is that every new {@code sideEffect} call in non-replay mode results in a new
849
   * marker event recorded into the history. However, {@code mutableSideEffect} only records a new
850
   * marker if a value has changed. During the replay, {@code mutableSideEffect} will not execute
851
   * the function again, but it will return the exact same value as it was returning during the
852
   * non-replay run.
853
   *
854
   * <p>One good use case of {@code mutableSideEffect} is to access a dynamically changing config
855
   * without breaking determinism. Even if called very frequently the config value is recorded only
856
   * when it changes not causing any performance degradation due to a large history size.
857
   *
858
   * <p>Caution: do not use {@code mutableSideEffect} function to modify any workflow state. Only
859
   * use the mutableSideEffect's return value.
860
   *
861
   * <p>If function throws any exception it is not delivered to the workflow code. It is wrapped in
862
   * {@link Error} causing failure of the current workflow task.
863
   *
864
   * @param id unique identifier of this side effect
865
   * @param updated used to decide if a new value should be recorded. A func result is recorded only
866
   *     if call to updated with stored and a new value as arguments returns true. It is not called
867
   *     for the first value.
868
   * @param resultClass class of the side effect
869
   * @param resultType type of the side effect. Differs from resultClass for generic types.
870
   * @param func function that produces a value. This function can contain non-deterministic code.
871
   * @see #sideEffect(Class, Functions.Func)
872
   */
873
  public static <R> R mutableSideEffect(
874
      String id, Class<R> resultClass, Type resultType, BiPredicate<R, R> updated, Func<R> func) {
875
    return WorkflowInternal.mutableSideEffect(id, resultClass, resultType, updated, func);
×
876
  }
877

878
  /**
879
   * {@code getVersion} is used to safely perform backwards incompatible changes to workflow
880
   * definitions. It is not allowed to update workflow code while there are workflows running as it
881
   * is going to break determinism. The solution is to have both old code that is used to replay
882
   * existing workflows as well as the new one that is used when it is executed for the first time.\
883
   *
884
   * <p>{@code getVersion} returns maxSupported version when is executed for the first time. This
885
   * version is recorded into the workflow history as a marker event. Even if maxSupported version
886
   * is changed the version that was recorded is returned on replay. DefaultVersion constant
887
   * contains version of code that wasn't versioned before.
888
   *
889
   * <p>For example initially workflow has the following code:
890
   *
891
   * <pre><code>
892
   * result = testActivities.activity1();
893
   * </code></pre>
894
   *
895
   * it should be updated to
896
   *
897
   * <pre><code>
898
   * result = testActivities.activity2();
899
   * </code></pre>
900
   *
901
   * The backwards compatible way to execute the update is
902
   *
903
   * <pre><code>
904
   * int version = Workflow.getVersion("fooChange", Workflow.DEFAULT_VERSION, 1);
905
   * String result;
906
   * if (version == Workflow.DEFAULT_VERSION) {
907
   *   result = testActivities.activity1();
908
   * } else {
909
   *   result = testActivities.activity2();
910
   * }
911
   * </code></pre>
912
   *
913
   * Then later if we want to have another change:
914
   *
915
   * <pre><code>
916
   * int version = Workflow.getVersion("fooChange", Workflow.DEFAULT_VERSION, 2);
917
   * String result;
918
   * if (version == Workflow.DEFAULT_VERSION) {
919
   *   result = testActivities.activity1();
920
   * } else if (version == 1) {
921
   *   result = testActivities.activity2();
922
   * } else {
923
   *   result = testActivities.activity3();
924
   * }
925
   * </code></pre>
926
   *
927
   * Later when there are no workflow executions running DefaultVersion the correspondent branch can
928
   * be removed:
929
   *
930
   * <pre><code>
931
   * int version = Workflow.getVersion("fooChange", 1, 2);
932
   * String result;
933
   * if (version == 1) {
934
   *   result = testActivities.activity2();
935
   * } else {
936
   *   result = testActivities.activity3();
937
   * }
938
   * </code></pre>
939
   *
940
   * It is recommended to keep the GetVersion() call even if single branch is left:
941
   *
942
   * <pre><code>
943
   * Workflow.getVersion("fooChange", 2, 2);
944
   * result = testActivities.activity3();
945
   * </code></pre>
946
   *
947
   * The reason to keep it is: 1) it ensures that if there is older version execution still running,
948
   * it will fail here and not proceed; 2) if you ever need to make more changes for “fooChange”,
949
   * for example change activity3 to activity4, you just need to update the maxVersion from 2 to 3.
950
   *
951
   * <p>Note that, you only need to preserve the first call to GetVersion() for each changeId. All
952
   * subsequent call to GetVersion() with same changeId are safe to remove. However, if you really
953
   * want to get rid of the first GetVersion() call as well, you can do so, but you need to make
954
   * sure: 1) all older version executions are completed; 2) you can no longer use “fooChange” as
955
   * changeId. If you ever need to make changes to that same part, you would need to use a different
956
   * changeId like “fooChange-fix2”, and start minVersion from DefaultVersion again.
957
   *
958
   * @param changeId identifier of a particular change. All calls to getVersion that share a
959
   *     changeId are guaranteed to return the same version number. Use this to perform multiple
960
   *     coordinated changes that should be enabled together.
961
   * @param minSupported min version supported for the change
962
   * @param maxSupported max version supported for the change, this version is used as the current
963
   *     one during the original execution.
964
   * @return {@code maxSupported} when is originally executed. Original version recorded in the
965
   *     history on replays.
966
   */
967
  public static int getVersion(String changeId, int minSupported, int maxSupported) {
968
    return WorkflowInternal.getVersion(changeId, minSupported, maxSupported);
1✔
969
  }
970

971
  /**
972
   * Get scope for reporting business metrics in workflow logic. This should be used instead of
973
   * creating new metrics scopes as it is able to dedupe metrics during replay.
974
   *
975
   * <p>The original metrics scope is set through {@link
976
   * WorkflowServiceStubsOptions.Builder#setMetricsScope(Scope)} when a worker starts up.
977
   */
978
  public static Scope getMetricsScope() {
979
    return WorkflowInternal.getMetricsScope();
1✔
980
  }
981

982
  /**
983
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
984
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
985
   *
986
   * @param clazz class name to appear in logging.
987
   * @return logger to use in workflow logic.
988
   */
989
  public static Logger getLogger(Class<?> clazz) {
990
    return WorkflowInternal.getLogger(clazz);
1✔
991
  }
992

993
  /**
994
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
995
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
996
   *
997
   * @param name name to appear in logging.
998
   * @return logger to use in workflow logic.
999
   */
1000
  public static Logger getLogger(String name) {
1001
    return WorkflowInternal.getLogger(name);
1✔
1002
  }
1003

1004
  /**
1005
   * GetLastCompletionResult extract last completion result from previous run for this cron
1006
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
1007
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
1008
   * return any data and that data will become available when next run starts.
1009
   *
1010
   * @param resultClass class of the return data from last run
1011
   * @return result of last run
1012
   * @see io.temporal.client.WorkflowOptions.Builder#setCronSchedule(String)
1013
   */
1014
  public static <R> R getLastCompletionResult(Class<R> resultClass) {
1015
    return WorkflowInternal.getLastCompletionResult(resultClass, resultClass);
1✔
1016
  }
1017

1018
  /**
1019
   * Extract the latest failure from a previous run of this workflow. If any previous run of this
1020
   * workflow has failed, this function returns that failure. If no previous runs have failed, an
1021
   * empty optional is returned. The run you are calling this from may have been created as a retry
1022
   * of the previous failed run or as a next cron invocation for cron workflows.
1023
   *
1024
   * @return The last {@link Exception} that occurred in this workflow, if there has been one.
1025
   */
1026
  public static Optional<Exception> getPreviousRunFailure() {
1027
    return WorkflowInternal.getPreviousRunFailure();
1✔
1028
  }
1029

1030
  /**
1031
   * GetLastCompletionResult extract last completion result from previous run for this cron
1032
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
1033
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
1034
   * return any data and that data will become available when next run starts.
1035
   *
1036
   * @param resultClass class of the return data from last run
1037
   * @param resultType type of the return data from last run. Differs from resultClass for generic
1038
   *     types.
1039
   * @return result of last run
1040
   */
1041
  public static <R> R getLastCompletionResult(Class<R> resultClass, Type resultType) {
1042
    return WorkflowInternal.getLastCompletionResult(resultClass, resultType);
×
1043
  }
1044

1045
  /**
1046
   * Get a single search attribute.
1047
   *
1048
   * @param name search attribute name
1049
   * @return deserialized search attribute value
1050
   * @throws IllegalStateException if the search attribute value is a collection of multiple (&gt;
1051
   *     1) elements
1052
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1053
   */
1054
  @Deprecated
1055
  @Nullable
1056
  public static <T> T getSearchAttribute(String name) {
1057
    return WorkflowInternal.getSearchAttribute(name);
1✔
1058
  }
1059

1060
  /**
1061
   * Collection returned from this method is immutable. To modify search attributes associated with
1062
   * this workflow use {@link #upsertSearchAttributes(Map)}.
1063
   *
1064
   * <p>Note: This method never returns an empty list. Empty list is considered an absent value for
1065
   * search attributes and will be returned as {@code null}.
1066
   *
1067
   * @param name search attribute name
1068
   * @return immutable list of deserialized search attribute values
1069
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1070
   */
1071
  @Deprecated
1072
  @Nullable
1073
  public static <T> List<T> getSearchAttributeValues(String name) {
1074
    return WorkflowInternal.getSearchAttributeValues(name);
×
1075
  }
1076

1077
  /**
1078
   * Map returned from this method is immutable. To modify search attributes associated with this
1079
   * workflow use {@link #upsertSearchAttributes(Map)}.
1080
   *
1081
   * @return immutable map of search attribute names to deserialized values.
1082
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1083
   */
1084
  @Deprecated
1085
  @Nonnull
1086
  public static Map<String, List<?>> getSearchAttributes() {
1087
    return WorkflowInternal.getSearchAttributes();
1✔
1088
  }
1089

1090
  /**
1091
   * Get immutable set of search attributes. To modify search attributes associated with this
1092
   * workflow use {@link #upsertTypedSearchAttributes}.
1093
   *
1094
   * @return immutable set of search attributes.
1095
   */
1096
  @Nonnull
1097
  public static SearchAttributes getTypedSearchAttributes() {
1098
    return WorkflowInternal.getTypedSearchAttributes();
1✔
1099
  }
1100

1101
  /**
1102
   * Updates Workflow Search Attributes by merging {@code searchAttributes} to the existing Search
1103
   * Attributes map attached to the workflow. Search Attributes are additional indexed information
1104
   * attributed to workflow and used for search and visibility.
1105
   *
1106
   * <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and its
1107
   * value type must be registered on Temporal server side.
1108
   *
1109
   * <p>Supported Java types of the value:
1110
   *
1111
   * <ul>
1112
   *   <li>{@link String}
1113
   *   <li>{@link Long}, {@link Integer}, {@link Short}, {@link Byte}
1114
   *   <li>{@link Boolean}
1115
   *   <li>{@link Double}
1116
   *   <li>{@link OffsetDateTime}
1117
   *   <li>{@link Collection} of the types above
1118
   *   <li>{@link io.temporal.common.SearchAttribute#UNSET_VALUE} can be used to unset or remove the
1119
   *       search attribute
1120
   * </ul>
1121
   *
1122
   * For example, workflow code:
1123
   *
1124
   * <pre><code>
1125
   *     Map&lt;String, Object&gt; attr1 = new HashMap&lt;&gt;();
1126
   *     attr1.put("CustomIntField", 1);
1127
   *     attr1.put("CustomBoolField", true);
1128
   *     Workflow.upsertSearchAttributes(attr1);
1129
   *
1130
   *     Map&lt;String, Object&gt; attr2 = new HashMap&lt;&gt;();
1131
   *     attr2.put("CustomIntField", Lists.newArrayList(1, 2));
1132
   *     attr2.put("CustomKeywordField", "Seattle");
1133
   *     Workflow.upsertSearchAttributes(attr2);
1134
   * </pre></code> will eventually have search attributes as:
1135
   *
1136
   * <pre><code>
1137
   *     {
1138
   *       "CustomIntField": 1, 2,
1139
   *       "CustomBoolField": true,
1140
   *       "CustomKeywordField": "Seattle",
1141
   *     }
1142
   * </pre></code>
1143
   *
1144
   * @param searchAttributes map of String to Object value that can be used to search in list APIs
1145
   * @deprecated use {@link #upsertTypedSearchAttributes} instead.
1146
   */
1147
  // WorkflowOptions#setSearchAttributes docs needs to be kept in sync with this method
1148
  @Deprecated
1149
  public static void upsertSearchAttributes(Map<String, ?> searchAttributes) {
1150
    WorkflowInternal.upsertSearchAttributes(searchAttributes);
1✔
1151
  }
1✔
1152

1153
  /**
1154
   * Updates Workflow Search Attributes by applying {@code searchAttributeUpdates} to the existing
1155
   * Search Attributes set attached to the workflow. Search Attributes are additional indexed
1156
   * information attributed to workflow and used for search and visibility.
1157
   *
1158
   * <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and its
1159
   * value type must be registered on Temporal server side.
1160
   *
1161
   * @param searchAttributeUpdates set of updates to apply to search attributes.
1162
   */
1163
  public static void upsertTypedSearchAttributes(
1164
      SearchAttributeUpdate<?>... searchAttributeUpdates) {
1165
    WorkflowInternal.upsertTypedSearchAttributes(searchAttributeUpdates);
1✔
1166
  }
1✔
1167

1168
  /**
1169
   * Sets the default activity options that will be used for activity stubs that have no {@link
1170
   * ActivityOptions} specified.<br>
1171
   * This overrides a value provided by {@link
1172
   * WorkflowImplementationOptions#getDefaultActivityOptions}.<br>
1173
   * A more specific per-activity-type option specified in {@link
1174
   * WorkflowImplementationOptions#getActivityOptions} or {@link #applyActivityOptions(Map)} takes
1175
   * precedence over this setting.
1176
   *
1177
   * @param defaultActivityOptions {@link ActivityOptions} to be used as a default
1178
   */
1179
  public static void setDefaultActivityOptions(ActivityOptions defaultActivityOptions) {
1180
    WorkflowInternal.setDefaultActivityOptions(defaultActivityOptions);
1✔
1181
  }
1✔
1182

1183
  /**
1184
   * @deprecated use {@link #applyActivityOptions(Map)}
1185
   */
1186
  @Deprecated
1187
  public static void setActivityOptions(Map<String, ActivityOptions> activityMethodOptions) {
1188
    WorkflowInternal.applyActivityOptions(activityMethodOptions);
×
1189
  }
×
1190

1191
  /**
1192
   * Adds activity options per activity type that will be used for an activity stub that has no
1193
   * {@link ActivityOptions} specified.<br>
1194
   * This method refines an original set of {@code Map<String, ActivityOptions>} provided by {@link
1195
   * WorkflowImplementationOptions#getActivityOptions()}<br>
1196
   * These more specific options take precedence over more generic setting {@link
1197
   * #setDefaultActivityOptions}
1198
   *
1199
   * <p>If an activity type already has a {@link ActivityOptions} set by an earlier call to this
1200
   * method or from {@link WorkflowImplementationOptions#getDefaultActivityOptions}, new {@link
1201
   * ActivityOptions} from {@code activityTypeToOptions} will be merged into the old ones using
1202
   * {@link ActivityOptions.Builder#mergeActivityOptions(ActivityOptions)}
1203
   *
1204
   * @param activityTypeToOptions a map of activity types to {@link ActivityOptions}
1205
   */
1206
  public static void applyActivityOptions(Map<String, ActivityOptions> activityTypeToOptions) {
1207
    WorkflowInternal.applyActivityOptions(activityTypeToOptions);
1✔
1208
  }
1✔
1209

1210
  /**
1211
   * Sets the default local activity options that will be used for activity stubs that have no
1212
   * {@link LocalActivityOptions} specified.<br>
1213
   * This overrides a value provided by {@link
1214
   * WorkflowImplementationOptions#getDefaultLocalActivityOptions}.<br>
1215
   * A more specific per-activity-type option specified in {@link
1216
   * WorkflowImplementationOptions#getLocalActivityOptions} or {@link
1217
   * #applyLocalActivityOptions(Map)} takes precedence over this setting.
1218
   *
1219
   * @param defaultLocalActivityOptions {@link LocalActivityOptions} to be used as a default
1220
   */
1221
  public static void setDefaultLocalActivityOptions(
1222
      LocalActivityOptions defaultLocalActivityOptions) {
1223
    WorkflowInternal.setDefaultLocalActivityOptions(defaultLocalActivityOptions);
1✔
1224
  }
1✔
1225

1226
  /**
1227
   * Adds local activity options per activity type that will be used for a local activity stub that
1228
   * has no {@link LocalActivityOptions} specified.<br>
1229
   * This method refines an original set of {@code Map<String, LocalActivityOptions>} provided by
1230
   * {@link WorkflowImplementationOptions#getLocalActivityOptions()}<br>
1231
   * These more specific options take precedence over more generic setting {@link
1232
   * #setDefaultLocalActivityOptions}
1233
   *
1234
   * <p>If an activity type already has a {@link LocalActivityOptions} set by an earlier call to
1235
   * this method or from {@link WorkflowImplementationOptions#getDefaultLocalActivityOptions}, new
1236
   * {@link LocalActivityOptions} from {@code activityTypeToOptions} will be merged into the old
1237
   * ones using {@link LocalActivityOptions.Builder#mergeActivityOptions(LocalActivityOptions)}
1238
   *
1239
   * @param activityTypeToOptions a map of activity types to {@link LocalActivityOptions}
1240
   */
1241
  public static void applyLocalActivityOptions(
1242
      Map<String, LocalActivityOptions> activityTypeToOptions) {
1243
    WorkflowInternal.applyLocalActivityOptions(activityTypeToOptions);
1✔
1244
  }
1✔
1245

1246
  /** Prohibit instantiation. */
1247
  private Workflow() {}
1248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc