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

temporalio / sdk-java / #333

16 Oct 2024 07:28PM UTC coverage: 78.65% (+0.6%) from 78.085%
#333

push

github

web-flow
Fix code coverage (#2275)

22670 of 28824 relevant lines covered (78.65%)

0.79 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

343
  /**
344
   * Returns information about current workflow execution.
345
   *
346
   * <p>Note: Can only be called within the context of a workflow. Will throw an error if called
347
   * outside a workflow context.
348
   *
349
   * @return current workflow info.
350
   */
351
  public static WorkflowInfo getInfo() {
352
    return WorkflowInternal.getWorkflowInfo();
1✔
353
  }
354

355
  /**
356
   * Returns information about current workflow update.
357
   *
358
   * <p>Note: Should only be called within the context of an update handler thread in a workflow.
359
   * Will return an empty Optional if called outside an update handler thread inside a workflow
360
   * context. Will throw an error if called outside a workflow context.
361
   *
362
   * @return current workflow update info.
363
   */
364
  public static Optional<UpdateInfo> getCurrentUpdateInfo() {
365
    return WorkflowInternal.getCurrentUpdateInfo();
1✔
366
  }
367

368
  /**
369
   * Extract deserialized Memo associated with given key
370
   *
371
   * @param key memo key
372
   * @param valueClass Java class to deserialize into
373
   * @return deserialized Memo or null if the key is not present in the memo
374
   */
375
  public static <T> Object getMemo(String key, Class<T> valueClass) {
376
    return getMemo(key, valueClass, valueClass);
1✔
377
  }
378

379
  /**
380
   * Extract Memo associated with the given key and deserialized into an object of generic type as
381
   * is done here: {@link DataConverter#fromPayloads(int, java.util.Optional, java.lang.Class,
382
   * java.lang.reflect.Type)} Ex: To deserialize into {@code HashMap<String, Integer>} {@code
383
   * Workflow.getMemo(key, Map.class, new TypeToken<HashMap<String, Integer>>() {}.getType())}
384
   *
385
   * @param key memo key
386
   * @param valueClass Java class to deserialize into
387
   * @param genericType type parameter for the generic class
388
   * @return deserialized Memo or null if the key is not present in the memo
389
   */
390
  public static <T> T getMemo(String key, Class<T> valueClass, Type genericType) {
391
    return WorkflowInternal.getMemo(key, valueClass, genericType);
1✔
392
  }
393

394
  /**
395
   * Wraps the Runnable method argument in a {@link CancellationScope}. The {@link
396
   * CancellationScope#run()} calls {@link Runnable#run()} on the wrapped Runnable. The returned
397
   * CancellationScope can be used to cancel the wrapped code. The cancellation semantic depends on
398
   * the operation the code is blocked on. For example activity or child workflow is first canceled
399
   * then throws a {@link CanceledFailure}. The same applies for {@link Workflow#sleep(long)}
400
   * operation. When an activity or a child workflow is invoked asynchronously then they get
401
   * canceled and a {@link Promise} that contains their result will throw {@link CanceledFailure}
402
   * when {@link Promise#get()} is called.
403
   *
404
   * <p>The new cancellation scope {@link CancellationScope#current()} is linked to the parent one.
405
   * If the parent one is canceled then all the children scopes are wrapped within a root
406
   * cancellation scope which gets canceled when a workflow is canceled through the Temporal
407
   * CancelWorkflowExecution API. To perform cleanup operations that require blocking after the
408
   * current scope is canceled use a scope created through {@link
409
   * #newDetachedCancellationScope(Runnable)}.
410
   *
411
   * <p>Example of running activities in parallel and cancelling them after a specified timeout.
412
   *
413
   * <pre><code>
414
   *     List&lt;Promise&lt;String&gt;&gt; results = new ArrayList&lt;&gt;();
415
   *     CancellationScope scope = Workflow.newDetachedCancellationScope(() -&gt; {
416
   *        Async.function(activities::a1);
417
   *        Async.function(activities::a2);
418
   *     });
419
   *     scope.run(); // returns immediately as the activities are invoked asynchronously
420
   *     Workflow.sleep(Duration.ofHours(1));
421
   *     // Cancels any activity in the scope that is still running
422
   *     scope.cancel("one hour passed");
423
   *
424
   * </code></pre>
425
   *
426
   * @param runnable parameter to wrap in a cancellation scope.
427
   * @return wrapped parameter.
428
   */
429
  public static CancellationScope newCancellationScope(Runnable runnable) {
430
    return WorkflowInternal.newCancellationScope(false, runnable);
1✔
431
  }
432

433
  /**
434
   * Wraps a procedure in a CancellationScope. The procedure receives the wrapping CancellationScope
435
   * as a parameter. Useful when cancellation is requested from within the wrapped code. The
436
   * following example cancels the sibling activity on any failure.
437
   *
438
   * <pre><code>
439
   *               Workflow.newCancellationScope(
440
   *                   (scope) -&gt; {
441
   *                     Promise<Void> p1 = Async.proc(activities::a1).exceptionally(ex-&gt;
442
   *                        {
443
   *                           scope.cancel("a1 failed");
444
   *                           return null;
445
   *                        });
446
   *
447
   *                     Promise<Void> p2 = Async.proc(activities::a2).exceptionally(ex-&gt;
448
   *                        {
449
   *                           scope.cancel("a2 failed");
450
   *                           return null;
451
   *                        });
452
   *                     Promise.allOf(p1, p2).get();
453
   *                   })
454
   *               .run();
455
   * </code></pre>
456
   *
457
   * @param proc code to wrap in the cancellation scope
458
   * @return wrapped proc
459
   */
460
  public static CancellationScope newCancellationScope(Functions.Proc1<CancellationScope> proc) {
461
    return WorkflowInternal.newCancellationScope(false, proc);
1✔
462
  }
463

464
  /**
465
   * Creates a CancellationScope {@link CancellationScope#run()} that is not linked to a parent
466
   * scope must be called to execute the code the scope wraps. The detached scope is needed to
467
   * execute cleanup code after a workflow is canceled which cancels the root scope that wraps
468
   * the @WorkflowMethod invocation. Here is an example usage:
469
   *
470
   * <pre><code>
471
   *  try {
472
   *     // workflow logic
473
   *  } catch (CanceledFailure e) {
474
   *     CancellationScope detached = Workflow.newDetachedCancellationScope(() -&gt; {
475
   *         // cleanup logic
476
   *     });
477
   *     detached.run();
478
   *  }
479
   * </code></pre>
480
   *
481
   * @param runnable parameter to wrap in a cancellation scope.
482
   * @return wrapped parameter.
483
   * @see #newCancellationScope(Runnable)
484
   */
485
  public static CancellationScope newDetachedCancellationScope(Runnable runnable) {
486
    return WorkflowInternal.newCancellationScope(true, runnable);
1✔
487
  }
488

489
  /**
490
   * Create new timer. Note that Temporal service time resolution is in seconds. So all durations
491
   * are rounded <b>up</b> to the nearest second.
492
   *
493
   * @return feature that becomes ready when at least specified number of seconds passes. promise is
494
   *     failed with {@link CanceledFailure} if enclosing scope is canceled.
495
   */
496
  public static Promise<Void> newTimer(Duration delay) {
497
    return WorkflowInternal.newTimer(delay);
1✔
498
  }
499

500
  /**
501
   * Create new timer with options. Note that Temporal service time resolution is in seconds. So all
502
   * durations are rounded <b>up</b> to the nearest second.
503
   *
504
   * @return feature that becomes ready when at least specified number of seconds passes. promise is
505
   *     failed with {@link CanceledFailure} if enclosing scope is canceled.
506
   */
507
  public static Promise<Void> newTimer(Duration delay, TimerOptions options) {
508
    return WorkflowInternal.newTimer(delay, options);
×
509
  }
510

511
  /**
512
   * @deprecated use {@link #newWorkflowQueue(int)} instead. An implementation returned by this
513
   *     method has a bug.
514
   */
515
  @Deprecated
516
  public static <E> WorkflowQueue<E> newQueue(int capacity) {
517
    return WorkflowInternal.newQueue(capacity);
×
518
  }
519

520
  /**
521
   * Create a new instance of a {@link WorkflowQueue} implementation that is adapted to be used from
522
   * a workflow code.
523
   *
524
   * @param capacity the maximum size of the queue
525
   * @return new instance of {@link WorkflowQueue}
526
   */
527
  public static <E> WorkflowQueue<E> newWorkflowQueue(int capacity) {
528
    return WorkflowInternal.newWorkflowQueue(capacity);
×
529
  }
530

531
  public static <E> CompletablePromise<E> newPromise() {
532
    return WorkflowInternal.newCompletablePromise();
1✔
533
  }
534

535
  public static <E> Promise<E> newPromise(E value) {
536
    return WorkflowInternal.newPromise(value);
1✔
537
  }
538

539
  public static <E> Promise<E> newFailedPromise(Exception failure) {
540
    return WorkflowInternal.newFailedPromise(failure);
1✔
541
  }
542

543
  /**
544
   * Creates a {@link WorkflowLock} implementation that can be used from workflow code.
545
   *
546
   * @apiNote The lock returned is not reentrant. If a workflow thread tries to acquire a lock that
547
   *     it already holds, the call will block indefinitely.
548
   * @return new instance of {@link WorkflowLock}
549
   */
550
  public static WorkflowLock newWorkflowLock() {
551
    return WorkflowInternal.newWorkflowLock();
×
552
  }
553

554
  /**
555
   * Creates a {@link WorkflowSemaphore} implementation that can be used from workflow code.
556
   *
557
   * @param permits the given number of permits for the semaphore.
558
   * @return new instance of {@link WorkflowSemaphore}
559
   */
560
  public static WorkflowSemaphore newWorkflowSemaphore(int permits) {
561
    return WorkflowInternal.newWorkflowSemaphore(permits);
×
562
  }
563

564
  /**
565
   * Registers an implementation object. The object must implement at least one interface annotated
566
   * with {@link WorkflowInterface}. All its methods annotated with @{@link SignalMethod}
567
   * and @{@link QueryMethod} are registered.
568
   *
569
   * <p>There is no need to register the top level workflow implementation object as it is done
570
   * implicitly by the framework on object startup.
571
   *
572
   * <p>An attempt to register a duplicated query is going to fail with {@link
573
   * IllegalArgumentException}
574
   */
575
  public static void registerListener(Object listener) {
576
    WorkflowInternal.registerListener(listener);
1✔
577
  }
1✔
578

579
  /**
580
   * Must be used to get current time instead of {@link System#currentTimeMillis()} to guarantee
581
   * determinism.
582
   */
583
  public static long currentTimeMillis() {
584
    return WorkflowInternal.currentTimeMillis();
1✔
585
  }
586

587
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
588
  public static void sleep(Duration duration) {
589
    WorkflowInternal.sleep(duration);
1✔
590
  }
1✔
591

592
  /** Must be called instead of {@link Thread#sleep(long)} to guarantee determinism. */
593
  public static void sleep(long millis) {
594
    WorkflowInternal.sleep(Duration.ofMillis(millis));
1✔
595
  }
1✔
596

597
  /**
598
   * Block current thread until unblockCondition is evaluated to true.
599
   *
600
   * @param unblockCondition condition that should return true to indicate that thread should
601
   *     unblock. The condition is called on every state transition, so it should never call any
602
   *     blocking operations or contain code that mutates any workflow state. It should also not
603
   *     contain any time based conditions. Use {@link #await(Duration, Supplier)} for those
604
   *     instead.
605
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
606
   */
607
  public static void await(Supplier<Boolean> unblockCondition) {
608
    WorkflowInternal.await(
1✔
609
        "await",
610
        () -> {
611
          CancellationScope.throwCanceled();
1✔
612
          return unblockCondition.get();
1✔
613
        });
614
  }
1✔
615

616
  /**
617
   * Block current workflow thread until unblockCondition is evaluated to true or timeoutMillis
618
   * passes.
619
   *
620
   * @param timeout time to unblock even if unblockCondition is not satisfied.
621
   * @param unblockCondition condition that should return true to indicate that thread should
622
   *     unblock. The condition is called on every state transition, so it should not contain any
623
   *     code that mutates any workflow state. It should also not contain any time based conditions.
624
   *     Use timeout parameter for those.
625
   * @return false if timed out.
626
   * @throws CanceledFailure if thread (or current {@link CancellationScope} was canceled).
627
   */
628
  public static boolean await(Duration timeout, Supplier<Boolean> unblockCondition) {
629
    return WorkflowInternal.await(
1✔
630
        timeout,
631
        "await",
632
        () -> {
633
          CancellationScope.throwCanceled();
1✔
634
          return unblockCondition.get();
1✔
635
        });
636
  }
637

638
  /**
639
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
640
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
641
   *
642
   * @param options retry options that specify retry policy
643
   * @param expiration stop retrying after this interval if provided
644
   * @param fn function to invoke and retry
645
   * @return result of the function or the last failure.
646
   */
647
  public static <R> R retry(
648
      RetryOptions options, Optional<Duration> expiration, Functions.Func<R> fn) {
649
    return WorkflowInternal.retry(options, expiration, fn);
×
650
  }
651

652
  /**
653
   * Invokes function retrying in case of failures according to retry options. Synchronous variant.
654
   * Use {@link Async#retry(RetryOptions, Optional, Functions.Func)} for asynchronous functions.
655
   *
656
   * @param options retry options that specify retry policy
657
   * @param expiration if specified stop retrying after this interval
658
   * @param proc procedure to invoke and retry
659
   */
660
  public static void retry(
661
      RetryOptions options, Optional<Duration> expiration, Functions.Proc proc) {
662
    WorkflowInternal.retry(
×
663
        options,
664
        expiration,
665
        () -> {
666
          proc.apply();
×
667
          return null;
×
668
        });
669
  }
×
670

671
  /**
672
   * If there is a need to return a checked exception from a workflow implementation do not add the
673
   * exception to a method signature but wrap it using this method before rethrowing. The library
674
   * code will unwrap it automatically using when propagating exception to a remote caller. {@link
675
   * RuntimeException} are just returned from this method without modification.
676
   *
677
   * <p>The reason for such design is that returning originally thrown exception from a remote call
678
   * (which child workflow and activity invocations are ) would not allow adding context information
679
   * about a failure, like activity and child workflow id. So stubs always throw a subclass of
680
   * {@link ActivityFailure} from calls to an activity and subclass of {@link ChildWorkflowFailure}
681
   * from calls to a child workflow. The original exception is attached as a cause to these wrapper
682
   * exceptions. So as exceptions are always wrapped adding checked ones to method signature causes
683
   * more pain than benefit.
684
   *
685
   * <p>
686
   *
687
   * <pre>
688
   * try {
689
   *     return someCall();
690
   * } catch (Exception e) {
691
   *     throw Workflow.wrap(e);
692
   * }
693
   * </pre>
694
   *
695
   * @return CheckedExceptionWrapper if e is checked or original exception if e extends
696
   *     RuntimeException.
697
   */
698
  public static RuntimeException wrap(Exception e) {
699
    return WorkflowInternal.wrap(e);
1✔
700
  }
701

702
  /**
703
   * Replay safe way to generate UUID.
704
   *
705
   * <p>Must be used instead of {@link UUID#randomUUID()} which relies on a random generator, thus
706
   * leads to non-deterministic code which is prohibited inside a workflow.
707
   */
708
  public static UUID randomUUID() {
709
    return WorkflowInternal.randomUUID();
1✔
710
  }
711

712
  /** Replay safe random numbers generator. Seeded differently for each workflow instance. */
713
  public static Random newRandom() {
714
    return WorkflowInternal.newRandom();
1✔
715
  }
716

717
  /**
718
   * True if workflow code is being replayed.
719
   *
720
   * <p><b>Warning!</b> Never make workflow logic depend on this flag as it is going to break
721
   * determinism. The only reasonable uses for this flag are deduping external never failing side
722
   * effects like logging or metric reporting.
723
   *
724
   * <p>This method always returns false if called from a non workflow thread.
725
   *
726
   * @deprecated use {@link WorkflowUnsafe#isReplaying()}
727
   */
728
  @Deprecated
729
  public static boolean isReplaying() {
730
    return WorkflowUnsafe.isReplaying();
×
731
  }
732

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

787
  /**
788
   * Executes the provided function once, records its result into the workflow history. The recorded
789
   * result on history will be returned without executing the provided function during replay. This
790
   * guarantees the deterministic requirement for workflow as the exact same result will be returned
791
   * in replay. Common use case is to run some short non-deterministic code in workflow, like
792
   * getting random number. The only way to fail SideEffect is to panic which causes workflow task
793
   * failure. The workflow task after timeout is rescheduled and re-executed giving SideEffect
794
   * another chance to succeed.
795
   *
796
   * <p>Caution: do not use sideEffect function to modify any workflow state. Only use the
797
   * SideEffect's return value. For example this code is BROKEN:
798
   *
799
   * <pre><code>
800
   *  // Bad example:
801
   *  AtomicInteger random = new AtomicInteger();
802
   *  Workflow.sideEffect(() -&gt; {
803
   *         random.set(random.nextInt(100));
804
   *         return null;
805
   *  });
806
   *  // random will always be 0 in replay, thus this code is non-deterministic
807
   *  if random.get() &lt; 50 {
808
   *         ....
809
   *  } else {
810
   *         ....
811
   *  }
812
   * </code></pre>
813
   *
814
   * On replay the provided function is not executed, the random will always be 0, and the workflow
815
   * could take a different path breaking the determinism.
816
   *
817
   * <p>Here is the correct way to use sideEffect:
818
   *
819
   * <pre><code>
820
   *  // Good example:
821
   *  int random = Workflow.sideEffect(Integer.class, () -&gt; random.nextInt(100));
822
   *  if random &lt; 50 {
823
   *         ....
824
   *  } else {
825
   *         ....
826
   *  }
827
   * </code></pre>
828
   *
829
   * If function throws any exception it is not delivered to the workflow code. It is wrapped in
830
   * {@link Error} causing failure of the current workflow task.
831
   *
832
   * @param resultClass class of the side effect
833
   * @param resultType type of the side effect. Differs from resultClass for generic types.
834
   * @param func function that returns side effect value
835
   * @return value of the side effect
836
   * @see #mutableSideEffect(String, Class, BiPredicate, Functions.Func)
837
   */
838
  public static <R> R sideEffect(Class<R> resultClass, Type resultType, Func<R> func) {
839
    return WorkflowInternal.sideEffect(resultClass, resultType, func);
×
840
  }
841

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

876
  /**
877
   * {@code mutableSideEffect} is similar to {@link #sideEffect(Class, Functions.Func)} in allowing
878
   * calls of non-deterministic functions from workflow code.
879
   *
880
   * <p>The difference between {@code mutableSideEffect} and {@link #sideEffect(Class,
881
   * Functions.Func)} is that every new {@code sideEffect} call in non-replay mode results in a new
882
   * marker event recorded into the history. However, {@code mutableSideEffect} only records a new
883
   * marker if a value has changed. During the replay, {@code mutableSideEffect} will not execute
884
   * the function again, but it will return the exact same value as it was returning during the
885
   * non-replay run.
886
   *
887
   * <p>One good use case of {@code mutableSideEffect} is to access a dynamically changing config
888
   * without breaking determinism. Even if called very frequently the config value is recorded only
889
   * when it changes not causing any performance degradation due to a large history size.
890
   *
891
   * <p>Caution: do not use {@code mutableSideEffect} function to modify any workflow state. Only
892
   * use the mutableSideEffect's return value.
893
   *
894
   * <p>If function throws any exception it is not delivered to the workflow code. It is wrapped in
895
   * {@link Error} causing failure of the current workflow task.
896
   *
897
   * @param id unique identifier of this side effect
898
   * @param updated used to decide if a new value should be recorded. A func result is recorded only
899
   *     if call to updated with stored and a new value as arguments returns true. It is not called
900
   *     for the first value.
901
   * @param resultClass class of the side effect
902
   * @param resultType type of the side effect. Differs from resultClass for generic types.
903
   * @param func function that produces a value. This function can contain non-deterministic code.
904
   * @see #sideEffect(Class, Functions.Func)
905
   */
906
  public static <R> R mutableSideEffect(
907
      String id, Class<R> resultClass, Type resultType, BiPredicate<R, R> updated, Func<R> func) {
908
    return WorkflowInternal.mutableSideEffect(id, resultClass, resultType, updated, func);
×
909
  }
910

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

1004
  /**
1005
   * Get scope for reporting business metrics in workflow logic. This should be used instead of
1006
   * creating new metrics scopes as it is able to dedupe metrics during replay.
1007
   *
1008
   * <p>The original metrics scope is set through {@link
1009
   * WorkflowServiceStubsOptions.Builder#setMetricsScope(Scope)} when a worker starts up.
1010
   */
1011
  public static Scope getMetricsScope() {
1012
    return WorkflowInternal.getMetricsScope();
1✔
1013
  }
1014

1015
  /**
1016
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
1017
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
1018
   *
1019
   * @param clazz class name to appear in logging.
1020
   * @return logger to use in workflow logic.
1021
   */
1022
  public static Logger getLogger(Class<?> clazz) {
1023
    return WorkflowInternal.getLogger(clazz);
1✔
1024
  }
1025

1026
  /**
1027
   * Get logger to use inside workflow. Logs in replay mode are omitted unless {@link
1028
   * WorkerFactoryOptions.Builder#setEnableLoggingInReplay(boolean)} is set to {@code true}.
1029
   *
1030
   * @param name name to appear in logging.
1031
   * @return logger to use in workflow logic.
1032
   */
1033
  public static Logger getLogger(String name) {
1034
    return WorkflowInternal.getLogger(name);
1✔
1035
  }
1036

1037
  /**
1038
   * GetLastCompletionResult extract last completion result from previous run for this cron
1039
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
1040
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
1041
   * return any data and that data will become available when next run starts.
1042
   *
1043
   * @param resultClass class of the return data from last run
1044
   * @return result of last run
1045
   * @see io.temporal.client.WorkflowOptions.Builder#setCronSchedule(String)
1046
   */
1047
  public static <R> R getLastCompletionResult(Class<R> resultClass) {
1048
    return WorkflowInternal.getLastCompletionResult(resultClass, resultClass);
1✔
1049
  }
1050

1051
  /**
1052
   * Extract the latest failure from a previous run of this workflow. If any previous run of this
1053
   * workflow has failed, this function returns that failure. If no previous runs have failed, an
1054
   * empty optional is returned. The run you are calling this from may have been created as a retry
1055
   * of the previous failed run or as a next cron invocation for cron workflows.
1056
   *
1057
   * @return The last {@link Exception} that occurred in this workflow, if there has been one.
1058
   */
1059
  public static Optional<Exception> getPreviousRunFailure() {
1060
    return WorkflowInternal.getPreviousRunFailure();
1✔
1061
  }
1062

1063
  /**
1064
   * GetLastCompletionResult extract last completion result from previous run for this cron
1065
   * workflow. This is used in combination with cron schedule. A workflow can be started with an
1066
   * optional cron schedule. If a cron workflow wants to pass some data to next schedule, it can
1067
   * return any data and that data will become available when next run starts.
1068
   *
1069
   * @param resultClass class of the return data from last run
1070
   * @param resultType type of the return data from last run. Differs from resultClass for generic
1071
   *     types.
1072
   * @return result of last run
1073
   */
1074
  public static <R> R getLastCompletionResult(Class<R> resultClass, Type resultType) {
1075
    return WorkflowInternal.getLastCompletionResult(resultClass, resultType);
×
1076
  }
1077

1078
  /**
1079
   * Get a single search attribute.
1080
   *
1081
   * @param name search attribute name
1082
   * @return deserialized search attribute value
1083
   * @throws IllegalStateException if the search attribute value is a collection of multiple (&gt;
1084
   *     1) elements
1085
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1086
   */
1087
  @Deprecated
1088
  @Nullable
1089
  public static <T> T getSearchAttribute(String name) {
1090
    return WorkflowInternal.getSearchAttribute(name);
1✔
1091
  }
1092

1093
  /**
1094
   * Collection returned from this method is immutable. To modify search attributes associated with
1095
   * this workflow use {@link #upsertSearchAttributes(Map)}.
1096
   *
1097
   * <p>Note: This method never returns an empty list. Empty list is considered an absent value for
1098
   * search attributes and will be returned as {@code null}.
1099
   *
1100
   * @param name search attribute name
1101
   * @return immutable list of deserialized search attribute values
1102
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1103
   */
1104
  @Deprecated
1105
  @Nullable
1106
  public static <T> List<T> getSearchAttributeValues(String name) {
1107
    return WorkflowInternal.getSearchAttributeValues(name);
×
1108
  }
1109

1110
  /**
1111
   * Map returned from this method is immutable. To modify search attributes associated with this
1112
   * workflow use {@link #upsertSearchAttributes(Map)}.
1113
   *
1114
   * @return immutable map of search attribute names to deserialized values.
1115
   * @deprecated use {@link #getTypedSearchAttributes} instead.
1116
   */
1117
  @Deprecated
1118
  @Nonnull
1119
  public static Map<String, List<?>> getSearchAttributes() {
1120
    return WorkflowInternal.getSearchAttributes();
1✔
1121
  }
1122

1123
  /**
1124
   * Get immutable set of search attributes. To modify search attributes associated with this
1125
   * workflow use {@link #upsertTypedSearchAttributes}.
1126
   *
1127
   * @return immutable set of search attributes.
1128
   */
1129
  @Nonnull
1130
  public static SearchAttributes getTypedSearchAttributes() {
1131
    return WorkflowInternal.getTypedSearchAttributes();
1✔
1132
  }
1133

1134
  /**
1135
   * Updates Workflow Search Attributes by merging {@code searchAttributes} to the existing Search
1136
   * Attributes map attached to the workflow. Search Attributes are additional indexed information
1137
   * attributed to workflow and used for search and visibility.
1138
   *
1139
   * <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and its
1140
   * value type must be registered on Temporal server side.
1141
   *
1142
   * <p>Supported Java types of the value:
1143
   *
1144
   * <ul>
1145
   *   <li>{@link String}
1146
   *   <li>{@link Long}, {@link Integer}, {@link Short}, {@link Byte}
1147
   *   <li>{@link Boolean}
1148
   *   <li>{@link Double}
1149
   *   <li>{@link OffsetDateTime}
1150
   *   <li>{@link Collection} of the types above
1151
   *   <li>{@link io.temporal.common.SearchAttribute#UNSET_VALUE} can be used to unset or remove the
1152
   *       search attribute
1153
   * </ul>
1154
   *
1155
   * For example, workflow code:
1156
   *
1157
   * <pre><code>
1158
   *     Map&lt;String, Object&gt; attr1 = new HashMap&lt;&gt;();
1159
   *     attr1.put("CustomIntField", 1);
1160
   *     attr1.put("CustomBoolField", true);
1161
   *     Workflow.upsertSearchAttributes(attr1);
1162
   *
1163
   *     Map&lt;String, Object&gt; attr2 = new HashMap&lt;&gt;();
1164
   *     attr2.put("CustomIntField", Lists.newArrayList(1, 2));
1165
   *     attr2.put("CustomKeywordField", "Seattle");
1166
   *     Workflow.upsertSearchAttributes(attr2);
1167
   * </pre></code> will eventually have search attributes as:
1168
   *
1169
   * <pre><code>
1170
   *     {
1171
   *       "CustomIntField": 1, 2,
1172
   *       "CustomBoolField": true,
1173
   *       "CustomKeywordField": "Seattle",
1174
   *     }
1175
   * </pre></code>
1176
   *
1177
   * @param searchAttributes map of String to Object value that can be used to search in list APIs
1178
   * @deprecated use {@link #upsertTypedSearchAttributes} instead.
1179
   */
1180
  // WorkflowOptions#setSearchAttributes docs needs to be kept in sync with this method
1181
  @Deprecated
1182
  public static void upsertSearchAttributes(Map<String, ?> searchAttributes) {
1183
    WorkflowInternal.upsertSearchAttributes(searchAttributes);
1✔
1184
  }
1✔
1185

1186
  /**
1187
   * Updates Workflow Search Attributes by applying {@code searchAttributeUpdates} to the existing
1188
   * Search Attributes set attached to the workflow. Search Attributes are additional indexed
1189
   * information attributed to workflow and used for search and visibility.
1190
   *
1191
   * <p>The search attributes can be used in query of List/Scan/Count workflow APIs. The key and its
1192
   * value type must be registered on Temporal server side.
1193
   *
1194
   * @param searchAttributeUpdates set of updates to apply to search attributes.
1195
   */
1196
  public static void upsertTypedSearchAttributes(
1197
      SearchAttributeUpdate<?>... searchAttributeUpdates) {
1198
    WorkflowInternal.upsertTypedSearchAttributes(searchAttributeUpdates);
1✔
1199
  }
1✔
1200

1201
  /**
1202
   * Updates a Workflow Memos by applying {@code memoUpdates} to the existing Memos set attached to
1203
   * the workflow. Memos are additional non-indexed information attributed to workflow and can
1204
   * return by describing or listing a workflow. The type of value can be any object that are
1205
   * serializable by {@link io.temporal.common.converter.DataConverter}. To remove a memo set the
1206
   * value null.
1207
   */
1208
  public static void upsertMemo(Map<String, Object> memo) {
1209
    WorkflowInternal.upsertMemo(memo);
1✔
1210
  }
1✔
1211

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

1227
  /**
1228
   * @deprecated use {@link #applyActivityOptions(Map)}
1229
   */
1230
  @Deprecated
1231
  public static void setActivityOptions(Map<String, ActivityOptions> activityMethodOptions) {
1232
    WorkflowInternal.applyActivityOptions(activityMethodOptions);
×
1233
  }
×
1234

1235
  /**
1236
   * Adds activity options per activity type that will be used for an activity stub that has no
1237
   * {@link ActivityOptions} specified.<br>
1238
   * This method refines an original set of {@code Map<String, ActivityOptions>} provided by {@link
1239
   * WorkflowImplementationOptions#getActivityOptions()}<br>
1240
   * These more specific options take precedence over more generic setting {@link
1241
   * #setDefaultActivityOptions}
1242
   *
1243
   * <p>If an activity type already has a {@link ActivityOptions} set by an earlier call to this
1244
   * method or from {@link WorkflowImplementationOptions#getDefaultActivityOptions}, new {@link
1245
   * ActivityOptions} from {@code activityTypeToOptions} will be merged into the old ones using
1246
   * {@link ActivityOptions.Builder#mergeActivityOptions(ActivityOptions)}
1247
   *
1248
   * @param activityTypeToOptions a map of activity types to {@link ActivityOptions}
1249
   */
1250
  public static void applyActivityOptions(Map<String, ActivityOptions> activityTypeToOptions) {
1251
    WorkflowInternal.applyActivityOptions(activityTypeToOptions);
1✔
1252
  }
1✔
1253

1254
  /**
1255
   * Sets the default local activity options that will be used for activity stubs that have no
1256
   * {@link LocalActivityOptions} specified.<br>
1257
   * This overrides a value provided by {@link
1258
   * WorkflowImplementationOptions#getDefaultLocalActivityOptions}.<br>
1259
   * A more specific per-activity-type option specified in {@link
1260
   * WorkflowImplementationOptions#getLocalActivityOptions} or {@link
1261
   * #applyLocalActivityOptions(Map)} takes precedence over this setting.
1262
   *
1263
   * @param defaultLocalActivityOptions {@link LocalActivityOptions} to be used as a default
1264
   */
1265
  public static void setDefaultLocalActivityOptions(
1266
      LocalActivityOptions defaultLocalActivityOptions) {
1267
    WorkflowInternal.setDefaultLocalActivityOptions(defaultLocalActivityOptions);
1✔
1268
  }
1✔
1269

1270
  /**
1271
   * Adds local activity options per activity type that will be used for a local activity stub that
1272
   * has no {@link LocalActivityOptions} specified.<br>
1273
   * This method refines an original set of {@code Map<String, LocalActivityOptions>} provided by
1274
   * {@link WorkflowImplementationOptions#getLocalActivityOptions()}<br>
1275
   * These more specific options take precedence over more generic setting {@link
1276
   * #setDefaultLocalActivityOptions}
1277
   *
1278
   * <p>If an activity type already has a {@link LocalActivityOptions} set by an earlier call to
1279
   * this method or from {@link WorkflowImplementationOptions#getDefaultLocalActivityOptions}, new
1280
   * {@link LocalActivityOptions} from {@code activityTypeToOptions} will be merged into the old
1281
   * ones using {@link LocalActivityOptions.Builder#mergeActivityOptions(LocalActivityOptions)}
1282
   *
1283
   * @param activityTypeToOptions a map of activity types to {@link LocalActivityOptions}
1284
   */
1285
  public static void applyLocalActivityOptions(
1286
      Map<String, LocalActivityOptions> activityTypeToOptions) {
1287
    WorkflowInternal.applyLocalActivityOptions(activityTypeToOptions);
1✔
1288
  }
1✔
1289

1290
  /**
1291
   * Checks if all update and signal handlers have finished executing.
1292
   *
1293
   * <p>Consider waiting on this condition before workflow return or continue-as-new, to prevent
1294
   * interruption of in-progress handlers by workflow return:
1295
   *
1296
   * <pre><code>
1297
   *    Workflow.await(() -&gt; Workflow.isEveryHandlerFinished());
1298
   *  </pre></code> will eventually have search attributes as:
1299
   *
1300
   * @return true if all handlers are finished, false otherwise.
1301
   */
1302
  public static boolean isEveryHandlerFinished() {
1303
    return WorkflowInternal.isEveryHandlerFinished();
1✔
1304
  }
1305

1306
  /**
1307
   * Creates a Nexus service stub that can be used to start Nexus operations on the given service
1308
   * interface.
1309
   *
1310
   * @param service interface that given service implements.
1311
   */
1312
  @Experimental
1313
  public static <T> T newNexusServiceStub(Class<T> service) {
1314
    return WorkflowInternal.newNexusServiceStub(service, null);
×
1315
  }
1316

1317
  /**
1318
   * Creates a Nexus service stub that can be used to start Nexus operations on the given service
1319
   * interface.
1320
   *
1321
   * @param service interface that given service implements.
1322
   * @param options options passed to the Nexus service.
1323
   */
1324
  @Experimental
1325
  public static <T> T newNexusServiceStub(Class<T> service, NexusServiceOptions options) {
1326
    return WorkflowInternal.newNexusServiceStub(service, options);
1✔
1327
  }
1328

1329
  /**
1330
   * Creates untyped nexus service stub that can be used to execute Nexus operations.
1331
   *
1332
   * @param service name of the service the operation is part of.
1333
   * @param options options passed to the Nexus service.
1334
   */
1335
  @Experimental
1336
  public static NexusServiceStub newUntypedNexusServiceStub(
1337
      String service, NexusServiceOptions options) {
1338
    return WorkflowInternal.newUntypedNexusServiceStub(service, options);
1✔
1339
  }
1340

1341
  /**
1342
   * Start a nexus operation.
1343
   *
1344
   * @param operation The only supported value is method reference to a proxy created through {@link
1345
   *     #newNexusServiceStub(Class)}.
1346
   * @param arg operation argument
1347
   * @return OperationHandle a handle to the operation.
1348
   */
1349
  @Experimental
1350
  public static <T, R> NexusOperationHandle<R> startNexusOperation(
1351
      Functions.Func1<T, R> operation, T arg) {
1352
    return WorkflowInternal.startNexusOperation(operation, arg);
1✔
1353
  }
1354

1355
  /**
1356
   * Start a Nexus operation.
1357
   *
1358
   * @param operation The only supported value is method reference to a proxy created through {@link
1359
   *     #newNexusServiceStub(Class)}.
1360
   * @return OperationHandle a handle to the operation.
1361
   */
1362
  @Experimental
1363
  public static <R> NexusOperationHandle<R> startNexusOperation(Functions.Func<R> operation) {
1364
    return WorkflowInternal.startNexusOperation(operation);
×
1365
  }
1366

1367
  /** Prohibit instantiation. */
1368
  private Workflow() {}
1369
}
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

© 2025 Coveralls, Inc