Skip to content

Commit 8427ffe

Browse files
committed
move all exception helper functions to ExceptionHelper
1 parent 1ec6d6b commit 8427ffe

8 files changed

Lines changed: 91 additions & 87 deletions

File tree

sdk/src/main/java/com/amazonaws/lambda/durable/DurableExecutor.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package com.amazonaws.lambda.durable;
44

5-
import com.amazonaws.lambda.durable.exception.DurableExecutionException;
65
import com.amazonaws.lambda.durable.exception.DurableOperationException;
76
import com.amazonaws.lambda.durable.execution.ExecutionManager;
87
import com.amazonaws.lambda.durable.model.DurableExecutionInput;
98
import com.amazonaws.lambda.durable.model.DurableExecutionOutput;
109
import com.amazonaws.lambda.durable.serde.SerDes;
11-
import com.amazonaws.lambda.durable.util.AsyncHelper;
10+
import com.amazonaws.lambda.durable.util.ExceptionHelper;
1211
import com.amazonaws.services.lambda.runtime.Context;
1312
import com.amazonaws.services.lambda.runtime.RequestHandler;
1413
import java.nio.charset.StandardCharsets;
@@ -101,7 +100,7 @@ public static <I, O> DurableExecutionOutput execute(
101100
try {
102101
handlerFuture.join(); // Will throw the exception
103102
} catch (Exception e) {
104-
Throwable cause = AsyncHelper.unwrap(e);
103+
Throwable cause = ExceptionHelper.unwrapCompletableFuture(e);
105104
logger.debug("Execution failed: {}", cause.getMessage());
106105
return DurableExecutionOutput.failure(buildErrorObject(cause, serDes));
107106
}
@@ -138,7 +137,7 @@ public static <I, O> DurableExecutionOutput execute(
138137
logger.debug("Execution completed");
139138
return DurableExecutionOutput.success(outputPayload);
140139
} catch (Exception e) {
141-
return DurableExecutionOutput.failure(buildErrorObject(AsyncHelper.unwrap(e), serDes));
140+
return DurableExecutionOutput.failure(buildErrorObject(ExceptionHelper.unwrapCompletableFuture(e), serDes));
142141
} finally {
143142
// We shutdown the execution to make sure remaining checkpoint calls in the queue are drained
144143
executionManager.shutdown();
@@ -155,12 +154,7 @@ private static ErrorObject buildErrorObject(Throwable e, SerDes serDes) {
155154
return ((DurableOperationException) e).getErrorObject();
156155
}
157156
// exceptions thrown from non-operation code
158-
return ErrorObject.builder()
159-
.errorType(e.getClass().getName())
160-
.errorMessage(e.getMessage())
161-
.stackTrace(DurableExecutionException.serializeStackTrace(e.getStackTrace()))
162-
.errorData(serDes.serialize(e))
163-
.build();
157+
return ExceptionHelper.buildErrorObject(e, serDes);
164158
}
165159

166160
private static <I> I extractUserInput(Operation executionOp, SerDes serDes, Class<I> inputType) {

sdk/src/main/java/com/amazonaws/lambda/durable/exception/DurableExecutionException.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package com.amazonaws.lambda.durable.exception;
44

5-
import java.util.Arrays;
6-
import java.util.List;
7-
85
public class DurableExecutionException extends RuntimeException {
96
public DurableExecutionException(String message, Throwable cause, StackTraceElement[] stackTrace) {
107
super(message, cause);
@@ -20,26 +17,4 @@ public DurableExecutionException(String message, Throwable cause) {
2017
public DurableExecutionException(String message) {
2118
this(message, null, null);
2219
}
23-
24-
// StackTraceElement.toString() is implementation-dependent, so we'll define our
25-
// own format.
26-
public static List<String> serializeStackTrace(StackTraceElement[] stackTrace) {
27-
return Arrays.stream(stackTrace)
28-
.map((element) -> String.format(
29-
"%s|%s|%s|%d",
30-
element.getClassName(),
31-
element.getMethodName(),
32-
element.getFileName(),
33-
element.getLineNumber()))
34-
.toList();
35-
}
36-
37-
public static StackTraceElement[] deserializeStackTrace(List<String> stackTrace) {
38-
return stackTrace.stream()
39-
.map((s) -> {
40-
String[] tokens = s.split("\\|");
41-
return new StackTraceElement(tokens[0], tokens[1], tokens[2], Integer.parseInt(tokens[3]));
42-
})
43-
.toArray(StackTraceElement[]::new);
44-
}
4520
}

sdk/src/main/java/com/amazonaws/lambda/durable/exception/DurableOperationException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
package com.amazonaws.lambda.durable.exception;
44

5+
import com.amazonaws.lambda.durable.util.ExceptionHelper;
56
import software.amazon.awssdk.services.lambda.model.ErrorObject;
67
import software.amazon.awssdk.services.lambda.model.Operation;
78
import software.amazon.awssdk.services.lambda.model.OperationStatus;
@@ -19,7 +20,7 @@ public DurableOperationException(Operation operation, ErrorObject errorObject, S
1920
operation,
2021
errorObject,
2122
errorMessage,
22-
errorObject != null ? DurableExecutionException.deserializeStackTrace(errorObject.stackTrace()) : null);
23+
errorObject != null ? ExceptionHelper.deserializeStackTrace(errorObject.stackTrace()) : null);
2324
}
2425

2526
public DurableOperationException(

sdk/src/main/java/com/amazonaws/lambda/durable/operation/StepOperation.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import com.amazonaws.lambda.durable.execution.ThreadType;
1414
import com.amazonaws.lambda.durable.logging.DurableLogger;
1515
import com.amazonaws.lambda.durable.serde.SerDes;
16-
import com.amazonaws.lambda.durable.util.SneakyThrow;
16+
import com.amazonaws.lambda.durable.util.ExceptionHelper;
1717
import java.time.Duration;
1818
import java.time.Instant;
1919
import java.util.concurrent.CompletableFuture;
@@ -206,13 +206,7 @@ private StepSemantics getSemantics() {
206206
}
207207

208208
private void handleStepError(Throwable exception, int attempt) {
209-
var errorObject = ErrorObject.builder()
210-
.errorType(exception.getClass().getName())
211-
.errorMessage(exception.getMessage())
212-
.errorData(serDes.serialize(exception))
213-
.stackTrace(StepFailedException.serializeStackTrace(exception.getStackTrace()))
214-
.build();
215-
handleStepFailure(exception, errorObject, attempt);
209+
handleStepFailure(exception, ExceptionHelper.buildErrorObject(exception, serDes), attempt);
216210
}
217211

218212
private void handleStepFailure(Throwable exception, ErrorObject errorObject, int attempt) {
@@ -331,8 +325,8 @@ public T get() {
331325
errorObject.errorData(), TypeToken.get(exceptionClass.asSubclass(Throwable.class)));
332326

333327
if (original != null) {
334-
original.setStackTrace(StepFailedException.deserializeStackTrace(errorObject.stackTrace()));
335-
SneakyThrow.sneakyThrow(original);
328+
original.setStackTrace(ExceptionHelper.deserializeStackTrace(errorObject.stackTrace()));
329+
ExceptionHelper.sneakyThrow(original);
336330
}
337331
}
338332
} catch (ClassNotFoundException e) {

sdk/src/main/java/com/amazonaws/lambda/durable/util/AsyncHelper.java

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.amazonaws.lambda.durable.util;
4+
5+
import com.amazonaws.lambda.durable.serde.SerDes;
6+
import java.util.Arrays;
7+
import java.util.List;
8+
import java.util.concurrent.CompletionException;
9+
import software.amazon.awssdk.services.lambda.model.ErrorObject;
10+
11+
/** Utility class for handling exceptions */
12+
public class ExceptionHelper {
13+
/**
14+
* Throws any exception as if it were unchecked using type erasure. This preserves the original exception type and
15+
* stack trace.
16+
*
17+
* @param exception the exception to throw
18+
* @param <T> the exception type (erased at runtime)
19+
* @throws T the exception as an unchecked exception
20+
*/
21+
@SuppressWarnings("unchecked")
22+
public static <T extends Throwable> void sneakyThrow(Throwable exception) throws T {
23+
throw (T) exception;
24+
}
25+
26+
/**
27+
* unwrap the exception that is wrapped by CompletionException
28+
*
29+
* @param throwable the throwable to unwrap
30+
* @return the unwrapped Throwable
31+
*/
32+
public static Throwable unwrapCompletableFuture(Throwable throwable) {
33+
if (throwable instanceof CompletionException) {
34+
return unwrapCompletableFuture(throwable.getCause());
35+
} else {
36+
return throwable;
37+
}
38+
}
39+
40+
/**
41+
* build an ErrorObject from a Throwable
42+
*
43+
* @param throwable the Throwable from which to build the errorObject
44+
* @return the ErrorObject
45+
*/
46+
public static ErrorObject buildErrorObject(Throwable throwable, SerDes serDes) {
47+
return ErrorObject.builder()
48+
.errorType(throwable.getClass().getName())
49+
.errorMessage(throwable.getMessage())
50+
.errorData(serDes.serialize(throwable))
51+
.stackTrace(serializeStackTrace(throwable.getStackTrace()))
52+
.build();
53+
}
54+
55+
// StackTraceElement.toString() is implementation-dependent, so we'll define our
56+
// own format.
57+
public static List<String> serializeStackTrace(StackTraceElement[] stackTrace) {
58+
return Arrays.stream(stackTrace)
59+
.map((element) -> String.format(
60+
"%s|%s|%s|%d",
61+
element.getClassName(),
62+
element.getMethodName(),
63+
element.getFileName(),
64+
element.getLineNumber()))
65+
.toList();
66+
}
67+
68+
public static StackTraceElement[] deserializeStackTrace(List<String> stackTrace) {
69+
return stackTrace.stream()
70+
.map((s) -> {
71+
String[] tokens = s.split("\\|");
72+
return new StackTraceElement(tokens[0], tokens[1], tokens[2], Integer.parseInt(tokens[3]));
73+
})
74+
.toArray(StackTraceElement[]::new);
75+
}
76+
}

sdk/src/main/java/com/amazonaws/lambda/durable/util/SneakyThrow.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

sdk/src/test/java/com/amazonaws/lambda/durable/exception/DurableExecutionExceptionTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.junit.jupiter.api.Assertions.assertEquals;
77
import static org.junit.jupiter.api.Assertions.assertNull;
88

9+
import com.amazonaws.lambda.durable.util.ExceptionHelper;
910
import java.util.List;
1011
import org.junit.jupiter.api.Test;
1112

@@ -47,7 +48,7 @@ void testSerializeStackTrace() {
4748
new StackTraceElement("com.example.OtherClass", "otherMethod", "OtherClass.java", 456)
4849
};
4950

50-
var serialized = DurableExecutionException.serializeStackTrace(stackTrace);
51+
var serialized = ExceptionHelper.serializeStackTrace(stackTrace);
5152

5253
assertEquals(2, serialized.size());
5354
assertEquals("com.example.MyClass|myMethod|MyClass.java|123", serialized.get(0));
@@ -60,7 +61,7 @@ void testDeserializeStackTrace() {
6061
"com.example.MyClass|myMethod|MyClass.java|123",
6162
"com.example.OtherClass|otherMethod|OtherClass.java|456");
6263

63-
var stackTrace = DurableExecutionException.deserializeStackTrace(serialized);
64+
var stackTrace = ExceptionHelper.deserializeStackTrace(serialized);
6465

6566
assertEquals(2, stackTrace.length);
6667
assertEquals("com.example.MyClass", stackTrace[0].getClassName());
@@ -78,8 +79,8 @@ void testSerializeDeserializeRoundTrip() {
7879
var original =
7980
new StackTraceElement[] {new StackTraceElement("TestClass", "testMethod", "TestClass.java", 100)};
8081

81-
var serialized = DurableExecutionException.serializeStackTrace(original);
82-
var deserialized = DurableExecutionException.deserializeStackTrace(serialized);
82+
var serialized = ExceptionHelper.serializeStackTrace(original);
83+
var deserialized = ExceptionHelper.deserializeStackTrace(serialized);
8384

8485
assertArrayEquals(original, deserialized);
8586
}

0 commit comments

Comments
 (0)