T filterStackTrace(T t) {
Throwable cause = t.getCause();
- if(cause != null) {
+ if (cause != null) {
filterStackTrace(cause);
}
+
+ Throwable[] suppressedExceptions = t.getSuppressed();
+ for (Throwable suppressed : suppressedExceptions) {
+ filterStackTrace(suppressed);
+ }
+
return t;
}
@@ -102,7 +108,7 @@ static UserFault makeClassNotFoundUserFault(Throwable e, String className) {
}
public String reportableError() {
- if(this.exception != null || this.trace != null) {
+ if (this.exception != null || this.trace != null) {
return String.format("%s: %s\n%s\n",
this.msg,
this.exception == null ? "" : this.exception,
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java
index 5a05810e5..73db5b940 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java
@@ -18,7 +18,7 @@ public class XRayErrorCause {
public XRayErrorCause(Throwable throwable) {
working_directory = System.getProperty("user.dir");
- exceptions = Collections.unmodifiableCollection(Collections.singletonList(new XRayException(throwable)));
+ exceptions = Collections.singletonList(new XRayException(throwable));
paths = Collections.unmodifiableCollection(
Arrays.stream(throwable.getStackTrace())
.map(XRayErrorCause::determineFileName)
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java
index 8887dfb24..3b8976b7b 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java
@@ -2,10 +2,10 @@
package com.amazonaws.services.lambda.runtime.api.client.api;
-import java.util.Map;
-
-import com.amazonaws.services.lambda.runtime.ClientContext;
import com.amazonaws.services.lambda.runtime.Client;
+import com.amazonaws.services.lambda.runtime.ClientContext;
+
+import java.util.Map;
public class LambdaClientContext implements ClientContext {
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java
index 689c9110d..3d57ce4c2 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java
@@ -22,16 +22,16 @@ public class LambdaContext implements Context {
private final LambdaLogger logger;
public LambdaContext(
- int memoryLimit,
- long deadlineTimeInMs,
- String requestId,
- String logGroupName,
- String logStreamName,
- String functionName,
- CognitoIdentity identity,
- String functionVersion,
- String invokedFunctionArn,
- ClientContext clientContext
+ int memoryLimit,
+ long deadlineTimeInMs,
+ String requestId,
+ String logGroupName,
+ String logStreamName,
+ String functionName,
+ CognitoIdentity identity,
+ String functionVersion,
+ String invokedFunctionArn,
+ ClientContext clientContext
) {
this.memoryLimit = memoryLimit;
this.deadlineTimeInMs = deadlineTimeInMs;
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLogger.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLogger.java
new file mode 100644
index 000000000..8246a3ad2
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLogger.java
@@ -0,0 +1,68 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+/**
+ * Provides default implementation of the convenience logger functions.
+ * When extending AbstractLambdaLogger, only one function has to be overridden:
+ * void logMessage(byte[] message, LogLevel logLevel);
+ */
+public abstract class AbstractLambdaLogger implements LambdaLogger {
+ private final LogFiltering logFiltering;
+ private final LogFormatter logFormatter;
+ protected final LogFormat logFormat;
+
+ public AbstractLambdaLogger(LogLevel logLevel, LogFormat logFormat) {
+ this.logFiltering = new LogFiltering(logLevel);
+
+ this.logFormat = logFormat;
+ if (logFormat == LogFormat.JSON) {
+ logFormatter = new JsonLogFormatter();
+ } else {
+ logFormatter = new TextLogFormatter();
+ }
+ }
+
+ protected abstract void logMessage(byte[] message, LogLevel logLevel);
+
+ protected void logMessage(String message, LogLevel logLevel) {
+ byte[] messageBytes = message == null ? null : message.getBytes(UTF_8);
+ logMessage(messageBytes, logLevel);
+ }
+
+ @Override
+ public void log(String message, LogLevel logLevel) {
+ if (logFiltering.isEnabled(logLevel)) {
+ this.logMessage(logFormatter.format(message, logLevel), logLevel);
+ }
+ }
+
+ @Override
+ public void log(byte[] message, LogLevel logLevel) {
+ if (logFiltering.isEnabled(logLevel)) {
+ // there is no formatting for byte[] messages
+ this.logMessage(message, logLevel);
+ }
+ }
+
+ @Override
+ public void log(String message) {
+ this.log(message, LogLevel.UNDEFINED);
+ }
+
+ @Override
+ public void log(byte[] message) {
+ this.log(message, LogLevel.UNDEFINED);
+ }
+
+ public void setLambdaContext(LambdaContext lambdaContext) {
+ this.logFormatter.setLambdaContext(lambdaContext);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java
index 351a39ede..6663cc1d9 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java
@@ -2,12 +2,40 @@
package com.amazonaws.services.lambda.runtime.api.client.logging;
-public enum FrameType {
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
- LOG(0xa55a0003);
+/**
+ * The first 4 bytes of the framing protocol is the Frame Type, that's made of a magic number (3 bytes) and 1 byte of flags.
+ * +-----------------------+
+ * | Frame Type - 4 bytes |
+ * +-----------------------+
+ * | a5 5a 00 | flgs |
+ * + - - - - - + - - - - - +
+ * \ bit |
+ * | view|
+ * +---------+ +
+ * | |
+ * v byte 3 v F - free
+ * +-+-+-+-+-+-+-+-+ J - { JsonLog = 0, PlainTextLog = 1 }
+ * |F|F|F|L|l|l|T|J| T - { NoTimeStamp = 0, TimeStampPresent = 1 }
+ * +-+-+-+-+-+-+-+-+ Lll -> Log Level in 3-bit binary (L-> most significant bit)
+ */
+public class FrameType {
+ private static final int LOG_MAGIC = 0xa55a0000;
+ private static final int OFFSET_LOG_FORMAT = 0;
+ private static final int OFFSET_TIMESTAMP_PRESENT = 1;
+ private static final int OFFSET_LOG_LEVEL = 2;
private final int val;
+ public static int getValue(LogLevel logLevel, LogFormat logFormat) {
+ return LOG_MAGIC |
+ (logLevel.ordinal() << OFFSET_LOG_LEVEL) |
+ (1 << OFFSET_TIMESTAMP_PRESENT) |
+ (logFormat.ordinal() << OFFSET_LOG_FORMAT);
+ }
+
FrameType(int val) {
this.val = val;
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java
index bb436217b..f20e77221 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java
@@ -9,20 +9,23 @@
import java.nio.ByteOrder;
import java.time.Instant;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+
/**
* FramedTelemetryLogSink implements the logging contract between runtimes and the platform. It implements a simple
* framing protocol so message boundaries can be determined. Each frame can be visualized as follows:
*
- *
+ *
* {@code
* +----------------------+------------------------+---------------------+-----------------------+
* | Frame Type - 4 bytes | Length (len) - 4 bytes | Timestamp - 8 bytes | Message - 'len' bytes |
* +----------------------+------------------------+---------------------+-----------------------+
* }
*
- *
+ *
* The first 4 bytes indicate the type of the frame - log frames have a type defined as the hex value 0xa55a0001. The
- * second 4 bytes should indicate the message's length. The next 8 bytes contain UNIX timestamp of the message in
+ * second 4 bytes should indicate the message's length. The next 8 bytes contain UNIX timestamp of the message in
* microsecond accuracy. The next 'len' bytes contain the message. The byte order is big-endian.
*/
public class FramedTelemetryLogSink implements LogSink {
@@ -38,16 +41,21 @@ public FramedTelemetryLogSink(FileDescriptor fd) throws IOException {
}
@Override
- public synchronized void log(byte[] message) {
+ public synchronized void log(LogLevel logLevel, LogFormat logFormat, byte[] message) {
try {
- writeFrame(message);
+ writeFrame(logLevel, logFormat, message);
} catch (IOException e) {
e.printStackTrace();
}
}
- private void writeFrame(byte[] message) throws IOException {
- updateHeader(message.length);
+ @Override
+ public void log(byte[] message) {
+ log(LogLevel.UNDEFINED, LogFormat.TEXT, message);
+ }
+
+ private void writeFrame(LogLevel logLevel, LogFormat logFormat, byte[] message) throws IOException {
+ updateHeader(logLevel, logFormat, message.length);
this.logOutputStream.write(this.headerBuf.array());
this.logOutputStream.write(message);
}
@@ -60,9 +68,9 @@ private long timestamp() {
/**
* Updates the header ByteBuffer with the provided length. The header comprises the frame type and message length.
*/
- private void updateHeader(int length) {
+ private void updateHeader(LogLevel logLevel, LogFormat logFormat, int length) {
this.headerBuf.clear();
- this.headerBuf.putInt(FrameType.LOG.getValue());
+ this.headerBuf.putInt(FrameType.getValue(logLevel, logFormat));
this.headerBuf.putInt(length);
this.headerBuf.putLong(timestamp());
this.headerBuf.flip();
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatter.java
new file mode 100644
index 000000000..ef9e1c410
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatter.java
@@ -0,0 +1,55 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+public class JsonLogFormatter implements LogFormatter {
+ private final PojoSerializer serializer = GsonFactory.getInstance().getSerializer(StructuredLogMessage.class);
+ private LambdaContext lambdaContext;
+
+ private static final DateTimeFormatter dateFormatter =
+ DateTimeFormatter
+ .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
+ .withZone(ZoneId.of("UTC"));
+
+ @Override
+ public String format(String message, LogLevel logLevel) {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ StructuredLogMessage msg = createLogMessage(message, logLevel);
+ serializer.toJson(msg, stream);
+ stream.write('\n');
+ return new String(stream.toByteArray(), StandardCharsets.UTF_8);
+ }
+
+ private StructuredLogMessage createLogMessage(String message, LogLevel logLevel) {
+ StructuredLogMessage msg = new StructuredLogMessage();
+ msg.timestamp = dateFormatter.format(LocalDateTime.now());
+ msg.message = message;
+ msg.level = logLevel;
+
+ if (lambdaContext != null) {
+ msg.AWSRequestId = lambdaContext.getAwsRequestId();
+ }
+ return msg;
+ }
+
+
+ /**
+ * Function to set the context for every invocation.
+ * This way the logger will be able to attach additional information to the log packet.
+ */
+ @Override
+ public void setLambdaContext(LambdaContext context) {
+ this.lambdaContext = context;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java
index eaa2e3ea0..4800b356f 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java
@@ -2,33 +2,29 @@
package com.amazonaws.services.lambda.runtime.api.client.logging;
-import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
import static java.nio.charset.StandardCharsets.UTF_8;
-public class LambdaContextLogger implements LambdaLogger {
+public class LambdaContextLogger extends AbstractLambdaLogger {
// If a null string is passed in, replace it with "null",
// replicating the behavior of System.out.println(null);
private static final byte[] NULL_BYTES_VALUE = "null".getBytes(UTF_8);
private final transient LogSink sink;
- public LambdaContextLogger(LogSink sink) {
+ public LambdaContextLogger(LogSink sink, LogLevel logLevel, LogFormat logFormat) {
+ super(logLevel, logFormat);
this.sink = sink;
}
- public void log(byte[] message) {
+ @Override
+ protected void logMessage(byte[] message, LogLevel logLevel) {
if (message == null) {
message = NULL_BYTES_VALUE;
}
- sink.log(message);
- }
-
- public void log(String message) {
- if (message == null) {
- this.log(NULL_BYTES_VALUE);
- } else {
- this.log(message.getBytes(UTF_8));
- }
+ sink.log(logLevel, this.logFormat, message);
}
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFiltering.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFiltering.java
new file mode 100644
index 000000000..59038efa5
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFiltering.java
@@ -0,0 +1,17 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+public class LogFiltering {
+ private final LogLevel minimumLogLevel;
+
+ public LogFiltering(LogLevel minimumLogLevel) {
+ this.minimumLogLevel = minimumLogLevel;
+ }
+
+ boolean isEnabled(LogLevel logLevel) {
+ return (logLevel == LogLevel.UNDEFINED || logLevel.ordinal() >= minimumLogLevel.ordinal());
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFormatter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFormatter.java
new file mode 100644
index 000000000..debe5af41
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogFormatter.java
@@ -0,0 +1,13 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+public interface LogFormatter {
+ String format(String message, LogLevel logLevel);
+
+ default void setLambdaContext(LambdaContext context) {
+ };
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java
index 11c3f92ce..77df08e25 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java
@@ -4,8 +4,13 @@
import java.io.Closeable;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+
public interface LogSink extends Closeable {
void log(byte[] message);
+ void log(LogLevel logLevel, LogFormat logFormat, byte[] message);
+
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java
index 5487c5dd0..6fd6b87c7 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java
@@ -4,9 +4,16 @@
import java.io.IOException;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+
public class StdOutLogSink implements LogSink {
@Override
public void log(byte[] message) {
+ log(LogLevel.UNDEFINED, LogFormat.TEXT, message);
+ }
+
+ public void log(LogLevel logLevel, LogFormat logFormat, byte[] message) {
try {
System.out.write(message);
} catch (IOException e) {
@@ -15,5 +22,6 @@ public void log(byte[] message) {
}
@Override
- public void close() {}
+ public void close() {
+ }
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StructuredLogMessage.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StructuredLogMessage.java
new file mode 100644
index 000000000..9c271adf7
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StructuredLogMessage.java
@@ -0,0 +1,12 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+class StructuredLogMessage {
+ public String timestamp;
+ public String message;
+ public LogLevel level;
+ public String AWSRequestId;
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatter.java
new file mode 100644
index 000000000..7c3454012
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatter.java
@@ -0,0 +1,28 @@
+/* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+import java.util.HashMap;
+
+public class TextLogFormatter implements LogFormatter {
+ private static final HashMap logLevelMapper = new HashMap() {{
+ for (LogLevel logLevel: LogLevel.values()) {
+ put(logLevel, "[" + logLevel.toString() + "] ");
+ }
+ }};
+
+ @Override
+ public String format(String message, LogLevel logLevel) {
+ if (logLevel == LogLevel.UNDEFINED) {
+ return message;
+ }
+
+ return new StringBuilder()
+ .append(logLevelMapper.get(logLevel))
+ .append(message)
+ .toString();
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java
index c8df04afd..6a6e7b129 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java
@@ -5,7 +5,7 @@
/**
* An invocation request represents the response of the runtime API's next invocation API.
- *
+ *
* Copyright (c) 2019 Amazon. All rights reserved.
*/
public class InvocationRequest {
@@ -41,39 +41,61 @@ public class InvocationRequest {
*/
private String cognitoIdentity;
- /**
- * An input stream of the invocation's request body.
- */
- private InputStream stream;
-
private byte[] content;
public String getId() {
return id;
}
+ public void setId(String id) {
+ this.id = id;
+ }
+
public String getXrayTraceId() {
return xrayTraceId;
}
+ public void setXrayTraceId(String xrayTraceId) {
+ this.xrayTraceId = xrayTraceId;
+ }
+
public String getInvokedFunctionArn() {
return invokedFunctionArn;
}
+ public void setInvokedFunctionArn(String invokedFunctionArn) {
+ this.invokedFunctionArn = invokedFunctionArn;
+ }
+
public long getDeadlineTimeInMs() {
return deadlineTimeInMs;
}
+ public void setDeadlineTimeInMs(long deadlineTimeInMs) {
+ this.deadlineTimeInMs = deadlineTimeInMs;
+ }
+
public String getClientContext() {
return clientContext;
}
+ public void setClientContext(String clientContext) {
+ this.clientContext = clientContext;
+ }
+
public String getCognitoIdentity() {
return cognitoIdentity;
}
+ public void setCognitoIdentity(String cognitoIdentity) {
+ this.cognitoIdentity = cognitoIdentity;
+ }
+
public InputStream getContentAsStream() {
return new ByteArrayInputStream(content);
}
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java
index 05aa50a1b..e79ba6142 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java
@@ -6,16 +6,19 @@
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
import static java.net.HttpURLConnection.HTTP_ACCEPTED;
+import static java.net.HttpURLConnection.HTTP_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* LambdaRuntimeClient is a client of the AWS Lambda Runtime HTTP API for custom runtimes.
- *
+ *
* API definition can be found at https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
- *
+ *
* Copyright (c) 2019 Amazon. All rights reserved.
*/
public class LambdaRuntimeClient {
@@ -35,14 +38,15 @@ public LambdaRuntimeClient(String hostnamePort) {
this.hostname = parts[0];
this.port = Integer.parseInt(parts[1]);
this.invocationEndpoint = invocationEndpoint();
+ NativeClient.init();
}
public InvocationRequest waitForNextInvocation() {
- return NativeClient.next();
+ return NativeClient.nextWrapper();
}
public void postInvocationResponse(String requestId, byte[] response) {
- NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response);
+ NativeClient.postInvocationResponseWrapper(requestId.getBytes(UTF_8), response);
}
public void postInvocationError(String requestId, byte[] errorResponse, String errorType) throws IOException {
@@ -55,6 +59,15 @@ public void postInvocationError(String requestId, byte[] errorResponse, String e
post(endpoint, errorResponse, errorType, errorCause);
}
+ public void getRestoreNext() throws IOException {
+ doGet(restoreNextEndpoint(), HTTP_OK);
+ }
+
+ public int postRestoreError(byte[] errorResponse, String errorType) throws IOException {
+ String endpoint = restoreErrorEndpoint();
+ return postError(endpoint, errorResponse, errorType, null);
+ }
+
public void postInitError(byte[] errorResponse, String errorType) throws IOException {
String endpoint = initErrorEndpoint();
post(endpoint, errorResponse, errorType, null);
@@ -65,10 +78,10 @@ private void post(String endpoint, byte[] errorResponse, String errorType, Strin
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", DEFAULT_CONTENT_TYPE);
- if(errorType != null && !errorType.isEmpty()) {
+ if (errorType != null && !errorType.isEmpty()) {
conn.setRequestProperty(ERROR_TYPE_HEADER, errorType);
}
- if(errorCause != null && errorCause.getBytes().length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) {
+ if (errorCause != null && errorCause.getBytes().length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) {
conn.setRequestProperty(XRAY_ERROR_CAUSE_HEADER, errorCause);
}
conn.setFixedLengthStreamingMode(errorResponse.length);
@@ -87,7 +100,7 @@ private void post(String endpoint, byte[] errorResponse, String errorType, Strin
}
private String invocationEndpoint() {
- return "http://" + hostname + ":" + port + "/2018-06-01/runtime/invocation/";
+ return getBaseUrl() + "/2018-06-01/runtime/invocation/";
}
private String invocationErrorEndpoint(String requestId) {
@@ -95,7 +108,80 @@ private String invocationErrorEndpoint(String requestId) {
}
private String initErrorEndpoint() {
- return "http://" + hostname + ":" + port + "/2018-06-01/runtime/init/error";
+ return getBaseUrl() + "/2018-06-01/runtime/init/error";
+ }
+
+ private String restoreErrorEndpoint() {
+ return getBaseUrl() + "/2018-06-01/runtime/restore/error";
+ }
+
+ private String restoreNextEndpoint() {
+ return getBaseUrl() + "/2018-06-01/runtime/restore/next";
+ }
+
+ private String getBaseUrl() {
+ return "http://" + hostname + ":" + port;
+ }
+
+ private int postError(String endpoint,
+ byte[] errorResponse,
+ String errorType,
+ String errorCause) throws IOException {
+
+ Map headers = new HashMap<>();
+ if (errorType != null && !errorType.isEmpty()) {
+ headers.put(ERROR_TYPE_HEADER, errorType);
+ }
+ if (errorCause != null && errorCause.getBytes().length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) {
+ headers.put(XRAY_ERROR_CAUSE_HEADER, errorCause);
+ }
+
+ return doPost(endpoint, DEFAULT_CONTENT_TYPE, headers, errorResponse);
+ }
+
+ private int doPost(String endpoint,
+ String contentType,
+ Map headers,
+ byte[] payload) throws IOException {
+
+ URL url = createUrl(endpoint);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Type", contentType);
+
+ for (Map.Entry header : headers.entrySet()) {
+ conn.setRequestProperty(header.getKey(), header.getValue());
+ }
+
+ conn.setFixedLengthStreamingMode(payload.length);
+ conn.setDoOutput(true);
+
+ try (OutputStream outputStream = conn.getOutputStream()) {
+ outputStream.write(payload);
+ }
+
+ // get response code before closing the stream
+ int responseCode = conn.getResponseCode();
+
+ // don't need to read the response, close stream to ensure connection re-use
+ closeInputStreamQuietly(conn);
+
+ return responseCode;
+ }
+
+ private void doGet(String endpoint, int expectedHttpResponseCode) throws IOException {
+
+ URL url = createUrl(endpoint);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+
+ int responseCode = conn.getResponseCode();
+ if (responseCode != expectedHttpResponseCode) {
+ throw new LambdaRuntimeClientException(endpoint, responseCode);
+ }
+
+ closeInputStreamQuietly(conn);
}
private URL createUrl(String endpoint) {
@@ -113,4 +199,23 @@ private void closeQuietly(InputStream inputStream) {
} catch (IOException e) {
}
}
+
+ private void closeInputStreamQuietly(HttpURLConnection conn) {
+
+ InputStream inputStream;
+ try {
+ inputStream = conn.getInputStream();
+ } catch (IOException e) {
+ return;
+ }
+
+ if (inputStream == null) {
+ return;
+ }
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java
index a9592e1a8..785b95864 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java
@@ -2,72 +2,147 @@
package com.amazonaws.services.lambda.runtime.api.client.runtimeapi;
+import org.crac.Context;
+import org.crac.Resource;
+
+import java.io.FileNotFoundException;
import java.io.InputStream;
+import java.lang.annotation.Native;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
/**
* This module defines the native Runtime Interface Client which is responsible for all HTTP
* interactions with the Runtime API.
*/
class NativeClient {
- private static final String nativeLibPath = "/tmp/.aws-lambda-runtime-interface-client";
- private static final String architecturePathSuffix = "/" + getArchIdentifier();
- // Implementation based on AWS CRT, but adopted to support 64-bit architectures only (ref. https://github.com/awslabs/aws-crt-java/blob/0e9c3db8b07258b57c2503cfc47c787ccef10670/src/main/java/software/amazon/awssdk/crt/CRT.java#L106-L134)
- private static final String supported_arm_architectures = "^(aarch64.*|arm64.*)$";
- private static final String supported_x86_architectures = "^(x8664|amd64|ia32e|em64t|x64|x86_64)$";
- private static final String[] libsToTry = {
- "aws-lambda-runtime-interface-client.glibc.so",
- "aws-lambda-runtime-interface-client.musl.so",
- };
- private static final Throwable[] exceptions = new Throwable[libsToTry.length];
- static {
- boolean loaded = false;
- for (int i = 0; !loaded && i < libsToTry.length; ++i) {
- try (InputStream lib = NativeClient.class.getResourceAsStream(
- Paths.get(architecturePathSuffix, libsToTry[i]).toString())) {
- Files.copy(lib, Paths.get(nativeLibPath), StandardCopyOption.REPLACE_EXISTING);
- System.load(nativeLibPath);
- loaded = true;
- } catch (UnsatisfiedLinkError e) {
- exceptions[i] = e;
- } catch (Exception e) {
- exceptions[i] = e;
+ static class CheckpointState implements Resource {
+ enum State {
+ WORKING,
+ SYNCING,
+ SYNCED,
+ };
+
+ State state = State.WORKING;
+
+ private void waitFor(State targetState) {
+ while (state != targetState) {
+ try {
+ this.wait();
+ } catch (InterruptedException interruptedException) {
}
}
- if (!loaded) {
- for (int i = 0; i < libsToTry.length; ++i) {
- System.err.printf("Failed to load the native runtime interface client library %s. Exception: %s\n", libsToTry[i], exceptions[i].getMessage());
- }
- System.exit(-1);
+ }
+
+ @Override
+ public synchronized void beforeCheckpoint(Context extends Resource> context) throws Exception {
+ state = State.SYNCING;
+ waitFor(State.SYNCED);
+ deinitializeClient();
+ }
+
+ @Override
+ public synchronized void afterRestore(Context extends Resource> context) throws Exception {
+ initUserAgent();
+ state = State.WORKING;
+ this.notifyAll();
+ }
+
+ public synchronized void syncPoint() {
+ if (state == State.SYNCING) {
+ state = State.SYNCED;
+ this.notifyAll();
}
- String userAgent = String.format(
- "aws-lambda-java/%s-%s" ,
- System.getProperty("java.vendor.version"),
- NativeClient.class.getPackage().getImplementationVersion());
- initializeClient(userAgent.getBytes());
+ waitFor(State.WORKING);
+ }
+ }
+
+ static CheckpointState checkpointState = new CheckpointState();
+
+ private static final String NATIVE_LIB_PATH = "/tmp/.libaws-lambda-jni.so";
+ public static final String NATIVE_CLIENT_JNI_PROPERTY = "com.amazonaws.services.lambda.runtime.api.client.runtimeapi.NativeClient.JNI";
+
+ static void init() {
+ loadJNILib();
+ initUserAgent();
+ org.crac.Core.getGlobalContext().register(checkpointState);
}
+ private static void loadJNILib() {
+ String jniLib = System.getProperty(NATIVE_CLIENT_JNI_PROPERTY);
+ if (jniLib != null) {
+ System.load(jniLib);
+ } else {
+ String[] libsToTry = new String[]{
+ "libaws-lambda-jni.linux-x86_64.so",
+ "libaws-lambda-jni.linux-aarch_64.so",
+ "libaws-lambda-jni.linux_musl-x86_64.so",
+ "libaws-lambda-jni.linux_musl-aarch_64.so"
+ };
+ unpackAndLoadNativeLibrary(libsToTry);
+ }
+ }
+
+
/**
- * @return a string describing the detected architecture the RIC is executing on
- * @throws UnknownPlatformException
+ * Unpacks JNI library from the JAR to a temporary location and tries to load it using System.load()
+ * Implementation based on AWS CRT
+ * (ref. ...)
+ *
+ * @param libsToTry - array of native libraries to try
*/
- static String getArchIdentifier() {
- String arch = System.getProperty("os.arch");
+ static void unpackAndLoadNativeLibrary(String[] libsToTry) {
- if (arch.matches(supported_x86_architectures)) {
- return "x86_64";
- } else if (arch.matches(supported_arm_architectures)) {
- return "aarch64";
+ List errorMessages = new ArrayList<>();
+ for (String libToTry : libsToTry) {
+ try (InputStream inputStream = NativeClient.class.getResourceAsStream(
+ Paths.get("/jni", libToTry).toString())) {
+ if (inputStream == null) {
+ throw new FileNotFoundException("Specified file not in the JAR: " + libToTry);
+ }
+ Files.copy(inputStream, Paths.get(NATIVE_LIB_PATH), StandardCopyOption.REPLACE_EXISTING);
+ System.load(NATIVE_LIB_PATH);
+ return;
+ } catch (UnsatisfiedLinkError | Exception e) {
+ errorMessages.add(e.getMessage());
+ }
}
- throw new UnknownPlatformException("architecture not supported: " + arch);
+ for (int i = 0; i < libsToTry.length; ++i) {
+ System.err.println("Failed to load the native runtime interface client library " + libsToTry[i] +
+ ". Exception: " + errorMessages.get(i));
+ }
+ System.exit(-1);
+ }
+
+ private static void initUserAgent() {
+ String userAgent = String.format(
+ "aws-lambda-java/%s-%s",
+ System.getProperty("java.vendor.version"),
+ NativeClient.class.getPackage().getImplementationVersion());
+
+ initializeClient(userAgent.getBytes());
+
}
static native void initializeClient(byte[] userAgent);
- static native InvocationRequest next();
+ private static native InvocationRequest next();
+
+ static InvocationRequest nextWrapper() {
+ return next();
+ }
+
+ private static native void postInvocationResponse(byte[] requestId, byte[] response);
+
+ static void postInvocationResponseWrapper(byte[] requestId, byte[] response) {
+ postInvocationResponse(requestId, response);
+ checkpointState.syncPoint();
+ }
- static native void postInvocationResponse(byte[] requestId, byte[] response);
+ static native void deinitializeClient();
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java
index 83ffcb9ac..556859a27 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java
@@ -14,7 +14,7 @@ public LambdaOutputStream(OutputStream inner) {
@Override
public void write(int b) throws IOException {
- write(new byte[] {(byte)b});
+ write(new byte[]{(byte) b});
}
@Override
@@ -25,6 +25,6 @@ public void write(byte[] bytes) throws IOException {
@Override
public void write(byte[] bytes, int offset, int length) throws IOException {
- inner.write(bytes, offset, length);
+ inner.write(bytes, offset, length);
}
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc
index 2f4a66553..dd4fdb22e 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc
@@ -7,14 +7,19 @@ RUN yum install -y \
tar \
gzip \
make \
+ patch \
gcc \
gcc-c++ \
java-11-amazon-corretto
# Install curl dependency
COPY ./deps/curl-$CURL_VERSION.tar.gz /src/deps/
+COPY ./deps/curl_001_disable_wakeup.patch /src/deps/
+
RUN tar xzf /src/deps/curl-$CURL_VERSION.tar.gz -C /src/deps
+
WORKDIR /src/deps/curl-$CURL_VERSION
+RUN patch lib/multihandle.h ../curl_001_disable_wakeup.patch
RUN ./configure \
--prefix $(pwd)/../artifacts \
--disable-shared \
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl
index fa91af809..e15f6adc5 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl
@@ -10,12 +10,17 @@ RUN apk update && \
g++ \
gcc \
make \
+ patch \
perl
# Install curl dependency
COPY ./deps/curl-$CURL_VERSION.tar.gz /src/deps/
+COPY ./deps/curl_001_disable_wakeup.patch /src/deps/
+
RUN tar xzf /src/deps/curl-$CURL_VERSION.tar.gz -C /src/deps
+
WORKDIR /src/deps/curl-$CURL_VERSION
+RUN patch lib/multihandle.h ../curl_001_disable_wakeup.patch
RUN ./configure \
--prefix $(pwd)/../artifacts \
--disable-shared \
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh
index e7122b205..3b505e74d 100755
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/bash -x
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
set -euo pipefail
@@ -6,46 +6,114 @@ set -euo pipefail
SRC_DIR=$(dirname "$0")
DST_DIR=${1}
MULTI_ARCH=${2}
-CURL_VERSION=7.86.0
-
-# Not using associative arrays to maintain bash 3 compatibility with building on MacOS
-# MacOS ships with bash 3 and associative arrays require bash 4+
-# Declaring a map as an array with the column character as a separator :
-declare -a ARCHITECTURES_TO_PLATFORM=(
- "x86_64:linux/amd64"
- "aarch64:linux/arm64/v8"
-)
-
-declare -a TARGETS=("glibc" "musl")
-
-for pair in "${ARCHITECTURES_TO_PLATFORM[@]}"; do
- arch=${pair%%:*}
- platform=${pair#*:}
-
- if [[ "${MULTI_ARCH}" != "true" ]] && [[ "$(arch)" != "${arch}" ]]; then
- echo "multi arch build not requested and host arch is $(arch), so skipping ${arch}:${platform} ..."
- continue
- fi
-
- mkdir -p "${DST_DIR}/classes/${arch}"
-
- for target in "${TARGETS[@]}"; do
- echo "Compiling the native library for target ${target} on architecture ${arch} using Docker platform ${platform}"
- artifact="${DST_DIR}/classes/${arch}/aws-lambda-runtime-interface-client.${target}.so"
-
- if [[ "${MULTI_ARCH}" == "true" ]]; then
- docker build --platform="${platform}" -f "${SRC_DIR}/Dockerfile.${target}" --build-arg CURL_VERSION=${CURL_VERSION} "${SRC_DIR}" -o - | tar -xOf - src/aws-lambda-runtime-interface-client.so > "${artifact}"
- else
- echo "multi-arch not requestsed, assuming this is a workaround to goofyness when docker buildx is enabled on Linux CI environments."
- echo "enabling docker buildx often updates the docker api version, so assuming that docker cli is also too old to use --output type=tar, so doing alternative build-tag-run approach"
- docker build --platform="${platform}" -t "lambda-java-jni-lib-${target}-${arch}" -f "${SRC_DIR}/Dockerfile.${target}" --build-arg CURL_VERSION=${CURL_VERSION} "${SRC_DIR}"
- docker run --rm --entrypoint /bin/cat "lambda-java-jni-lib-${target}-${arch}" /src/aws-lambda-runtime-interface-client.so > "${artifact}"
- fi
-
- [ -f "${artifact}" ]
- if ! file -b "${artifact}" | tr '-' '_' | tee /dev/stderr | grep -q "${arch}"; then
- echo "${artifact} did not appear to be the correct architecture, check that Docker buildx is enabled"
- exit 1
- fi
- done
-done
+BUILD_OS=${3}
+BUILD_ARCH=${4}
+CURL_VERSION=7.83.1
+
+function get_docker_platform() {
+ arch=$1
+
+ if [ "${arch}" == "x86_64" ]; then
+ echo "linux/amd64"
+ elif [ "${arch}" == "aarch_64" ]; then
+ echo "linux/arm64/v8"
+ else
+ echo "UNKNOWN_DOCKER_PLATFORM"
+ fi
+}
+
+function get_target_os() {
+ libc_impl=$1
+
+ if [ "${libc_impl}" == "glibc" ]; then
+ echo "linux"
+ elif [ "${libc_impl}" == "musl" ]; then
+ echo "linux_musl"
+ else
+ echo "UNKNOWN_OS"
+ fi
+}
+
+function build_for_libc_arch() {
+ libc_impl=$1
+ arch=$2
+ artifact=$3
+
+ docker_platform=$(get_docker_platform ${arch})
+
+ echo "Compiling the native library with libc implementation \`${libc_impl}\` on architecture \`${arch}\` using Docker platform \`${docker_platform}\`"
+
+ if [[ "${MULTI_ARCH}" == "true" ]]; then
+ docker build --platform="${docker_platform}" -f "${SRC_DIR}/Dockerfile.${libc_impl}" \
+ --build-arg CURL_VERSION=${CURL_VERSION} "${SRC_DIR}" -o - \
+ | tar -xOf - src/aws-lambda-runtime-interface-client.so > "${artifact}"
+ else
+ echo "multi-arch not requested, assuming this is a workaround to goofyness when docker buildx is enabled on Linux CI environments."
+ echo "enabling docker buildx often updates the docker api version, so assuming that docker cli is also too old to use --output type=tar, so doing alternative build-tag-run approach"
+ image_name="lambda-java-jni-lib-${libc_impl}-${arch}"
+ docker build --platform="${docker_platform}" \
+ -t "${image_name}" \
+ -f "${SRC_DIR}/Dockerfile.${libc_impl}" \
+ --build-arg CURL_VERSION=${CURL_VERSION} "${SRC_DIR}"
+
+ docker run --rm --entrypoint /bin/cat "${image_name}" \
+ /src/aws-lambda-runtime-interface-client.so > "${artifact}"
+ fi
+
+ [ -f "${artifact}" ]
+
+ # file -b ${artifact} produces lines like this:
+ # x86_64: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=582888b42da34895828e1281cbbae15d279175b7, not stripped
+ # aarch_64: ELF 64-bit LSB shared object, ARM aarch64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=fa54218974fb2c17772b6acf22467a2c67a87011, not stripped
+ # we need to ensure it has the expected architecture in it
+ #
+ # cut -d "," -f2 will extract second field (' x86-64' or ' ARM aarch64')
+ # tr -d '-' removes '-', so we'll have (' x8664' or ' ARM aarch64')
+ # grep -q is for quiet mode, no output
+ # ${arch//_} removes '_' chars from the `aarch` variable, (aarch_64 => aarch64, x86_64 => x8664)
+ if ! file -b "${artifact}" | cut -d "," -f2 | tr -d '-' | grep -q "${arch//_}"; then
+ echo "${artifact} did not appear to be the correct architecture, check that Docker buildx is enabled"
+ exit 1
+ fi
+}
+
+function get_target_artifact() {
+ target_os=$1
+ target_arch=$2
+
+ target_file="${DST_DIR}/classes/jni/libaws-lambda-jni.${target_os}-${target_arch}.so"
+ target_dir=$(dirname "$target_file")
+ mkdir -p "$target_dir"
+ echo "$target_file"
+}
+
+
+
+if [ -n "$BUILD_OS" ] && [ -n "$BUILD_ARCH" ]; then
+ # build for the specified arch and libc implementation
+ libc_impl="glibc"
+ if [ "$BUILD_OS" == "linux_musl" ]; then
+ libc_impl="musl"
+ fi
+ target_artifact=$(get_target_artifact "$BUILD_OS" "$BUILD_ARCH")
+ build_for_libc_arch "$libc_impl" "$BUILD_ARCH" "$target_artifact"
+else
+ # build for all architectures and libc implementations
+ declare -a ARCHITECTURES=("x86_64" "aarch_64")
+ declare -a LIBC_IMPLS=("glibc" "musl")
+
+ for arch in "${ARCHITECTURES[@]}"; do
+
+ if [[ "${MULTI_ARCH}" != "true" ]] && [[ "$(arch)" != "${arch}" ]]; then
+ echo "multi arch build not requested and host arch is $(arch), so skipping ${arch}..."
+ continue
+ fi
+
+ for libc_impl in "${LIBC_IMPLS[@]}"; do
+ target_os=$(get_target_os $libc_impl)
+ target_artifact=$(get_target_artifact "$target_os" "$arch")
+ build_for_libc_arch "$libc_impl" "$arch" "$target_artifact"
+ done
+
+ done
+fi
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp
index 87fa9f028..240a8a795 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp
@@ -149,3 +149,9 @@ JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_run
throwLambdaRuntimeClientException(env, errorMessage, outcome.get_failure());
}
}
+
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_deinitializeClient(JNIEnv *env, jobject thisObject) {
+ delete CLIENT;
+ CLIENT = NULL;
+}
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h
index 28a6f444a..47d1265df 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h
@@ -15,6 +15,9 @@ JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_
JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_postInvocationResponse
(JNIEnv *, jobject, jbyteArray, jbyteArray);
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_deinitializeClient
+ (JNIEnv *, jobject);
+
#ifdef __cplusplus
}
#endif
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.83.1.tar.gz b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.83.1.tar.gz
new file mode 100644
index 000000000..b71926a37
Binary files /dev/null and b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.83.1.tar.gz differ
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.86.0.tar.gz b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.86.0.tar.gz
deleted file mode 100644
index 650bee5ba..000000000
Binary files a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl-7.86.0.tar.gz and /dev/null differ
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl_001_disable_wakeup.patch b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl_001_disable_wakeup.patch
new file mode 100644
index 000000000..1bb067054
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/curl_001_disable_wakeup.patch
@@ -0,0 +1,14 @@
+diff --git a/multihandle.h b/multihandle.h
+index a26fb619a..18080f1c3 100644
+--- a/multihandle.h
++++ b/multihandle.h
+@@ -70,10 +70,6 @@ typedef enum {
+
+ #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
+
+-#if !defined(CURL_DISABLE_SOCKETPAIR)
+-#define ENABLE_WAKEUP
+-#endif
+-
+ /* value for MAXIMUM CONCURRENT STREAMS upper limit */
+ #define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)
\ No newline at end of file
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/crac/ContextImplTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/crac/ContextImplTest.java
new file mode 100644
index 000000000..b81660730
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/crac/ContextImplTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+package com.amazonaws.services.lambda.crac;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.doThrow;
+
+public class ContextImplTest {
+
+ private Resource throwsWithSuppressedException, noop, noop2, throwsException, throwCustomException;
+
+ @BeforeEach
+ public void setup() throws Exception {
+
+ throwsWithSuppressedException = Mockito.mock(Resource.class);
+ CheckpointException checkpointException = new CheckpointException();
+ checkpointException.addSuppressed(new NumberFormatException());
+
+ RestoreException restoreException = new RestoreException();
+ restoreException.addSuppressed(new NumberFormatException());
+
+ doThrow(checkpointException).when(throwsWithSuppressedException).beforeCheckpoint(ArgumentMatchers.any());
+ doThrow(restoreException).when(throwsWithSuppressedException).afterRestore(ArgumentMatchers.any());
+
+ noop = Mockito.mock(Resource.class);
+ Mockito.doNothing().when(noop).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.doNothing().when(noop).afterRestore(ArgumentMatchers.any());
+
+ noop2 = Mockito.mock(Resource.class);
+ Mockito.doNothing().when(noop2).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.doNothing().when(noop2).afterRestore(ArgumentMatchers.any());
+
+ throwsException = Mockito.mock(Resource.class);
+ doThrow(CheckpointException.class).when(throwsException).beforeCheckpoint(ArgumentMatchers.any());
+ doThrow(RestoreException.class).when(throwsException).afterRestore(ArgumentMatchers.any());
+
+ throwCustomException = Mockito.mock(Resource.class);
+ doThrow(IndexOutOfBoundsException.class).when(throwCustomException).beforeCheckpoint(ArgumentMatchers.any());
+ doThrow(UnsupportedOperationException.class).when(throwCustomException).afterRestore(ArgumentMatchers.any());
+
+ Core.resetGlobalContext();
+ }
+
+ static class StatefulResource implements Resource {
+
+ int state = 0;
+
+ @Override
+ public void afterRestore(Context extends Resource> context) {
+ state += 1;
+ }
+
+ @Override
+ public void beforeCheckpoint(Context extends Resource> context) {
+ state += 2;
+ }
+
+ int getValue() {
+ return state;
+ }
+ }
+
+ static int GLOBAL_STATE;
+
+ static class ChangeGlobalStateResource implements Resource {
+
+ ChangeGlobalStateResource() {
+ GLOBAL_STATE = 0;
+ }
+
+ @Override
+ public void afterRestore(Context extends Resource> context) {
+ GLOBAL_STATE += 1;
+ }
+
+ @Override
+ public void beforeCheckpoint(Context extends Resource> context) {
+ GLOBAL_STATE += 2;
+ }
+ }
+
+ /**
+ * Happy path test with real / not mocked resource
+ */
+ @Test
+ public void verifyHooksWereExecuted() throws CheckpointException, RestoreException {
+ StatefulResource resource = new StatefulResource();
+ Core.getGlobalContext().register(resource);
+
+ Core.getGlobalContext().beforeCheckpoint(null);
+ Core.getGlobalContext().afterRestore(null);
+
+ assertEquals(3, resource.getValue());
+ }
+
+ /**
+ * This test is to validate GC intervention
+ */
+ @Test
+ public void verifyHooksWereExecutedWithGC() throws CheckpointException, RestoreException {
+ StatefulResource resource = new StatefulResource();
+ Core.getGlobalContext().register(resource);
+ gcAndSleep();
+
+ Core.getGlobalContext().beforeCheckpoint(null);
+ Core.getGlobalContext().afterRestore(null);
+
+ assertEquals(3, resource.getValue());
+ }
+
+ @Test
+ public void verifyHooksAreNotExecutedForGarbageCollectedResources() throws CheckpointException, RestoreException {
+ Core.getGlobalContext().register(new ChangeGlobalStateResource());
+ gcAndSleep();
+
+ Core.getGlobalContext().beforeCheckpoint(null);
+ Core.getGlobalContext().afterRestore(null);
+
+
+ assertEquals(0, GLOBAL_STATE);
+ }
+
+ private static void gcAndSleep() {
+ for (int i = 0; i < 10; i++) {
+ System.gc();
+ }
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println("thread was interrupted");
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void Should_NotifyResourcesInReverseOrderOfRegistration_When_CheckpointNotification() throws Exception {
+ // Given
+ InOrder checkpointNotificationOrder = Mockito.inOrder(noop, noop2);
+ Core.getGlobalContext().register(noop);
+ Core.getGlobalContext().register(noop2);
+
+ // When
+ Core.getGlobalContext().beforeCheckpoint(null);
+
+ // Then
+ checkpointNotificationOrder.verify(noop2).beforeCheckpoint(ArgumentMatchers.any());
+ checkpointNotificationOrder.verify(noop).beforeCheckpoint(ArgumentMatchers.any());
+ }
+
+ @Test
+ public void Should_NotifyResourcesInOrderOfRegistration_When_RestoreNotification() throws Exception {
+ // Given
+ InOrder restoreNotificationOrder = Mockito.inOrder(noop, noop2);
+ Core.getGlobalContext().register(noop);
+ Core.getGlobalContext().register(noop2);
+
+ // When
+ Core.getGlobalContext().afterRestore(null);
+
+ // Then
+ restoreNotificationOrder.verify(noop).afterRestore(ArgumentMatchers.any());
+ restoreNotificationOrder.verify(noop2).afterRestore(ArgumentMatchers.any());
+ }
+
+ @Test
+ public void Should_ResourcesAreAlwaysNotified_When_AnyNotificationThrowsException() throws Exception {
+
+ // Given
+ Core.getGlobalContext().register(throwsWithSuppressedException);
+ Core.getGlobalContext().register(noop);
+ Core.getGlobalContext().register(noop2);
+ Core.getGlobalContext().register(throwsException);
+ Core.getGlobalContext().register(throwCustomException);
+
+ // When
+ try {
+ Core.getGlobalContext().beforeCheckpoint(null);
+ } catch (Exception ignored) {
+ }
+
+ try {
+ Core.getGlobalContext().afterRestore(null);
+ } catch (Exception ignored) {
+ }
+
+ // Then
+ Mockito.verify(throwsWithSuppressedException, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.verify(noop, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.verify(noop2, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.verify(throwsException, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.verify(throwCustomException, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+
+ Mockito.verify(throwsWithSuppressedException, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ Mockito.verify(noop, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ Mockito.verify(noop2, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ Mockito.verify(throwsException, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ Mockito.verify(throwCustomException, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ }
+
+ @Test
+ public void Should_CatchAndSuppressAnyExceptionsAsCheckpointException_When_CheckpointNotification() {
+ // Given
+ Core.getGlobalContext().register(throwsWithSuppressedException);
+ Core.getGlobalContext().register(throwCustomException);
+
+ // When
+ try {
+ Core.getGlobalContext().beforeCheckpoint(null);
+ } catch (CheckpointException e1) {
+ // Then
+ assertEquals(2, e1.getSuppressed().length);
+ } catch (Throwable e2) {
+ fail("All exceptions thrown during checkpoint notification should be reported as CheckpointException");
+ }
+ }
+
+ @Test
+ public void Should_CatchAndSuppressAnyExceptionsAsRestoreException_When_RestoreNotification() {
+ // Given
+ Core.getGlobalContext().register(throwsWithSuppressedException);
+ Core.getGlobalContext().register(throwCustomException);
+
+ // When
+ try {
+ Core.getGlobalContext().afterRestore(null);
+ } catch (RestoreException e1) {
+ // Then
+ assertEquals(2, e1.getSuppressed().length);
+ } catch (Exception e2) {
+ fail("All exceptions thrown during restore notification should be reported as RestoreException");
+ }
+ }
+
+ @Test
+ public void Should_SuppressOriginalCheckpointExceptionUnderAnotherCheckpointException_When_ResourceIsAContext() throws Exception {
+ // Given
+ Context c0 = Mockito.mock(Context.class);
+ doThrow(CheckpointException.class).when(c0).beforeCheckpoint(ArgumentMatchers.any());
+
+ Core.getGlobalContext().register(c0);
+
+ // When
+ try {
+ Core.getGlobalContext().beforeCheckpoint(null);
+ } catch (CheckpointException e1) {
+ // Then
+ assertEquals(1, e1.getSuppressed().length);
+ assertTrue(e1.getSuppressed()[0] instanceof CheckpointException,
+ "When the Resource is a Context and it throws CheckpointException it should be suppressed under another CheckpointException");
+
+ } catch (Exception e2) {
+ fail("All exceptions thrown during checkpoint notification should be reported as CheckpointException");
+ }
+ }
+
+ @Test
+ public void Should_SuppressOriginalRestoreExceptionUnderAnotherRestoreException_When_ResourceIsAContext() throws Exception {
+ // Given
+ Context c0 = Mockito.mock(Context.class);
+ doThrow(RestoreException.class).when(c0).afterRestore(ArgumentMatchers.any());
+
+ Core.getGlobalContext().register(c0);
+
+ // When
+ try {
+ Core.getGlobalContext().afterRestore(null);
+ } catch (RestoreException e1) {
+ // Then
+ assertEquals(1, e1.getSuppressed().length);
+ assertTrue(e1.getSuppressed()[0] instanceof RestoreException,
+ "When the Resource is a Context and it throws RestoreException it should be suppressed under another RestoreException");
+ } catch (Exception e2) {
+ fail("All exceptions thrown during restore notification should be reported as RestoreException");
+ }
+ }
+
+ @Test
+ public void Should_NotifyOnlyOnce_When_ResourceRegistersMultipleTimes() throws Exception {
+ // Given
+ Core.getGlobalContext().register(noop);
+ Core.getGlobalContext().register(noop);
+
+ // When
+ Core.getGlobalContext().beforeCheckpoint(null);
+ Core.getGlobalContext().afterRestore(null);
+
+ // Then
+ Mockito.verify(noop, Mockito.times(1)).beforeCheckpoint(ArgumentMatchers.any());
+ Mockito.verify(noop, Mockito.times(1)).afterRestore(ArgumentMatchers.any());
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoaderTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoaderTest.java
new file mode 100644
index 000000000..171985dea
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoaderTest.java
@@ -0,0 +1,77 @@
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class EventHandlerLoaderTest {
+
+ @Test
+ void RequestHandlerTest() throws Exception {
+ String handler = "test.lambda.handlers.RequestHandlerImpl";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+ @Test
+ void RequestStreamHandlerTest() throws Exception {
+ String handler = "test.lambda.handlers.RequestStreamHandlerImpl";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+ @Test
+ void PojoHandlerTest_noParams() throws Exception {
+ String handler = "test.lambda.handlers.POJOHanlderImpl::noParamsHandler";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+ @Test
+ void PojoHandlerTest_oneParamEvent() throws Exception {
+ String handler = "test.lambda.handlers.POJOHanlderImpl::oneParamHandler_event";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+
+ @Test
+ void PojoHandlerTest_oneParamContext() throws Exception {
+ String handler = "test.lambda.handlers.POJOHanlderImpl::oneParamHandler_context";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+ @Test
+ void PojoHandlerTest_twoParams() throws Exception {
+ String handler = "test.lambda.handlers.POJOHanlderImpl::twoParamsHandler";
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandler(handler);
+ assertSuccessfulInvocation(lambdaRequestHandler);
+ }
+
+ private LambdaRequestHandler getLambdaRequestHandler(String handler) throws ClassNotFoundException {
+ ClassLoader cl = this.getClass().getClassLoader();
+ HandlerInfo handlerInfo = HandlerInfo.fromString(handler, cl);
+ return EventHandlerLoader.loadEventHandler(handlerInfo);
+ }
+
+ private static void assertSuccessfulInvocation(LambdaRequestHandler lambdaRequestHandler) throws Exception {
+ InvocationRequest invocationRequest = getTestInvocationRequest();
+
+ ByteArrayOutputStream resultBytes = lambdaRequestHandler.call(invocationRequest);
+ String result = resultBytes.toString();
+
+ assertEquals("\"success\"", result);
+ }
+
+ private static InvocationRequest getTestInvocationRequest() {
+ InvocationRequest invocationRequest = new InvocationRequest();
+ invocationRequest.setContent("\"Hello\"".getBytes());
+ invocationRequest.setId("id");
+ invocationRequest.setXrayTraceId("traceId");
+ return invocationRequest;
+ }
+}
\ No newline at end of file
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/UserFaultTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/UserFaultTest.java
index cdc7a9b1d..9e88024b0 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/UserFaultTest.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/UserFaultTest.java
@@ -4,10 +4,9 @@
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.*;
import static testpkg.StackTraceHelper.callThenThrowRuntimeException;
+import static testpkg.StackTraceHelper.throwCheckpointExceptionWithTwoSuppressedExceptions;
import static testpkg.StackTraceHelper.throwRuntimeException;
public class UserFaultTest {
@@ -71,4 +70,18 @@ public void testReportableErrorOnlyMessage() {
String actual = userFault.reportableError();
assertEquals(expected, actual);
}
+
+ @Test
+ public void testSuppressedExceptionsAreIncluded() {
+ try{
+ throwCheckpointExceptionWithTwoSuppressedExceptions("error 1", "error 2");
+ } catch(Exception e1) {
+ UserFault userFault = UserFault.makeUserFault(e1);
+ String reportableUserFault = userFault.reportableError();
+
+ assertTrue(reportableUserFault.contains("com.amazonaws.services.lambda.crac.CheckpointException"), "CheckpointException missing in reported UserFault");
+ assertTrue(reportableUserFault.contains("Suppressed: java.lang.RuntimeException: error 1"), "Suppressed error 1 missing in reported UserFault");
+ assertTrue(reportableUserFault.contains("Suppressed: java.lang.RuntimeException: error 2"), "Suppressed error 2 missing in reported UserFault");
+ }
+ }
}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLoggerTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLoggerTest.java
new file mode 100644
index 000000000..baeb4c242
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/AbstractLambdaLoggerTest.java
@@ -0,0 +1,143 @@
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+import org.junit.jupiter.api.Test;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+
+public class AbstractLambdaLoggerTest {
+ class TestSink implements LogSink {
+ private List messages = new LinkedList<>();
+
+ public TestSink() {
+ }
+
+ @Override
+ public void log(byte[] message) {
+ messages.add(message);
+ }
+
+ @Override
+ public void log(LogLevel logLevel, LogFormat logFormat, byte[] message) {
+ messages.add(message);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ List getMessages() {
+ return messages;
+ }
+ }
+
+ private void logMessages(LambdaLogger logger) {
+ logger.log("trace", LogLevel.TRACE);
+ logger.log("debug", LogLevel.DEBUG);
+ logger.log("info", LogLevel.INFO);
+ logger.log("warn", LogLevel.WARN);
+ logger.log("error", LogLevel.ERROR);
+ logger.log("fatal", LogLevel.FATAL);
+ }
+
+ @Test
+ public void testLoggingNullValuesWithoutLogLevelInText() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.INFO, LogFormat.TEXT);
+
+ String isNullString = null;
+ byte[] isNullBytes = null;
+
+ logger.log(isNullString);
+ logger.log(isNullBytes);
+
+ assertEquals("null", new String(sink.getMessages().get(0)));
+ assertEquals("null", new String(sink.getMessages().get(1)));
+ }
+
+ @Test
+ public void testLoggingNullValuesWithoutLogLevelInJSON() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.INFO, LogFormat.JSON);
+
+ String isNullString = null;
+ byte[] isNullBytes = null;
+
+ logger.log(isNullString);
+ logger.log(isNullBytes);
+
+ assertEquals(2, sink.getMessages().size());
+ }
+
+ @Test
+ public void testLoggingNullValuesWithLogLevelInText() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.INFO, LogFormat.TEXT);
+
+ String isNullString = null;
+ byte[] isNullBytes = null;
+
+ logger.log(isNullString, LogLevel.ERROR);
+ logger.log(isNullBytes, LogLevel.ERROR);
+
+ assertEquals("[ERROR] null", new String(sink.getMessages().get(0)));
+ assertEquals("null", new String(sink.getMessages().get(1)));
+ }
+
+ @Test
+ public void testLoggingNullValuesWithLogLevelInJSON() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.INFO, LogFormat.JSON);
+
+ String isNullString = null;
+ byte[] isNullBytes = null;
+
+ logger.log(isNullString, LogLevel.ERROR);
+ logger.log(isNullBytes, LogLevel.ERROR);
+
+ assertEquals(2, sink.getMessages().size());
+ }
+ @Test
+ public void testWithoutFiltering() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.UNDEFINED, LogFormat.TEXT);
+ logMessages(logger);
+
+ assertEquals(6, sink.getMessages().size());
+ }
+
+ @Test
+ public void testWithFiltering() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.WARN, LogFormat.TEXT);
+ logMessages(logger);
+
+ assertEquals(3, sink.getMessages().size());
+ }
+
+ @Test
+ public void testUndefinedLogLevelWithFiltering() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.WARN, LogFormat.TEXT);
+ logger.log("undefined");
+
+ assertEquals(1, sink.getMessages().size());
+ }
+
+ @Test
+ public void testFormattingLogMessages() {
+ TestSink sink = new TestSink();
+ LambdaLogger logger = new LambdaContextLogger(sink, LogLevel.INFO, LogFormat.TEXT);
+ logger.log("test message", LogLevel.INFO);
+
+ assertEquals(1, sink.getMessages().size());
+ assertEquals("[INFO] test message", new String(sink.getMessages().get(0)));
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameTypeTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameTypeTest.java
new file mode 100644
index 000000000..65078790c
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameTypeTest.java
@@ -0,0 +1,39 @@
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+
+public class FrameTypeTest {
+
+ @Test
+ public void logFrames() {
+ assertHexEquals(
+ 0xa55a0003,
+ FrameType.getValue(LogLevel.UNDEFINED, LogFormat.TEXT)
+ );
+
+ assertHexEquals(
+ 0xa55a001b,
+ FrameType.getValue(LogLevel.FATAL, LogFormat.TEXT)
+ );
+ }
+
+
+ /**
+ * Helper function to make it easier to debug failing test.
+ *
+ * @param expected Expected value as int
+ * @param actual Actual value as int
+ */
+ private void assertHexEquals(int expected, int actual) {
+ assertEquals(
+ Integer.toHexString(expected),
+ Integer.toHexString(actual)
+ );
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSinkTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSinkTest.java
index e8dbb73bb..e3e68a693 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSinkTest.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSinkTest.java
@@ -20,6 +20,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+
public class FramedTelemetryLogSinkTest {
private static final int DEFAULT_BUFFER_SIZE = 256;
@@ -35,13 +38,16 @@ private long timestamp() {
@Test
public void logSingleFrame() throws IOException {
- byte[] message = "hello world\nsomething on a new line!\n".getBytes();
+ byte[] message = "{\"message\": \"hello world\nsomething on a new line!\"}".getBytes();
+ LogLevel logLevel = LogLevel.ERROR;
+ LogFormat logFormat = LogFormat.JSON;
+
File tmpFile = tmpFolder.resolve("pipe").toFile();
FileOutputStream fos = new FileOutputStream(tmpFile);
FileDescriptor fd = fos.getFD();
long before = timestamp();
try (FramedTelemetryLogSink logSink = new FramedTelemetryLogSink(fd)) {
- logSink.log(message);
+ logSink.log(logLevel, logFormat, message);
}
long after = timestamp();
@@ -54,7 +60,7 @@ public void logSingleFrame() throws IOException {
// first 4 bytes indicate the type
int type = buf.getInt();
- assertEquals(FrameType.LOG.getValue(), type);
+ assertEquals(FrameType.getValue(logLevel, logFormat), type);
// next 4 bytes indicate the length of the message
int len = buf.getInt();
@@ -71,7 +77,7 @@ public void logSingleFrame() throws IOException {
assertArrayEquals(message, actual);
// rest of buffer should be empty
- while(buf.hasRemaining())
+ while (buf.hasRemaining())
assertEquals(ZERO_BYTE, buf.get());
}
@@ -79,13 +85,16 @@ public void logSingleFrame() throws IOException {
public void logMultipleFrames() throws IOException {
byte[] firstMessage = "hello world\nsomething on a new line!".getBytes();
byte[] secondMessage = "hello again\nhere's another message\n".getBytes();
+ LogLevel logLevel = LogLevel.ERROR;
+ LogFormat logFormat = LogFormat.TEXT;
+
File tmpFile = tmpFolder.resolve("pipe").toFile();
FileOutputStream fos = new FileOutputStream(tmpFile);
FileDescriptor fd = fos.getFD();
long before = timestamp();
try (FramedTelemetryLogSink logSink = new FramedTelemetryLogSink(fd)) {
- logSink.log(firstMessage);
- logSink.log(secondMessage);
+ logSink.log(logLevel, logFormat, firstMessage);
+ logSink.log(logLevel, logFormat, secondMessage);
}
long after = timestamp();
@@ -96,10 +105,10 @@ public void logMultipleFrames() throws IOException {
// reset the position to the start
buf.position(0);
- for(byte[] message : Arrays.asList(firstMessage, secondMessage)) {
+ for (byte[] message : Arrays.asList(firstMessage, secondMessage)) {
// first 4 bytes indicate the type
int type = buf.getInt();
- assertEquals(FrameType.LOG.getValue(), type);
+ assertEquals(FrameType.getValue(logLevel, logFormat), type);
// next 4 bytes indicate the length of the message
int len = buf.getInt();
@@ -117,7 +126,7 @@ public void logMultipleFrames() throws IOException {
}
// rest of buffer should be empty
- while(buf.hasRemaining())
+ while (buf.hasRemaining())
assertEquals(ZERO_BYTE, buf.get());
}
@@ -125,7 +134,7 @@ public void logMultipleFrames() throws IOException {
* The implementation of FramedTelemetryLogSink was based on java.nio.channels.WritableByteChannel which would
* throw ClosedByInterruptException if Thread.currentThread.interrupt() was called. The implementation was changed
* and this test ensures that logging works even if the current thread was interrupted.
- *
+ *
* https://t.corp.amazon.com/0304370986/
*/
@Test
@@ -138,7 +147,7 @@ public void interruptedThread() throws IOException {
try (FramedTelemetryLogSink logSink = new FramedTelemetryLogSink(fd)) {
Thread.currentThread().interrupt();
- logSink.log(message);
+ logSink.log(LogLevel.ERROR, LogFormat.TEXT, message);
}
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
@@ -150,8 +159,8 @@ public void interruptedThread() throws IOException {
assertEquals(expectedBytes, readBytes);
- for(int i = 0; i < message.length; i++) {
- assertEquals(buffer[i + headerSizeBytes], message[i]);
+ for (int i = 0; i < message.length; i++) {
+ assertEquals(message[i], buffer[i + headerSizeBytes]);
}
} finally {
// clear interrupted status of the current thread
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatterTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatterTest.java
new file mode 100644
index 000000000..8630d5fe6
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/JsonLogFormatterTest.java
@@ -0,0 +1,57 @@
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+public class JsonLogFormatterTest {
+
+ @Test
+ void testFormattingWithoutLambdaContext() {
+ assertFormatsString("test log", LogLevel.WARN, null);
+ }
+
+ @Test
+ void testFormattingWithLambdaContext() {
+ LambdaContext context = new LambdaContext(
+ 0,
+ 0,
+ "request-id",
+ null,
+ null,
+ "function-name",
+ null,
+ null,
+ "function-arn",
+ null
+ );
+ assertFormatsString("test log", LogLevel.WARN, context);
+ }
+
+ void assertFormatsString(String message, LogLevel logLevel, LambdaContext context) {
+ JsonLogFormatter logFormatter = new JsonLogFormatter();
+ if (context != null) {
+ logFormatter.setLambdaContext(context);
+ }
+ String output = logFormatter.format(message, logLevel);
+
+ PojoSerializer serializer = GsonFactory.getInstance().getSerializer(StructuredLogMessage.class);
+ assert_expected_log_message(serializer.fromJson(output), message, logLevel, context);
+ }
+
+ void assert_expected_log_message(StructuredLogMessage result, String message, LogLevel logLevel, LambdaContext context) {
+ assertEquals(message, result.message);
+ assertEquals(logLevel, result.level);
+ assertNotNull(result.timestamp);
+
+ if (context != null) {
+ assertEquals(context.getAwsRequestId(), result.AWSRequestId);
+ }
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSinkTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSinkTest.java
index 83399646b..b1bbefc4c 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSinkTest.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSinkTest.java
@@ -10,6 +10,9 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
public class StdOutLogSinkTest {
private final PrintStream originalOutPrintStream = System.out;
@@ -35,6 +38,20 @@ public void testSingleLog() {
assertEquals("hello\nworld", bos.toString());
}
+ @Test
+ public void testSingleLogWithLogLevel() {
+ System.setOut(capturedOutPrintStream);
+ try {
+ try (StdOutLogSink logSink = new StdOutLogSink()) {
+ logSink.log(LogLevel.ERROR, LogFormat.TEXT, "hello\nworld".getBytes());
+ }
+ } finally {
+ System.setOut(originalOutPrintStream);
+ }
+
+ assertEquals("hello\nworld", bos.toString());
+ }
+
@Test
public void testContextLoggerWithStdoutLogSink_logBytes() {
System.setOut(capturedOutPrintStream);
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatterTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatterTest.java
new file mode 100644
index 000000000..598074a3b
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/logging/TextLogFormatterTest.java
@@ -0,0 +1,25 @@
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+
+class TextLogFormatterTest {
+ @Test
+ void testFormattingStringWithLogLevel() {
+ assertFormatsString("test log", LogLevel.WARN, "[WARN] test log");
+ }
+
+ @Test
+ void testFormattingStringWithoutLogLevel() {
+ assertFormatsString("test log", LogLevel.UNDEFINED, "test log");
+ }
+
+ void assertFormatsString(String input, LogLevel logLevel, String expected) {
+ LogFormatter logFormatter = new TextLogFormatter();
+ String output = logFormatter.format(input, logLevel);
+ assertEquals(expected, output);
+ }
+}
\ No newline at end of file
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactoryTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactoryTest.java
deleted file mode 100644
index 8fc97f2b0..000000000
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactoryTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
-
-package com.amazonaws.services.lambda.runtime.serialization.factories;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-public class JacksonFactoryTest {
-
- @Test
- public void deserializeVoidAsNonNull() throws Exception {
- JacksonFactory instance = JacksonFactory.getInstance();
- Void actual = instance.getMapper().readValue("{}", Void.class);
- assertNotNull(actual);
- }
-
-}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java
new file mode 100644
index 000000000..ca1a6bd4f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java
@@ -0,0 +1,26 @@
+package test.lambda.handlers;
+
+import com.amazonaws.services.lambda.runtime.Context;
+
+@SuppressWarnings("unused")
+public class POJOHanlderImpl {
+ @SuppressWarnings("unused")
+ public String noParamsHandler() {
+ return "success";
+ }
+
+ @SuppressWarnings("unused")
+ public String oneParamHandler_event(String event) {
+ return "success";
+ }
+
+ @SuppressWarnings("unused")
+ public String oneParamHandler_context(Context context) {
+ return "success";
+ }
+
+ @SuppressWarnings("unused")
+ public String twoParamsHandler(String event, Context context) {
+ return "success";
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestHandlerImpl.java b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestHandlerImpl.java
new file mode 100644
index 000000000..47fbade4d
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestHandlerImpl.java
@@ -0,0 +1,12 @@
+package test.lambda.handlers;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+
+
+public class RequestHandlerImpl implements RequestHandler {
+ @Override
+ public String handleRequest(String event, Context context) {
+ return "success";
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestStreamHandlerImpl.java b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestStreamHandlerImpl.java
new file mode 100644
index 000000000..2bf2212c1
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/RequestStreamHandlerImpl.java
@@ -0,0 +1,16 @@
+package test.lambda.handlers;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+@SuppressWarnings("unused")
+public class RequestStreamHandlerImpl implements RequestStreamHandler {
+ @Override
+ public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
+ output.write("\"success\"".getBytes());
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/testpkg/StackTraceHelper.java b/aws-lambda-java-runtime-interface-client/src/test/java/testpkg/StackTraceHelper.java
index b9442c25e..c7d8cb834 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/testpkg/StackTraceHelper.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/testpkg/StackTraceHelper.java
@@ -2,9 +2,11 @@
package testpkg;
+import com.amazonaws.services.lambda.crac.CheckpointException;
+
/**
- * A helper class for throwing exception which is not in the `lambdainternal` package to avoid the stack traces from
- * being filtered out.
+ * A helper class for throwing exception which is not in the com.amazonaws.services.lambda.runtime.api.client package
+ * to avoid the stack traces from being filtered out.
*
*/
public class StackTraceHelper {
@@ -21,4 +23,11 @@ public static void throwRuntimeException(String msg){
public static void callThenThrowRuntimeException(String msg){
throwRuntimeException(msg);
}
+
+ public static void throwCheckpointExceptionWithTwoSuppressedExceptions(String msg1, String msg2) throws CheckpointException {
+ CheckpointException e1 = new CheckpointException();
+ e1.addSuppressed(new RuntimeException(msg1));
+ e1.addSuppressed(new RuntimeException(msg2));
+ throw e1;
+ }
}
diff --git a/aws-lambda-java-runtime-interface-client/test/integration/test-handler/pom.xml b/aws-lambda-java-runtime-interface-client/test/integration/test-handler/pom.xml
index 5cf074d1e..028e92f56 100644
--- a/aws-lambda-java-runtime-interface-client/test/integration/test-handler/pom.xml
+++ b/aws-lambda-java-runtime-interface-client/test/integration/test-handler/pom.xml
@@ -15,7 +15,7 @@
com.amazonaws
aws-lambda-java-runtime-interface-client
- 2.1.1
+ 2.4.1
diff --git a/aws-lambda-java-serialization/RELEASE.CHANGELOG.md b/aws-lambda-java-serialization/RELEASE.CHANGELOG.md
index d1a3cd97d..5ca416845 100644
--- a/aws-lambda-java-serialization/RELEASE.CHANGELOG.md
+++ b/aws-lambda-java-serialization/RELEASE.CHANGELOG.md
@@ -1,3 +1,16 @@
+### December 1, 2023
+`1.1.5`:
+- Add support for DynamodbEvent.DynamodbStreamRecord serialization
+
+### October 19, 2023
+`1.1.4`:
+- Update org.json version to 20231013
+- Rollback relocation changes(1.1.3 version)
+
+### September 21, 2023
+`1.1.3`:
+- Add support for event v4 lib
+
### February 22, 2023
`1.1.1`:
- Register `JodaModule` to JacksonFactory
diff --git a/aws-lambda-java-serialization/pom.xml b/aws-lambda-java-serialization/pom.xml
index 1885311cb..07ccecc8c 100644
--- a/aws-lambda-java-serialization/pom.xml
+++ b/aws-lambda-java-serialization/pom.xml
@@ -4,7 +4,7 @@
com.amazonaws
aws-lambda-java-serialization
- 1.1.1
+ 1.1.5
jar
AWS Lambda Java Runtime Serialization
@@ -34,7 +34,7 @@
com.amazonaws.lambda.thirdparty
2.14.2
2.10.1
- 5.9.1
+ 20231013
7.3.2
@@ -68,26 +68,7 @@
org.json
json
- 20160810
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- ${junit.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-params
- ${junit.version}
- test
-
-
- com.amazonaws
- aws-lambda-java-events
- 3.11.0
- test
+ ${json.version}
@@ -235,13 +216,19 @@
com.google.gson
${relocation.prefix}.com.google.gson
+
+ org.json
+ ${relocation.prefix}.org.json
+
org.joda.time
${relocation.prefix}.org.joda.time
+
- org.json
- ${relocation.prefix}.org.json
+ com.amazonaws.lambda.unshade.thirdparty.org.joda.time
+ org.joda.time
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
index b24b40609..4173211e1 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
@@ -158,7 +158,7 @@ public class LambdaEventSerializers {
* If mixins are required for inner classes of an event, then those nested classes must be specified here.
*/
@SuppressWarnings("rawtypes")
- private static final Map> NESTED_CLASS_MAP = Stream.of(
+ private static final Map> NESTED_CLASS_MAP = Stream.of(
new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.CodeCommitEvent",
Arrays.asList(
new NestedClass("com.amazonaws.services.lambda.runtime.events.CodeCommitEvent$Record"))),
@@ -180,6 +180,14 @@ public class LambdaEventSerializers {
"com.amazonaws.services.lambda.runtime.events.models.dynamodb.StreamRecord",
"com.amazonaws.services.dynamodbv2.model.StreamRecord"),
new NestedClass("com.amazonaws.services.lambda.runtime.events.DynamodbEvent$DynamodbStreamRecord"))),
+ new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.DynamodbEvent$DynamodbStreamRecord",
+ Arrays.asList(
+ new AlternateNestedClass(
+ "com.amazonaws.services.lambda.runtime.events.models.dynamodb.AttributeValue",
+ "com.amazonaws.services.dynamodbv2.model.AttributeValue"),
+ new AlternateNestedClass(
+ "com.amazonaws.services.lambda.runtime.events.models.dynamodb.StreamRecord",
+ "com.amazonaws.services.dynamodbv2.model.StreamRecord"))),
new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.DynamodbTimeWindowEvent",
Arrays.asList(
new AlternateNestedClass(
@@ -236,7 +244,7 @@ public static PojoSerializer serializerFor(Class eventClass, ClassLoad
}
// if event model has nested classes then load those classes and check if mixins apply
if (NESTED_CLASS_MAP.containsKey(eventClass.getName())) {
- List nestedClasses = NESTED_CLASS_MAP.get(eventClass.getName());
+ List extends NestedClass> nestedClasses = NESTED_CLASS_MAP.get(eventClass.getName());
for (NestedClass nestedClass: nestedClasses) {
// if mixin exists for nested class then apply
if (MIXIN_MAP.containsKey(nestedClass.className)) {
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateTimeModule.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateTimeModule.java
index 592d05338..a02857e00 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateTimeModule.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateTimeModule.java
@@ -23,7 +23,8 @@ public class DateTimeModule extends JodaModule {
* creates a DateTimeModule using customer class loader to pull org.joda.time.DateTime
*/
public DateTimeModule(ClassLoader classLoader) {
- Class dateTimeClass = SerializeUtil.loadCustomerClass("org.joda.time.DateTime", classLoader);
+ // Workaround not to let maven shade plugin relocating string literals https://issues.apache.org/jira/browse/MSHADE-156
+ Class dateTimeClass = SerializeUtil.loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.DateTime", classLoader);
this.addSerializer(dateTimeClass, getSerializer(dateTimeClass, classLoader));
this.addDeserializer(dateTimeClass, getDeserializer(dateTimeClass));
}
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializer.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializer.java
index 6c6f65870..c833abcc1 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializer.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializer.java
@@ -220,7 +220,8 @@ private JSONObject serializeEventNotificationRecord(A eventNotificationRecor
Class requestParametersClass = SerializeUtil.loadCustomerClass(baseClassName + "$RequestParametersEntity", classLoader);
Class responseElementsClass = SerializeUtil.loadCustomerClass(baseClassName + "$ResponseElementsEntity", classLoader);
Class userIdentityClass = SerializeUtil.loadCustomerClass(baseClassName + "$UserIdentityEntity", classLoader);
- Class dateTimeClass = SerializeUtil.loadCustomerClass("org.joda.time.DateTime", classLoader);
+ // Workaround not to let maven shade plugin relocating string literals https://issues.apache.org/jira/browse/MSHADE-156
+ Class dateTimeClass = SerializeUtil.loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.DateTime", classLoader);
// serialize object
JSONObject jsonObject = new JSONObject();
Functions.R0 getAwsRegionMethod =
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactory.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactory.java
index f7c1dd4a4..660ca8f58 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactory.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/factories/JacksonFactory.java
@@ -114,37 +114,12 @@ private static ObjectMapper createObjectMapper() {
mapper.setConfig(dcfg);
mapper.setSerializationInclusion(Include.NON_NULL);
- SimpleModule module = new SimpleModule();
- module.addDeserializer(Void.class, new VoidDeserializer());
- mapper.registerModule(module);
-
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new Jdk8Module());
return mapper;
}
- public static final class VoidDeserializer extends JsonDeserializer {
-
- private final static Void VOID = createVoid();
-
- private static Void createVoid() {
- try {
- Constructor constructor = Void.class.getDeclaredConstructor();
- constructor.setAccessible(true);
- return constructor.newInstance();
- } catch(Exception e) {
- return null;
- }
- }
-
- @Override
- public Void deserialize(JsonParser parser, DeserializationContext ctx) {
- return VOID;
- }
-
- }
-
private static JsonFactory createJsonFactory() {
JsonFactory factory = JsonFactory.builder()
//Json Read enabled
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/util/SerializeUtil.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/util/SerializeUtil.java
index bd4c7450e..f6acb528f 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/util/SerializeUtil.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/util/SerializeUtil.java
@@ -71,9 +71,10 @@ public static T deserializeDateTime(Class dateTimeClass, String dateTimeS
*/
@SuppressWarnings({"unchecked"})
public static String serializeDateTime(T dateTime, ClassLoader classLoader) {
- Class dateTimeFormatterClass = loadCustomerClass("org.joda.time.format.DateTimeFormatter", classLoader);
- Class dateTimeFormatClass = loadCustomerClass("org.joda.time.format.ISODateTimeFormat", classLoader);
- Class readableInstantInterface = loadCustomerClass("org.joda.time.ReadableInstant", classLoader);
+ // Workaround not to let maven shade plugin relocating string literals https://issues.apache.org/jira/browse/MSHADE-156
+ Class dateTimeFormatterClass = loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.format.DateTimeFormatter", classLoader);
+ Class dateTimeFormatClass = loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.format.ISODateTimeFormat", classLoader);
+ Class readableInstantInterface = loadCustomerClass("com.amazonaws.lambda.unshade.thirdparty.org.joda.time.ReadableInstant", classLoader);
return serializeDateTimeHelper(dateTime, dateTimeFormatterClass, dateTimeFormatClass, readableInstantInterface);
}
diff --git a/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializersTest.java b/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializersTest.java
deleted file mode 100644
index 13358f722..000000000
--- a/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializersTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
-
-package com.amazonaws.services.lambda.runtime.serialization.events;
-
-import com.amazonaws.services.lambda.runtime.events.*;
-import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.stream.Stream;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class LambdaEventSerializersTest {
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- public static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
-
- private static Stream serdeArguments() {
- return Stream.of(
- Arguments.of("api_gateway_proxy_request_event.json", APIGatewayProxyRequestEvent.class),
- Arguments.of("api_gateway_proxy_response_event.json", APIGatewayProxyResponseEvent.class),
- Arguments.of("cloud_front_event.json", CloudFrontEvent.class),
- Arguments.of("cloud_watch_logs_event.json", CloudWatchLogsEvent.class),
- Arguments.of("code_commit_event.json", CodeCommitEvent.class),
- Arguments.of("api_gateway_proxy_response_event.json", APIGatewayProxyResponseEvent.class),
- Arguments.of("cognito_event.json", CognitoEvent.class),
- Arguments.of("config_event.json", ConfigEvent.class),
- Arguments.of("dynamodb_event.json", DynamodbEvent.class),
- Arguments.of("dynamodb_time_window_event.json", DynamodbTimeWindowEvent.class),
- Arguments.of("iot_button_event.json", IoTButtonEvent.class),
- Arguments.of("kinesis_analytics_firehose_input_preprocessing_event.json", KinesisAnalyticsFirehoseInputPreprocessingEvent.class),
- Arguments.of("kinesis_analytics_input_preprocessing_response_event.json", KinesisAnalyticsInputPreprocessingResponse.class),
- Arguments.of("kinesis_analytics_output_delivery_event.json", KinesisAnalyticsOutputDeliveryEvent.class),
- Arguments.of("kinesis_analytics_output_delivery_response_event.json", KinesisAnalyticsOutputDeliveryResponse.class),
- Arguments.of("kinesis_analytics_streams_input_preprocessing_event.json", KinesisAnalyticsStreamsInputPreprocessingEvent.class),
- Arguments.of("kinesis_event.json", KinesisEvent.class),
- Arguments.of("kinesis_time_window_event.json", KinesisTimeWindowEvent.class),
- Arguments.of("kinesis_firehose_event.json", KinesisFirehoseEvent.class),
- Arguments.of("lex_event.json", LexEvent.class),
- Arguments.of("s3_event.json", S3Event.class),
- Arguments.of("scheduled_event.json", ScheduledEvent.class),
- Arguments.of("sns_event.json", SNSEvent.class),
- Arguments.of("sqs_event.json", SQSEvent.class)
- );
- }
-
- @ParameterizedTest(name = "Serde {0} Event")
- @MethodSource("serdeArguments")
- public void testAPIGatewayProxyRequestEvent(final String json,
- final Class> eventClass) throws IOException {
- String expected = readEvent(json);
- String actual = deserializeSerializeJsonToString(expected, eventClass);
-
- assertJsonEqual(expected, actual);
- }
-
- private String readEvent(String filename) throws IOException {
- Path filePath = Paths.get("src", "test", "resources", "event_models", filename);
- byte[] bytes = Files.readAllBytes(filePath);
- return bytesToString(bytes);
- }
-
- private String deserializeSerializeJsonToString(String expected, Class modelClass) {
- PojoSerializer serializer = LambdaEventSerializers.serializerFor(modelClass, SYSTEM_CLASS_LOADER);
-
- T event = serializer.fromJson(expected);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- serializer.toJson(event, baos);
- return bytesToString(baos.toByteArray());
- }
-
- private String bytesToString(byte[] bytes) {
- return new String(bytes, StandardCharsets.UTF_8);
- }
-
- private void assertJsonEqual(String expected, String actual) throws IOException {
- assertEquals(OBJECT_MAPPER.readTree(expected), OBJECT_MAPPER.readTree(actual));
- }
-}
diff --git a/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializerTest.java b/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializerTest.java
deleted file mode 100644
index 8b2d2e875..000000000
--- a/aws-lambda-java-serialization/src/test/java/com/amazonaws/services/lambda/runtime/serialization/events/serializers/S3EventSerializerTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
-
-package com.amazonaws.services.lambda.runtime.serialization.events.serializers;
-
-import com.amazonaws.services.lambda.runtime.events.S3Event;
-import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-
-public class S3EventSerializerTest {
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- public static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
-
- @Test
- public void testSerDeS3Event() throws IOException {
- S3EventSerializer s3EventSerializer = getS3EventSerializerWithClass(S3Event.class);
-
- String expected = readEvent("s3_event.json");
- String actual = deserializeSerializeJsonToString(s3EventSerializer, expected);
-
- assertJsonEqual(expected, actual);
- }
-
- @Test
- public void testSerDeS3EventNotification() throws IOException {
- S3EventSerializer s3EventSerializer = getS3EventSerializerWithClass(S3EventNotification.class);
-
- String expected = readEvent("s3_event.json");
- String actual = deserializeSerializeJsonToString(s3EventSerializer, expected);
-
- assertJsonEqual(expected, actual);
- }
-
- private S3EventSerializer getS3EventSerializerWithClass(Class modelClass) {
- return new S3EventSerializer()
- .withClass(modelClass)
- .withClassLoader(SYSTEM_CLASS_LOADER);
- }
-
- private String readEvent(String filename) throws IOException {
- Path filePath = Paths.get("src", "test", "resources", "event_models", filename);
- byte[] bytes = Files.readAllBytes(filePath);
- return bytesToString(bytes);
- }
-
- private String bytesToString(byte[] bytes) {
- return new String(bytes, StandardCharsets.UTF_8);
- }
-
- private void assertJsonEqual(String expected, String actual) throws IOException {
- assertEquals(OBJECT_MAPPER.readTree(expected), OBJECT_MAPPER.readTree(actual));
- }
-
- private String deserializeSerializeJsonToString(S3EventSerializer s3EventSerializer, String expected) {
- T event = s3EventSerializer.fromJson(expected);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- s3EventSerializer.toJson(event, baos);
- return bytesToString(baos.toByteArray());
- }
-
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_request_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_request_event.json
deleted file mode 100644
index 66c5d9535..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_request_event.json
+++ /dev/null
@@ -1,60 +0,0 @@
-{
- "version": "1.0",
- "path": "/test/hello",
- "headers": {
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
- "Accept-Encoding": "gzip, deflate, lzma, sdch, br",
- "Accept-Language": "en-US,en;q=0.8",
- "CloudFront-Forwarded-Proto": "https",
- "CloudFront-Is-Desktop-Viewer": "true",
- "CloudFront-Is-Mobile-Viewer": "false",
- "CloudFront-Is-SmartTV-Viewer": "false",
- "CloudFront-Is-Tablet-Viewer": "false",
- "CloudFront-Viewer-Country": "US",
- "Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
- "Upgrade-Insecure-Requests": "1",
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
- "Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
- "X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
- "X-Forwarded-For": "192.168.100.1, 192.168.1.1",
- "X-Forwarded-Port": "443",
- "X-Forwarded-Proto": "https"
- },
- "pathParameters": {
- "proxy": "hello"
- },
- "requestContext": {
- "path": "/{proxy+}",
- "accountId": "123456789012",
- "resourceId": "nl9h80",
- "stage": "test-invoke-stage",
- "requestId": "test-invoke-request",
- "identity": {
- "cognitoIdentityPoolId": "",
- "accountId": "123456789012",
- "cognitoIdentityId": "",
- "caller": "AIDAJTIRKKKER4HCKVJZG",
- "apiKey": "test-invoke-api-key",
- "sourceIp": "test-invoke-source-ip",
- "accessKey": "ASIAI6ANUE2RZBMJDQ5A",
- "cognitoAuthenticationType": "",
- "cognitoAuthenticationProvider": "",
- "userArn": "arn:aws:iam::123456789012:user/kdeding",
- "userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_131)",
- "user": "AIDAJTIRKKKER4HCKVJZG"
- },
- "resourcePath": "/{proxy+}",
- "httpMethod": "POST",
- "apiId": "r275xc9bmd"
- },
- "resource": "/{proxy+}",
- "httpMethod": "GET",
- "isBase64Encoded": false,
- "body": "{ \"callerName\": \"John\" }",
- "queryStringParameters": {
- "name": "me"
- },
- "stageVariables": {
- "stageVarName": "stageVarValue"
- }
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_response_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_response_event.json
deleted file mode 100644
index 1b18bcfde..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/api_gateway_proxy_response_event.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "statusCode": 200,
- "headers": {
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
- "Accept-Encoding": "gzip, deflate, lzma, sdch, br",
- "Accept-Language": "en-US,en;q=0.8",
- "CloudFront-Forwarded-Proto": "https",
- "CloudFront-Is-Desktop-Viewer": "true",
- "CloudFront-Is-Mobile-Viewer": "false",
- "CloudFront-Is-SmartTV-Viewer": "false",
- "CloudFront-Is-Tablet-Viewer": "false",
- "CloudFront-Viewer-Country": "US",
- "Host": "wt6mne2s9k.execute-api.us-west-2.amazonaws.com",
- "Upgrade-Insecure-Requests": "1",
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36 OPR/39.0.2256.48",
- "Via": "1.1 fb7cca60f0ecd82ce07790c9c5eef16c.cloudfront.net (CloudFront)",
- "X-Amz-Cf-Id": "nBsWBOrSHMgnaROZJK1wGCZ9PcRcSpq_oSXZNQwQ10OTZL4cimZo3g==",
- "X-Forwarded-For": "192.168.100.1, 192.168.1.1",
- "X-Forwarded-Port": "443",
- "X-Forwarded-Proto": "https"
- },
- "body": "Hello World"
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/cloud_front_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/cloud_front_event.json
deleted file mode 100644
index e91674807..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/cloud_front_event.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "Records": [
- {
- "cf": {
- "config": {
- "distributionId": "EDFDVBD6EXAMPLE"
- },
- "request": {
- "clientIp": "2001:0db8:85a3:0:0:8a2e:0370:7334",
- "method": "GET",
- "uri": "/picture.jpg",
- "headers": {
- "host": [
- {
- "key": "Host",
- "value": "d111111abcdef8.cloudfront.net"
- }
- ],
- "user-agent": [
- {
- "key": "User-Agent",
- "value": "curl/7.51.0"
- }
- ]
- }
- }
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/cloud_watch_logs_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/cloud_watch_logs_event.json
deleted file mode 100644
index 2b455b9bc..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/cloud_watch_logs_event.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "awslogs": {
- "data": "H4sIAAAAAAAAAHWPwQqCQBCGX0Xm7EFtK+smZBEUgXoLCdMhFtKV3akI8d0bLYmibvPPN3wz00CJxmQnTO41whwWQRIctmEcB6sQbFC3CjW3XW8kxpOpP+OC22d1Wml1qZkQGtoMsScxaczKN3plG8zlaHIta5KqWsozoTYw3/djzwhpLwivWFGHGpAFe7DL68JlBUk+l7KSN7tCOEJ4M3/qOI49vMHj+zCKdlFqLaU2ZHV2a4Ct/an0/ivdX8oYc1UVX860fQDQiMdxRQEAAA=="
- }
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/code_commit_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/code_commit_event.json
deleted file mode 100644
index 4e111cdd1..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/code_commit_event.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "Records": [
- {
- "eventId": "163179e8-a983-4e22-8423-38473a2589fd",
- "eventVersion": "1.0",
- "eventTime": "2017-07-17T19:38:56.745Z",
- "eventTriggerName": "notifyme",
- "eventPartNumber": 1,
- "codecommit": {
- "references": [
- {
- "commit": "7a95e78397313b32dc3c1beda4f5c2676c0c1bea",
- "ref": "refs/heads/master",
- "created": true
- }
- ]
- },
- "eventName": "ReferenceChanges",
- "eventTriggerConfigId": "e6f61a39-7e60-47b0-b206-0dfbd2ca0e6e",
- "eventSourceARN": "arn:aws:codecommit:us-west-2:303480592763:garbage",
- "userIdentityARN": "arn:aws:sts::303480592763:assumed-role/Admin/adsuresh-Isengard",
- "eventSource": "aws:codecommit",
- "awsRegion": "us-west-2",
- "eventTotalParts": 1
- }
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/cognito_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/cognito_event.json
deleted file mode 100644
index 88cc60627..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/cognito_event.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "version": 2,
- "eventType": "SyncTrigger",
- "region": "us-east-1",
- "identityPoolId": "identityPoolId",
- "identityId": "identityId",
- "datasetName": "datasetName",
- "datasetRecords": {
- "sampleKey1": {
- "oldValue": "oldValue1",
- "newValue": "newValue1",
- "op": "replace"
- },
- "sampleKey2": {
- "oldValue": "oldValue2",
- "newValue": "newValue2",
- "op": "replace"
- }
- }
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/config_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/config_event.json
deleted file mode 100644
index a5d158de4..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/config_event.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-02-17T01:36:34.043Z\",\"awsAccountId\":\"000000000000\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"ARN\":\"arn:aws:ec2:us-east-1:000000000000:instance/i-00000000\",\"awsRegion\":\"us-east-1\",\"availabilityZone\":\"us-east-1a\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"Foo\":\"Bar\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"foo\":\"bar\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}",
- "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}",
- "resultToken": "myResultToken",
- "eventLeftScope": false,
- "executionRoleArn": "arn:aws:iam::012345678912:role/config-role",
- "configRuleArn": "arn:aws:config:us-east-1:012345678912:config-rule/config-rule-0123456",
- "configRuleName": "change-triggered-config-rule",
- "configRuleId": "config-rule-0123456",
- "accountId": "012345678912",
- "version": "1.0"
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_event.json
deleted file mode 100644
index 6a9c6ac4f..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_event.json
+++ /dev/null
@@ -1,166 +0,0 @@
-{
- "Records": [
- {
- "eventID": "1",
- "dynamodb": {
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "NewImage": {
- "Message": {
- "S": "New item!"
- },
- "Id": {
- "N": "101"
- }
- },
- "StreamViewType": "NEW_AND_OLD_IMAGES",
- "SequenceNumber": "111",
- "SizeBytes": 26
- },
- "awsRegion": "us-west-2",
- "eventName": "INSERT",
- "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
- "eventSource": "aws:dynamodb"
- },
- {
- "eventID": "2",
- "dynamodb": {
- "OldImage": {
- "Message": {
- "S": "New item!"
- },
- "Id": {
- "N": "101"
- }
- },
- "SequenceNumber": "222",
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "SizeBytes": 59,
- "NewImage": {
- "Message": {
- "S": "This item has changed"
- },
- "Id": {
- "N": "101"
- }
- },
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "awsRegion": "us-west-2",
- "eventName": "MODIFY",
- "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
- "eventSource": "aws:dynamodb"
- },
- {
- "eventID": "3",
- "dynamodb": {
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "SizeBytes": 38,
- "SequenceNumber": "333",
- "OldImage": {
- "Message": {
- "S": "This item has changed"
- },
- "Id": {
- "N": "101"
- }
- },
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "awsRegion": "us-west-2",
- "eventName": "REMOVE",
- "eventSourceARN": "arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
- "eventSource": "aws:dynamodb"
- },
- {
- "eventID": "77742f55658ca1effb9ae758c65c8a51",
- "eventName": "INSERT",
- "eventSource": "aws:dynamodb",
- "awsRegion": "us-west-2",
- "dynamodb": {
- "ApproximateCreationDateTime": 1.44130524E9,
- "Keys": {
- "Id": {
- "N": "147"
- }
- },
- "NewImage": {
- "attr_M": {
- "M": {
- "attr_M1": {
- "N": "1580756226"
- },
- "attr_M2": {
- "S": "1580756226"
- }
- }
- },
- "attr_L": {
- "L": [
- {
- "N": "1580756226"
- },
- {
- "S": "1580756226"
- }
- ]
- },
- "attr_BOOL": {
- "BOOL": true
- },
- "s3info": {
- "S": "lambda-ddb-streams-int-test/a2b4c06b-2360-44ac-9bd7-2bab669246b8"
- },
- "attr_NULL": {
- "NULL": true
- },
- "attr_NS": {
- "NS": [
- "1580756227",
- "1580756226"
- ]
- },
- "attr_SS": {
- "SS": [
- "1580756226",
- "1580756227"
- ]
- },
- "attr_BS": {
- "BS": [
- "MTU4MDc1NjIyNg==",
- "MTU4MDc1NjIyNw=="
- ]
- },
- "attr_S": {
- "S": "1580756226"
- },
- "Id": {
- "N": "147"
- },
- "attr_B": {
- "B": "MTU4MDc1NjIyNg=="
- },
- "attr_N": {
- "N": "1580756226"
- }
- },
- "SequenceNumber": "2700000000000002229109",
- "SizeBytes": 285,
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "eventSourceARN": "arn:aws:dynamodb:us-west-2:059493405231:table/lambda-ddb-streams-int-test/stream/2015-06-26T18:21:25.123"
- }
- ]
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_time_window_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_time_window_event.json
deleted file mode 100644
index 237b7bd2f..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/dynamodb_time_window_event.json
+++ /dev/null
@@ -1,101 +0,0 @@
-{
- "Records": [
- {
- "eventID": "1",
- "eventName": "INSERT",
- "eventVersion": "1.0",
- "eventSource": "aws:dynamodb",
- "awsRegion": "us-east-1",
- "dynamodb": {
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "NewImage": {
- "Message": {
- "S": "New item!"
- },
- "Id": {
- "N": "101"
- }
- },
- "SequenceNumber": "111",
- "SizeBytes": 26,
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "eventSourceARN": "stream-ARN"
- },
- {
- "eventID": "2",
- "eventName": "MODIFY",
- "eventVersion": "1.0",
- "eventSource": "aws:dynamodb",
- "awsRegion": "us-east-1",
- "dynamodb": {
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "NewImage": {
- "Message": {
- "S": "This item has changed"
- },
- "Id": {
- "N": "101"
- }
- },
- "OldImage": {
- "Message": {
- "S": "New item!"
- },
- "Id": {
- "N": "101"
- }
- },
- "SequenceNumber": "222",
- "SizeBytes": 59,
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "eventSourceARN": "stream-ARN"
- },
- {
- "eventID": "3",
- "eventName": "REMOVE",
- "eventVersion": "1.0",
- "eventSource": "aws:dynamodb",
- "awsRegion": "us-east-1",
- "dynamodb": {
- "Keys": {
- "Id": {
- "N": "101"
- }
- },
- "OldImage": {
- "Message": {
- "S": "This item has changed"
- },
- "Id": {
- "N": "101"
- }
- },
- "SequenceNumber": "333",
- "SizeBytes": 38,
- "StreamViewType": "NEW_AND_OLD_IMAGES"
- },
- "eventSourceARN": "stream-ARN"
- }
- ],
- "window": {
- "start": "2021-10-26T17:00:00Z",
- "end": "2021-10-26T17:05:00Z"
- },
- "state": {
- "1": "state1"
- },
- "shardId": "shard123456789",
- "eventSourceARN": "stream-ARN",
- "isFinalInvokeForWindow": false,
- "isWindowTerminatedEarly": false
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/iot_button_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/iot_button_event.json
deleted file mode 100644
index 8988caf0e..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/iot_button_event.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "serialNumber": "ABCDEFG12345",
- "clickType": "SINGLE",
- "batteryVoltage": "2000 mV"
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_firehose_input_preprocessing_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_firehose_input_preprocessing_event.json
deleted file mode 100644
index 862f17f29..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_firehose_input_preprocessing_event.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "invocationId":"00540a87-5050-496a-84e4-e7d92bbaf5e2",
- "applicationArn":"arn:aws:kinesisanalytics:us-east-1:12345678911:application/lambda-test",
- "streamArn":"arn:aws:firehose:us-east-1:AAAAAAAAAAAA:deliverystream/lambda-test",
- "records":[
- {
- "recordId":"49572672223665514422805246926656954630972486059535892482",
- "data":"aGVsbG8gd29ybGQ=",
- "kinesisFirehoseRecordMetadata":{
- "approximateArrivalTimestamp":1520280173
- }
- }
- ]
- }
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_input_preprocessing_response_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_input_preprocessing_response_event.json
deleted file mode 100644
index 59b899d6b..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_input_preprocessing_response_event.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "records": [
- {
- "recordId": "49572672223665514422805246926656954630972486059535892482",
- "result": "Ok",
- "data": "SEVMTE8gV09STEQ="
- }
- ]
- }
-
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_event.json
deleted file mode 100644
index d42688d73..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_event.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "invocationId": "00540a87-5050-496a-84e4-e7d92bbaf5e2",
- "applicationArn": "arn:aws:kinesisanalytics:us-east-1:12345678911:application/lambda-test",
- "records": [
- {
- "recordId": "49572672223665514422805246926656954630972486059535892482",
- "data": "aGVsbG8gd29ybGQ="
- }
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_response_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_response_event.json
deleted file mode 100644
index dba7b21d1..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_output_delivery_response_event.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "records": [
- {
- "recordId": "49572672223665514422805246926656954630972486059535892482",
- "result": "Ok"
- }
- ]
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_streams_input_preprocessing_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_streams_input_preprocessing_event.json
deleted file mode 100644
index f7ec383bd..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_analytics_streams_input_preprocessing_event.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "invocationId": "00540a87-5050-496a-84e4-e7d92bbaf5e2",
- "applicationArn": "arn:aws:kinesisanalytics:us-east-1:12345678911:application/lambda-test",
- "streamArn": "arn:aws:kinesis:us-east-1:AAAAAAAAAAAA:stream/lambda-test",
- "records": [
- {
- "recordId": "49572672223665514422805246926656954630972486059535892482",
- "data": "aGVsbG8gd29ybGQ=",
- "kinesisStreamRecordMetadata":{
- "shardId" :"shardId-000000000003",
- "partitionKey":"7400791606",
- "sequenceNumber":"49572672223665514422805246926656954630972486059535892482",
- "approximateArrivalTimestamp":1520280173
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_event.json
deleted file mode 100644
index 854794cfd..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_event.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "Records": [
- {
- "kinesis": {
- "partitionKey": "partitionKey-3",
- "kinesisSchemaVersion": "1.0",
- "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=",
- "sequenceNumber": "49545115243490985018280067714973144582180062593244200961"
- },
- "eventSource": "aws:kinesis",
- "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961",
- "invokeIdentityArn": "arn:aws:iam::EXAMPLE",
- "eventVersion": "1.0",
- "eventName": "aws:kinesis:record",
- "eventSourceARN": "arn:aws:kinesis:EXAMPLE",
- "awsRegion": "us-east-1"
- },
- {
- "kinesis": {
- "partitionKey": "partitionKey-3",
- "kinesisSchemaVersion": "1.0",
- "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=",
- "sequenceNumber": "49545115243490985018280067714973144582180062593244200961",
- "approximateArrivalTimestamp": 1.455606024806E9
- },
- "eventSource": "aws:kinesis",
- "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961",
- "invokeIdentityArn": "arn:aws:iam::EXAMPLE",
- "eventVersion": "1.0",
- "eventName": "aws:kinesis:record",
- "eventSourceARN": "arn:aws:kinesis:EXAMPLE",
- "awsRegion": "us-east-1"
- }
- ]
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_firehose_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_firehose_event.json
deleted file mode 100644
index 3b1cd6c76..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_firehose_event.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "invocationId": "invoked123",
- "deliveryStreamArn": "aws:lambda:events",
- "region": "us-west-2",
- "records": [
- {
- "data": "SGVsbG8gV29ybGQ=",
- "recordId": "record1",
- "approximateArrivalEpoch": 1507217624302,
- "approximateArrivalTimestamp": 1507217624302,
- "kinesisRecordMetadata": {
- "shardId": "shardId-000000000000",
- "partitionKey": "4d1ad2b9-24f8-4b9d-a088-76e9947c317a",
- "approximateArrivalTimestamp": "1507217624302",
- "sequenceNumber": "49546986683135544286507457936321625675700192471156785154",
- "subsequenceNumber": ""
- }
- },
- {
- "data": "SGVsbG8gV29ybGQ=",
- "recordId": "record2",
- "approximateArrivalEpoch": 1507217624302,
- "approximateArrivalTimestamp": 1507217624302,
- "kinesisRecordMetadata": {
- "shardId": "shardId-000000000001",
- "partitionKey": "4d1ad2b9-24f8-4b9d-a088-76e9947c318a",
- "approximateArrivalTimestamp": "1507217624302",
- "sequenceNumber": "49546986683135544286507457936321625675700192471156785155",
- "subsequenceNumber": ""
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_time_window_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_time_window_event.json
deleted file mode 100644
index 024992260..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/kinesis_time_window_event.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "Records": [
- {
- "kinesis": {
- "kinesisSchemaVersion": "1.0",
- "partitionKey": "1",
- "sequenceNumber": "49590338271490256608559692538361571095921575989136588898",
- "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==",
- "approximateArrivalTimestamp": 1545084650.987
- },
- "eventSource": "aws:kinesis",
- "eventVersion": "1.0",
- "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898",
- "eventName": "aws:kinesis:record",
- "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-kinesis-role",
- "awsRegion": "us-east-2",
- "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream"
- }
- ],
- "window": {
- "start": "2021-10-26T17:00:00Z",
- "end": "2021-10-26T17:05:00Z"
- },
- "state": {
- "1": "state1"
- },
- "shardId": "shardId-000000000006",
- "eventSourceARN": "arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream",
- "isFinalInvokeForWindow": false,
- "isWindowTerminatedEarly": false
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/lex_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/lex_event.json
deleted file mode 100644
index 2961ac28c..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/lex_event.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "messageVersion": "1.0",
- "invocationSource": "FulfillmentCodeHook or DialogCodeHook",
- "userId": "user-id specified in the POST request to Amazon Lex.",
- "sessionAttributes": {
- "key1": "value1",
- "key2": "value2"
- },
- "bot": {
- "name": "bot-name",
- "alias": "bot-alias",
- "version": "bot-version"
- },
- "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request",
- "currentIntent": {
- "name": "intent-name",
- "slots": {
- "slot1": "value",
- "slot2": "value",
- "slot3": "value"
- },
- "confirmationStatus": "None"
- }
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/s3_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/s3_event.json
deleted file mode 100644
index 519bc1ce0..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/s3_event.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "Records": [
- {
- "eventVersion": "2.0",
- "eventSource": "aws:s3",
- "awsRegion": "us-east-1",
- "eventTime": "1970-01-01T00:00:00.000Z",
- "eventName": "ObjectCreated:Put",
- "userIdentity": {
- "principalId": "EXAMPLE"
- },
- "requestParameters": {
- "sourceIPAddress": "127.0.0.1"
- },
- "responseElements": {
- "x-amz-request-id": "C3D13FE58DE4C810",
- "x-amz-id-2": "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
- },
- "s3": {
- "s3SchemaVersion": "1.0",
- "configurationId": "testConfigRule",
- "bucket": {
- "name": "sourcebucket",
- "ownerIdentity": {
- "principalId": "EXAMPLE"
- },
- "arn": "arn:aws:s3:::mybucket"
- },
- "object": {
- "key": "Happy%20Face.jpg",
- "size": 1024,
- "urlDecodedKey": "Happy Face.jpg",
- "versionId": "version",
- "eTag": "d41d8cd98f00b204e9800998ecf8427e",
- "sequencer": "Happy Sequencer"
- }
- }
- }
- ]
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/scheduled_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/scheduled_event.json
deleted file mode 100644
index 3d803cc24..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/scheduled_event.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "account": "123456789012",
- "region": "us-east-1",
- "detail": {},
- "detail-type": "Scheduled Event",
- "source": "aws.events",
- "time": "1970-01-01T00:00:00.000Z",
- "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c",
- "resources": [
- "arn:aws:events:us-east-1:123456789012:rule/my-schedule"
- ]
-}
\ No newline at end of file
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/sns_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/sns_event.json
deleted file mode 100644
index ff6246355..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/sns_event.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "Records": [
- {
- "EventVersion": "1.0",
- "EventSubscriptionArn": "arn:aws:sns:EXAMPLE",
- "EventSource": "aws:sns",
- "Sns": {
- "Signature": "EXAMPLE",
- "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
- "Type": "Notification",
- "TopicArn": "arn:aws:sns:EXAMPLE",
- "MessageAttributes": {
- "Test": {
- "Type": "String",
- "Value": "TestString"
- },
- "TestBinary": {
- "Type": "Binary",
- "Value": "TestBinary"
- }
- },
- "SignatureVersion": "1",
- "Timestamp": "2015-06-03T17:43:27.020Z",
- "SigningCertUrl": "EXAMPLE",
- "Message": "Hello from SNS!",
- "UnsubscribeUrl": "EXAMPLE",
- "Subject": "TestInvoke"
- }
- }
- ]
-}
diff --git a/aws-lambda-java-serialization/src/test/resources/event_models/sqs_event.json b/aws-lambda-java-serialization/src/test/resources/event_models/sqs_event.json
deleted file mode 100644
index aa69c3509..000000000
--- a/aws-lambda-java-serialization/src/test/resources/event_models/sqs_event.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "Records": [
- {
- "messageId" : "MessageID_1",
- "receiptHandle" : "MessageReceiptHandle",
- "body" : "Message Body",
- "md5OfBody" : "fce0ea8dd236ccb3ed9b37dae260836f",
- "md5OfMessageAttributes" : "582c92c5c5b6ac403040a4f3ab3115c9",
- "eventSourceARN": "arn:aws:sqs:us-west-2:123456789012:SQSQueue",
- "eventSource": "aws:sqs",
- "awsRegion": "us-west-2",
- "attributes" : {
- "ApproximateReceiveCount" : "2",
- "SentTimestamp" : "1520621625029",
- "SenderId" : "AROAIWPX5BD2BHG722MW4:sender",
- "ApproximateFirstReceiveTimestamp" : "1520621634884"
- },
- "messageAttributes" : {
- "Attribute3" : {
- "binaryValue" : "MTEwMA==",
- "stringListValues" : ["abc", "123"],
- "binaryListValues" : ["MA==", "MQ==", "MA=="],
- "dataType" : "Binary"
- },
- "Attribute2" : {
- "stringValue" : "123",
- "stringListValues" : [ ],
- "binaryListValues" : ["MQ==", "MA=="],
- "dataType" : "Number"
- },
- "Attribute1" : {
- "stringValue" : "AttributeValue1",
- "stringListValues" : [ ],
- "binaryListValues" : [ ],
- "dataType" : "String"
- }
- }
- }
- ]
-}
diff --git a/aws-lambda-java-tests/pom.xml b/aws-lambda-java-tests/pom.xml
index fa986750e..4b04ecf0a 100644
--- a/aws-lambda-java-tests/pom.xml
+++ b/aws-lambda-java-tests/pom.xml
@@ -32,7 +32,7 @@
1.8
1.8
UTF-8
- 5.7.0
+ 5.9.2
0.8.7
@@ -40,12 +40,12 @@
com.amazonaws
aws-lambda-java-serialization
- 1.0.0
+ 1.1.5
com.amazonaws
aws-lambda-java-events
- 3.11.0
+ 3.11.4
org.junit.jupiter
@@ -65,13 +65,13 @@
org.apache.commons
commons-lang3
- 3.11
+ 3.12.0
org.assertj
assertj-core
- 3.18.1
+ 3.24.2
test
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
index 68cd37d3d..7228fb90d 100644
--- a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
@@ -65,6 +65,10 @@ public static DynamodbEvent loadDynamoDbEvent(String filename) {
return loadEvent(filename, DynamodbEvent.class);
}
+ public static DynamodbEvent.DynamodbStreamRecord loadDynamoDbStreamRecord(String filename) {
+ return loadEvent(filename, DynamodbEvent.DynamodbStreamRecord.class);
+ }
+
public static KafkaEvent loadKafkaEvent(String filename) {
return loadEvent(filename, KafkaEvent.class);
}
diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
index 8f8fe50a4..3177b9ccc 100644
--- a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
+++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
@@ -160,27 +160,43 @@ public void testLoadSNSEvent() {
@Test
public void testLoadDynamoEvent() {
- DynamodbEvent event = EventLoader.loadDynamoDbEvent("dynamo_event.json");
+ DynamodbEvent event = EventLoader.loadDynamoDbEvent("ddb/dynamo_event.json");
assertThat(event).isNotNull();
assertThat(event.getRecords()).hasSize(3);
+ assertDynamoDbStreamRecord(event.getRecords().get(1));
+ }
+
+ @Test
+ public void testLoadDynamoDbStreamRecord() {
+ assertDynamoDbStreamRecord(EventLoader.loadDynamoDbStreamRecord("ddb/dynamo_ddb_stream_record.json"));
+ }
- DynamodbEvent.DynamodbStreamRecord record = event.getRecords().get(0);
+ private static void assertDynamoDbStreamRecord(final DynamodbEvent.DynamodbStreamRecord record) {
assertThat(record)
+ .isNotNull()
.returns("arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", from(DynamodbEvent.DynamodbStreamRecord::getEventSourceARN))
- .returns("INSERT", from(Record::getEventName));
+ .returns("MODIFY", from(Record::getEventName));
StreamRecord streamRecord = record.getDynamodb();
assertThat(streamRecord)
- .returns("4421584500000000017450439091", StreamRecord::getSequenceNumber)
- .returns(26L, StreamRecord::getSizeBytes)
+ .returns("4421584500000000017450439092", StreamRecord::getSequenceNumber)
+ .returns(59L, StreamRecord::getSizeBytes)
.returns("NEW_AND_OLD_IMAGES", StreamRecord::getStreamViewType)
- .returns(Date.from(ofEpochSecond(1428537600)), StreamRecord::getApproximateCreationDateTime);
-
- assertThat(streamRecord.getKeys()).contains(entry("Id", new AttributeValue().withN("101")));
- assertThat(streamRecord.getNewImage()).containsAnyOf(
- entry("Message", new AttributeValue("New item!")),
- entry("Id", new AttributeValue().withN("101"))
- );
+ .returns(Date.from(ofEpochSecond(1635734407).plusNanos(123456789)), StreamRecord::getApproximateCreationDateTime);
+
+ assertThat(streamRecord.getKeys())
+ .isNotNull()
+ .contains(entry("Id", new AttributeValue().withN("101")));
+ assertThat(streamRecord.getNewImage())
+ .isNotNull()
+ .containsAnyOf(
+ entry("Message", new AttributeValue("This item has changed")),
+ entry("Id", new AttributeValue().withN("101")));
+ assertThat(streamRecord.getOldImage())
+ .isNotNull()
+ .containsAnyOf(
+ entry("Message", new AttributeValue("New item!")),
+ entry("Id", new AttributeValue().withN("101")));
}
@Test
@@ -205,6 +221,15 @@ public void testLoadActiveMQEvent() {
assertThat(event.getMessages().get(1).getMessageID()).isEqualTo("ID:b-8bcfa572-428a-4642-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1");
}
+ @Test
+ public void testLoadActiveMQEventWithProperties() {
+ ActiveMQEvent event = EventLoader.loadActiveMQEvent("mq_event.json");
+ assertThat(event).isNotNull();
+ assertThat(event.getMessages()).hasSize(2);
+ assertThat(event.getMessages().get(0).getProperties().get("testKey")).isEqualTo("testValue");
+ assertThat(event.getMessages().get(1).getProperties().get("testKey")).isEqualTo("testValue");
+ }
+
@Test
public void testLoadCodeCommitEvent() {
CodeCommitEvent event = EventLoader.loadCodeCommitEvent("codecommit_event.json");
diff --git a/aws-lambda-java-tests/src/test/resources/ddb/dynamo_ddb_stream_record.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_ddb_stream_record.json
new file mode 100644
index 000000000..f5df23ff5
--- /dev/null
+++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_ddb_stream_record.json
@@ -0,0 +1,35 @@
+{
+ "eventID": "c81e728d9d4c2f636f067f89cc14862c",
+ "eventName": "MODIFY",
+ "eventVersion": "1.1",
+ "eventSource": "aws:dynamodb",
+ "awsRegion": "eu-central-1",
+ "dynamodb": {
+ "Keys": {
+ "Id": {
+ "N": "101"
+ }
+ },
+ "NewImage": {
+ "Message": {
+ "S": "This item has changed"
+ },
+ "Id": {
+ "N": "101"
+ }
+ },
+ "OldImage": {
+ "Message": {
+ "S": "New item!"
+ },
+ "Id": {
+ "N": "101"
+ }
+ },
+ "ApproximateCreationDateTime": 1.635734407123456789E9,
+ "SequenceNumber": "4421584500000000017450439092",
+ "SizeBytes": 59,
+ "StreamViewType": "NEW_AND_OLD_IMAGES"
+ },
+ "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899"
+}
diff --git a/aws-lambda-java-tests/src/test/resources/dynamo_event.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event.json
similarity index 97%
rename from aws-lambda-java-tests/src/test/resources/dynamo_event.json
rename to aws-lambda-java-tests/src/test/resources/ddb/dynamo_event.json
index f28ce0e6e..2e43ba497 100644
--- a/aws-lambda-java-tests/src/test/resources/dynamo_event.json
+++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event.json
@@ -59,7 +59,7 @@
"N": "101"
}
},
- "ApproximateCreationDateTime": 1428537600,
+ "ApproximateCreationDateTime": 1.635734407123456789E9,
"SequenceNumber": "4421584500000000017450439092",
"SizeBytes": 59,
"StreamViewType": "NEW_AND_OLD_IMAGES"
diff --git a/aws-lambda-java-tests/src/test/resources/mq_event.json b/aws-lambda-java-tests/src/test/resources/mq_event.json
index a9a798546..6505a22d4 100644
--- a/aws-lambda-java-tests/src/test/resources/mq_event.json
+++ b/aws-lambda-java-tests/src/test/resources/mq_event.json
@@ -13,7 +13,10 @@
},
"timestamp": 1598827811958,
"brokerInTime": 1598827811958,
- "brokerOutTime": 1598827811959
+ "brokerOutTime": 1598827811959,
+ "properties": {
+ "testKey": "testValue"
+ }
},
{
"messageID": "ID:b-8bcfa572-428a-4642-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1",
@@ -26,7 +29,10 @@
},
"timestamp": 1598827811958,
"brokerInTime": 1598827811958,
- "brokerOutTime": 1598827811959
+ "brokerOutTime": 1598827811959,
+ "properties": {
+ "testKey": "testValue"
+ }
}
]
}
\ No newline at end of file
diff --git a/samples/kinesis-firehose-event-handler/pom.xml b/samples/kinesis-firehose-event-handler/pom.xml
index b4a949ba0..2a14336d3 100644
--- a/samples/kinesis-firehose-event-handler/pom.xml
+++ b/samples/kinesis-firehose-event-handler/pom.xml
@@ -41,12 +41,12 @@
com.amazonaws
aws-lambda-java-core
- 1.2.1
+ 1.2.3
com.amazonaws
aws-lambda-java-events
- 3.11.0
+ 3.11.4