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

temporalio / sdk-java / #167

pending completion
#167

push

github-actions

web-flow
Add comment on Workflow#newQueue (#1747)

17117 of 20962 relevant lines covered (81.66%)

0.82 hits per line

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

72.22
/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.converter.DataConverter;
29
import io.temporal.failure.ActivityFailure;
30
import io.temporal.failure.CanceledFailure;
31
import io.temporal.failure.ChildWorkflowFailure;
32
import io.temporal.internal.sync.WorkflowInternal;
33
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
34
import io.temporal.worker.WorkerFactoryOptions;
35
import io.temporal.worker.WorkflowImplementationOptions;
36
import io.temporal.workflow.Functions.Func;
37
import io.temporal.workflow.unsafe.WorkflowUnsafe;
38
import java.lang.reflect.Type;
39
import java.time.Duration;
40
import java.time.OffsetDateTime;
41
import java.util.*;
42
import java.util.function.BiPredicate;
43
import java.util.function.Supplier;
44
import javax.annotation.Nonnull;
45
import javax.annotation.Nullable;
46
import org.slf4j.Logger;
47

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

340
  public static WorkflowInfo getInfo() {
341
    return WorkflowInternal.getWorkflowInfo();
342
  }
343

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

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

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

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

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

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

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

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

496
  public static <E> CompletablePromise<E> newPromise() {
497
    return WorkflowInternal.newCompletablePromise();
498
  }
499

500
  public static <E> Promise<E> newPromise(E value) {
501
    return WorkflowInternal.newPromise(value);
502
  }
503

504
  public static <E> Promise<E> newFailedPromise(Exception failure) {
505
    return WorkflowInternal.newFailedPromise(failure);
506
  }
507

508
  /**
509
   * Registers an implementation object. The object must implement at least one interface annotated
510
   * with {@link WorkflowInterface}. All its methods annotated with @{@link SignalMethod}
511
   * and @{@link QueryMethod} are registered.
512
   *
513
   * <p>There is no need to register the top level workflow implementation object as it is done
514
   * implicitly by the framework on object startup.
515
   *
516
   * <p>An attempt to register a duplicated query is going to fail with {@link
517
   * IllegalArgumentException}
518
   */
519
  public static void registerListener(Object listener) {
520
    WorkflowInternal.registerListener(listener);
521
  }
522

523
  /**
524
   * Must be used to get current time instead of {@link System#currentTimeMillis()} to guarantee
525
   * determinism.
526
   */
527
  public static long currentTimeMillis() {
528
    return WorkflowInternal.currentTimeMillis();
529
  }
530

531
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
532
  public static void sleep(Duration duration) {
533
    WorkflowInternal.sleep(duration);
534
  }
535

536
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
537
  public static void sleep(long millis) {
538
    WorkflowInternal.sleep(Duration.ofMillis(millis));
539
  }
540

541
  /**
542
   * Block current thread until unblockCondition is evaluated to true.
543
   *
544
   * @param unblockCondition condition that should return true to indicate that thread should
545
   *     unblock. The condition is called on every state transition, so it should never call any
546
   *     blocking operations or contain code that mutates any workflow state. It should also not
547
   *     contain any time based conditions. Use {@link #await(Duration, Supplier)} for those
548
   *     instead.
549
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
550
   */
551
  public static void await(Supplier<Boolean> unblockCondition) {
552
    WorkflowInternal.await(
553
        "await",
554
        () -> {
555
          CancellationScope.throwCanceled();
556
          return unblockCondition.get();
557
        });
558
  }
559

560
  /**
561
   * Block current workflow thread until unblockCondition is evaluated to true or timeoutMillis
562
   * passes.
563
   *
564
   * @param timeout time to unblock even if unblockCondition is not satisfied.
565
   * @param unblockCondition condition that should return true to indicate that thread should
566
   *     unblock. The condition is called on every state transition, so it should not contain any
567
   *     code that mutates any workflow state. It should also not contain any time based conditions.
568
   *     Use timeout parameter for those.
569
   * @return false if timed out.
570
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
571
   */
572
  public static boolean await(Duration timeout, Supplier<Boolean> unblockCondition) {
573
    return WorkflowInternal.await(
574
        timeout,
575
        "await",
576
        () -> {
577
          CancellationScope.throwCanceled();
578
          return unblockCondition.get();
579
        });
580
  }
581

582
  /**
583
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
584
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
585
   *
586
   * @param options retry options that specify retry policy
587
   * @param expiration stop retrying after this interval if provided
588
   * @param fn function to invoke and retry
589
   * @return result of the function or the last failure.
590
   */
591
  public static <R> R retry(
592
      RetryOptions options, Optional<Duration> expiration, Functions.Func<R> fn) {
593
    return WorkflowInternal.retry(options, expiration, fn);
594
  }
595

596
  /**
597
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
598
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
599
   *
600
   * @param options retry options that specify retry policy
601
   * @param expiration if specified stop retrying after this interval
602
   * @param proc procedure to invoke and retry
603
   */
604
  public static void retry(
605
      RetryOptions options, Optional<Duration> expiration, Functions.Proc proc) {
606
    WorkflowInternal.retry(
607
        options,
608
        expiration,
609
        () -> {
610
          proc.apply();
611
          return null;
612
        });
613
  }
614

615
  /**
616
   * If there is a need to return a checked exception from a workflow implementation do not add the
617
   * exception to a method signature but wrap it using this method before rethrowing. The library
618
   * code will unwrap it automatically using when propagating exception to a remote caller. {@link
619
   * RuntimeException} are just returned from this method without modification.
620
   *
621
   * <p>The reason for such design is that returning originally thrown exception from a remote call
622
   * (which child workflow and activity invocations are ) would not allow adding context information
623
   * about a failure, like activity and child workflow id. So stubs always throw a subclass of
624
   * {@link ActivityFailure} from calls to an activity and subclass of {@link ChildWorkflowFailure}
625
   * from calls to a child workflow. The original exception is attached as a cause to these wrapper
626
   * exceptions. So as exceptions are always wrapped adding checked ones to method signature causes
627
   * more pain than benefit.
628
   *
629
   * <p>
630
   *
631
   * <pre>
632
   * try {
633
   *     return someCall();
634
   * } catch (Exception e) {
635
   *     throw Workflow.wrap(e);
636
   * }
637
   * </pre>
638
   *
639
   * @return CheckedExceptionWrapper if e is checked or original exception if e extends
640
   *     RuntimeException.
641
   */
642
  public static RuntimeException wrap(Exception e) {
643
    return WorkflowInternal.wrap(e);
644
  }
645

646
  /**
647
   * Replay safe way to generate UUID.
648
   *
649
   * <p>Must be used instead of {@link UUID#randomUUID()} which relies on a random generator, thus
650
   * leads to non-deterministic code which is prohibited inside a workflow.
651
   */
652
  public static UUID randomUUID() {
653
    return WorkflowInternal.randomUUID();
654
  }
655

656
  /** Replay safe random numbers generator. Seeded differently for each workflow instance. */
657
  public static Random newRandom() {
658
    return WorkflowInternal.newRandom();
659
  }
660

661
  /**
662
   * True if workflow code is being replayed.
663
   *
664
   * <p><b>Warning!</b> Never make workflow logic depend on this flag as it is going to break
665
   * determinism. The only reasonable uses for this flag are deduping external never failing side
666
   * effects like logging or metric reporting.
667
   *
668
   * <p>This method always returns false if called from a non workflow thread.
669
   *
670
   * @deprecated use {@link WorkflowUnsafe#isReplaying()}
671
   */
672
  @Deprecated
673
  public static boolean isReplaying() {
674
    return WorkflowUnsafe.isReplaying();
675
  }
676

677
  /**
678
   * Executes the provided function once, records its result into the workflow history. The recorded
679
   * result on history will be returned without executing the provided function during replay. This
680
   * guarantees the deterministic requirement for workflow as the exact same result will be returned
681
   * in replay. Common use case is to run some short non-deterministic code in workflow, like
682
   * getting random number. The only way to fail SideEffect is to panic which causes workflow task
683
   * failure. The workflow task after timeout is rescheduled and re-executed giving SideEffect
684
   * another chance to succeed.
685
   *
686
   * <p>Caution: do not use sideEffect function to modify any workflow state. Only use the
687
   * SideEffect's return value. For example this code is BROKEN:
688
   *
689
   * <pre><code>
690
   *  // Bad example:
691
   *  AtomicInteger random = new AtomicInteger();
692
   *  Workflow.sideEffect(() -&gt; {
693
   *         random.set(random.nextInt(100));
694
   *         return null;
695
   *  });
696
   *  // random will always be 0 in replay, thus this code is non-deterministic
697
   *  if random.get() &lt; 50 {
698
   *         ....
699
   *  } else {
700
   *         ....
701
   *  }
702
   * </code></pre>
703
   *
704
   * On replay the provided function is not executed, the random will always be 0, and the workflow
705
   * could take a different path breaking the determinism.
706
   *
707
   * <p>Here is the correct way to use sideEffect:
708
   *
709
   * <pre><code>
710
   *  // Good example:
711
   *  int random = Workflow.sideEffect(Integer.class, () -&gt; random.nextInt(100));
712
   *  if random &lt; 50 {
713
   *         ....
714
   *  } else {
715
   *         ....
716
   *  }
717
   * </code></pre>
718
   *
719
   * If function throws any exception it is not delivered to the workflow code. It is wrapped in
720
   * {@link Error} causing failure of the current workflow task.
721
   *
722
   * @param resultClass type of the side effect
723
   * @param func function that returns side effect value
724
   * @return value of the side effect
725
   * @see #mutableSideEffect(String, Class, BiPredicate, Functions.Func)
726
   */
727
  public static <R> R sideEffect(Class<R> resultClass, Func<R> func) {
728
    return WorkflowInternal.sideEffect(resultClass, resultClass, func);
729
  }
730

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

786
  /**
787
   * {@code mutableSideEffect} is similar to {@link #sideEffect(Class, Functions.Func)} in allowing
788
   * calls of non-deterministic functions from workflow code.
789
   *
790
   * <p>The difference between {@code mutableSideEffect} and {@link #sideEffect(Class,
791
   * Functions.Func)} is that every new {@code sideEffect} call in non-replay mode results in a new
792
   * marker event recorded into the history. However, {@code mutableSideEffect} only records a new
793
   * marker if a value has changed. During the replay, {@code mutableSideEffect} will not execute
794
   * the function again, but it will return the exact same value as it was returning during the
795
   * non-replay run.
796
   *
797
   * <p>One good use case of {@code mutableSideEffect} is to access a dynamically changing config
798
   * without breaking determinism. Even if called very frequently the config value is recorded only
799
   * when it changes not causing any performance degradation due to a large history size.
800
   *
801
   * <p>Caution: do not use {@code mutableSideEffect} function to modify any workflow state. Only
802
   * use the mutableSideEffect's return value.
803
   *
804
   * <p>If function throws any exception it is not delivered to the workflow code. It is wrapped in
805
   * {@link Error} causing failure of the current workflow task.
806
   *
807
   * @param id unique identifier of this side effect
808
   * @param updated used to decide if a new value should be recorded. A func result is recorded only
809
   *     if call to updated with stored and a new value as arguments returns true. It is not called
810
   *     for the first value.
811
   * @param resultClass class of the side effect
812
   * @param func function that produces a value. This function can contain non-deterministic code.
813
   * @see #sideEffect(Class, Functions.Func)
814
   */
815
  public static <R> R mutableSideEffect(
816
      String id, Class<R> resultClass, BiPredicate<R, R> updated, Func<R> func) {
817
    return WorkflowInternal.mutableSideEffect(id, resultClass, resultClass, updated, func);
818
  }
819

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

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

948
  /**
949
   * Get scope for reporting business metrics in workflow logic. This should be used instead of
950
   * creating new metrics scopes as it is able to dedupe metrics during replay.
951
   *
952
   * <p>The original metrics scope is set through {@link
953
   * WorkflowServiceStubsOptions.Builder#setMetricsScope(Scope)} when a worker starts up.
954
   */
955
  public static Scope getMetricsScope() {
956
    return WorkflowInternal.getMetricsScope();
957
  }
958

959
  /**
960
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
961
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
962
   *
963
   * @param clazz class name to appear in logging.
964
   * @return logger to use in workflow logic.
965
   */
966
  public static Logger getLogger(Class<?> clazz) {
967
    return WorkflowInternal.getLogger(clazz);
968
  }
969

970
  /**
971
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
972
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
973
   *
974
   * @param name name to appear in logging.
975
   * @return logger to use in workflow logic.
976
   */
977
  public static Logger getLogger(String name) {
978
    return WorkflowInternal.getLogger(name);
979
  }
980

981
  /**
982
   * GetLastCompletionResult extract last completion result from previous run for this cron
983
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
984
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
985
   * return any data and that data will become available when next run starts.
986
   *
987
   * @param resultClass class of the return data from last run
988
   * @return result of last run
989
   * @see io.temporal.client.WorkflowOptions.Builder#setCronSchedule(String)
990
   */
991
  public static <R> R getLastCompletionResult(Class<R> resultClass) {
992
    return WorkflowInternal.getLastCompletionResult(resultClass, resultClass);
993
  }
994

995
  /**
996
   * Extract the latest failure from a previous run of this workflow. If any previous run of this
997
   * workflow has failed, this function returns that failure. If no previous runs have failed, an
998
   * empty optional is returned. The run you are calling this from may have been created as a retry
999
   * of the previous failed run or as a next cron invocation for cron workflows.
1000
   *
1001
   * @return The last {@link Exception} that occurred in this workflow, if there has been one.
1002
   */
1003
  public static Optional<Exception> getPreviousRunFailure() {
1004
    return WorkflowInternal.getPreviousRunFailure();
1005
  }
1006

1007
  /**
1008
   * GetLastCompletionResult extract last completion result from previous run for this cron
1009
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
1010
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
1011
   * return any data and that data will become available when next run starts.
1012
   *
1013
   * @param resultClass class of the return data from last run
1014
   * @param resultType type of the return data from last run. Differs from resultClass for generic
1015
   *     types.
1016
   * @return result of last run
1017
   */
1018
  public static <R> R getLastCompletionResult(Class<R> resultClass, Type resultType) {
1019
    return WorkflowInternal.getLastCompletionResult(resultClass, resultType);
1020
  }
1021

1022
  /**
1023
   * Use {@link #getSearchAttributeValues(String)} to access safe version of this method that always
1024
   * returns a collection of values.
1025
   *
1026
   * @param name search attribute name
1027
   * @return deserialized search attribute value
1028
   * @throws IllegalStateException if the search attribute value is a collection of multiple (&gt;
1029
   *     1) elements
1030
   */
1031
  @Nullable
1032
  public static <T> T getSearchAttribute(String name) {
1033
    return WorkflowInternal.getSearchAttribute(name);
1034
  }
1035

1036
  /**
1037
   * Collection returned from this method is immutable. To modify search attributes associated with
1038
   * this workflow use {@link #upsertSearchAttributes(Map)}.
1039
   *
1040
   * <p>Note: This method never returns an empty list. Empty list is considered an absent value for
1041
   * search attributes and will be returned as {@code null}.
1042
   *
1043
   * @param name search attribute name
1044
   * @return immutable list of deserialized search attribute values
1045
   */
1046
  @Nullable
1047
  public static <T> List<T> getSearchAttributeValues(String name) {
1048
    return WorkflowInternal.getSearchAttributeValues(name);
1049
  }
1050

1051
  /**
1052
   * Map returned from this method is immutable. To modify search attributes associated with this
1053
   * workflow use {@link #upsertSearchAttributes(Map)}.
1054
   *
1055
   * @return immutable map of search attribute names to deserialized values.
1056
   */
1057
  @Nonnull
1058
  public static Map<String, List<?>> getSearchAttributes() {
1059
    return WorkflowInternal.getSearchAttributes();
1060
  }
1061

1062
  /**
1063
   * Updates Workflow Search Attributes by merging {@code searchAttributes} to the existing Search
1064
   * Attributes map attached to the workflow. Search Attributes are additional indexed information
1065
   * attributed to workflow and used for search and visibility.
1066
   *
1067
   * <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and its
1068
   * value type must be registered on Temporal server side.
1069
   *
1070
   * <p>Supported Java types of the value:
1071
   *
1072
   * <ul>
1073
   *   <li>{@link String}
1074
   *   <li>{@link Long}, {@link Integer}, {@link Short}, {@link Byte}
1075
   *   <li>{@link Boolean}
1076
   *   <li>{@link Double}
1077
   *   <li>{@link OffsetDateTime}
1078
   *   <li>{@link Collection} of the types above
1079
   *   <li>{@link io.temporal.common.SearchAttribute#UNSET_VALUE} can be used to unset or remove the
1080
   *       search attribute
1081
   * </ul>
1082
   *
1083
   * For example, workflow code:
1084
   *
1085
   * <pre><code>
1086
   *     Map&lt;String, Object&gt; attr1 = new HashMap&lt;&gt;();
1087
   *     attr1.put("CustomIntField", 1);
1088
   *     attr1.put("CustomBoolField", true);
1089
   *     Workflow.upsertSearchAttributes(attr1);
1090
   *
1091
   *     Map&lt;String, Object&gt; attr2 = new HashMap&lt;&gt;();
1092
   *     attr2.put("CustomIntField", Lists.newArrayList(1, 2));
1093
   *     attr2.put("CustomKeywordField", "Seattle");
1094
   *     Workflow.upsertSearchAttributes(attr2);
1095
   * </pre></code> will eventually have search attributes as:
1096
   *
1097
   * <pre><code>
1098
   *     {
1099
   *       "CustomIntField": 1, 2,
1100
   *       "CustomBoolField": true,
1101
   *       "CustomKeywordField": "Seattle",
1102
   *     }
1103
   * </pre></code>
1104
   *
1105
   * @param searchAttributes map of String to Object value that can be used to search in list APIs
1106
   */
1107
  // WorkflowOptions#setSearchAttributes docs needs to be kept in sync with this method
1108
  public static void upsertSearchAttributes(Map<String, ?> searchAttributes) {
1109
    WorkflowInternal.upsertSearchAttributes(searchAttributes);
1110
  }
1111

1112
  /**
1113
   * Sets the default activity options that will be used for activity stubs that have no {@link
1114
   * ActivityOptions} specified.<br>
1115
   * This overrides a value provided by {@link
1116
   * WorkflowImplementationOptions#getDefaultActivityOptions}.<br>
1117
   * A more specific per-activity-type option specified in {@link
1118
   * WorkflowImplementationOptions#getActivityOptions} or {@link #applyActivityOptions(Map)} takes
1119
   * precedence over this setting.
1120
   *
1121
   * @param defaultActivityOptions {@link ActivityOptions} to be used as a default
1122
   */
1123
  public static void setDefaultActivityOptions(ActivityOptions defaultActivityOptions) {
1124
    WorkflowInternal.setDefaultActivityOptions(defaultActivityOptions);
1125
  }
1126

1127
  /**
1128
   * @deprecated use {@link #applyActivityOptions(Map)}
1129
   */
1130
  @Deprecated
1131
  public static void setActivityOptions(Map<String, ActivityOptions> activityMethodOptions) {
1132
    WorkflowInternal.applyActivityOptions(activityMethodOptions);
1133
  }
1134

1135
  /**
1136
   * Adds activity options per activity type that will be used for an activity stub that has no
1137
   * {@link ActivityOptions} specified.<br>
1138
   * This method refines an original set of {@code Map<String, ActivityOptions>} provided by {@link
1139
   * WorkflowImplementationOptions#getActivityOptions()}<br>
1140
   * These more specific options take precedence over more generic setting {@link
1141
   * #setDefaultActivityOptions}
1142
   *
1143
   * <p>If an activity type already has a {@link ActivityOptions} set by an earlier call to this
1144
   * method or from {@link WorkflowImplementationOptions#getDefaultActivityOptions}, new {@link
1145
   * ActivityOptions} from {@code activityTypeToOptions} will be merged into the old ones using
1146
   * {@link ActivityOptions.Builder#mergeActivityOptions(ActivityOptions)}
1147
   *
1148
   * @param activityTypeToOptions a map of activity types to {@link ActivityOptions}
1149
   */
1150
  public static void applyActivityOptions(Map<String, ActivityOptions> activityTypeToOptions) {
1151
    WorkflowInternal.applyActivityOptions(activityTypeToOptions);
1152
  }
1153

1154
  /**
1155
   * Sets the default local activity options that will be used for activity stubs that have no
1156
   * {@link LocalActivityOptions} specified.<br>
1157
   * This overrides a value provided by {@link
1158
   * WorkflowImplementationOptions#getDefaultLocalActivityOptions}.<br>
1159
   * A more specific per-activity-type option specified in {@link
1160
   * WorkflowImplementationOptions#getLocalActivityOptions} or {@link
1161
   * #applyLocalActivityOptions(Map)} takes precedence over this setting.
1162
   *
1163
   * @param defaultLocalActivityOptions {@link LocalActivityOptions} to be used as a default
1164
   */
1165
  public static void setDefaultLocalActivityOptions(
1166
      LocalActivityOptions defaultLocalActivityOptions) {
1167
    WorkflowInternal.setDefaultLocalActivityOptions(defaultLocalActivityOptions);
1168
  }
1169

1170
  /**
1171
   * Adds local activity options per activity type that will be used for a local activity stub that
1172
   * has no {@link LocalActivityOptions} specified.<br>
1173
   * This method refines an original set of {@code Map<String, LocalActivityOptions>} provided by
1174
   * {@link WorkflowImplementationOptions#getLocalActivityOptions()}<br>
1175
   * These more specific options take precedence over more generic setting {@link
1176
   * #setDefaultLocalActivityOptions}
1177
   *
1178
   * <p>If an activity type already has a {@link LocalActivityOptions} set by an earlier call to
1179
   * this method or from {@link WorkflowImplementationOptions#getDefaultLocalActivityOptions}, new
1180
   * {@link LocalActivityOptions} from {@code activityTypeToOptions} will be merged into the old
1181
   * ones using {@link LocalActivityOptions.Builder#mergeActivityOptions(LocalActivityOptions)}
1182
   *
1183
   * @param activityTypeToOptions a map of activity types to {@link LocalActivityOptions}
1184
   */
1185
  public static void applyLocalActivityOptions(
1186
      Map<String, LocalActivityOptions> activityTypeToOptions) {
1187
    WorkflowInternal.applyLocalActivityOptions(activityTypeToOptions);
1188
  }
1189

1190
  /** Prohibit instantiation. */
1191
  private Workflow() {}
1192
}
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