Skip to content

Commit 7ab0f6c

Browse files
Refactor workflow init validation (#2316)
Refactor workflow init validation
1 parent 02ff5cd commit 7ab0f6c

File tree

4 files changed

+37
-24
lines changed

4 files changed

+37
-24
lines changed

temporal-sdk/src/main/java/io/temporal/common/metadata/POJOWorkflowImplMetadata.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,27 @@ private POJOWorkflowImplMetadata(
175175
this.queryMethods = ImmutableList.copyOf(queryMethods.values());
176176
this.updateMethods = ImmutableList.copyOf(updateMethods.values());
177177
this.updateValidatorMethods = ImmutableList.copyOf(updateValidatorMethods.values());
178-
if (!listener && validateConstructor) {
178+
if (!listener) {
179179
this.workflowInit =
180-
ReflectionUtils.getConstructor(
180+
ReflectionUtils.getWorkflowInitConstructor(
181181
implClass,
182182
this.workflowMethods.stream()
183183
.map(POJOWorkflowMethodMetadata::getWorkflowMethod)
184184
.collect(Collectors.toList()))
185185
.orElse(null);
186+
if (validateConstructor) {
187+
Constructor<?> defaultConstructor =
188+
ReflectionUtils.getPublicDefaultConstructor(implClass).orElse(null);
189+
if (defaultConstructor == null && this.workflowInit == null) {
190+
throw new IllegalArgumentException(
191+
"No default constructor or constructor annotated with @WorkflowInit found: "
192+
+ implClass.getName());
193+
} else if (defaultConstructor != null && this.workflowInit != null) {
194+
throw new IllegalArgumentException(
195+
"Found both a default constructor and constructor annotated with @WorkflowInit: "
196+
+ implClass.getName());
197+
}
198+
}
186199
} else {
187200
this.workflowInit = null;
188201
}

temporal-sdk/src/main/java/io/temporal/internal/common/env/ReflectionUtils.java

+20-20
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,26 @@
3232
public final class ReflectionUtils {
3333
private ReflectionUtils() {}
3434

35-
public static Optional<Constructor<?>> getConstructor(
35+
public static Optional<Constructor<?>> getWorkflowInitConstructor(
3636
Class<?> clazz, List<Method> workflowMethod) {
3737
// We iterate through all constructors to find the one annotated with @WorkflowInit
3838
// and check if it has the same parameters as the workflow method.
3939
// We check all declared constructors to find any constructors that are annotated with
40-
// @WorkflowInit, but not public,
41-
// to give a more informative error message.
40+
// @WorkflowInit, but are not public, to give a more informative error message.
4241
Optional<Constructor<?>> workflowInit = Optional.empty();
43-
Constructor<?> defaultConstructors = null;
4442
for (Constructor<?> ctor : clazz.getDeclaredConstructors()) {
4543
WorkflowInit wfInit = ctor.getAnnotation(WorkflowInit.class);
4644
if (wfInit == null) {
47-
if (ctor.getParameterCount() == 0 && Modifier.isPublic(ctor.getModifiers())) {
48-
if (workflowInit.isPresent() || defaultConstructors != null) {
49-
throw new IllegalArgumentException(
50-
"Multiple constructors annotated with @WorkflowInit or a default constructor found: "
51-
+ clazz.getName());
52-
}
53-
defaultConstructors = ctor;
54-
continue;
55-
}
5645
continue;
5746
}
5847
if (workflowMethod.size() != 1) {
5948
throw new IllegalArgumentException(
6049
"Multiple interfaces implemented while using @WorkflowInit annotation. Only one is allowed: "
6150
+ clazz.getName());
6251
}
63-
if (workflowInit.isPresent() || defaultConstructors != null) {
52+
if (workflowInit.isPresent()) {
6453
throw new IllegalArgumentException(
65-
"Multiple constructors annotated with @WorkflowInit or a default constructor found: "
54+
"Multiple constructors annotated with @WorkflowInit found. Only one is allowed: "
6655
+ clazz.getName());
6756
}
6857
if (!Modifier.isPublic(ctor.getModifiers())) {
@@ -76,14 +65,25 @@ public static Optional<Constructor<?>> getConstructor(
7665
}
7766
workflowInit = Optional.of(ctor);
7867
}
79-
if (!workflowInit.isPresent() && defaultConstructors == null) {
80-
throw new IllegalArgumentException(
81-
"No default constructor or constructor annotated with @WorkflowInit found: "
82-
+ clazz.getName());
83-
}
8468
return workflowInit;
8569
}
8670

71+
public static Optional<Constructor<?>> getPublicDefaultConstructor(Class<?> clazz) {
72+
Constructor<?> defaultConstructors = null;
73+
for (Constructor<?> ctor : clazz.getDeclaredConstructors()) {
74+
if (ctor.getParameterCount() != 0) {
75+
continue;
76+
}
77+
if (!Modifier.isPublic(ctor.getModifiers())) {
78+
throw new IllegalArgumentException(
79+
"Default constructor must be public: " + clazz.getName());
80+
}
81+
defaultConstructors = ctor;
82+
break;
83+
}
84+
return Optional.ofNullable(defaultConstructors);
85+
}
86+
8787
public static String getMethodNameForStackTraceCutoff(
8888
Class<?> clazz, String methodName, Class<?>... parameterTypes) throws RuntimeException {
8989
try {

temporal-sdk/src/main/java/io/temporal/internal/sync/POJOWorkflowImplementationFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ private <T> void registerWorkflowImplementationType(
191191
Method executeMethod =
192192
workflowImplementationClass.getMethod("execute", EncodedValues.class);
193193
Optional<Constructor<?>> ctor =
194-
ReflectionUtils.getConstructor(
194+
ReflectionUtils.getWorkflowInitConstructor(
195195
workflowImplementationClass, Collections.singletonList(executeMethod));
196196
dynamicWorkflowImplementationFactory =
197197
(encodedValues) -> {

temporal-sdk/src/test/java/io/temporal/common/metadata/POJOWorkflowImplMetadataTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public void testWorkflowWithMultipleInit() {
196196
assertTrue(
197197
e.getMessage()
198198
.contains(
199-
"Multiple constructors annotated with @WorkflowInit or a default constructor found:"));
199+
"Found both a default constructor and constructor annotated with @WorkflowInit"));
200200
}
201201
}
202202

0 commit comments

Comments
 (0)