From 0e534034f7a2207597c02e8ad614de3ce00fe88e Mon Sep 17 00:00:00 2001 From: Shijie Sheng Date: Mon, 29 Sep 2025 14:30:01 -0700 Subject: [PATCH 1/3] fix: fail open on exception not found in default data converter Signed-off-by: Shijie Sheng --- .../converter/CustomThrowableTypeAdapter.java | 2 +- .../converter/JsonDataConverterTest.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java index 86cca6939..85bdc2d46 100644 --- a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java +++ b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java @@ -137,7 +137,7 @@ public T read(JsonReader jsonReader) throws IOException { try { classType = Class.forName(className); } catch (ClassNotFoundException e) { - throw new IOException("Cannot deserialize " + className + " exception", e); + return null; } if (!Throwable.class.isAssignableFrom(classType)) { throw new IOException("Expected type that extends Throwable: " + className); diff --git a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java index 57862ddc1..ee18f4cbc 100644 --- a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java +++ b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java @@ -270,4 +270,21 @@ public void testException() { assertEquals("root exception", causeFromConverted.getSuppressed()[0].getMessage()); } + + @Test + public void testExceptionNotFound() { + String convertedString = + "{\n" + + " \"detailMessage\": \"application exception\",\n" + + " \"stackTrace\": \"com.uber.cadence.converter.JsonDataConverterTest.testExceptionNotFound(JsonDataConverterTest.java:282)\\nsun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\\nsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\\nsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\njava.lang.reflect.Method.invoke(Method.java:498)\\norg.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)\\norg.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)\\norg.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)\\norg.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)\\norg.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)\\norg.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)\\norg.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)\\norg.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)\\norg.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)\\norg.junit.runners.ParentRunner$4.run(ParentRunner.java:331)\\norg.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)\\norg.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)\\norg.junit.runners.ParentRunner.access$100(ParentRunner.java:66)\\norg.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)\\norg.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)\\norg.junit.runners.ParentRunner.run(ParentRunner.java:413)\\norg.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93)\\norg.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)\\norg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:520)\\norg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:748)\\norg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:443)\\norg.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:211)\\n\",\n" + + " \"suppressedExceptions\": [],\n" + + " \"class\": \"com.uber.cadence.converter.ExceptionNotFound\"\n" + + "}"; + RuntimeException fromConverted = + converter.fromData( + convertedString.getBytes(StandardCharsets.UTF_8), + RuntimeException.class, + RuntimeException.class); + assertNull(fromConverted); + } } From 700479d543419f5a1ab2d07515d2aba5bd3e163c Mon Sep 17 00:00:00 2001 From: Shijie Sheng Date: Wed, 1 Oct 2025 17:02:21 -0700 Subject: [PATCH 2/3] add applicationfailureexception class and let data converter to fallback on this exception instead Signed-off-by: Shijie Sheng --- .../client/ApplicationFailureException.java | 43 +++++++++++++++++++ .../converter/CustomThrowableTypeAdapter.java | 3 +- .../converter/JsonDataConverterTest.java | 6 ++- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/uber/cadence/client/ApplicationFailureException.java diff --git a/src/main/java/com/uber/cadence/client/ApplicationFailureException.java b/src/main/java/com/uber/cadence/client/ApplicationFailureException.java new file mode 100644 index 000000000..b7967054f --- /dev/null +++ b/src/main/java/com/uber/cadence/client/ApplicationFailureException.java @@ -0,0 +1,43 @@ +/** + * Copyright 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 file + * except in compliance with the License. A copy of the License is located at + * + *

http://aws.amazon.com/apache2.0 + * + *

or in the "license" file accompanying this file. This file 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 com.uber.cadence.client; + +import com.uber.cadence.CadenceError; +import java.util.Objects; + +public final class ApplicationFailureException extends CadenceError { + + public ApplicationFailureException(String message) { + super(message); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ApplicationFailureException that = (ApplicationFailureException) obj; + return Objects.equals(getMessage(), that.getMessage()) + && Objects.equals(getCause(), that.getCause()); + } + + @Override + public int hashCode() { + return Objects.hash(getMessage(), getCause()); + } +} diff --git a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java index 85bdc2d46..fc8bb47b0 100644 --- a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java +++ b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java @@ -22,6 +22,7 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import com.uber.cadence.client.ApplicationFailureException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -137,7 +138,7 @@ public T read(JsonReader jsonReader) throws IOException { try { classType = Class.forName(className); } catch (ClassNotFoundException e) { - return null; + return (T) new ApplicationFailureException("Class not found: " + className); } if (!Throwable.class.isAssignableFrom(classType)) { throw new IOException("Expected type that extends Throwable: " + className); diff --git a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java index ee18f4cbc..adae1bb2f 100644 --- a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java +++ b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java @@ -27,6 +27,7 @@ import com.uber.cadence.WorkflowExecutionStartedEventAttributes; import com.uber.cadence.WorkflowType; import com.uber.cadence.activity.Activity; +import com.uber.cadence.client.ApplicationFailureException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -285,6 +286,9 @@ public void testExceptionNotFound() { convertedString.getBytes(StandardCharsets.UTF_8), RuntimeException.class, RuntimeException.class); - assertNull(fromConverted); + assertEquals(ApplicationFailureException.class, fromConverted.getClass()); + assertEquals( + "Class not found: com.uber.cadence.converter.ExceptionNotFound", + fromConverted.getMessage()); } } From 0705365844a8ae7b5f3c7c4c1a81f90d708a6016 Mon Sep 17 00:00:00 2001 From: Shijie Sheng Date: Thu, 2 Oct 2025 14:46:40 -0700 Subject: [PATCH 3/3] address comment Signed-off-by: Shijie Sheng --- .../client/ApplicationFailureException.java | 19 ------------------- .../converter/CustomThrowableTypeAdapter.java | 2 +- .../converter/JsonDataConverterTest.java | 5 ++--- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/uber/cadence/client/ApplicationFailureException.java b/src/main/java/com/uber/cadence/client/ApplicationFailureException.java index b7967054f..cb2c45e23 100644 --- a/src/main/java/com/uber/cadence/client/ApplicationFailureException.java +++ b/src/main/java/com/uber/cadence/client/ApplicationFailureException.java @@ -15,29 +15,10 @@ package com.uber.cadence.client; import com.uber.cadence.CadenceError; -import java.util.Objects; public final class ApplicationFailureException extends CadenceError { public ApplicationFailureException(String message) { super(message); } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ApplicationFailureException that = (ApplicationFailureException) obj; - return Objects.equals(getMessage(), that.getMessage()) - && Objects.equals(getCause(), that.getCause()); - } - - @Override - public int hashCode() { - return Objects.hash(getMessage(), getCause()); - } } diff --git a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java index fc8bb47b0..98ca4ca2b 100644 --- a/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java +++ b/src/main/java/com/uber/cadence/converter/CustomThrowableTypeAdapter.java @@ -138,7 +138,7 @@ public T read(JsonReader jsonReader) throws IOException { try { classType = Class.forName(className); } catch (ClassNotFoundException e) { - return (T) new ApplicationFailureException("Class not found: " + className); + classType = ApplicationFailureException.class; } if (!Throwable.class.isAssignableFrom(classType)) { throw new IOException("Expected type that extends Throwable: " + className); diff --git a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java index adae1bb2f..83eeccfc6 100644 --- a/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java +++ b/src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java @@ -287,8 +287,7 @@ public void testExceptionNotFound() { RuntimeException.class, RuntimeException.class); assertEquals(ApplicationFailureException.class, fromConverted.getClass()); - assertEquals( - "Class not found: com.uber.cadence.converter.ExceptionNotFound", - fromConverted.getMessage()); + assertEquals("application exception", fromConverted.getMessage()); + assertNotSame(fromConverted.getStackTrace().length, 0); } }