diff --git a/.gitignore b/.gitignore
index b80b599dc8..1fb5dd59bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@ target
.gradle
/build
/*/build
-/out
+**/out
/lib
dummy
$buildDir
diff --git a/build.gradle b/build.gradle
index 6225aafc29..b7678d474d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,20 +24,22 @@ ext {
// We have to use specific versions with platforms until Maven 4.0.0 is released
// See https://github.com/temporalio/sdk-java/issues/1033
//
- // grpcVersion = '[1.34.0,)!!1.44.1'
+ // grpcVersion = '[1.34.0,)!!1.47.1'
// jacksonVersion = '[2.9.0,)!!2.13.1'
- // micrometerVersion = '[1.0.0,)!!1.8.3'
+ // micrometerVersion = '[1.0.0,)!!1.9.1'
grpcVersion = '1.47.0'
jacksonVersion = '2.13.1'
micrometerVersion = '1.9.1'
+ springBootVersion = '2.4.0'
+ slf4jVersion = '[1.4.0,)!!1.7.36'
protoVersion = '[3.10.0,3.99)!!3.20.1'
annotationApiVersion = '1.3.2'
guavaVersion = '[10.0,)!!31.1-jre'
- jsonPathVersion = '2.7.0'
tallyVersion = '[0.4.0,)!!0.11.1'
+
+ jsonPathVersion = '2.7.0'
gsonVersion = '[2.0,)!!2.9.0'
- slf4jVersion = '[1.4.0,)!!1.7.36'
logbackVersion = '1.2.11'
mockitoVersion = '4.6.1'
diff --git a/settings.gradle b/settings.gradle
index 26dc285e6e..957bdb1e41 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -5,3 +5,5 @@ include 'temporal-testing'
include 'temporal-test-server'
include 'temporal-opentracing'
include 'temporal-kotlin'
+include 'temporal-spring-boot-autoconfigure-alpha'
+include 'temporal-spring-boot-starter-alpha'
diff --git a/temporal-sdk/build.gradle b/temporal-sdk/build.gradle
index bf83e275e7..0f60f464a9 100644
--- a/temporal-sdk/build.gradle
+++ b/temporal-sdk/build.gradle
@@ -17,9 +17,7 @@ dependencies {
api "com.fasterxml.jackson.core:jackson-databind"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jdk8"
- if (!JavaVersion.current().isJava8()) {
- implementation 'javax.annotation:javax.annotation-api:1.3.2'
- }
+
// compileOnly and testImplementation because this dependency is needed only to work with json format of history
// which shouldn't be needed for any production usage of temporal-sdk.
// It's useful only for unit tests and debugging.
diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ChildWorkflowTaskFailedException.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ChildWorkflowTaskFailedException.java
index f8d851c786..1b5aef519f 100644
--- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ChildWorkflowTaskFailedException.java
+++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ChildWorkflowTaskFailedException.java
@@ -28,9 +28,9 @@
* machines in case of child workflow task execution failure and contains an original unparsed
* Failure message with details from the attributes in the exception.
*
- *
This class is needed to don't make Failure -> Exception conversion inside the state machines.
- * So the state machine forms ChildWorkflowFailure without cause and parse the original Failure, so
- * the outside code may join them together.
+ *
This class is needed to don't make Failure -> Exception conversion inside the state
+ * machines. So the state machine forms ChildWorkflowFailure without cause and parse the original
+ * Failure, so the outside code may join them together.
*/
public class ChildWorkflowTaskFailedException extends RuntimeException {
diff --git a/temporal-serviceclient/build.gradle b/temporal-serviceclient/build.gradle
index f01110e8c5..7b34aaab21 100644
--- a/temporal-serviceclient/build.gradle
+++ b/temporal-serviceclient/build.gradle
@@ -13,8 +13,8 @@ dependencies {
api "io.grpc:grpc-stub" //Part of WorkflowServiceStubs API
api "io.grpc:grpc-netty-shaded" //Part of WorkflowServiceStubs API, specifically SslContext
api "com.google.protobuf:protobuf-java-util:$protoVersion" //proto request and response objects are a part of this module's API
- if (!JavaVersion.current().isJava8()) {
- //needed for the generated grpc stubs
+ if (JavaVersion.current().isJava9Compatible()) {
+ //needed for the generated grpc stubs and is not a part of JDK since java 9
implementation "javax.annotation:javax.annotation-api:$annotationApiVersion"
}
diff --git a/temporal-spring-boot-autoconfigure-alpha/build.gradle b/temporal-spring-boot-autoconfigure-alpha/build.gradle
new file mode 100644
index 0000000000..cb95f6ca19
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/build.gradle
@@ -0,0 +1,30 @@
+description = '''Spring Boot AutoConfigure for Temporal Java SDK'''
+
+dependencies {
+ api(platform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"))
+
+ api project(':temporal-sdk')
+ compileOnly project(':temporal-testing')
+
+ implementation "org.springframework.boot:spring-boot"
+ implementation "org.springframework.boot:spring-boot-autoconfigure"
+
+ // this dependency is not mandatory for the module to work, but it provides a better IDE experience developing the module
+ // it's needed to auto-generate configuration metadata. See for more details:
+ // https://docs.spring.io/spring-boot/docs/2.4.0/reference/html/appendix-configuration-metadata.html#configuration-metadata-annotation-processor
+ annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:$springBootVersion"
+
+ testImplementation project(':temporal-testing')
+
+ testImplementation "org.mockito:mockito-core:${mockitoVersion}"
+ testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: "${logbackVersion}"
+
+ testImplementation "org.springframework.boot:spring-boot-starter-test"
+}
+
+tasks.test {
+ useJUnitPlatform()
+ testLogging {
+ events("passed", "skipped", "failed")
+ }
+}
\ No newline at end of file
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/ActivityImpl.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/ActivityImpl.java
new file mode 100644
index 0000000000..044d9df95e
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/ActivityImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface ActivityImpl {
+ String[] taskQueues();
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/WorkflowImpl.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/WorkflowImpl.java
new file mode 100644
index 0000000000..2f72fdc148
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/WorkflowImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface WorkflowImpl {
+ String[] taskQueues();
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/ClientAutoConfiguration.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/ClientAutoConfiguration.java
new file mode 100644
index 0000000000..a01832f53f
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/ClientAutoConfiguration.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowClientOptions;
+import io.temporal.serviceclient.WorkflowServiceStubs;
+import io.temporal.serviceclient.WorkflowServiceStubsOptions;
+import io.temporal.spring.boot.autoconfigure.properties.ClientProperties;
+import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
+import io.temporal.testing.TestWorkflowEnvironment;
+import javax.annotation.Nullable;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.support.BeanDefinitionValidationException;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/** Provides a client based on `spring.temporal.client` section */
+@Configuration
+@EnableConfigurationProperties(TemporalProperties.class)
+@ConditionalOnClass(name = "io.temporal.client.WorkflowClient")
+@ConditionalOnProperty(prefix = "spring.temporal", name = "client.target")
+public class ClientAutoConfiguration {
+
+ @Bean(name = "temporalWorkflowClient")
+ @ConditionalOnMissingBean(name = "temporalWorkflowClient")
+ public WorkflowClient client(
+ TemporalProperties temporalProperties,
+ @Qualifier("temporalTestWorkflowEnvironment") @Autowired(required = false) @Nullable
+ TestWorkflowEnvironment testWorkflowEnvironment) {
+ ClientProperties clientProperties = temporalProperties.getClient();
+ WorkflowServiceStubs workflowServiceStubs;
+ switch (temporalProperties.getClient().getTarget().toLowerCase()) {
+ case ClientProperties.TARGET_IN_PROCESS_TEST_SERVER:
+ if (testWorkflowEnvironment == null) {
+ throw new BeanDefinitionValidationException(
+ "Test workflow environment is not available. "
+ + "Please make sure that you have enabled the 'temporal-test' module "
+ + "and configured `spring.temporal.testServer.enabled: true`.");
+ }
+ return testWorkflowEnvironment.getWorkflowClient();
+ case ClientProperties.TARGET_LOCAL_SERVICE:
+ workflowServiceStubs = WorkflowServiceStubs.newLocalServiceStubs();
+ break;
+ default:
+ WorkflowServiceStubsOptions.Builder stubsOptionsBuilder =
+ WorkflowServiceStubsOptions.newBuilder();
+
+ if (clientProperties.getTarget() != null) {
+ stubsOptionsBuilder.setTarget(clientProperties.getTarget());
+ }
+
+ workflowServiceStubs =
+ WorkflowServiceStubs.newServiceStubs(
+ stubsOptionsBuilder.validateAndBuildWithDefaults());
+ }
+
+ WorkflowClientOptions.Builder clientOptionsBuilder = WorkflowClientOptions.newBuilder();
+
+ if (clientProperties.getNamespace() != null) {
+ clientOptionsBuilder.setNamespace(clientProperties.getNamespace());
+ }
+
+ return WorkflowClient.newInstance(
+ workflowServiceStubs, clientOptionsBuilder.validateAndBuildWithDefaults());
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/TestServerAutoConfiguration.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/TestServerAutoConfiguration.java
new file mode 100644
index 0000000000..d18d6ca326
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/TestServerAutoConfiguration.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
+import io.temporal.testing.TestWorkflowEnvironment;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/** Provides a client based on `spring.temporal.testServer` section */
+@Configuration
+@EnableConfigurationProperties(TemporalProperties.class)
+@ConditionalOnClass(name = "io.temporal.testing.TestWorkflowEnvironment")
+@ConditionalOnProperty(
+ prefix = "spring.temporal",
+ name = "testServer.enabled",
+ havingValue = "true")
+public class TestServerAutoConfiguration {
+ @Bean(name = "temporalTestWorkflowEnvironment", destroyMethod = "close")
+ public TestWorkflowEnvironment testServer() {
+ return TestWorkflowEnvironment.newInstance();
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoConfiguration.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoConfiguration.java
new file mode 100644
index 0000000000..c6c60f2473
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoConfiguration.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.client.WorkflowClient;
+import io.temporal.common.metadata.POJOWorkflowImplMetadata;
+import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata;
+import io.temporal.common.metadata.POJOWorkflowMethodMetadata;
+import io.temporal.spring.boot.WorkflowImpl;
+import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
+import io.temporal.spring.boot.autoconfigure.properties.WorkerProperties;
+import io.temporal.testing.TestWorkflowEnvironment;
+import io.temporal.worker.Worker;
+import io.temporal.worker.WorkerFactory;
+import java.util.*;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionValidationException;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.annotation.*;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/** Provides a client based on `spring.temporal.workers` section */
+@Configuration
+@EnableConfigurationProperties(TemporalProperties.class)
+@ConditionalOnClass(name = "io.temporal.worker.WorkerFactory")
+@Conditional(WorkersPresentCondition.class)
+@Import({WorkersAutoConfiguration.ActivityExplicitConfigProcessor.class})
+public class WorkersAutoConfiguration {
+ private static final Logger log = LoggerFactory.getLogger(WorkersAutoConfiguration.class);
+
+ private final TemporalProperties properties;
+ private final ConfigurableListableBeanFactory beanFactory;
+
+ public WorkersAutoConfiguration(
+ TemporalProperties properties, ConfigurableListableBeanFactory beanFactory) {
+ this.properties = properties;
+ this.beanFactory = beanFactory;
+ }
+
+ @Bean(name = "temporalWorkerFactory", destroyMethod = "shutdown")
+ public WorkerFactory workerFactory(
+ WorkflowClient workflowClient,
+ @Qualifier("temporalTestWorkflowEnvironment") @Autowired(required = false) @Nullable
+ TestWorkflowEnvironment testWorkflowEnvironment) {
+ if (testWorkflowEnvironment != null) {
+ return testWorkflowEnvironment.getWorkerFactory();
+ } else {
+ return WorkerFactory.newInstance(workflowClient);
+ }
+ }
+
+ @Bean(name = "temporalWorkers")
+ public Collection workers(
+ WorkerFactory workerFactory,
+ @Qualifier("autoDiscoveredWorkflowImplementations") @Autowired(required = false) @Nullable
+ Collection> autoDiscoveredWorkflowImplementationClasses) {
+ Set workers =
+ properties.getWorkers().stream()
+ .map(workerProperties -> newWorker(workerFactory, workerProperties))
+ .collect(Collectors.toSet());
+
+ if (autoDiscoveredWorkflowImplementationClasses != null) {
+ for (Class> clazz : autoDiscoveredWorkflowImplementationClasses) {
+ WorkflowImpl annotation = clazz.getAnnotation(WorkflowImpl.class);
+ for (String taskQueue : annotation.taskQueues()) {
+ log.info(
+ "Registering auto-discovered workflow class {} on a task queue {}", clazz, taskQueue);
+ Worker worker = workerFactory.newWorker(taskQueue);
+ configureWorkflowImplementation(worker, clazz);
+ workers.add(worker);
+ }
+ }
+ }
+
+ workers.forEach(
+ worker -> beanFactory.registerSingleton("temporalWorker-" + worker.getTaskQueue(), worker));
+
+ return workers;
+ }
+
+ @ConditionalOnProperty(prefix = "spring.temporal", name = "startWorkers", matchIfMissing = true)
+ @Bean
+ public WorkerFactoryStarter workerFactoryStarter(WorkerFactory workerFactory) {
+ return new WorkerFactoryStarter(workerFactory);
+ }
+
+ Worker newWorker(WorkerFactory workerFactory, WorkerProperties workerProperties) {
+ Worker worker = workerFactory.newWorker(workerProperties.getTaskQueue());
+
+ Collection> workflowClasses = workerProperties.getWorkflowClasses();
+ if (workflowClasses != null) {
+ workflowClasses.forEach(
+ clazz -> {
+ log.info(
+ "Registering configured workflow class {} on a task queue {}",
+ clazz,
+ workerProperties.getTaskQueue());
+ configureWorkflowImplementation(worker, clazz);
+ });
+ }
+
+ return worker;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void configureWorkflowImplementation(Worker worker, Class> clazz) {
+ // TODO this code is a copy-past of a portion of
+ // POJOWorkflowImplementationFactory#registerWorkflowImplementationType
+ // we should refactor it to separate the logic into reusable methods
+ boolean hasWorkflowMethod = false;
+ POJOWorkflowImplMetadata workflowMetadata = POJOWorkflowImplMetadata.newInstance(clazz);
+ for (POJOWorkflowInterfaceMetadata workflowInterface :
+ workflowMetadata.getWorkflowInterfaces()) {
+ Optional workflowMethod = workflowInterface.getWorkflowMethod();
+ if (workflowMethod.isPresent()) {
+ // TODO this is ugly. POJOWorkflowMethodMetadata needs to be generified
+ worker.addWorkflowImplementationFactory(
+ (Class) workflowInterface.getInterfaceClass(),
+ () -> (T) beanFactory.createBean(clazz));
+ hasWorkflowMethod = true;
+ break;
+ }
+ }
+
+ if (!hasWorkflowMethod) {
+ throw new BeanDefinitionValidationException(clazz + " doesn't have workflowMethod");
+ }
+ }
+
+ public static class WorkerFactoryStarter implements ApplicationListener {
+ private final WorkerFactory workerFactory;
+
+ public WorkerFactoryStarter(WorkerFactory workerFactory) {
+ this.workerFactory = workerFactory;
+ }
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ workerFactory.start();
+ }
+ }
+
+ public static class ActivityExplicitConfigProcessor implements BeanPostProcessor {
+ private final ObjectFactory propertiesProvider;
+ private final ObjectFactory workerFactoryProvider;
+ private Map> activityBeanToTaskQueues;
+
+ // By using ObjectFactory we avoid eager initialization of the properties and workerFactory
+ // which leads to a ton of warnings like
+ // "PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean
+ // 'spring.temporal-io.temporal.spring.boot.autoconfigure.properties.TemporalProperties' of type
+ // [io.temporal.spring.boot.autoconfigure.properties.TemporalProperties] is not eligible for
+ // getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)"
+ // because BeanPostProcessors are created before other beans and eager dependency on other beans
+ // messes with an initialization ordering.
+ public ActivityExplicitConfigProcessor(
+ ObjectFactory propertiesProvider,
+ ObjectFactory workerFactoryProvider) {
+ this.propertiesProvider = propertiesProvider;
+ this.workerFactoryProvider = workerFactoryProvider;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName)
+ throws BeansException {
+ if (activityBeanToTaskQueues == null) {
+ // don't move it to init method, PostConstruct, constructor or any other steps that happen
+ // before BeanPostProcessor#postProcessAfterInitialization
+ // Otherwise you will get a nasty warning in console
+ // "PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean
+ // 'spring.temporal-io.temporal.spring.boot.autoconfigure.properties.TemporalProperties' of
+ // type [io.temporal.spring.boot.autoconfigure.properties.TemporalProperties] is not
+ // eligible for getting processed by all BeanPostProcessors (for example: not eligible for
+ // auto-proxying)"
+ // because it will lead to an early usage of TemporalProperties before BeanPostProcessor
+ // could be called on it.
+ // For more context of the warning:
+ // https://www.baeldung.com/spring-not-eligible-for-auto-proxying
+ parseConfig();
+ }
+
+ if (activityBeanToTaskQueues.containsKey(beanName)) {
+ activityBeanToTaskQueues.get(beanName).stream()
+ .map(workerFactoryProvider.getObject()::newWorker)
+ .forEach(
+ worker -> {
+ log.info(
+ "Registering configured activity bean '{}' class {} on task queue {}",
+ beanName,
+ AopUtils.getTargetClass(bean),
+ worker.getTaskQueue());
+ worker.registerActivitiesImplementations(bean);
+ });
+ }
+ return bean;
+ }
+
+ private void parseConfig() {
+ activityBeanToTaskQueues = new HashMap<>();
+ for (WorkerProperties workerProperties : propertiesProvider.getObject().getWorkers()) {
+ Collection activityBeans = workerProperties.getActivityBeans();
+ if (activityBeans != null) {
+ activityBeans.forEach(
+ activityBeanName ->
+ activityBeanToTaskQueues
+ .computeIfAbsent(activityBeanName, k -> new ArrayList<>())
+ .add(workerProperties.getTaskQueue()));
+ }
+ }
+ }
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryAutoConfiguration.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryAutoConfiguration.java
new file mode 100644
index 0000000000..b07f156952
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryAutoConfiguration.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.spring.boot.ActivityImpl;
+import io.temporal.spring.boot.WorkflowImpl;
+import io.temporal.spring.boot.autoconfigure.properties.TemporalProperties;
+import io.temporal.worker.Worker;
+import io.temporal.worker.WorkerFactory;
+import java.util.*;
+import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.support.BeanDefinitionValidationException;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.*;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+
+/**
+ * Works only if `spring.temporal.workersAutoDiscovery` is true and responsible for auto-discovery
+ * of workflow and activity implementations that are annotated with {@link
+ * io.temporal.spring.boot.ActivityImpl} and {@link io.temporal.spring.boot.WorkflowImpl}
+ */
+@Configuration
+@EnableConfigurationProperties(TemporalProperties.class)
+@Import({WorkersAutoDiscoveryAutoConfiguration.ActivityAutoDiscoveryProcessor.class})
+@Conditional(WorkersAutoDiscoveryPackagesPresentCondition.class)
+public class WorkersAutoDiscoveryAutoConfiguration {
+ private static final Logger log =
+ LoggerFactory.getLogger(WorkersAutoDiscoveryAutoConfiguration.class);
+
+ private final TemporalProperties properties;
+
+ public WorkersAutoDiscoveryAutoConfiguration(
+ TemporalProperties properties) {
+ this.properties = properties;
+ }
+
+ @Bean(name = "autoDiscoveredWorkflowImplementations")
+ public Collection> autoDiscoveredWorkflowImplementations() {
+ ClassPathScanningCandidateComponentProvider scanner =
+ new ClassPathScanningCandidateComponentProvider(false);
+ scanner.addIncludeFilter(new AnnotationTypeFilter(WorkflowImpl.class));
+ Set> implementations = new HashSet<>();
+ for (String pckg : properties.getWorkersAutoDiscovery().getPackages()) {
+ Set candidateComponents = scanner.findCandidateComponents(pckg);
+ for (BeanDefinition beanDefinition : candidateComponents) {
+ try {
+ implementations.add(Class.forName(beanDefinition.getBeanClassName()));
+ } catch (ClassNotFoundException e) {
+ throw new BeanDefinitionValidationException(
+ "Fail loading class for bean definition " + beanDefinition, e);
+ }
+ }
+ }
+ return implementations;
+ }
+
+ /**
+ * Activities to be registered on a worker need to be already instantiated. This is the reason
+ * auto-discovery of activities has to be done in a post processor, when all bean definitions are
+ * loaded and instances are created by Spring.
+ */
+ public static class ActivityAutoDiscoveryProcessor implements BeanPostProcessor {
+ private final ObjectFactory workerFactoryProvider;
+
+ public ActivityAutoDiscoveryProcessor(ObjectFactory workerFactoryProvider) {
+ this.workerFactoryProvider = workerFactoryProvider;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName)
+ throws BeansException {
+ Class> targetClass = AopUtils.getTargetClass(bean);
+ ActivityImpl activityAnnotation =
+ AnnotationUtils.findAnnotation(targetClass, ActivityImpl.class);
+ if (activityAnnotation != null) {
+ for (String taskQueue : activityAnnotation.taskQueues()) {
+ log.info(
+ "Registering auto-discovered activity bean '{}' of class {} on task queue {}",
+ beanName,
+ targetClass,
+ taskQueue);
+ Worker worker = workerFactoryProvider.getObject().newWorker(taskQueue);
+ worker.registerActivitiesImplementations(bean);
+ }
+ }
+ return bean;
+ }
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryPackagesPresentCondition.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryPackagesPresentCondition.java
new file mode 100644
index 0000000000..b346a753d5
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersAutoDiscoveryPackagesPresentCondition.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.OnPropertyListCondition;
+
+class WorkersAutoDiscoveryPackagesPresentCondition extends OnPropertyListCondition {
+ public WorkersAutoDiscoveryPackagesPresentCondition() {
+ super(
+ "spring.temporal.workersAutoDiscovery.packages".toLowerCase(),
+ () -> ConditionMessage.forCondition("Present Workers Auto Discovery Packages"));
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersPresentCondition.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersPresentCondition.java
new file mode 100644
index 0000000000..5dfbe228ee
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/WorkersPresentCondition.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.spring.boot.autoconfigure.properties.WorkerProperties;
+import java.util.List;
+import org.springframework.boot.autoconfigure.condition.ConditionMessage;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.context.properties.bind.BindResult;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+
+class WorkersPresentCondition extends SpringBootCondition {
+ private static final Bindable> WORKER_PROPERTIES_LIST =
+ Bindable.listOf(WorkerProperties.class);
+ private static final String KEY = "spring.temporal.workers";
+
+ public WorkersPresentCondition() {}
+
+ @Override
+ public ConditionOutcome getMatchOutcome(
+ ConditionContext context, AnnotatedTypeMetadata metadata) {
+ BindResult> property = Binder.get(context.getEnvironment()).bind(KEY, WORKER_PROPERTIES_LIST);
+ ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition("Present Workers");
+ if (property.isBound()) {
+ return ConditionOutcome.match(messageBuilder.found("property").items(KEY));
+ }
+ return ConditionOutcome.noMatch(messageBuilder.didNotFind("property").items(KEY));
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/ClientProperties.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/ClientProperties.java
new file mode 100644
index 0000000000..86a660106f
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/ClientProperties.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure.properties;
+
+import com.google.common.base.MoreObjects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.springframework.boot.context.properties.ConstructorBinding;
+
+@ConstructorBinding
+public class ClientProperties {
+ public static final String TARGET_IN_PROCESS_TEST_SERVER = "inprocess";
+
+ public static final String TARGET_LOCAL_SERVICE = "local";
+
+ @Nonnull private final String target;
+ @Nonnull private final String namespace;
+
+ public ClientProperties(@Nonnull String target, @Nullable String namespace) {
+ this.target = target;
+ this.namespace = MoreObjects.firstNonNull(namespace, "default");
+ }
+
+ /**
+ * @see io.temporal.serviceclient.WorkflowServiceStubsOptions.Builder#setTarget(String)
+ */
+ @Nonnull
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * @see io.temporal.client.WorkflowClientOptions.Builder#setNamespace(String)
+ */
+ @Nonnull
+ public String getNamespace() {
+ return namespace;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TemporalProperties.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TemporalProperties.java
new file mode 100644
index 0000000000..f28572d4da
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TemporalProperties.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure.properties;
+
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.ConstructorBinding;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+
+import javax.annotation.Nullable;
+
+@ConfigurationProperties(prefix = "spring.temporal")
+@ConstructorBinding
+public class TemporalProperties {
+ @NestedConfigurationProperty private final TestServerProperties testServer;
+ @NestedConfigurationProperty private final WorkersAutoDiscoveryProperties workersAutoDiscovery;
+ @NestedConfigurationProperty private final ClientProperties client;
+ private final List workers;
+ private final @Nullable Boolean startWorkers;
+
+ public TemporalProperties(
+ TestServerProperties testServer,
+ WorkersAutoDiscoveryProperties workersAutoDiscovery,
+ ClientProperties client,
+ List workers,
+ @Nullable Boolean startWorkers) {
+ this.testServer = testServer;
+ this.workersAutoDiscovery = workersAutoDiscovery;
+ this.client = client;
+ this.workers = workers;
+ this.startWorkers = startWorkers;
+ }
+
+ public TestServerProperties getTestServer() {
+ return testServer;
+ }
+
+ public WorkersAutoDiscoveryProperties getWorkersAutoDiscovery() {
+ return workersAutoDiscovery;
+ }
+
+ public ClientProperties getClient() {
+ return client;
+ }
+
+ public List getWorkers() {
+ return workers;
+ }
+
+ @Nullable
+ public Boolean getStartWorkers() {
+ return startWorkers;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TestServerProperties.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TestServerProperties.java
new file mode 100644
index 0000000000..10aa920639
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/TestServerProperties.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure.properties;
+
+import org.springframework.boot.context.properties.ConstructorBinding;
+
+@ConstructorBinding
+public class TestServerProperties {
+ private final boolean enabled;
+
+ public TestServerProperties(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkerProperties.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkerProperties.java
new file mode 100644
index 0000000000..0b55a95b1a
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkerProperties.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure.properties;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.springframework.boot.context.properties.ConstructorBinding;
+
+@ConstructorBinding
+public class WorkerProperties {
+ private @Nonnull String taskQueue;
+
+ private @Nullable Collection> workflowClasses;
+
+ private @Nullable Collection activityBeans;
+
+ public WorkerProperties(
+ @Nonnull String taskQueue,
+ @Nullable Collection> workflowClasses,
+ @Nullable Collection activityBeans) {
+ this.taskQueue = taskQueue;
+ this.workflowClasses = workflowClasses;
+ this.activityBeans = activityBeans;
+ }
+
+ @Nonnull
+ public String getTaskQueue() {
+ return taskQueue;
+ }
+
+ public void setTaskQueue(String taskQueue) {
+ this.taskQueue = taskQueue;
+ }
+
+ @Nullable
+ public Collection> getWorkflowClasses() {
+ return workflowClasses;
+ }
+
+ public void setWorkflowClasses(Collection> workflowClasses) {
+ this.workflowClasses = workflowClasses;
+ }
+
+ @Nullable
+ public Collection getActivityBeans() {
+ return activityBeans;
+ }
+
+ public void setActivityBeans(Collection activityBeans) {
+ this.activityBeans = activityBeans;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkersAutoDiscoveryProperties.java b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkersAutoDiscoveryProperties.java
new file mode 100644
index 0000000000..ea525ddd55
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/java/io/temporal/spring/boot/autoconfigure/properties/WorkersAutoDiscoveryProperties.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure.properties;
+
+import java.util.List;
+import org.springframework.boot.context.properties.ConstructorBinding;
+
+@ConstructorBinding
+public class WorkersAutoDiscoveryProperties {
+ private final List packages;
+
+ public WorkersAutoDiscoveryProperties(List packages) {
+ this.packages = packages;
+ }
+
+ public List getPackages() {
+ return packages;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/main/resources/META-INF/spring.factories b/temporal-spring-boot-autoconfigure-alpha/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000000..2ede87e67d
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+io.temporal.spring.boot.autoconfigure.TestServerAutoConfiguration,\
+io.temporal.spring.boot.autoconfigure.ClientAutoConfiguration,\
+io.temporal.spring.boot.autoconfigure.WorkersAutoConfiguration,\
+io.temporal.spring.boot.autoconfigure.WorkersAutoDiscoveryAutoConfiguration
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryTest.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryTest.java
new file mode 100644
index 0000000000..0698b4ab89
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowOptions;
+import io.temporal.testing.TestWorkflowEnvironment;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(classes = AutoDiscoveryTest.Configuration.class)
+@ActiveProfiles(profiles = "auto-discovery")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class AutoDiscoveryTest {
+ @Autowired TestWorkflowEnvironment testWorkflowEnvironment;
+
+ @Autowired WorkflowClient workflowClient;
+
+ @Test
+ public void testAutoDiscovery() {
+ TestWorkflow testWorkflow =
+ workflowClient.newWorkflowStub(
+ TestWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue("UnitTest").build());
+ testWorkflow.execute("input");
+ }
+
+ @EnableAutoConfiguration
+ @ComponentScan
+ public static class Configuration {}
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/CustomClientConfigTest.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/CustomClientConfigTest.java
new file mode 100644
index 0000000000..5ef347d1ac
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/CustomClientConfigTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.temporal.client.WorkflowClient;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(classes = CustomClientConfigTest.Configuration.class)
+@ActiveProfiles(profiles = "custom-namespace")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class CustomClientConfigTest {
+ @Autowired protected WorkflowClient workflowClient;
+
+ @Test
+ public void shouldCustomizeNamespace() {
+ assertEquals("custom", workflowClient.getOptions().getNamespace());
+ }
+
+ @EnableAutoConfiguration
+ public static class Configuration {}
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/ExplicitConfigTest.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/ExplicitConfigTest.java
new file mode 100644
index 0000000000..aadce6f235
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/ExplicitConfigTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.client.WorkflowClient;
+import io.temporal.client.WorkflowOptions;
+import io.temporal.testing.TestWorkflowEnvironment;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(classes = ExplicitConfigTest.Configuration.class)
+@ActiveProfiles(profiles = "explicit-config")
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class ExplicitConfigTest {
+ @Autowired TestWorkflowEnvironment testWorkflowEnvironment;
+
+ @Autowired WorkflowClient workflowClient;
+
+ @Test
+ public void testExplicitConfig() {
+ TestWorkflow testWorkflow =
+ workflowClient.newWorkflowStub(
+ TestWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue("UnitTest").build());
+ testWorkflow.execute("input");
+ }
+
+ @EnableAutoConfiguration
+ @ComponentScan
+ public static class Configuration {}
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivity.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivity.java
new file mode 100644
index 0000000000..2aa4e17fce
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.activity.ActivityInterface;
+
+@ActivityInterface
+public interface TestActivity {
+ String execute(String input);
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivityImpl.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivityImpl.java
new file mode 100644
index 0000000000..da85595c72
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestActivityImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.spring.boot.ActivityImpl;
+import org.springframework.stereotype.Component;
+
+@Component("TestActivityImpl")
+@ActivityImpl(taskQueues = "UnitTest")
+public class TestActivityImpl implements TestActivity {
+ @Override
+ public String execute(String input) {
+ return input;
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflow.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflow.java
new file mode 100644
index 0000000000..7741d7619e
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflow.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.workflow.WorkflowInterface;
+import io.temporal.workflow.WorkflowMethod;
+
+@WorkflowInterface
+public interface TestWorkflow {
+
+ @WorkflowMethod(name = "testWorkflow1")
+ String execute(String input);
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflowImpl.java b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflowImpl.java
new file mode 100644
index 0000000000..4cfbd637cd
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/java/io/temporal/spring/boot/autoconfigure/TestWorkflowImpl.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.spring.boot.autoconfigure;
+
+import io.temporal.activity.ActivityOptions;
+import io.temporal.spring.boot.WorkflowImpl;
+import io.temporal.workflow.Workflow;
+import java.time.Duration;
+
+@WorkflowImpl(taskQueues = "UnitTest")
+public class TestWorkflowImpl implements TestWorkflow {
+ @Override
+ public String execute(String input) {
+ return Workflow.newActivityStub(
+ TestActivity.class,
+ ActivityOptions.newBuilder()
+ .setStartToCloseTimeout(Duration.ofSeconds(1))
+ .validateAndBuildWithDefaults())
+ .execute("done");
+ }
+}
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application-custom-namespace.yml b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application-custom-namespace.yml
new file mode 100644
index 0000000000..66f5616693
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application-custom-namespace.yml
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+#
+# Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Modifications copyright (C) 2017 Uber Technologies, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this material except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+spring.temporal:
+ startWorkers: false
+ testServer:
+ enabled: false
+ client:
+ target: local
+ namespace: custom
+
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application.yml b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application.yml
new file mode 100644
index 0000000000..9f073eca23
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/application.yml
@@ -0,0 +1,50 @@
+#
+# Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+#
+# Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Modifications copyright (C) 2017 Uber Technologies, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this material except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+spring.temporal:
+ testServer:
+ enabled: true
+ client:
+ target: inprocess
+ workers:
+ - taskQueue: UnitTest
+
+---
+spring:
+ config:
+ activate:
+ on-profile: auto-discovery
+ temporal:
+ workersAutoDiscovery:
+ packages:
+ - io.temporal
+
+---
+spring:
+ config:
+ activate:
+ on-profile: explicit-config
+ temporal:
+ workers:
+ - taskQueue: UnitTest
+ workflowClasses:
+ - io.temporal.spring.boot.autoconfigure.TestWorkflowImpl
+ activityBeans:
+ - TestActivityImpl
diff --git a/temporal-spring-boot-autoconfigure-alpha/src/test/resources/logback-test.xml b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..9fd1fd8fd1
--- /dev/null
+++ b/temporal-spring-boot-autoconfigure-alpha/src/test/resources/logback-test.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/temporal-spring-boot-starter-alpha/build.gradle b/temporal-spring-boot-starter-alpha/build.gradle
new file mode 100644
index 0000000000..8a57f4d23e
--- /dev/null
+++ b/temporal-spring-boot-starter-alpha/build.gradle
@@ -0,0 +1,6 @@
+description = '''Spring Boot Starter for Temporal Java SDK'''
+
+dependencies {
+ implementation project(':temporal-sdk')
+ implementation project(':temporal-spring-boot-autoconfigure-alpha')
+}
\ No newline at end of file
diff --git a/temporal-test-server/build.gradle b/temporal-test-server/build.gradle
index 0de3930ce4..5dc0ed9982 100644
--- a/temporal-test-server/build.gradle
+++ b/temporal-test-server/build.gradle
@@ -22,8 +22,8 @@ dependencies {
api project(':temporal-sdk')
implementation("io.grpc:grpc-core")
implementation("io.grpc:grpc-services")
- if (!JavaVersion.current().isJava8()) {
- //needed for the generated grpc stubs
+ if (JavaVersion.current().isJava9Compatible()) {
+ //needed for the generated grpc stubs and is not a part of JDK since java 9
implementation "javax.annotation:javax.annotation-api:$annotationApiVersion"
}