From cd839a6d4937b4d6381d13b630e6021ec574f5cc Mon Sep 17 00:00:00 2001 From: sachinsharma Date: Sun, 9 Aug 2026 18:19:00 -0700 Subject: [PATCH 1/3] Support Spring property placeholders in @WorkflowImpl workers attribute taskQueues already resolves placeholders via environment.resolvePlaceholders(), but workers did not. Add the same resolution to configureWorkflowImplementationsByWorkerName, configureActivityBeansByWorkerName, and configureNexusServiceBeansByWorkerName. Fixes #2747 --- .../spring/boot/autoconfigure/template/WorkersTemplate.java | 3 +++ 1 file changed, 3 insertions(+) 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 1ab6bc7a2b..b02d5d946f 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 = clazz.getAnnotation(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( From cf8b6bc837cbcd36657e2330d53cd8ba8ed50f3a Mon Sep 17 00:00:00 2001 From: sachinsharma Date: Fri, 14 Aug 2026 00:16:08 -0700 Subject: [PATCH 2/3] Add test for Spring property placeholder resolution in workers attribute Verifies that @WorkflowImpl(workers = "${worker.name}") and @ActivityImpl(workers = "${worker.name}") correctly resolve Spring property placeholders at registration time. --- ...AutoDiscoveryByWorkerNameResolverTest.java | 49 +++++++++++++++++++ .../byworkernameresolver/TestActivity.java | 8 +++ .../TestActivityImpl.java | 13 +++++ .../byworkernameresolver/TestWorkflow.java | 11 +++++ .../TestWorkflowImpl.java | 20 ++++++++ .../src/test/resources/application.yml | 16 ++++++ 6 files changed, 117 insertions(+) create mode 100644 temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameResolverTest.java create mode 100644 temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivity.java create mode 100644 temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java create mode 100644 temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflow.java create mode 100644 temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java 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..c42bfa5fa1 --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameResolverTest.java @@ -0,0 +1,49 @@ +package io.temporal.spring.boot.autoconfigure; + +import io.temporal.client.WorkflowClient; +import io.temporal.client.WorkflowOptions; +import io.temporal.spring.boot.autoconfigure.byworkernameresolver.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-resolver") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class AutoDiscoveryByWorkerNameResolverTest { + @Autowired ConfigurableApplicationContext applicationContext; + + @Autowired TestWorkflowEnvironment testWorkflowEnvironment; + + @Autowired WorkflowClient workflowClient; + + @BeforeEach + void setUp() { + applicationContext.start(); + } + + @Test + @Timeout(value = 10) + public void testWorkerNamePropertyResolution() { + TestWorkflow testWorkflow = + workflowClient.newWorkflowStub( + TestWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue("UnitTest").build()); + String result = testWorkflow.execute("done"); + Assertions.assertEquals("done", result); + } + + @ComponentScan( + excludeFilters = { + @ComponentScan.Filter( + pattern = + "io\\.temporal\\.spring\\.boot\\.autoconfigure" + + "\\.(bytaskqueue|byworkername|workerversioning)\\..*", + type = FilterType.REGEX) + }) + public static class Configuration {} +} diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivity.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivity.java new file mode 100644 index 0000000000..787daaa3bc --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivity.java @@ -0,0 +1,8 @@ +package io.temporal.spring.boot.autoconfigure.byworkernameresolver; + +import io.temporal.activity.ActivityInterface; + +@ActivityInterface +public interface TestActivity { + String execute(String input); +} diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java new file mode 100644 index 0000000000..78d6df9f49 --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java @@ -0,0 +1,13 @@ +package io.temporal.spring.boot.autoconfigure.byworkernameresolver; + +import io.temporal.spring.boot.ActivityImpl; +import org.springframework.stereotype.Component; + +@Component("ResolverTestActivityImpl") +@ActivityImpl(workers = "${worker.name}") +public class TestActivityImpl implements TestActivity { + @Override + public String execute(String input) { + return input; + } +} diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflow.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflow.java new file mode 100644 index 0000000000..db40e5677c --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflow.java @@ -0,0 +1,11 @@ +package io.temporal.spring.boot.autoconfigure.byworkernameresolver; + +import io.temporal.workflow.WorkflowInterface; +import io.temporal.workflow.WorkflowMethod; + +@WorkflowInterface +public interface TestWorkflow { + + @WorkflowMethod + String execute(String input); +} diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java new file mode 100644 index 0000000000..bbacb0f027 --- /dev/null +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java @@ -0,0 +1,20 @@ +package io.temporal.spring.boot.autoconfigure.byworkernameresolver; + +import io.temporal.activity.ActivityOptions; +import io.temporal.spring.boot.WorkflowImpl; +import io.temporal.workflow.Workflow; +import java.time.Duration; + +@WorkflowImpl(workers = "${worker.name}") +public class TestWorkflowImpl implements TestWorkflow { + + @Override + public String execute(String input) { + return Workflow.newActivityStub( + TestActivity.class, + ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(1)) + .validateAndBuildWithDefaults()) + .execute(input); + } +} diff --git a/temporal-spring-boot-autoconfigure/src/test/resources/application.yml b/temporal-spring-boot-autoconfigure/src/test/resources/application.yml index d33d50b46d..7bf31ef063 100644 --- a/temporal-spring-boot-autoconfigure/src/test/resources/application.yml +++ b/temporal-spring-boot-autoconfigure/src/test/resources/application.yml @@ -109,6 +109,22 @@ spring: register-activity-beans: true register-nexus-service-beans: true +--- +spring: + config: + activate: + on-profile: auto-discovery-by-worker-name-resolver + temporal: + workers: + - task-queue: UnitTest + name: mainWorker + workers-auto-discovery: + workflow-packages: + - io.temporal.spring.boot.autoconfigure.byworkernameresolver + register-activity-beans: true +worker: + name: mainWorker + --- spring: config: From 0c3e898999026103c2edf246411fa205c66c9eeb Mon Sep 17 00:00:00 2001 From: sachinsharma Date: Fri, 14 Aug 2026 00:23:49 -0700 Subject: [PATCH 3/3] Add @Profile to resolver test beans to prevent leaking into other test contexts --- .../autoconfigure/byworkernameresolver/TestActivityImpl.java | 2 ++ .../autoconfigure/byworkernameresolver/TestWorkflowImpl.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java index 78d6df9f49..9e1d7db254 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestActivityImpl.java @@ -1,10 +1,12 @@ package io.temporal.spring.boot.autoconfigure.byworkernameresolver; import io.temporal.spring.boot.ActivityImpl; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; @Component("ResolverTestActivityImpl") @ActivityImpl(workers = "${worker.name}") +@Profile("auto-discovery-by-worker-name-resolver") public class TestActivityImpl implements TestActivity { @Override public String execute(String input) { diff --git a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java index bbacb0f027..e9c90864d5 100644 --- a/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java +++ b/temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/byworkernameresolver/TestWorkflowImpl.java @@ -4,8 +4,10 @@ import io.temporal.spring.boot.WorkflowImpl; import io.temporal.workflow.Workflow; import java.time.Duration; +import org.springframework.context.annotation.Profile; @WorkflowImpl(workers = "${worker.name}") +@Profile("auto-discovery-by-worker-name-resolver") public class TestWorkflowImpl implements TestWorkflow { @Override