Skip to content

Commit 7011bd6

Browse files
authored
Fixed JsonDataConverter to correctly report non serializable exceptions (#238)
1 parent 6f81ae9 commit 7011bd6

File tree

3 files changed

+123
-4
lines changed

3 files changed

+123
-4
lines changed

src/main/java/com/uber/cadence/converter/DataConverterException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ public DataConverterException(byte[] content, Type[] valueTypes, Throwable cause
3232
super(toMessage(null, content, valueTypes), cause);
3333
}
3434

35-
public DataConverterException(Exception cause) {
35+
public DataConverterException(Throwable cause) {
3636
super(cause);
3737
}
3838

39+
public DataConverterException(String message, Throwable cause) {
40+
super(message, cause);
41+
}
42+
3943
public DataConverterException(String message, byte[] content, Type[] valueTypes) {
4044
super(toMessage(message, content, valueTypes));
4145
}

src/main/java/com/uber/cadence/converter/JsonDataConverter.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,27 @@ public byte[] toData(Object... values) throws DataConverterException {
120120
if (value instanceof TBase) {
121121
return newThriftSerializer().toString((TBase) value).getBytes(StandardCharsets.UTF_8);
122122
}
123-
String json = gson.toJson(value);
124-
return json.getBytes(StandardCharsets.UTF_8);
123+
try {
124+
String json = gson.toJson(value);
125+
return json.getBytes(StandardCharsets.UTF_8);
126+
} catch (Throwable e) {
127+
if (value instanceof Throwable) {
128+
// Keep original exception stack trace, but replace it with a DataConverterException
129+
// which is guaranteed to be serializable.
130+
DataConverterException ee =
131+
new DataConverterException("Failure serializing exception: " + value.toString(), e);
132+
ee.setStackTrace(((Throwable) value).getStackTrace());
133+
String json = gson.toJson(ee);
134+
return json.getBytes(StandardCharsets.UTF_8);
135+
}
136+
throw e;
137+
}
125138
}
126139
String json = gson.toJson(values);
127140
return json.getBytes(StandardCharsets.UTF_8);
128-
} catch (Exception e) {
141+
} catch (DataConverterException e) {
142+
throw e;
143+
} catch (Throwable e) {
129144
throw new DataConverterException(e);
130145
}
131146
}

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

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@
5757
import com.uber.cadence.worker.WorkflowImplementationOptions;
5858
import com.uber.cadence.workflow.Functions.Func;
5959
import com.uber.cadence.workflow.Functions.Func1;
60+
import java.io.File;
61+
import java.io.FileInputStream;
6062
import java.io.FileNotFoundException;
6163
import java.io.IOException;
64+
import java.io.InputStream;
6265
import java.lang.management.ManagementFactory;
6366
import java.lang.reflect.Type;
6467
import java.time.Duration;
@@ -3851,6 +3854,103 @@ public void testGenericParametersWorkflow() throws ExecutionException, Interrupt
38513854
assertEquals(expectedResult, result);
38523855
}
38533856

3857+
public static class NonSerializableException extends RuntimeException {
3858+
private final InputStream file; // gson chokes on this field
3859+
3860+
public NonSerializableException() {
3861+
try {
3862+
file = new FileInputStream(File.createTempFile("foo", "bar"));
3863+
} catch (IOException e) {
3864+
throw Activity.wrap(e);
3865+
}
3866+
}
3867+
}
3868+
3869+
public interface NonSerializableExceptionActivity {
3870+
3871+
@ActivityMethod(scheduleToCloseTimeoutSeconds = 5)
3872+
void execute();
3873+
}
3874+
3875+
public static class NonSerializableExceptionActivityImpl
3876+
implements NonSerializableExceptionActivity {
3877+
3878+
@Override
3879+
public void execute() {
3880+
throw new NonSerializableException();
3881+
}
3882+
}
3883+
3884+
public static class TestNonSerializableExceptionInActivityWorkflow implements TestWorkflow1 {
3885+
3886+
@Override
3887+
public String execute(String taskList) {
3888+
NonSerializableExceptionActivity activity =
3889+
Workflow.newActivityStub(NonSerializableExceptionActivity.class);
3890+
try {
3891+
activity.execute();
3892+
} catch (ActivityFailureException e) {
3893+
return e.getMessage();
3894+
}
3895+
return "done";
3896+
}
3897+
}
3898+
3899+
@Test
3900+
public void testNonSerializableExceptionInActivity() {
3901+
worker.registerActivitiesImplementations(new NonSerializableExceptionActivityImpl());
3902+
startWorkerFor(TestNonSerializableExceptionInActivityWorkflow.class);
3903+
TestWorkflow1 workflowStub =
3904+
workflowClient.newWorkflowStub(
3905+
TestWorkflow1.class, newWorkflowOptionsBuilder(taskList).build());
3906+
3907+
String result = workflowStub.execute(taskList);
3908+
assertTrue(result.contains("NonSerializableException"));
3909+
}
3910+
3911+
public interface NonSerializableExceptionChildWorkflow {
3912+
3913+
@WorkflowMethod
3914+
String execute(String taskList);
3915+
}
3916+
3917+
public static class NonSerializableExceptionChildWorkflowImpl
3918+
implements NonSerializableExceptionChildWorkflow {
3919+
3920+
@Override
3921+
public String execute(String taskList) {
3922+
throw new NonSerializableException();
3923+
}
3924+
}
3925+
3926+
public static class TestNonSerializableExceptionInChildWorkflow implements TestWorkflow1 {
3927+
3928+
@Override
3929+
public String execute(String taskList) {
3930+
NonSerializableExceptionChildWorkflow child =
3931+
Workflow.newChildWorkflowStub(NonSerializableExceptionChildWorkflow.class);
3932+
try {
3933+
child.execute(taskList);
3934+
} catch (ChildWorkflowFailureException e) {
3935+
return e.getMessage();
3936+
}
3937+
return "done";
3938+
}
3939+
}
3940+
3941+
@Test
3942+
public void testNonSerializableExceptionInChildWorkflow() {
3943+
startWorkerFor(
3944+
TestNonSerializableExceptionInChildWorkflow.class,
3945+
NonSerializableExceptionChildWorkflowImpl.class);
3946+
TestWorkflow1 workflowStub =
3947+
workflowClient.newWorkflowStub(
3948+
TestWorkflow1.class, newWorkflowOptionsBuilder(taskList).build());
3949+
3950+
String result = workflowStub.execute(taskList);
3951+
assertTrue(result.contains("NonSerializableException"));
3952+
}
3953+
38543954
public interface TestLargeWorkflow {
38553955

38563956
@WorkflowMethod

0 commit comments

Comments
 (0)