Skip to content

Commit 2e1c89e

Browse files
Add support for dynamics workflows to Springboot (#2506)
Add support for dynamics workflows to Springboot
1 parent 1feb16f commit 2e1c89e

File tree

3 files changed

+69
-7
lines changed

3 files changed

+69
-7
lines changed

temporal-spring-boot-autoconfigure/src/main/java/io/temporal/spring/boot/autoconfigure/template/WorkersTemplate.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package io.temporal.spring.boot.autoconfigure.template;
22

3+
import static io.temporal.serviceclient.CheckedExceptionWrapper.wrap;
4+
35
import com.google.common.base.Preconditions;
46
import io.nexusrpc.ServiceDefinition;
57
import io.nexusrpc.handler.ServiceImplInstance;
68
import io.opentracing.Tracer;
79
import io.temporal.client.WorkflowClient;
810
import io.temporal.common.Experimental;
11+
import io.temporal.common.converter.EncodedValues;
912
import io.temporal.common.metadata.POJOActivityImplMetadata;
1013
import io.temporal.common.metadata.POJOWorkflowImplMetadata;
1114
import io.temporal.common.metadata.POJOWorkflowMethodMetadata;
15+
import io.temporal.internal.common.env.ReflectionUtils;
1216
import io.temporal.internal.sync.POJOWorkflowImplementationFactory;
1317
import io.temporal.spring.boot.ActivityImpl;
1418
import io.temporal.spring.boot.NexusServiceImpl;
@@ -17,15 +21,11 @@
1721
import io.temporal.spring.boot.autoconfigure.properties.NamespaceProperties;
1822
import io.temporal.spring.boot.autoconfigure.properties.WorkerProperties;
1923
import io.temporal.worker.*;
24+
import io.temporal.workflow.DynamicWorkflow;
2025
import java.lang.reflect.Constructor;
2126
import java.lang.reflect.InvocationTargetException;
22-
import java.util.ArrayList;
23-
import java.util.Collection;
24-
import java.util.HashMap;
25-
import java.util.HashSet;
26-
import java.util.List;
27-
import java.util.Map;
28-
import java.util.Set;
27+
import java.lang.reflect.Method;
28+
import java.util.*;
2929
import javax.annotation.Nonnull;
3030
import javax.annotation.Nullable;
3131
import org.slf4j.Logger;
@@ -492,6 +492,48 @@ private void configureWorkflowImplementationAutoDiscovery(
492492

493493
@SuppressWarnings("unchecked")
494494
private <T> void configureWorkflowImplementation(Worker worker, Class<?> clazz) {
495+
// Handle dynamic workflows
496+
if (DynamicWorkflow.class.isAssignableFrom(clazz)) {
497+
try {
498+
Method executeMethod = clazz.getMethod("execute", EncodedValues.class);
499+
Optional<Constructor<?>> ctor =
500+
ReflectionUtils.getWorkflowInitConstructor(
501+
clazz, Collections.singletonList(executeMethod));
502+
WorkflowImplementationOptions workflowImplementationOptions =
503+
new WorkflowImplementationOptionsTemplate(workflowImplementationCustomizer)
504+
.createWorkflowImplementationOptions();
505+
worker.registerWorkflowImplementationFactory(
506+
DynamicWorkflow.class,
507+
(encodedValues) -> {
508+
if (ctor.isPresent()) {
509+
try {
510+
return (DynamicWorkflow) ctor.get().newInstance(encodedValues);
511+
} catch (InstantiationException
512+
| IllegalAccessException
513+
| InvocationTargetException e) {
514+
throw wrap(e);
515+
}
516+
} else {
517+
try {
518+
return (DynamicWorkflow) clazz.getDeclaredConstructor().newInstance();
519+
} catch (NoSuchMethodException
520+
| InstantiationException
521+
| IllegalAccessException
522+
| InvocationTargetException e) {
523+
// Error to fail workflow task as this can be fixed by a new deployment.
524+
throw new Error(
525+
"Failure instantiating workflow implementation class " + clazz.getName(), e);
526+
}
527+
}
528+
},
529+
workflowImplementationOptions);
530+
return;
531+
} catch (NoSuchMethodException e) {
532+
throw new BeanDefinitionValidationException(
533+
"Dynamic workflow implementation doesn't have execute method: " + clazz, e);
534+
}
535+
}
536+
495537
POJOWorkflowImplMetadata workflowMetadata =
496538
POJOWorkflowImplMetadata.newInstanceForWorkflowFactory(clazz);
497539
List<POJOWorkflowMethodMetadata> workflowMethods = workflowMetadata.getWorkflowMethods();

temporal-spring-boot-autoconfigure/src/test/java/io/temporal/spring/boot/autoconfigure/AutoDiscoveryByWorkerNameTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.temporal.api.nexus.v1.Endpoint;
44
import io.temporal.client.WorkflowClient;
55
import io.temporal.client.WorkflowOptions;
6+
import io.temporal.client.WorkflowStub;
67
import io.temporal.spring.boot.autoconfigure.bytaskqueue.TestWorkflow;
78
import io.temporal.testing.TestWorkflowEnvironment;
89
import org.junit.jupiter.api.*;
@@ -44,6 +45,12 @@ public void testAutoDiscovery() {
4445
workflowClient.newWorkflowStub(
4546
TestWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue("UnitTest").build());
4647
testWorkflow.execute("nexus");
48+
49+
WorkflowStub dynamicStub =
50+
workflowClient.newUntypedWorkflowStub(
51+
"DynamicWorkflow", WorkflowOptions.newBuilder().setTaskQueue("UnitTest").build());
52+
dynamicStub.start();
53+
Assertions.assertEquals("hello from dynamic workflow", dynamicStub.getResult(String.class));
4754
}
4855

4956
@ComponentScan(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.temporal.spring.boot.autoconfigure.byworkername;
2+
3+
import io.temporal.common.converter.EncodedValues;
4+
import io.temporal.spring.boot.WorkflowImpl;
5+
import io.temporal.workflow.DynamicWorkflow;
6+
7+
@WorkflowImpl(workers = "mainWorker")
8+
public class TestDynamicWorkflowImpl implements DynamicWorkflow {
9+
@Override
10+
public Object execute(EncodedValues args) {
11+
return "hello from dynamic workflow";
12+
}
13+
}

0 commit comments

Comments
 (0)