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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ to docs, or any other relevant information.
set by Temporal SDK in worker context.
- Spring Boot: the `workers` attribute of `@WorkflowImpl`, `@ActivityImpl` and `@NexusServiceImpl` now resolves property
placeholders such as `${my.worker.name}`, as `taskQueues` already does.
- Spring Boot now provides singleton `NexusClient` beans for the root and configured non-root namespaces,
with namespace-specific data converters and `TemporalOptionsCustomizer<NexusClientOptions.Builder>`
customization. `NexusClientInterceptor` beans are automatically registered for the root client.
- `Workflow.sleep(Duration, TimerOptions)` and `Workflow.await(Duration, TimerOptions, Supplier)` let workflows set a
summary on the timer behind a sleep or a timed await, like `Workflow.newTimer(Duration, TimerOptions)` already does.
- Add nonblocking `Async.await` overloads whose promises complete when a workflow condition becomes true or a timeout expires.
Expand Down
17 changes: 17 additions & 0 deletions temporal-spring-boot-autoconfigure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

For documentation on the Temporal Spring Boot Integration, please visit [https://docs.temporal.io/develop/java/spring-boot-integration](https://docs.temporal.io/develop/java/spring-boot-integration)

# NexusClient

Inject `NexusClient` for the root namespace, or use `@Qualifier("<alias>NexusClient")`
for a non-root namespace (namespace replaces alias when omitted). Each client is a singleton
using its namespace's connection and data converter.

```java
@Bean
TemporalOptionsCustomizer<NexusClientOptions.Builder> nexusClientCustomizer() {
return builder -> builder.setIdentity("my-application");
}
```

For non-root customization, name the bean `<alias>NexusClientCustomizer`.
`NexusClientInterceptor` beans apply to the root client only; configure non-root interceptors
through the namespace's options customizer.

# Running Multiple Name Space (experimental)

Along with the root namespace, you can configure multiple non-root namespaces in the application.yml file. Different namespaces can have different configurations including but not limited to different connection options, registered workflows/activities, data converters etc.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
*
* @param <T> Temporal Options Builder to customize. Respected types: {@link
* WorkflowServiceStubsOptions.Builder}, {@link WorkflowClientOptions.Builder}, {@link
* TestEnvironmentOptions.Builder}, {@link WorkerOptions.Builder}, {@link
* WorkerFactoryOptions.Builder}, {@link WorkflowImplementationOptions.Builder}
* io.temporal.client.NexusClientOptions.Builder}, {@link TestEnvironmentOptions.Builder},
* {@link WorkerOptions.Builder}, {@link WorkerFactoryOptions.Builder}, {@link
* WorkflowImplementationOptions.Builder}
* @see WorkerOptionsCustomizer to get access to a name and task queue of a worker that's being
* customized
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.google.common.base.MoreObjects;
import com.uber.m3.tally.Scope;
import io.opentracing.Tracer;
import io.temporal.client.NexusClient;
import io.temporal.client.NexusClientOptions;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowClientPlugin;
Expand Down Expand Up @@ -134,6 +136,8 @@ private void injectBeanByNonRootNamespace(NonRootNamespaceProperties ns) {
}
List<TemporalOptionsCustomizer<ScheduleClientOptions.Builder>> scheduleClientCustomizers =
findBeanByNameSpaceForTemporalCustomizer(beanPrefix, ScheduleClientOptions.Builder.class);
List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> nexusClientCustomizers =
findBeanByNameSpaceForTemporalCustomizer(beanPrefix, NexusClientOptions.Builder.class);
List<TemporalOptionsCustomizer<WorkflowImplementationOptions.Builder>>
workflowImplementationCustomizers =
findBeanByNameSpaceForTemporalCustomizer(
Expand Down Expand Up @@ -169,11 +173,14 @@ private void injectBeanByNonRootNamespace(NonRootNamespaceProperties ns) {
workflowImplementationCustomizers,
workflowClientPlugins,
scheduleClientPlugins,
workerPlugins);
workerPlugins,
null,
nexusClientCustomizers);

ClientTemplate clientTemplate = namespaceTemplate.getClientTemplate();
WorkflowClient workflowClient = clientTemplate.getWorkflowClient();
ScheduleClient scheduleClient = clientTemplate.getScheduleClient();
NexusClient nexusClient = clientTemplate.getNexusClient();
WorkersTemplate workersTemplate = namespaceTemplate.getWorkersTemplate();
WorkerFactory workerFactory = workersTemplate.getWorkerFactory();

Expand All @@ -192,6 +199,7 @@ private void injectBeanByNonRootNamespace(NonRootNamespaceProperties ns) {
beanPrefix + WorkflowClient.class.getSimpleName(), workflowClient);
beanFactory.registerSingleton(
beanPrefix + ScheduleClient.class.getSimpleName(), scheduleClient);
beanFactory.registerSingleton(beanPrefix + NexusClient.class.getSimpleName(), nexusClient);
beanFactory.registerSingleton(beanPrefix + WorkerFactory.class.getSimpleName(), workerFactory);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package io.temporal.spring.boot.autoconfigure;

import io.opentracing.Tracer;
import io.temporal.client.NexusClient;
import io.temporal.client.NexusClientOptions;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowClientPlugin;
import io.temporal.client.schedules.ScheduleClient;
import io.temporal.client.schedules.ScheduleClientOptions;
import io.temporal.client.schedules.ScheduleClientPlugin;
import io.temporal.common.converter.DataConverter;
import io.temporal.common.interceptors.NexusClientInterceptor;
import io.temporal.common.interceptors.ScheduleClientInterceptor;
import io.temporal.common.interceptors.WorkerInterceptor;
import io.temporal.common.interceptors.WorkflowClientInterceptor;
Expand Down Expand Up @@ -93,7 +96,11 @@ public NamespaceTemplate rootNamespaceTemplate(
workflowImplementationCustomizerMap,
@Autowired(required = false) @Nullable List<WorkflowClientPlugin> workflowClientPlugins,
@Autowired(required = false) @Nullable List<ScheduleClientPlugin> scheduleClientPlugins,
@Autowired(required = false) @Nullable List<WorkerPlugin> workerPlugins) {
@Autowired(required = false) @Nullable List<WorkerPlugin> workerPlugins,
@Autowired(required = false) @Nullable List<NexusClientInterceptor> nexusClientInterceptors,
@Autowired(required = false) @Nullable
Map<String, TemporalOptionsCustomizer<NexusClientOptions.Builder>>
nexusClientCustomizerMap) {
DataConverter chosenDataConverter =
AutoConfigurationUtils.chooseDataConverter(dataConverters, mainDataConverter, properties);
List<WorkflowClientInterceptor> chosenClientInterceptors =
Expand All @@ -119,6 +126,9 @@ public NamespaceTemplate rootNamespaceTemplate(
List<TemporalOptionsCustomizer<ScheduleClientOptions.Builder>> scheduleCustomizer =
AutoConfigurationUtils.chooseTemporalCustomizerBeans(
beanFactory, scheduleCustomizerMap, ScheduleClientOptions.Builder.class, properties);
List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> nexusClientCustomizers =
AutoConfigurationUtils.chooseTemporalCustomizerBeans(
beanFactory, nexusClientCustomizerMap, NexusClientOptions.Builder.class, properties);
List<TemporalOptionsCustomizer<WorkflowImplementationOptions.Builder>>
workflowImplementationCustomizer =
AutoConfigurationUtils.chooseTemporalCustomizerBeans(
Expand Down Expand Up @@ -166,7 +176,9 @@ public NamespaceTemplate rootNamespaceTemplate(
workflowImplementationCustomizer,
filteredClientPlugins,
filteredSchedulePlugins,
filteredWorkerPlugins);
filteredWorkerPlugins,
nexusClientInterceptors,
nexusClientCustomizers);
}

/** Client */
Expand All @@ -189,6 +201,12 @@ public ScheduleClient scheduleClient(ClientTemplate clientTemplate) {
return clientTemplate.getScheduleClient();
}

@Primary
@Bean(name = "temporalNexusClient")
public NexusClient nexusClient(ClientTemplate clientTemplate) {
return clientTemplate.getNexusClient();
}

/** Workers */
@Primary
@Bean(name = "temporalWorkersTemplate")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class NonRootNamespaceProperties extends NamespaceProperties {
* - WorkersTemplate <br>
* - WorkflowClient <br>
* - ScheduleClient <br>
* - NexusClient <br>
* - WorkerFactory <br>
* You guys can use this alias to get the beans. <br>
* for example if you set spring.temporal.namespace[0].alias=foo <br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import com.google.common.base.Preconditions;
import io.opentracing.Tracer;
import io.temporal.client.NexusClient;
import io.temporal.client.NexusClientOptions;
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowClientPlugin;
import io.temporal.client.schedules.ScheduleClient;
import io.temporal.client.schedules.ScheduleClientOptions;
import io.temporal.client.schedules.ScheduleClientPlugin;
import io.temporal.common.converter.DataConverter;
import io.temporal.common.interceptors.NexusClientInterceptor;
import io.temporal.common.interceptors.ScheduleClientInterceptor;
import io.temporal.common.interceptors.WorkflowClientInterceptor;
import io.temporal.serviceclient.WorkflowServiceStubs;
Expand All @@ -19,12 +22,14 @@

public class ClientTemplate {
private final @Nonnull WorkflowClientOptionsTemplate optionsTemplate;
private final @Nonnull NexusClientOptionsTemplate nexusOptionsTemplate;
private final @Nullable WorkflowServiceStubs workflowServiceStubs;
// if not null, we work with an environment with defined test server
private final @Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment;

private WorkflowClient workflowClient;
private ScheduleClient scheduleClient;
private NexusClient nexusClient;

public ClientTemplate(
@Nonnull String namespace,
Expand All @@ -37,7 +42,10 @@ public ClientTemplate(
@Nullable List<TemporalOptionsCustomizer<WorkflowClientOptions.Builder>> clientCustomizers,
@Nullable List<TemporalOptionsCustomizer<ScheduleClientOptions.Builder>> scheduleCustomizers,
@Nullable List<WorkflowClientPlugin> workflowClientPlugins,
@Nullable List<ScheduleClientPlugin> scheduleClientPlugins) {
@Nullable List<ScheduleClientPlugin> scheduleClientPlugins,
@Nullable List<NexusClientInterceptor> nexusClientInterceptors,
@Nullable
List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> nexusClientCustomizers) {
this.optionsTemplate =
new WorkflowClientOptionsTemplate(
namespace,
Expand All @@ -49,39 +57,13 @@ public ClientTemplate(
scheduleCustomizers,
workflowClientPlugins,
scheduleClientPlugins);
this.nexusOptionsTemplate =
new NexusClientOptionsTemplate(
namespace, dataConverter, nexusClientInterceptors, nexusClientCustomizers);
this.workflowServiceStubs = workflowServiceStubs;
this.testWorkflowEnvironment = testWorkflowEnvironment;
}

/**
* @deprecated Use constructor with plugins parameters
*/
@Deprecated
public ClientTemplate(
@Nonnull String namespace,
@Nullable DataConverter dataConverter,
@Nullable List<WorkflowClientInterceptor> workflowClientInterceptors,
@Nullable List<ScheduleClientInterceptor> scheduleClientInterceptors,
@Nullable Tracer tracer,
@Nullable WorkflowServiceStubs workflowServiceStubs,
@Nullable TestWorkflowEnvironmentAdapter testWorkflowEnvironment,
@Nullable List<TemporalOptionsCustomizer<WorkflowClientOptions.Builder>> clientCustomizers,
@Nullable
List<TemporalOptionsCustomizer<ScheduleClientOptions.Builder>> scheduleCustomizers) {
this(
namespace,
dataConverter,
workflowClientInterceptors,
scheduleClientInterceptors,
tracer,
workflowServiceStubs,
testWorkflowEnvironment,
clientCustomizers,
scheduleCustomizers,
null,
null);
}

public WorkflowClient getWorkflowClient() {
if (workflowClient == null) {
this.workflowClient = createWorkflowClient();
Expand All @@ -96,6 +78,20 @@ public ScheduleClient getScheduleClient() {
return scheduleClient;
}

public NexusClient getNexusClient() {
if (nexusClient == null) {
WorkflowServiceStubs serviceStubs =
testWorkflowEnvironment != null
? testWorkflowEnvironment.getWorkflowClient().getWorkflowServiceStubs()
: workflowServiceStubs;
Preconditions.checkState(
serviceStubs != null, "ClientTemplate was created without workflowServiceStubs");
this.nexusClient =
NexusClient.newInstance(serviceStubs, nexusOptionsTemplate.createNexusClientOptions());
}
return nexusClient;
}

private WorkflowClient createWorkflowClient() {
if (testWorkflowEnvironment != null) {
return testWorkflowEnvironment.getWorkflowClient();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.temporal.spring.boot.autoconfigure.template;

import io.opentracing.Tracer;
import io.temporal.client.NexusClientOptions;
import io.temporal.client.WorkflowClientOptions;
import io.temporal.client.WorkflowClientPlugin;
import io.temporal.client.schedules.ScheduleClientOptions;
import io.temporal.client.schedules.ScheduleClientPlugin;
import io.temporal.common.converter.DataConverter;
import io.temporal.common.interceptors.NexusClientInterceptor;
import io.temporal.common.interceptors.ScheduleClientInterceptor;
import io.temporal.common.interceptors.WorkerInterceptor;
import io.temporal.common.interceptors.WorkflowClientInterceptor;
Expand Down Expand Up @@ -42,6 +44,9 @@ public class NamespaceTemplate {
private final @Nullable List<WorkflowClientPlugin> workflowClientPlugins;
private final @Nullable List<ScheduleClientPlugin> scheduleClientPlugins;
private final @Nullable List<WorkerPlugin> workerPlugins;
private final @Nullable List<NexusClientInterceptor> nexusClientInterceptors;
private final @Nullable List<TemporalOptionsCustomizer<NexusClientOptions.Builder>>
nexusClientCustomizers;

private ClientTemplate clientTemplate;
private WorkersTemplate workersTemplate;
Expand All @@ -65,7 +70,10 @@ public NamespaceTemplate(
workflowImplementationCustomizers,
@Nullable List<WorkflowClientPlugin> workflowClientPlugins,
@Nullable List<ScheduleClientPlugin> scheduleClientPlugins,
@Nullable List<WorkerPlugin> workerPlugins) {
@Nullable List<WorkerPlugin> workerPlugins,
@Nullable List<NexusClientInterceptor> nexusClientInterceptors,
@Nullable
List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> nexusClientCustomizers) {
this.namespaceProperties = namespaceProperties;
this.workflowServiceStubs = workflowServiceStubs;
this.dataConverter = dataConverter;
Expand All @@ -83,6 +91,8 @@ public NamespaceTemplate(
this.workflowClientPlugins = workflowClientPlugins;
this.scheduleClientPlugins = scheduleClientPlugins;
this.workerPlugins = workerPlugins;
this.nexusClientInterceptors = nexusClientInterceptors;
this.nexusClientCustomizers = nexusClientCustomizers;
}

public ClientTemplate getClientTemplate() {
Expand All @@ -99,7 +109,9 @@ public ClientTemplate getClientTemplate() {
clientCustomizers,
scheduleCustomizers,
workflowClientPlugins,
scheduleClientPlugins);
scheduleClientPlugins,
nexusClientInterceptors,
nexusClientCustomizers);
}
return clientTemplate;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.temporal.spring.boot.autoconfigure.template;

import io.temporal.client.NexusClientOptions;
import io.temporal.common.converter.DataConverter;
import io.temporal.common.interceptors.NexusClientInterceptor;
import io.temporal.spring.boot.TemporalOptionsCustomizer;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class NexusClientOptionsTemplate {
private final @Nonnull String namespace;
private final @Nullable DataConverter dataConverter;
private final @Nullable List<NexusClientInterceptor> interceptors;
private final @Nullable List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> customizers;

public NexusClientOptionsTemplate(
@Nonnull String namespace,
@Nullable DataConverter dataConverter,
@Nullable List<NexusClientInterceptor> interceptors,
@Nullable List<TemporalOptionsCustomizer<NexusClientOptions.Builder>> customizers) {
this.namespace = namespace;
this.dataConverter = dataConverter;
this.interceptors = interceptors;
this.customizers = customizers;
}

public NexusClientOptions createNexusClientOptions() {
NexusClientOptions.Builder options = NexusClientOptions.newBuilder().setNamespace(namespace);
if (dataConverter != null) {
options.setDataConverter(dataConverter);
}
if (interceptors != null) {
options.setInterceptors(interceptors);
}
if (customizers != null) {
for (TemporalOptionsCustomizer<NexusClientOptions.Builder> customizer : customizers) {
options = customizer.customize(options);
}
}
return options.build();
}
}
Loading
Loading