Skip to content

Commit 7cd5617

Browse files
authored
Fix for NPE when method has base type return type like int. (#244)
* Fixed workflow start through WorkflowInvocationHandler when return type is a base type * Fixed return of the dynamic proxies for activities, child workflows, external workflows and continue as new
1 parent 5750259 commit 7cd5617

File tree

7 files changed

+60
-35
lines changed

7 files changed

+60
-35
lines changed

src/main/java/com/uber/cadence/internal/common/InternalUtils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.uber.cadence.internal.common;
1919

20+
import com.google.common.base.Defaults;
2021
import com.uber.cadence.TaskList;
2122
import com.uber.cadence.TaskListKind;
2223
import com.uber.cadence.internal.worker.Shutdownable;
@@ -118,6 +119,13 @@ public static long awaitTermination(long timeoutMillis, Runnable toTerminate) {
118119
return remainingTimeout;
119120
}
120121

122+
public static Object getValueOrDefault(Object value, Class<?> valueClass) {
123+
if (value != null) {
124+
return value;
125+
}
126+
return Defaults.defaultValue(valueClass);
127+
}
128+
121129
/** Prohibit instantiation */
122130
private InternalUtils() {}
123131
}

src/main/java/com/uber/cadence/internal/sync/ActivityInvocationHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package com.uber.cadence.internal.sync;
1919

20+
import static com.uber.cadence.internal.common.InternalUtils.getValueOrDefault;
21+
2022
import com.uber.cadence.activity.ActivityMethod;
2123
import com.uber.cadence.activity.ActivityOptions;
2224
import com.uber.cadence.common.MethodRetry;
@@ -91,6 +93,6 @@ public Object invoke(Object proxy, Method method, Object[] args) {
9193
throw Workflow.wrap(e);
9294
}
9395
}
94-
return function.apply(args);
96+
return getValueOrDefault(function.apply(args), method.getReturnType());
9597
}
9698
}

src/main/java/com/uber/cadence/internal/sync/ChildWorkflowInvocationHandler.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.uber.cadence.internal.sync;
1919

20+
import static com.uber.cadence.internal.common.InternalUtils.getValueOrDefault;
2021
import static com.uber.cadence.internal.common.InternalUtils.getWorkflowMethod;
2122
import static com.uber.cadence.internal.common.InternalUtils.getWorkflowType;
2223

@@ -70,7 +71,9 @@ public Object invoke(Object proxy, Method method, Object[] args) {
7071
+ "from @WorkflowMethod, @QueryMethod or @SignalMethod");
7172
}
7273
if (workflowMethod != null) {
73-
return stub.execute(method.getReturnType(), method.getGenericReturnType(), args);
74+
return getValueOrDefault(
75+
stub.execute(method.getReturnType(), method.getGenericReturnType(), args),
76+
method.getReturnType());
7477
}
7578
if (queryMethod != null) {
7679
throw new UnsupportedOperationException(

src/main/java/com/uber/cadence/internal/sync/ContinueAsNewWorkflowInvocationHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package com.uber.cadence.internal.sync;
1919

20+
import static com.uber.cadence.internal.common.InternalUtils.getValueOrDefault;
21+
2022
import com.uber.cadence.internal.common.InternalUtils;
2123
import com.uber.cadence.workflow.ContinueAsNewOptions;
2224
import com.uber.cadence.workflow.QueryMethod;
@@ -60,6 +62,6 @@ public Object invoke(Object proxy, Method method, Object[] args) {
6062
String workflowType = InternalUtils.getWorkflowType(method, workflowMethod);
6163
WorkflowInternal.continueAsNew(
6264
Optional.of(workflowType), Optional.of(options), args, decisionContext);
63-
return null;
65+
return getValueOrDefault(null, method.getReturnType());
6466
}
6567
}

src/main/java/com/uber/cadence/internal/sync/ExternalWorkflowInvocationHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package com.uber.cadence.internal.sync;
1919

20+
import static com.uber.cadence.internal.common.InternalUtils.getValueOrDefault;
21+
2022
import com.uber.cadence.WorkflowExecution;
2123
import com.uber.cadence.internal.common.InternalUtils;
2224
import com.uber.cadence.workflow.ExternalWorkflowStub;
@@ -62,7 +64,7 @@ public Object invoke(Object proxy, Method method, Object[] args) {
6264
+ "created through Workflow.newExternalWorkflowStub");
6365
}
6466
if (queryMethod != null) {
65-
return queryWorkflow(method, queryMethod, args);
67+
return getValueOrDefault(queryWorkflow(method, queryMethod, args), method.getReturnType());
6668
}
6769
if (signalMethod != null) {
6870
signalWorkflow(method, signalMethod, args);

src/main/java/com/uber/cadence/internal/sync/WorkflowInvocationHandler.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.uber.cadence.internal.sync;
1919

20+
import static com.uber.cadence.internal.common.InternalUtils.getValueOrDefault;
2021
import static com.uber.cadence.internal.common.InternalUtils.getWorkflowMethod;
2122
import static com.uber.cadence.internal.common.InternalUtils.getWorkflowType;
2223

@@ -179,18 +180,20 @@ public Object invoke(Object proxy, Method method, Object[] args) {
179180
+ " must contain at most one annotation "
180181
+ "from @WorkflowMethod, @QueryMethod or @SignalMethod");
181182
}
183+
Object result;
182184
if (workflowMethod != null) {
183-
return startWorkflow(method, args);
184-
}
185-
if (queryMethod != null) {
186-
return queryWorkflow(method, queryMethod, args);
187-
}
188-
if (signalMethod != null) {
185+
result = startWorkflow(method, args);
186+
} else if (queryMethod != null) {
187+
result = queryWorkflow(method, queryMethod, args);
188+
} else if (signalMethod != null) {
189189
signalWorkflow(method, signalMethod, args);
190-
return null;
190+
result = null;
191+
} else {
192+
throw new IllegalArgumentException(
193+
method + " is not annotated with @WorkflowMethod or @QueryMethod");
191194
}
192-
throw new IllegalArgumentException(
193-
method + " is not annotated with @WorkflowMethod or @QueryMethod");
195+
// Returning null for a built in type leads to NullPointerException
196+
return getValueOrDefault(result, method.getReturnType());
194197
}
195198

196199
private void signalWorkflow(Method method, SignalMethod signalMethod, Object[] args) {

src/test/java/com/uber/cadence/workflow/WorkflowTest.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -866,8 +866,7 @@ public String execute(String taskList) {
866866
testActivities.activityWithDelay(100000, true);
867867
fail("unreachable");
868868
} catch (CancellationException e) {
869-
Workflow.newDetachedCancellationScope(
870-
() -> assertEquals("a1", testActivities.activity1("a1")));
869+
Workflow.newDetachedCancellationScope(() -> assertEquals(1, testActivities.activity1(1)));
871870
}
872871
try {
873872
Workflow.sleep(Duration.ofHours(1));
@@ -954,14 +953,14 @@ public String execute(String taskList) {
954953
TestActivities testActivities =
955954
Workflow.newActivityStub(TestActivities.class, newActivityOptions2());
956955
Promise<String> a = Async.function(testActivities::activity);
957-
Promise<String> a1 = Async.function(testActivities::activity1, "1");
956+
Promise<Integer> a1 = Async.function(testActivities::activity1, 1);
958957
Promise<String> a2 = Async.function(testActivities::activity2, "1", 2);
959958
Promise<String> a3 = Async.function(testActivities::activity3, "1", 2, 3);
960959
Promise<String> a4 = Async.function(testActivities::activity4, "1", 2, 3, 4);
961960
Promise<String> a5 = Async.function(testActivities::activity5, "1", 2, 3, 4, 5);
962961
Promise<String> a6 = Async.function(testActivities::activity6, "1", 2, 3, 4, 5, 6);
963962
assertEquals("activity", a.get());
964-
assertEquals("1", a1.get());
963+
assertEquals(1, (int) a1.get());
965964
assertEquals("12", a2.get());
966965
assertEquals("123", a3.get());
967966
assertEquals("1234", a4.get());
@@ -1133,6 +1132,12 @@ private void assertResult(String expected, WorkflowExecution execution) {
11331132
assertEquals(expected, result);
11341133
}
11351134

1135+
private void assertResult(int expected, WorkflowExecution execution) {
1136+
int result =
1137+
workflowClient.newUntypedWorkflowStub(execution, Optional.empty()).getResult(int.class);
1138+
assertEquals(expected, result);
1139+
}
1140+
11361141
private void waitForProc(WorkflowExecution execution) {
11371142
workflowClient.newUntypedWorkflowStub(execution, Optional.empty()).getResult(Void.class);
11381143
}
@@ -1150,8 +1155,8 @@ public void testStart() {
11501155

11511156
if (!useExternalService) {
11521157
// Use worker that polls on a task list configured through @WorkflowMethod annotation of func1
1153-
assertResult("1", WorkflowClient.start(stubF1::func1, "1"));
1154-
assertEquals("1", stubF1.func1("1")); // Check that duplicated start just returns the result.
1158+
assertResult(1, WorkflowClient.start(stubF1::func1, 1));
1159+
assertEquals(1, stubF1.func1(1)); // Check that duplicated start just returns the result.
11551160
}
11561161
// Check that duplicated start is not allowed for AllowDuplicate IdReusePolicy
11571162
TestMultiargsWorkflowsFunc2 stubF2 =
@@ -1227,8 +1232,8 @@ public void testExecute() throws ExecutionException, InterruptedException {
12271232
assertEquals("func", WorkflowClient.execute(stubF::func).get());
12281233
TestMultiargsWorkflowsFunc1 stubF1 =
12291234
workflowClient.newWorkflowStub(TestMultiargsWorkflowsFunc1.class, workflowOptions);
1230-
assertEquals("1", WorkflowClient.execute(stubF1::func1, "1").get());
1231-
assertEquals("1", stubF1.func1("1")); // Check that duplicated start just returns the result.
1235+
assertEquals(1, (int) WorkflowClient.execute(stubF1::func1, 1).get());
1236+
assertEquals(1, stubF1.func1(1)); // Check that duplicated start just returns the result.
12321237
TestMultiargsWorkflowsFunc2 stubF2 =
12331238
workflowClient.newWorkflowStub(TestMultiargsWorkflowsFunc2.class, workflowOptions);
12341239
assertEquals("12", WorkflowClient.execute(stubF2::func2, "1", 2).get());
@@ -1294,7 +1299,7 @@ public String execute(String taskList) {
12941299
assertEquals("func", Async.function(stubF::func).get());
12951300
TestMultiargsWorkflowsFunc1 stubF1 =
12961301
Workflow.newChildWorkflowStub(TestMultiargsWorkflowsFunc1.class, workflowOptions);
1297-
assertEquals("1", Async.function(stubF1::func1, "1").get());
1302+
assertEquals(1, (int) Async.function(stubF1::func1, 1).get());
12981303
TestMultiargsWorkflowsFunc2 stubF2 =
12991304
Workflow.newChildWorkflowStub(TestMultiargsWorkflowsFunc2.class, workflowOptions);
13001305
assertEquals("12", Async.function(stubF2::func2, "1", 2).get());
@@ -2889,7 +2894,7 @@ public interface TestActivities {
28892894
String activity();
28902895

28912896
@ActivityMethod(name = "customActivity1")
2892-
String activity1(String input);
2897+
int activity1(int input);
28932898

28942899
String activity2(String a1, int a2);
28952900

@@ -2994,7 +2999,7 @@ public String activity() {
29942999
}
29953000

29963001
@Override
2997-
public String activity1(String a1) {
3002+
public int activity1(int a1) {
29983003
invocations.add("activity1");
29993004
return a1;
30003005
}
@@ -3150,7 +3155,7 @@ public interface TestMultiargsWorkflowsFunc1 {
31503155
workflowIdReusePolicy = WorkflowIdReusePolicy.RejectDuplicate,
31513156
executionStartToCloseTimeoutSeconds = 10
31523157
)
3153-
String func1(String input);
3158+
int func1(int input);
31543159
}
31553160

31563161
public interface TestMultiargsWorkflowsFunc2 {
@@ -3249,7 +3254,7 @@ public String func() {
32493254
}
32503255

32513256
@Override
3252-
public String func1(String a1) {
3257+
public int func1(int a1) {
32533258
return a1;
32543259
}
32553260

@@ -3375,7 +3380,7 @@ public String execute(String taskList) {
33753380
Workflow.sleep(Duration.ofSeconds(1));
33763381
String result;
33773382
if (workflowTime == time) {
3378-
result = testActivities.activity1("activity1");
3383+
result = "activity" + testActivities.activity1(1);
33793384
} else {
33803385
result = testActivities.activity2("activity2", 2);
33813386
}
@@ -3452,27 +3457,27 @@ public String execute(String taskList) {
34523457
int version = Workflow.getVersion("test_change", Workflow.DEFAULT_VERSION, 1);
34533458
String result = "";
34543459
if (version == Workflow.DEFAULT_VERSION) {
3455-
result += testActivities.activity1("activity1");
3460+
result += "activity" + testActivities.activity1(1);
34563461
} else {
34573462
result += testActivities.activity2("activity2", 2); // This is executed.
34583463
}
34593464

34603465
// Test version change in non-replay code.
34613466
version = Workflow.getVersion("test_change", 1, 2);
34623467
if (version == 1) {
3463-
result += testActivities.activity1("activity1"); // This is executed.
3468+
result += "activity" + testActivities.activity1(1); // This is executed.
34643469
} else {
34653470
result += testActivities.activity2("activity2", 2);
34663471
}
34673472

34683473
// Test adding a version check in replay code.
34693474
if (!getVersionExecuted.contains(taskList + "-test_change_2")) {
3470-
result += testActivities.activity1("activity1"); // This is executed in non-replay mode.
3475+
result += "activity" + testActivities.activity1(1); // This is executed in non-replay mode.
34713476
getVersionExecuted.add(taskList + "-test_change_2");
34723477
} else {
34733478
int version2 = Workflow.getVersion("test_change_2", Workflow.DEFAULT_VERSION, 1);
34743479
if (version2 == Workflow.DEFAULT_VERSION) {
3475-
result += testActivities.activity1("activity1"); // This is executed in replay mode.
3480+
result += "activity" + testActivities.activity1(1); // This is executed in replay mode.
34763481
} else {
34773482
result += testActivities.activity2("activity2", 2);
34783483
}
@@ -3482,7 +3487,7 @@ public String execute(String taskList) {
34823487
Workflow.sleep(1000);
34833488
version = Workflow.getVersion("test_change", 1, 2);
34843489
if (version == 1) {
3485-
result += testActivities.activity1("activity1"); // This is executed.
3490+
result += "activity" + testActivities.activity1(1); // This is executed.
34863491
} else {
34873492
result += testActivities.activity2("activity2", 2);
34883493
}
@@ -3523,7 +3528,7 @@ public String execute(String taskList) {
35233528
if (!getVersionExecuted.contains(taskList)) {
35243529
int version = Workflow.getVersion("test_change", Workflow.DEFAULT_VERSION, 1);
35253530
if (version == Workflow.DEFAULT_VERSION) {
3526-
result = testActivities.activity1("activity1");
3531+
result = "activity" + testActivities.activity1(1);
35273532
} else {
35283533
result = testActivities.activity2("activity2", 2); // This is executed in non-replay mode.
35293534
}
@@ -3594,7 +3599,7 @@ public String execute(String taskList) {
35943599
int version = Workflow.getVersion("test_change", Workflow.DEFAULT_VERSION, 1);
35953600
String result = "";
35963601
if (version == Workflow.DEFAULT_VERSION) {
3597-
result += testActivities.activity1("activity1");
3602+
result += "activity" + testActivities.activity1(1);
35983603
} else {
35993604
result += testActivities.activity2("activity2", 2); // This is executed.
36003605
}
@@ -3638,7 +3643,7 @@ public void execute(String taskList) {
36383643
TestActivities activities =
36393644
Workflow.newActivityStub(TestActivities.class, newActivityOptions1(taskList));
36403645
if (!Workflow.isReplaying()) {
3641-
activities.activity1("foo");
3646+
activities.activity1(1);
36423647
}
36433648
}
36443649
}

0 commit comments

Comments
 (0)