Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ final class SyncWorkflowContext implements WorkflowContext, WorkflowOutboundCall
private Map<String, LocalActivityOptions> localActivityOptionsMap;
private NexusServiceOptions defaultNexusServiceOptions = null;
private Map<String, NexusServiceOptions> nexusServiceOptionsMap;
private ChildWorkflowOptions defaultChildWorkflowOptions = null;
private Map<String, ChildWorkflowOptions> childWorkflowOptionsMap;
private boolean readOnly = false;
private final WorkflowThreadLocal<UpdateInfo> currentUpdateInfo = new WorkflowThreadLocal<>();
@Nullable private String currentDetails;
Expand Down Expand Up @@ -136,6 +138,10 @@ public SyncWorkflowContext(
workflowImplementationOptions.getDefaultNexusServiceOptions();
this.nexusServiceOptionsMap =
new HashMap<>(workflowImplementationOptions.getNexusServiceOptions());
this.defaultChildWorkflowOptions =
workflowImplementationOptions.getDefaultChildWorkflowOptions();
this.childWorkflowOptionsMap =
new HashMap<>(workflowImplementationOptions.getChildWorkflowOptions());
}
this.workflowImplementationOptions =
workflowImplementationOptions == null
Expand Down Expand Up @@ -215,6 +221,16 @@ public NexusServiceOptions getDefaultNexusServiceOptions() {
: Collections.emptyMap();
}

public ChildWorkflowOptions getDefaultChildWorkflowOptions() {
return defaultChildWorkflowOptions;
}

public @Nonnull Map<String, ChildWorkflowOptions> getChildWorkflowOptions() {
return childWorkflowOptionsMap != null
? Collections.unmodifiableMap(childWorkflowOptionsMap)
: Collections.emptyMap();
}

public void setDefaultActivityOptions(ActivityOptions defaultActivityOptions) {
this.defaultActivityOptions =
(this.defaultActivityOptions == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,27 @@ public static ActivityStub newUntypedLocalActivityStub(LocalActivityOptions opti
@SuppressWarnings("unchecked")
public static <T> T newChildWorkflowStub(
Class<T> workflowInterface, ChildWorkflowOptions options) {
SyncWorkflowContext context = getRootWorkflowContext();
options = (options == null) ? context.getDefaultChildWorkflowOptions() : options;

POJOWorkflowInterfaceMetadata workflowMetadata =
POJOWorkflowInterfaceMetadata.newInstance(workflowInterface);
Optional<POJOWorkflowMethodMetadata> workflowMethodMetadata =
workflowMetadata.getWorkflowMethod();
if (workflowMethodMetadata.isPresent()) {
String workflowType = workflowMethodMetadata.get().getName();
@Nonnull
Map<String, ChildWorkflowOptions> predefinedChildWorkflowOptions =
context.getChildWorkflowOptions();
ChildWorkflowOptions perTypeOptions = predefinedChildWorkflowOptions.get(workflowType);
if (perTypeOptions != null) {
options =
ChildWorkflowOptions.newBuilder(perTypeOptions)
.mergeChildWorkflowOptions(options)
.build();
}
}

return (T)
Proxy.newProxyInstance(
workflowInterface.getClassLoader(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.temporal.activity.ActivityOptions;
import io.temporal.activity.LocalActivityOptions;
import io.temporal.common.Experimental;
import io.temporal.workflow.ChildWorkflowOptions;
import io.temporal.workflow.NexusServiceOptions;
import io.temporal.workflow.Workflow;
import java.util.*;
Expand Down Expand Up @@ -42,6 +43,8 @@ public static final class Builder {
private LocalActivityOptions defaultLocalActivityOptions;
private Map<String, NexusServiceOptions> nexusServiceOptions;
private NexusServiceOptions defaultNexusServiceOptions;
private Map<String, ChildWorkflowOptions> childWorkflowOptions;
private ChildWorkflowOptions defaultChildWorkflowOptions;
private boolean enableUpsertVersionSearchAttributes;

private Builder() {}
Expand All @@ -57,6 +60,8 @@ private Builder(WorkflowImplementationOptions options) {
this.defaultLocalActivityOptions = options.getDefaultLocalActivityOptions();
this.nexusServiceOptions = options.getNexusServiceOptions();
this.defaultNexusServiceOptions = options.getDefaultNexusServiceOptions();
this.childWorkflowOptions = options.getChildWorkflowOptions();
this.defaultChildWorkflowOptions = options.getDefaultChildWorkflowOptions();
this.enableUpsertVersionSearchAttributes = options.isEnableUpsertVersionSearchAttributes();
}

Expand Down Expand Up @@ -158,6 +163,34 @@ public Builder setDefaultNexusServiceOptions(NexusServiceOptions defaultNexusSer
return this;
}

/**
* Set individual child workflow options per workflow type. Will be merged with the options from
* {@link io.temporal.workflow.Workflow#newChildWorkflowStub(Class, ChildWorkflowOptions)} which
* has the highest precedence.
*
* @param childWorkflowOptions map from workflow type to ChildWorkflowOptions
*/
public Builder setChildWorkflowOptions(Map<String, ChildWorkflowOptions> childWorkflowOptions) {
this.childWorkflowOptions = new HashMap<>(Objects.requireNonNull(childWorkflowOptions));
return this;
}

/**
* These child workflow options have the lowest precedence across all child workflow options.
* Will be overwritten entirely by {@link
* io.temporal.workflow.Workflow#newChildWorkflowStub(Class, ChildWorkflowOptions)} and then by
* the individual child workflow options if any are set through {@link
* #setChildWorkflowOptions(Map)}
*
* @param defaultChildWorkflowOptions ChildWorkflowOptions for all child workflows in the
* workflow.
*/
public Builder setDefaultChildWorkflowOptions(
ChildWorkflowOptions defaultChildWorkflowOptions) {
this.defaultChildWorkflowOptions = Objects.requireNonNull(defaultChildWorkflowOptions);
return this;
}

/**
* Enable upserting version search attributes on {@link Workflow#getVersion}. This will cause
* the SDK to automatically add the <b>TemporalChangeVersion</b> search attributes to the
Expand Down Expand Up @@ -187,6 +220,8 @@ public WorkflowImplementationOptions build() {
defaultLocalActivityOptions,
nexusServiceOptions == null ? null : nexusServiceOptions,
defaultNexusServiceOptions,
childWorkflowOptions == null ? null : childWorkflowOptions,
defaultChildWorkflowOptions,
enableUpsertVersionSearchAttributes);
}
}
Expand All @@ -198,6 +233,8 @@ public WorkflowImplementationOptions build() {
private final LocalActivityOptions defaultLocalActivityOptions;
private final @Nullable Map<String, NexusServiceOptions> nexusServiceOptions;
private final NexusServiceOptions defaultNexusServiceOptions;
private final @Nullable Map<String, ChildWorkflowOptions> childWorkflowOptions;
private final ChildWorkflowOptions defaultChildWorkflowOptions;
private final boolean enableUpsertVersionSearchAttributes;

public WorkflowImplementationOptions(
Expand All @@ -208,6 +245,8 @@ public WorkflowImplementationOptions(
LocalActivityOptions defaultLocalActivityOptions,
@Nullable Map<String, NexusServiceOptions> nexusServiceOptions,
NexusServiceOptions defaultNexusServiceOptions,
@Nullable Map<String, ChildWorkflowOptions> childWorkflowOptions,
ChildWorkflowOptions defaultChildWorkflowOptions,
boolean enableUpsertVersionSearchAttributes) {
this.failWorkflowExceptionTypes = failWorkflowExceptionTypes;
this.activityOptions = activityOptions;
Expand All @@ -216,6 +255,8 @@ public WorkflowImplementationOptions(
this.defaultLocalActivityOptions = defaultLocalActivityOptions;
this.nexusServiceOptions = nexusServiceOptions;
this.defaultNexusServiceOptions = defaultNexusServiceOptions;
this.childWorkflowOptions = childWorkflowOptions;
this.defaultChildWorkflowOptions = defaultChildWorkflowOptions;
this.enableUpsertVersionSearchAttributes = enableUpsertVersionSearchAttributes;
}

Expand Down Expand Up @@ -253,6 +294,16 @@ public NexusServiceOptions getDefaultNexusServiceOptions() {
return defaultNexusServiceOptions;
}

public @Nonnull Map<String, ChildWorkflowOptions> getChildWorkflowOptions() {
return childWorkflowOptions != null
? Collections.unmodifiableMap(childWorkflowOptions)
: Collections.emptyMap();
}

public ChildWorkflowOptions getDefaultChildWorkflowOptions() {
return defaultChildWorkflowOptions;
}

@Experimental
public boolean isEnableUpsertVersionSearchAttributes() {
return enableUpsertVersionSearchAttributes;
Expand All @@ -275,6 +326,10 @@ public String toString() {
+ nexusServiceOptions
+ ", defaultNexusServiceOptions="
+ defaultNexusServiceOptions
+ ", childWorkflowOptions="
+ childWorkflowOptions
+ ", defaultChildWorkflowOptions="
+ defaultChildWorkflowOptions
+ ", enableUpsertVersionSearchAttributes="
+ enableUpsertVersionSearchAttributes
+ '}';
Expand All @@ -292,6 +347,8 @@ public boolean equals(Object o) {
&& Objects.equals(defaultLocalActivityOptions, that.defaultLocalActivityOptions)
&& Objects.equals(nexusServiceOptions, that.nexusServiceOptions)
&& Objects.equals(defaultNexusServiceOptions, that.defaultNexusServiceOptions)
&& Objects.equals(childWorkflowOptions, that.childWorkflowOptions)
&& Objects.equals(defaultChildWorkflowOptions, that.defaultChildWorkflowOptions)
&& Objects.equals(
enableUpsertVersionSearchAttributes, that.enableUpsertVersionSearchAttributes);
}
Expand All @@ -306,6 +363,8 @@ public int hashCode() {
defaultLocalActivityOptions,
nexusServiceOptions,
defaultNexusServiceOptions,
childWorkflowOptions,
defaultChildWorkflowOptions,
enableUpsertVersionSearchAttributes);
result = 31 * result + Arrays.hashCode(failWorkflowExceptionTypes);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,75 @@ public Builder setPriority(Priority priority) {
return this;
}

/**
* Merges the provided override options into this builder. Any non-null fields in the override
* will take precedence over the fields in this builder.
*
* @param override ChildWorkflowOptions that overrides the current builder values.
* @return this builder.
*/
@SuppressWarnings("deprecation")
public Builder mergeChildWorkflowOptions(ChildWorkflowOptions override) {
if (override == null) {
return this;
}
this.namespace = (override.getNamespace() == null) ? this.namespace : override.getNamespace();
this.workflowId =
(override.getWorkflowId() == null) ? this.workflowId : override.getWorkflowId();
this.workflowIdReusePolicy =
(override.getWorkflowIdReusePolicy() == null)
? this.workflowIdReusePolicy
: override.getWorkflowIdReusePolicy();
this.workflowRunTimeout =
(override.getWorkflowRunTimeout() == null)
? this.workflowRunTimeout
: override.getWorkflowRunTimeout();
this.workflowExecutionTimeout =
(override.getWorkflowExecutionTimeout() == null)
? this.workflowExecutionTimeout
: override.getWorkflowExecutionTimeout();
this.workflowTaskTimeout =
(override.getWorkflowTaskTimeout() == null)
? this.workflowTaskTimeout
: override.getWorkflowTaskTimeout();
this.taskQueue = (override.getTaskQueue() == null) ? this.taskQueue : override.getTaskQueue();
this.retryOptions =
(override.getRetryOptions() == null) ? this.retryOptions : override.getRetryOptions();
this.cronSchedule =
(override.getCronSchedule() == null) ? this.cronSchedule : override.getCronSchedule();
this.parentClosePolicy =
(override.getParentClosePolicy() == null)
? this.parentClosePolicy
: override.getParentClosePolicy();
this.memo = (override.getMemo() == null) ? this.memo : override.getMemo();
this.searchAttributes =
(override.getSearchAttributes() == null)
? this.searchAttributes
: override.getSearchAttributes();
this.typedSearchAttributes =
(override.getTypedSearchAttributes() == null)
? this.typedSearchAttributes
: override.getTypedSearchAttributes();
this.contextPropagators =
(override.getContextPropagators() == null)
? this.contextPropagators
: override.getContextPropagators();
this.cancellationType =
(override.getCancellationType() == null)
? this.cancellationType
: override.getCancellationType();
this.versioningIntent =
(override.getVersioningIntent() == null)
? this.versioningIntent
: override.getVersioningIntent();
this.staticSummary =
(override.getStaticSummary() == null) ? this.staticSummary : override.getStaticSummary();
this.staticDetails =
(override.getStaticDetails() == null) ? this.staticDetails : override.getStaticDetails();
this.priority = (override.getPriority() == null) ? this.priority : override.getPriority();
return this;
}

public ChildWorkflowOptions build() {
return new ChildWorkflowOptions(
namespace,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package io.temporal.workflow;

import static org.junit.Assert.*;

import io.temporal.worker.WorkflowImplementationOptions;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import org.junit.Test;

public class ChildWorkflowOptionsInWorkflowImplementationOptionsTest {

@Test
public void testBuilderSetAndGet() {
ChildWorkflowOptions defaultOpts =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(100))
.setTaskQueue("default-queue")
.build();

ChildWorkflowOptions perTypeOpts =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(200))
.setTaskQueue("per-type-queue")
.build();

Map<String, ChildWorkflowOptions> optionsMap =
Collections.singletonMap("MyWorkflow", perTypeOpts);

WorkflowImplementationOptions options =
WorkflowImplementationOptions.newBuilder()
.setDefaultChildWorkflowOptions(defaultOpts)
.setChildWorkflowOptions(optionsMap)
.build();

assertEquals(defaultOpts, options.getDefaultChildWorkflowOptions());
assertEquals(1, options.getChildWorkflowOptions().size());
assertEquals(perTypeOpts, options.getChildWorkflowOptions().get("MyWorkflow"));
}

@Test
public void testDefaultInstanceHasEmptyChildWorkflowOptions() {
WorkflowImplementationOptions options = WorkflowImplementationOptions.getDefaultInstance();
assertNull(options.getDefaultChildWorkflowOptions());
assertNotNull(options.getChildWorkflowOptions());
assertTrue(options.getChildWorkflowOptions().isEmpty());
}

@Test
public void testToBuilder() {
ChildWorkflowOptions defaultOpts =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(100))
.build();

Map<String, ChildWorkflowOptions> optionsMap =
Collections.singletonMap("MyWorkflow", defaultOpts);

WorkflowImplementationOptions original =
WorkflowImplementationOptions.newBuilder()
.setDefaultChildWorkflowOptions(defaultOpts)
.setChildWorkflowOptions(optionsMap)
.build();

WorkflowImplementationOptions copy = original.toBuilder().build();
assertEquals(original.getDefaultChildWorkflowOptions(), copy.getDefaultChildWorkflowOptions());
assertEquals(original.getChildWorkflowOptions(), copy.getChildWorkflowOptions());
}

@Test
public void testMergeChildWorkflowOptionsOverridesNonNull() {
ChildWorkflowOptions base =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(100))
.setTaskQueue("base-queue")
.setWorkflowRunTimeout(Duration.ofSeconds(50))
.build();

ChildWorkflowOptions override =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(200))
.build();

ChildWorkflowOptions merged =
ChildWorkflowOptions.newBuilder(base).mergeChildWorkflowOptions(override).build();

// Override takes precedence for workflowExecutionTimeout
assertEquals(Duration.ofSeconds(200), merged.getWorkflowExecutionTimeout());
// Base values are preserved for fields not set in override
assertEquals("base-queue", merged.getTaskQueue());
assertEquals(Duration.ofSeconds(50), merged.getWorkflowRunTimeout());
}

@Test
public void testMergeChildWorkflowOptionsWithNull() {
ChildWorkflowOptions base =
ChildWorkflowOptions.newBuilder()
.setWorkflowExecutionTimeout(Duration.ofSeconds(100))
.setTaskQueue("base-queue")
.build();

ChildWorkflowOptions merged =
ChildWorkflowOptions.newBuilder(base).mergeChildWorkflowOptions(null).build();

assertEquals(Duration.ofSeconds(100), merged.getWorkflowExecutionTimeout());
assertEquals("base-queue", merged.getTaskQueue());
}
}
Loading