getGlobalContext() {
+ return globalContext;
+ }
+
+ public static void checkpointRestore() {
+ throw new UnsupportedOperationException();
+ }
+
+ static void resetGlobalContext() {
+ globalContext = new ContextImpl();
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/DNSManager.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/DNSManager.java
new file mode 100644
index 000000000..6c485ec80
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/DNSManager.java
@@ -0,0 +1,10 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.crac;
+
+class DNSManager {
+ static native void clearCache();
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/Resource.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/Resource.java
new file mode 100644
index 000000000..7ef933202
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/Resource.java
@@ -0,0 +1,12 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.crac;
+
+public interface Resource {
+ void afterRestore(Context extends Resource> context) throws Exception;
+
+ void beforeCheckpoint(Context extends Resource> context) throws Exception;
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/RestoreException.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/RestoreException.java
new file mode 100644
index 000000000..cef38e00f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/crac/RestoreException.java
@@ -0,0 +1,13 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.crac;
+
+public class RestoreException extends Exception {
+ private static final long serialVersionUID = -823900409868237860L;
+
+ public RestoreException() {
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java
new file mode 100644
index 000000000..e5b221a80
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java
@@ -0,0 +1,376 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.crac.Core;
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.api.client.LambdaRequestHandler.UserFaultHandler;
+import com.amazonaws.services.lambda.runtime.api.client.logging.FramedTelemetryLogSink;
+import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger;
+import com.amazonaws.services.lambda.runtime.api.client.logging.LogSink;
+import com.amazonaws.services.lambda.runtime.api.client.logging.StdOutLogSink;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaError;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClient;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClientImpl;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClientMaxRetriesExceededException;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.RapidErrorType;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.LambdaErrorConverter;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.XRayErrorCauseConverter;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest;
+import com.amazonaws.services.lambda.runtime.api.client.util.ConcurrencyConfig;
+import com.amazonaws.services.lambda.runtime.api.client.util.LambdaOutputStream;
+import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
+import com.amazonaws.services.lambda.runtime.logging.LogFormat;
+import com.amazonaws.services.lambda.runtime.logging.LogLevel;
+import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.net.URLClassLoader;
+import java.security.Security;
+import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import software.amazon.awssdk.utilslite.SdkInternalThreadLocal;
+
+/**
+ * The entrypoint of this class is {@link AWSLambda#startRuntime}. It performs two main tasks:
+ *
+ *
+ * 1. loads the user's handler.
+ *
+ * 2. enters the Lambda runtime loop which handles function invocations as defined in the Lambda Custom Runtime API.
+ *
+ *
+ * Once initialized, {@link AWSLambda#startRuntime} will halt only if an irrecoverable error occurs.
+ */
+public class AWSLambda {
+
+ private static URLClassLoader customerClassLoader;
+
+ private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+
+ private static final String JAVA_SECURITY_PROPERTIES = "java.security.properties";
+
+ private static final String NETWORKADDRESS_CACHE_NEGATIVE_TTL_ENV_VAR = "AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL";
+
+ private static final String NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY = "networkaddress.cache.negative.ttl";
+
+ private static final String DEFAULT_NEGATIVE_CACHE_TTL = "1";
+
+ // System property for Lambda tracing, see aws-xray-sdk-java/LambdaSegmentContext
+ // https://github.com/aws/aws-xray-sdk-java/blob/2f467e50db61abb2ed2bd630efc21bddeabd64d9/aws-xray-recorder-sdk-core/src/main/java/com/amazonaws/xray/contexts/LambdaSegmentContext.java#L39-L40
+ private static final String LAMBDA_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader";
+
+ private static final String INIT_TYPE_SNAP_START = "snap-start";
+
+ private static final String AWS_LAMBDA_INITIALIZATION_TYPE = System.getenv(ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_INITIALIZATION_TYPE);
+
+ private static final String CONCURRENT_TRACE_ID_KEY = "AWS_LAMBDA_X_TRACE_ID";
+
+ static {
+ // Override the disabledAlgorithms setting to match configuration for openjdk8-u181.
+ // This is to keep DES ciphers around while we deploying security updates.
+ Security.setProperty(
+ "jdk.tls.disabledAlgorithms",
+ "SSLv3, RC4, MD5withRSA, DH keySize < 1024, EC keySize < 224, DES40_CBC, RC4_40, 3DES_EDE_CBC"
+ );
+ // Override the location of the trusted certificate authorities to be provided by the system.
+ // The ca-certificates package provides /etc/pki/java/cacerts which becomes the symlink destination
+ // of $java_home/lib/security/cacerts when java is installed in the chroot. Given that java is provided
+ // in /var/lang as opposed to installed in the chroot, this brings it closer.
+ if (System.getProperty(TRUST_STORE_PROPERTY) == null) {
+ final File systemCacerts = new File("/etc/pki/java/cacerts");
+ if (systemCacerts.exists() && systemCacerts.isFile()) {
+ System.setProperty(TRUST_STORE_PROPERTY, systemCacerts.getPath());
+ }
+ }
+
+ if (isNegativeCacheOverridable()) {
+ String ttlFromEnv = System.getenv(NETWORKADDRESS_CACHE_NEGATIVE_TTL_ENV_VAR);
+ String negativeCacheTtl = ttlFromEnv == null ? DEFAULT_NEGATIVE_CACHE_TTL : ttlFromEnv;
+ Security.setProperty(NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY, negativeCacheTtl);
+ }
+ }
+
+ private static boolean isNegativeCacheOverridable() {
+ String securityPropertiesPath = System.getProperty(JAVA_SECURITY_PROPERTIES);
+ if (securityPropertiesPath == null) {
+ return true;
+ }
+ try (FileInputStream inputStream = new FileInputStream(securityPropertiesPath)) {
+ Properties secProps = new Properties();
+ secProps.load(inputStream);
+ return !secProps.containsKey(NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY);
+ } catch (IOException e) {
+ return true;
+ }
+ }
+
+ private static LambdaRequestHandler findRequestHandler(final String handlerString, ClassLoader customerClassLoader) {
+ final HandlerInfo handlerInfo;
+ try {
+ handlerInfo = HandlerInfo.fromString(handlerString, customerClassLoader);
+ } catch (HandlerInfo.InvalidHandlerException e) {
+ UserFault userFault = UserFault.makeUserFault("Invalid handler: `" + handlerString + "'");
+ return new UserFaultHandler(userFault);
+ } catch (ClassNotFoundException e) {
+ return LambdaRequestHandler.classNotFound(e, HandlerInfo.className(handlerString));
+ } catch (NoClassDefFoundError e) {
+ return LambdaRequestHandler.initErrorHandler(e, HandlerInfo.className(handlerString));
+ } catch (Throwable t) {
+ throw UserFault.makeInitErrorUserFault(t, HandlerInfo.className(handlerString));
+ }
+
+ final LambdaRequestHandler requestHandler = EventHandlerLoader.loadEventHandler(handlerInfo);
+ // if loading the handler failed and the failure is fatal (for e.g. the constructor threw an exception)
+ // we want to report this as an init error rather than deferring to the first invoke.
+ if (requestHandler instanceof UserFaultHandler) {
+ UserFault userFault = ((UserFaultHandler) requestHandler).fault;
+ if (userFault.fatal) {
+ throw userFault;
+ }
+ }
+ return requestHandler;
+ }
+
+ private static LambdaRequestHandler getLambdaRequestHandlerObject(String handler, LambdaContextLogger lambdaLogger, LambdaRuntimeApiClient runtimeClient) throws ClassNotFoundException, IOException {
+ UnsafeUtil.disableIllegalAccessWarning();
+
+ System.setOut(new PrintStream(new LambdaOutputStream(System.out), false, "UTF-8"));
+ System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8"));
+ setupRuntimeLogger(lambdaLogger);
+
+ String taskRoot = System.getProperty("user.dir");
+ String libRoot = "/opt/java";
+ // Make system classloader the customer classloader's parent to ensure any aws-lambda-java-core classes
+ // are loaded from the system classloader.
+ customerClassLoader = new CustomerClassLoader(taskRoot, libRoot, ClassLoader.getSystemClassLoader());
+ Thread.currentThread().setContextClassLoader(customerClassLoader);
+
+ // Load the user's handler
+ LambdaRequestHandler requestHandler = null;
+ try {
+ requestHandler = findRequestHandler(handler, customerClassLoader);
+ } catch (UserFault userFault) {
+ lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
+ LambdaError error = new LambdaError(
+ LambdaErrorConverter.fromUserFault(userFault),
+ RapidErrorType.BadFunctionCode);
+ runtimeClient.reportInitError(error);
+ System.exit(1);
+ }
+
+ if (INIT_TYPE_SNAP_START.equals(AWS_LAMBDA_INITIALIZATION_TYPE)) {
+ onInitComplete(lambdaLogger, runtimeClient);
+ }
+
+ return requestHandler;
+ }
+
+ private static void setupRuntimeLogger(LambdaLogger lambdaLogger)
+ throws ClassNotFoundException {
+ ReflectUtil.setStaticField(
+ Class.forName("com.amazonaws.services.lambda.runtime.LambdaRuntime"),
+ "logger",
+ true,
+ lambdaLogger
+ );
+ }
+
+ /**
+ * convert an integer into a FileDescriptor object using reflection to access private members.
+ */
+ private static FileDescriptor intToFd(int fd) throws RuntimeException {
+ try {
+ Class clazz = FileDescriptor.class;
+ Constructor c = clazz.getDeclaredConstructor(Integer.TYPE);
+ c.setAccessible(true);
+ return c.newInstance(fd);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static LogSink createLogSink() {
+ final String fdStr = System.getenv("_LAMBDA_TELEMETRY_LOG_FD");
+ if (fdStr == null) {
+ return new StdOutLogSink();
+ }
+
+ try {
+ int fdInt = Integer.parseInt(fdStr);
+ FileDescriptor fd = intToFd(fdInt);
+ return new FramedTelemetryLogSink(fd);
+ } catch (Exception e) {
+ return new StdOutLogSink();
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ try (LambdaContextLogger lambdaLogger = initLogger()) {
+ LambdaRuntimeApiClient runtimeClient = new LambdaRuntimeApiClientImpl(LambdaEnvironment.RUNTIME_API);
+ LambdaRequestHandler lambdaRequestHandler = getLambdaRequestHandlerObject(args[0], lambdaLogger, runtimeClient);
+ ConcurrencyConfig concurrencyConfig = new ConcurrencyConfig(lambdaLogger);
+ startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
+ } catch (IOException | ClassNotFoundException t) {
+ throw new Error(t);
+ }
+ }
+
+ private static LambdaContextLogger initLogger() {
+ LogSink logSink = createLogSink();
+ LambdaContextLogger logger = new LambdaContextLogger(
+ logSink,
+ LogLevel.fromString(LambdaEnvironment.LAMBDA_LOG_LEVEL),
+ LogFormat.fromString(LambdaEnvironment.LAMBDA_LOG_FORMAT));
+
+ return logger;
+ }
+
+ private static void startRuntimeLoopWithExecutor(LambdaRequestHandler lambdaRequestHandler, LambdaContextLogger lambdaLogger, ExecutorService executorService, LambdaRuntimeApiClient runtimeClient) {
+ executorService.submit(() -> {
+ try {
+ startRuntimeLoop(lambdaRequestHandler, lambdaLogger, runtimeClient, false);
+ } catch (Exception e) {
+ lambdaLogger.log(String.format("Runtime Loop on Thread ID: %s Failed.\n%s", Thread.currentThread().getName(), UserFault.trace(e)), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
+ }
+ });
+ }
+
+ protected static void startRuntimeLoops(LambdaRequestHandler lambdaRequestHandler, LambdaContextLogger lambdaLogger, ConcurrencyConfig concurrencyConfig, LambdaRuntimeApiClient runtimeClient) throws Exception {
+ if (concurrencyConfig.isMultiConcurrent()) {
+ lambdaLogger.log(concurrencyConfig.getConcurrencyConfigMessage(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.INFO : LogLevel.UNDEFINED);
+ ExecutorService platformThreadExecutor = Executors.newFixedThreadPool(concurrencyConfig.getNumberOfPlatformThreads());
+ try {
+ for (int i = 0; i < concurrencyConfig.getNumberOfPlatformThreads(); i++) {
+ startRuntimeLoopWithExecutor(lambdaRequestHandler, lambdaLogger, platformThreadExecutor, runtimeClient);
+ }
+ } finally {
+ platformThreadExecutor.shutdown();
+ try {
+ platformThreadExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ } else {
+ startRuntimeLoop(lambdaRequestHandler, lambdaLogger, runtimeClient, true);
+ }
+ }
+
+ private static LambdaError createLambdaErrorFromThrowableOrUserFault(Throwable t) {
+ if (t instanceof UserFault) {
+ return new LambdaError(
+ LambdaErrorConverter.fromUserFault((UserFault) t),
+ RapidErrorType.BadFunctionCode);
+ } else {
+ return new LambdaError(
+ LambdaErrorConverter.fromThrowable(t),
+ XRayErrorCauseConverter.fromThrowable(t),
+ RapidErrorType.UserException);
+ }
+ }
+
+ private static void setEnvVarForXrayTraceId(InvocationRequest request) {
+ if (request.getXrayTraceId() != null) {
+ System.setProperty(LAMBDA_TRACE_HEADER_PROP, request.getXrayTraceId());
+ } else {
+ System.clearProperty(LAMBDA_TRACE_HEADER_PROP);
+ }
+ }
+
+ private static void reportNonLoopTerminatingException(LambdaContextLogger lambdaLogger, Throwable t) {
+ lambdaLogger.log(
+ String.format(
+ "Runtime Loop on Thread ID: %s Faced and Exception. This exception will not stop the runtime loop.\nException:\n%s",
+ Thread.currentThread().getName(), UserFault.trace(t)),
+ lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
+ }
+
+ /*
+ * In multiconcurrent mode (exitLoopOnErrors = false), The Runtime Loop will not exit unless LambdaRuntimeClientMaxRetriesExceededException is thrown when calling nextInvocationWithExponentialBackoff.
+ * In normal/sequential mode (exitLoopOnErrors = true), The Runtime Loop will exit if nextInvocation call fails, when UserFault is fatal, or an Error of type VirtualMachineError or IOError is thrown.
+ */
+ private static void startRuntimeLoop(LambdaRequestHandler lambdaRequestHandler, LambdaContextLogger lambdaLogger, LambdaRuntimeApiClient runtimeClient, boolean exitLoopOnErrors) throws Exception {
+ boolean shouldExit = false;
+ while (!shouldExit) {
+ try {
+ UserFault userFault = null;
+ InvocationRequest request = exitLoopOnErrors ? runtimeClient.nextInvocation() : runtimeClient.nextInvocationWithExponentialBackoff(lambdaLogger);
+ if (exitLoopOnErrors) {
+ setEnvVarForXrayTraceId(request);
+ } else {
+ SdkInternalThreadLocal.put(CONCURRENT_TRACE_ID_KEY, request.getXrayTraceId());
+ }
+
+ try {
+ ByteArrayOutputStream payload = lambdaRequestHandler.call(request);
+ runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray());
+ // clear interrupted flag in case if it was set by user's code
+ Thread.interrupted();
+ } catch (Throwable t) {
+ UserFault.filterStackTrace(t);
+ userFault = UserFault.makeUserFault(t);
+ shouldExit = exitLoopOnErrors && (t instanceof VirtualMachineError || t instanceof IOError || userFault.fatal);
+ LambdaError error = createLambdaErrorFromThrowableOrUserFault(t);
+ runtimeClient.reportInvocationError(request.getId(), error);
+ } finally {
+ if (userFault != null) {
+ lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
+ }
+
+ SdkInternalThreadLocal.remove(CONCURRENT_TRACE_ID_KEY);
+ }
+ } catch (Throwable t) {
+ if (exitLoopOnErrors || t instanceof LambdaRuntimeClientMaxRetriesExceededException) {
+ throw t;
+ }
+
+ reportNonLoopTerminatingException(lambdaLogger, t);
+ }
+ }
+ }
+
+ private static void onInitComplete(final LambdaContextLogger lambdaLogger, LambdaRuntimeApiClient runtimeClient) throws IOException {
+ try {
+ Core.getGlobalContext().beforeCheckpoint(null);
+ runtimeClient.restoreNext();
+ } catch (Exception e1) {
+ logExceptionCloudWatch(lambdaLogger, e1);
+ runtimeClient.reportInitError(new LambdaError(
+ LambdaErrorConverter.fromThrowable(e1),
+ RapidErrorType.BeforeCheckpointError));
+ System.exit(64);
+ }
+
+ try {
+ Core.getGlobalContext().afterRestore(null);
+ } catch (Exception restoreExc) {
+ logExceptionCloudWatch(lambdaLogger, restoreExc);
+ runtimeClient.reportRestoreError(new LambdaError(
+ LambdaErrorConverter.fromThrowable(restoreExc),
+ RapidErrorType.AfterRestoreError));
+ System.exit(64);
+ }
+ }
+
+ private static void logExceptionCloudWatch(LambdaContextLogger lambdaLogger, Exception exc) {
+ UserFault.filterStackTrace(exc);
+ UserFault userFault = UserFault.makeUserFault(exc, true);
+ lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
+ }
+
+ protected static URLClassLoader getCustomerClassLoader() {
+ return customerClassLoader;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java
new file mode 100644
index 000000000..4204f3010
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java
@@ -0,0 +1,95 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * This class loads all of the classes that are in jars on the classpath.
+ *
+ * It is used to generate a class list and Application CDS archive that includes all the possible classes that could be
+ * loaded by the runtime. This simplifies the process of generating the Application CDS archive.
+ */
+public class ClasspathLoader {
+
+ private static final Set BLOCKLIST = new HashSet<>();
+ private static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
+ private static final int CLASS_SUFFIX_LEN = ".class".length();
+
+ static {
+ // Ignore module info class for serialization lib
+ BLOCKLIST.add("META-INF.versions.9.module-info");
+ }
+
+ private static String pathToClassName(final String path) {
+ return path.substring(0, path.length() - CLASS_SUFFIX_LEN).replaceAll("/|\\\\", "\\.");
+ }
+
+ private static void loadClass(String name) {
+ try {
+ Class.forName(name, true, SYSTEM_CLASS_LOADER);
+ System.out.println("Loaded " + name);
+ } catch (ClassNotFoundException e) {
+ System.err.println("[WARN] Failed to load " + name + ": " + e.getMessage());
+ }
+ }
+
+ private static void loadClassesInJar(File file) throws IOException {
+ JarFile jar = new JarFile(file);
+ Enumeration en = jar.entries();
+ while (en.hasMoreElements()) {
+ JarEntry entry = en.nextElement();
+
+ if (!entry.getName().endsWith(".class")) {
+ continue;
+ }
+
+ String name = pathToClassName(entry.getName());
+
+ if (BLOCKLIST.contains(name)) {
+ continue;
+ }
+
+ loadClass(name);
+ }
+ }
+
+ private static void loadClassesInClasspathEntry(String entry) throws IOException {
+ File file = new File(entry);
+
+ if (!file.exists()) {
+ throw new FileNotFoundException("Classpath entry does not exist: " + file.getPath());
+ }
+
+ if (file.isDirectory() || !file.getPath().endsWith(".jar")) {
+ System.err.println("[WARN] Only jar classpath entries are supported. Skipping " + file.getPath());
+ return;
+ }
+
+ loadClassesInJar(file);
+ }
+
+ private static void loadAllClasses() throws IOException {
+ final String classPath = System.getProperty("java.class.path");
+ if (classPath == null) {
+ return;
+ }
+ for (String classPathEntry : classPath.split(File.pathSeparator)) {
+ loadClassesInClasspathEntry(classPathEntry);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ loadAllClasses();
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java
new file mode 100644
index 000000000..b8aabbf37
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java
@@ -0,0 +1,70 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+class CustomerClassLoader extends URLClassLoader {
+ /**
+ * This Comparator is used to ensure that jars added to this classloader are added in a deterministic order which
+ * does not depend on the underlying filesystem.
+ */
+ private final static Comparator LEXICAL_SORT_ORDER = Comparator.comparing(String::toString);
+ private final static FilenameFilter JAR_FILE_NAME_FILTER = new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ int offset = name.length() - 4;
+ // must be at least A.jar
+ if (offset <= 0) {
+ return false;
+ } else {
+ return name.startsWith(".jar", offset);
+ }
+ }
+ };
+
+ CustomerClassLoader(String taskRoot, String optRoot, ClassLoader parent) throws IOException {
+ super(getUrls(taskRoot, optRoot), parent);
+ }
+
+ private static URL[] getUrls(String taskRoot, String optRoot) throws MalformedURLException {
+ File taskDir = new File(taskRoot + "/");
+ List res = new ArrayList<>();
+ res.add(newURL(taskDir, ""));
+ appendJars(new File(taskRoot + "/lib"), res);
+ appendJars(new File(optRoot + "/lib"), res);
+ return res.toArray(new URL[res.size()]);
+ }
+
+ private static void appendJars(File dir, List result) throws MalformedURLException {
+ if (!dir.isDirectory()) {
+ return;
+ }
+ String[] names = dir.list(CustomerClassLoader.JAR_FILE_NAME_FILTER);
+ if (names == null) {
+ return;
+ }
+ Arrays.sort(names, CustomerClassLoader.LEXICAL_SORT_ORDER);
+
+ for (String path : names) {
+ result.add(newURL(dir, path));
+ }
+ }
+
+ private static URL newURL(File parent, String path) throws MalformedURLException {
+ return new URL("file", null, -1, parent.getPath() + "/" + path);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java
new file mode 100644
index 000000000..f679c217c
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java
@@ -0,0 +1,921 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.ClientContext;
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.services.lambda.runtime.api.client.LambdaRequestHandler.UserFaultHandler;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaClientContext;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaCognitoIdentity;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest;
+import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
+import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.util.Functions;
+import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.filterStackTrace;
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.makeUserFault;
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.trace;
+
+public final class EventHandlerLoader {
+ private static final byte[] _JsonNull = new byte[]{'n', 'u', 'l', 'l'};
+
+ private enum Platform {
+ ANDROID,
+ IOS,
+ UNKNOWN
+ }
+
+ private static volatile ThreadLocal> contextSerializer = new ThreadLocal<>();
+ private static volatile ThreadLocal> cognitoSerializer = new ThreadLocal<>();
+
+ private static final ThreadLocal>>> typeCache = ThreadLocal.withInitial(() -> new EnumMap<>(Platform.class));
+
+ private static final Comparator methodPriority = new Comparator() {
+ public int compare(Method lhs, Method rhs) {
+
+ //1. Non bridge methods are preferred over bridge methods.
+ if (!lhs.isBridge() && rhs.isBridge()) {
+ return -1;
+ } else if (!rhs.isBridge() && lhs.isBridge()) {
+ return 1;
+ }
+
+ //2. We prefer longer signatures to shorter signatures. Except we count a method whose last argument is
+ //Context as having 1 more argument than it really does. This is a stupid thing to do, but we
+ //need to keep it for back compat reasons.
+ Class>[] lParams = lhs.getParameterTypes();
+ Class>[] rParams = rhs.getParameterTypes();
+
+ int lParamCompareLength = lParams.length;
+ int rParamCompareLength = rParams.length;
+
+ if (lastParameterIsContext(lParams)) {
+ ++lParamCompareLength;
+ }
+
+ if (lastParameterIsContext(rParams)) {
+ ++rParamCompareLength;
+ }
+
+ return -Integer.compare(lParamCompareLength, rParamCompareLength);
+ }
+ };
+
+ private EventHandlerLoader() {
+ }
+
+ /**
+ * returns the appropriate serializer for the class based on platform and whether the class is a supported event
+ *
+ * @param platform enum platform
+ * @param type Type of object used
+ * @return PojoSerializer
+ * @see Platform for which platforms are used
+ * @see LambdaEventSerializers for how mixins and modules are added to the serializer
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static PojoSerializer