diff --git a/CHANGELOG.md b/CHANGELOG.md index af4bac6c19..2ce39e0abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ to docs, or any other relevant information. Child workflow overrides and one-time routing require Temporal Server 1.32.0 or later. - `WorkerFactoryOptions.Builder.setLoggerTagPrefix` that can be used to customized structured logging tags (MDC keys) 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. ### Changed - Release notes for all future releases are now in a single CHANGELOG.md file. `releases` directory with old release diff --git a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/ActivityImpl.java b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/ActivityImpl.java index a500009d8d..2e3275f9fa 100644 --- a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/ActivityImpl.java +++ b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/ActivityImpl.java @@ -15,7 +15,7 @@ /** * @return names of Workers to register this activity bean with. Workers with these names must be * present in the application config. Worker is named by its task queue if its name is not - * specified. + * specified. Can be specified as a property key, e.g.: ${propertyKey}. */ String[] workers() default {}; diff --git a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/NexusServiceImpl.java b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/NexusServiceImpl.java index 791b12da0f..c9e2dce8d7 100644 --- a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/NexusServiceImpl.java +++ b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/NexusServiceImpl.java @@ -15,7 +15,7 @@ /** * @return names of Workers to register this nexus service bean with. Workers with these names * must be present in the application config. Worker is named by its task queue if its name is - * not specified. + * not specified. Can be specified as a property key, e.g.: ${propertyKey}. */ String[] workers() default {}; diff --git a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/WorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/WorkflowImpl.java index ed1dc9f74d..e0986bd016 100644 --- a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/WorkflowImpl.java +++ b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/WorkflowImpl.java @@ -15,7 +15,8 @@ /** * @return names of Workers to register this workflow implementation with. Workers with these * names must be present in the application config or auto-discovered from {@link - * #taskQueues()}. Worker is named by its task queue if its name is not specified. + * #taskQueues()}. Worker is named by its task queue if its name is not specified. Can be + * specified by a property key, e.g.: ${propertyKey}. */ String[] workers() default {}; diff --git a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/autoconfigure/template/WorkersTemplate.java b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/autoconfigure/template/WorkersTemplate.java index dfbc18d7f6..46b0dc6dd5 100644 --- a/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/autoconfigure/template/WorkersTemplate.java +++ b/temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/autoconfigure/template/WorkersTemplate.java @@ -306,6 +306,7 @@ private void configureWorkflowImplementationsByWorkerName( WorkflowImpl annotation = AnnotationUtils.findAnnotation(clazz, WorkflowImpl.class); for (String workerName : annotation.workers()) { + workerName = environment.resolvePlaceholders(workerName); Worker worker = workers.getByName(workerName); if (worker == null) { throw new BeanDefinitionValidationException( @@ -328,6 +329,7 @@ private void configureActivityBeansByWorkerName( ActivityImpl annotation = AnnotationUtils.findAnnotation(targetClass, ActivityImpl.class); if (annotation != null) { for (String workerName : annotation.workers()) { + workerName = environment.resolvePlaceholders(workerName); Worker worker = workers.getByName(workerName); if (worker == null) { throw new BeanDefinitionValidationException( @@ -353,6 +355,7 @@ private void configureNexusServiceBeansByWorkerName( AnnotationUtils.findAnnotation(targetClass, NexusServiceImpl.class); if (annotation != null) { for (String workerName : annotation.workers()) { + workerName = environment.resolvePlaceholders(workerName); Worker worker = workers.getByName(workerName); if (worker == null) { throw new BeanDefinitionValidationException( diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameResolverTest.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameResolverTest.java new file mode 100644 index 0000000000..ab5f1a9838 --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameResolverTest.java @@ -0,0 +1,65 @@ +package io.temporal.spring.boot.autoconfigure; + +import io.temporal.api.nexus.v1.Endpoint; +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import io.temporal.client.WorkflowStub; +import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow; +import io.temporal.testing.TestWorkflowEnvironment; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest(classes = AutoDiscoveryByWorkerNameResolverTest.Configuration.class) +@ActiveProfiles(profiles = "auto-discovery-by-worker-name-dynamic-name") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class AutoDiscoveryByWorkerNameResolverTest { + @Autowired ConfigurableApplicationContext applicationContext; + + @Autowired TestWorkflowEnvironment testWorkflowEnvironment; + + @Autowired WorkflowClient workflowClient; + + Endpoint endpoint; + + @BeforeEach + void setUp() { + applicationContext.start(); + endpoint = + testWorkflowEnvironment.createNexusEndpoint( + "AutoDiscoveryByWorkerNameTestEndpoint", "PropertyResolverTest"); + } + + @AfterEach + void tearDown() { + testWorkflowEnvironment.deleteNexusEndpoint(endpoint); + } + + @Test + @Timeout(value = 10) + public void testAutoDiscovery() { + TestWorkflow testWorkflow = + workflowClient.newWorkflowStub( + TestWorkflow.class, + WorkflowOptions.newBuilder().setTaskQueue("PropertyResolverTest").build()); + testWorkflow.execute("nexus"); + + WorkflowStub dynamicStub = + workflowClient.newUntypedWorkflowStub( + "DynamicWorkflow", + WorkflowOptions.newBuilder().setTaskQueue("PropertyResolverTest").build()); + dynamicStub.start(); + Assertions.assertEquals("hello from dynamic workflow", dynamicStub.getResult(String.class)); + } + + @ComponentScan( + excludeFilters = + @ComponentScan.Filter( + pattern = "io\\.temporal\\.spring\\.boot\\.autoconfigure\\.bytaskqueue\\..*", + type = FilterType.REGEX)) + public static class Configuration {} +} diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestActivityImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestActivityImpl.java index 128325d70a..cef95ded66 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestActivityImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestActivityImpl.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Component; @Component("TestActivityImpl") -@ActivityImpl(workers = "mainWorker") +@ActivityImpl(workers = "${default-worker.name:mainWorker}") @Profile("auto-discovery-with-profile") public class OtherTestActivityImpl implements TestActivity { @Override diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestWorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestWorkflowImpl.java index 2abffff02a..1170e75104 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestWorkflowImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/OtherTestWorkflowImpl.java @@ -7,7 +7,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Profile; -@WorkflowImpl(workers = "mainWorker") +@WorkflowImpl(workers = "${default-worker.name:mainWorker}") @Profile("auto-discovery-with-profile") public class OtherTestWorkflowImpl implements TestWorkflow { diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestActivityImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestActivityImpl.java index 8e68a1db4f..7b91e39827 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestActivityImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestActivityImpl.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Component; @Component("TestActivityImpl") -@ActivityImpl(workers = "mainWorker") +@ActivityImpl(workers = "${default-worker.name:mainWorker}") @Profile("!auto-discovery-with-profile") public class TestActivityImpl implements TestActivity { @Override diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestDynamicWorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestDynamicWorkflowImpl.java index f1d3ddccd8..68f4d2c9ee 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestDynamicWorkflowImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestDynamicWorkflowImpl.java @@ -4,7 +4,7 @@ import io.temporal.spring.boot.WorkflowImpl; import io.temporal.workflow.DynamicWorkflow; -@WorkflowImpl(workers = "mainWorker") +@WorkflowImpl(workers = "${default-worker.name:mainWorker}") public class TestDynamicWorkflowImpl implements DynamicWorkflow { @Override public Object execute(EncodedValues args) { diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestNexusServiceImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestNexusServiceImpl.java index 949519504c..1786eeeefe 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestNexusServiceImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestNexusServiceImpl.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; @Component("TestNexusServiceImpl") -@NexusServiceImpl(workers = "mainWorker") +@NexusServiceImpl(workers = "${default-worker.name:mainWorker}") @ServiceImpl(service = TestNexusService.class) public class TestNexusServiceImpl { @OperationImpl diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestWorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestWorkflowImpl.java index 78ea99aa0a..797bd05d64 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestWorkflowImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkername/TestWorkflowImpl.java @@ -9,7 +9,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Profile; -@WorkflowImpl(workers = "mainWorker") +@WorkflowImpl(workers = "${default-worker.name:mainWorker}") @Profile("!auto-discovery-with-profile") public class TestWorkflowImpl implements TestWorkflow { diff --git a/temporal-spring-boot-autoconfigure/src/test/resources/application.yml b/temporal-spring-boot-autoconfigure/src/test/resources/application.yml index afc8654c92..e9b8568161 100644 --- a/temporal-spring-boot-autoconfigure/src/test/resources/application.yml +++ b/temporal-spring-boot-autoconfigure/src/test/resources/application.yml @@ -109,6 +109,23 @@ spring: register-activity-beans: true register-nexus-service-beans: true +--- +spring: + config: + activate: + on-profile: auto-discovery-by-worker-name-dynamic-name + temporal: + workers: + - task-queue: PropertyResolverTest + name: propertyResolverWorker + workers-auto-discovery: + workflow-packages: + - io.temporal.spring.boot.autoconfigure.byworkername + register-activity-beans: true + register-nexus-service-beans: true +default-worker: + name: propertyResolverWorker + --- spring: config: