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 index 9c6c6435..b7c5641a 100644 --- 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 @@ -1,8 +1,7 @@ -// -// AWSLambda.java -// -// Copyright (c) 2013 Amazon. All rights reserved. -// +/* +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; @@ -12,23 +11,25 @@ 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.InvocationRequest; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClient; +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.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.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; 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.PojoSerializer; -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.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.OutputStream; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.net.URLClassLoader; @@ -67,6 +68,10 @@ public class AWSLambda { private static final String AWS_LAMBDA_INITIALIZATION_TYPE = System.getenv(ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_INITIALIZATION_TYPE); + protected static URLClassLoader customerClassLoader; + + private static LambdaRuntimeApiClient runtimeClient; + static { // Override the disabledAlgorithms setting to match configuration for openjdk8-u181. // This is to keep DES ciphers around while we deploying security updates. @@ -143,17 +148,6 @@ public static void setupRuntimeLogger(LambdaLogger lambdaLogger) ); } - public static String getEnvOrExit(String envVariableName) { - String value = System.getenv(envVariableName); - if (value == null) { - System.err.println("Could not get environment variable " + envVariableName); - System.exit(-1); - } - return value; - } - - protected static URLClassLoader customerClassLoader; - /** * convert an integer into a FileDescriptor object using reflection to access private members. */ @@ -207,8 +201,7 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8")); setupRuntimeLogger(lambdaLogger); - String runtimeApi = getEnvOrExit(ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_RUNTIME_API); - LambdaRuntimeClient runtimeClient = new LambdaRuntimeClient(runtimeApi); + runtimeClient = new LambdaRuntimeApiClientImpl(LambdaEnvironment.RUNTIME_API); String taskRoot = System.getProperty("user.dir"); String libRoot = "/opt/java"; @@ -223,17 +216,18 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge requestHandler = findRequestHandler(handler, customerClassLoader); } catch (UserFault userFault) { lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED); - reportInitError(new Failure(userFault), runtimeClient); + LambdaError error = LambdaErrorConverter.fromUserFault(userFault); + runtimeClient.reportInitError(error); System.exit(1); return; } if (INIT_TYPE_SNAP_START.equals(AWS_LAMBDA_INITIALIZATION_TYPE)) { - onInitComplete(runtimeClient, lambdaLogger); + onInitComplete(lambdaLogger); } boolean shouldExit = false; while (!shouldExit) { UserFault userFault = null; - InvocationRequest request = runtimeClient.waitForNextInvocation(); + InvocationRequest request = runtimeClient.nextInvocation(); if (request.getXrayTraceId() != null) { System.setProperty(LAMBDA_TRACE_HEADER_PROP, request.getXrayTraceId()); } else { @@ -243,26 +237,23 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge ByteArrayOutputStream payload; try { payload = requestHandler.call(request); - runtimeClient.postInvocationResponse(request.getId(), payload.toByteArray()); + runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray()); boolean ignored = Thread.interrupted(); // clear interrupted flag in case if it was set by user's code } catch (UserFault f) { + shouldExit = f.fatal; userFault = f; UserFault.filterStackTrace(f); - payload = new ByteArrayOutputStream(1024); - Failure failure = new Failure(f); - GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload); - shouldExit = f.fatal; - runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType()); + + LambdaError error = LambdaErrorConverter.fromUserFault(f); + runtimeClient.reportInvocationError(request.getId(), error); } catch (Throwable t) { + shouldExit = t instanceof VirtualMachineError || t instanceof IOError; UserFault.filterStackTrace(t); userFault = UserFault.makeUserFault(t); - payload = new ByteArrayOutputStream(1024); - Failure failure = new Failure(t); - GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload); - // These two categories of errors are considered fatal. - shouldExit = Failure.isInvokeFailureFatal(t); - runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType(), - serializeAsXRayJson(t)); + + LambdaError error = LambdaErrorConverter.fromThrowable(t); + XRayErrorCause xRayErrorCause = XRayErrorCauseConverter.fromThrowable(t); + runtimeClient.reportInvocationError(request.getId(), error, xRayErrorCause); } finally { if (userFault != null) { lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED); @@ -271,23 +262,22 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge } } - static void onInitComplete(final LambdaRuntimeClient runtimeClient, final LambdaContextLogger lambdaLogger) throws IOException { + static void onInitComplete(final LambdaContextLogger lambdaLogger) throws IOException { try { Core.getGlobalContext().beforeCheckpoint(null); - // Blocking call to RAPID /restore/next API, will return after taking snapshot. - // This will also be the 'entrypoint' when resuming from snapshots. - runtimeClient.getRestoreNext(); + runtimeClient.restoreNext(); } catch (Exception e1) { logExceptionCloudWatch(lambdaLogger, e1); - reportInitError(new Failure(e1), runtimeClient); + LambdaError error = LambdaErrorConverter.fromThrowable(e1); + runtimeClient.reportInitError(error); System.exit(64); } try { Core.getGlobalContext().afterRestore(null); } catch (Exception restoreExc) { logExceptionCloudWatch(lambdaLogger, restoreExc); - Failure errorPayload = new Failure(restoreExc); - reportRestoreError(errorPayload, runtimeClient); + LambdaError error = LambdaErrorConverter.fromThrowable(restoreExc); + runtimeClient.reportRestoreError(error); System.exit(64); } } @@ -297,40 +287,4 @@ private static void logExceptionCloudWatch(LambdaContextLogger lambdaLogger, Exc UserFault userFault = UserFault.makeUserFault(exc, true); lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED); } - - static void reportInitError(final Failure failure, - final LambdaRuntimeClient runtimeClient) throws IOException { - - ByteArrayOutputStream payload = new ByteArrayOutputStream(1024); - JacksonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload); - runtimeClient.postInitError(payload.toByteArray(), failure.getErrorType()); - } - - static int reportRestoreError(final Failure failure, - final LambdaRuntimeClient runtimeClient) throws IOException { - - ByteArrayOutputStream payload = new ByteArrayOutputStream(1024); - JacksonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload); - return runtimeClient.postRestoreError(payload.toByteArray(), failure.getErrorType()); - } - - private static PojoSerializer xRayErrorCauseSerializer; - - /** - * @param throwable throwable to convert - * @return json as string expected by XRay's web console. On conversion failure, returns null. - */ - private static String serializeAsXRayJson(Throwable throwable) { - try { - final OutputStream outputStream = new ByteArrayOutputStream(); - final XRayErrorCause cause = new XRayErrorCause(throwable); - if (xRayErrorCauseSerializer == null) { - xRayErrorCauseSerializer = JacksonFactory.getInstance().getSerializer(XRayErrorCause.class); - } - xRayErrorCauseSerializer.toJson(cause, outputStream); - return outputStream.toString(); - } catch (Exception e) { - return null; - } - } } 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 index 8e72ea4a..28f11bbf 100644 --- 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 @@ -35,6 +35,7 @@ private static String pathToClassName(final String path) { 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()); } 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 index 9e3c48eb..7c23be6e 100644 --- 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 @@ -13,7 +13,7 @@ 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.InvocationRequest; +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; @@ -22,6 +22,7 @@ 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; @@ -902,7 +903,8 @@ public ByteArrayOutputStream call(InvocationRequest request) throws Error, Excep } } - handler.handleRequest(request.getContentAsStream(), output, context); + ByteArrayInputStream bais = new ByteArrayInputStream(request.getContent()); + handler.handleRequest(bais, output, context); return output; } }; diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java deleted file mode 100644 index c445bafe..00000000 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ - -package com.amazonaws.services.lambda.runtime.api.client; - -import java.io.IOError; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -public class Failure { - - private static final Class[] reportableExceptionsArray = { - Error.class, - ClassNotFoundException.class, - IOError.class, - Throwable.class, - VirtualMachineError.class, - LinkageError.class, - ExceptionInInitializerError.class, - NoClassDefFoundError.class, - HandlerInfo.InvalidHandlerException.class - }; - - private static final List sortedExceptions = Collections.unmodifiableList( - Arrays.stream(reportableExceptionsArray).sorted(new ClassHierarchyComparator()).collect(Collectors.toList())); - - private final String errorMessage; - private final String errorType; - private final String[] stackTrace; - private final Failure cause; - - public Failure(Throwable t) { - this.errorMessage = t.getLocalizedMessage() == null ? t.getClass().getName() : t.getLocalizedMessage(); - this.errorType = t.getClass().getName(); - StackTraceElement[] trace = t.getStackTrace(); - this.stackTrace = new String[trace.length]; - for (int i = 0; i < trace.length; i++) { - this.stackTrace[i] = trace[i].toString(); - } - Throwable cause = t.getCause(); - this.cause = (cause == null) ? null : new Failure(cause); - } - - public Failure(UserFault userFault) { - this.errorMessage = userFault.msg; - this.errorType = userFault.exception; - // Not setting stacktrace for compatibility with legacy/native runtime - this.stackTrace = null; - this.cause = null; - } - - public static Class getReportableExceptionClass(Throwable customerException) { - return sortedExceptions - .stream() - .filter(e -> e.isAssignableFrom(customerException.getClass())) - .findFirst() - .orElse(Throwable.class); - } - - public static String getReportableExceptionClassName(Throwable f) { - return getReportableExceptionClass(f).getName(); - } - - public static boolean isInvokeFailureFatal(Throwable t) { - return t instanceof VirtualMachineError || t instanceof IOError; - } - - private static class ClassHierarchyComparator implements Comparator { - @Override - public int compare(Class o1, Class o2) { - if (o1.isAssignableFrom(o2)) { - return 1; - } else { - return -1; - } - } - } - - public String getErrorType() { - return errorType; - } - - public String getErrorMessage() { - return errorMessage; - } -} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java index af357850..62ba7b0f 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java @@ -16,4 +16,5 @@ public class LambdaEnvironment { public static final String LAMBDA_LOG_FORMAT = ENV_READER.getEnvOrDefault(AWS_LAMBDA_LOG_FORMAT, "TEXT"); public static final String FUNCTION_NAME = ENV_READER.getEnv(AWS_LAMBDA_FUNCTION_NAME); public static final String FUNCTION_VERSION = ENV_READER.getEnv(AWS_LAMBDA_FUNCTION_VERSION); + public static final String RUNTIME_API = ENV_READER.getEnv(AWS_LAMBDA_RUNTIME_API); } diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java index d22d6394..89993b2b 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java @@ -2,7 +2,7 @@ package com.amazonaws.services.lambda.runtime.api.client; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; import java.io.ByteArrayOutputStream; 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 deleted file mode 100644 index 73db5b94..00000000 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ - -package com.amazonaws.services.lambda.runtime.api.client; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * helper class for serializing an exception in the format expected by XRay's web console. - */ -public class XRayErrorCause { - private final String working_directory; - private final Collection exceptions; - private final Collection paths; - - public XRayErrorCause(Throwable throwable) { - working_directory = System.getProperty("user.dir"); - exceptions = Collections.singletonList(new XRayException(throwable)); - paths = Collections.unmodifiableCollection( - Arrays.stream(throwable.getStackTrace()) - .map(XRayErrorCause::determineFileName) - .collect(Collectors.toSet())); - } - - public String getWorking_directory() { - return working_directory; - } - - public Collection getExceptions() { - return exceptions; - } - - public Collection getPaths() { - return paths; - } - - /** - * This method provides compatibility between Java 8 and Java 11 in determining the fileName of the class in the - * StackTraceElement. - * - * If the fileName property of the StackTraceElement is null (as it can be for native methods in Java 11), it - * constructs it using the className by stripping out the package and appending ".java". - */ - private static String determineFileName(StackTraceElement e) { - String fileName = null; - if(e.getFileName() != null) { - fileName = e.getFileName(); - } - if(fileName == null) { - String className = e.getClassName(); - fileName = className == null ? null : className.substring(className.lastIndexOf('.') + 1) + ".java"; - } - return fileName; - } - - public static class XRayException { - private final String message; - private final String type; - private final List stack; - - public XRayException(Throwable throwable) { - this.message = throwable.getMessage(); - this.type = throwable.getClass().getName(); - this.stack = Arrays.stream(throwable.getStackTrace()).map(this::toStackElement).collect(Collectors.toList()); - } - - private StackElement toStackElement(StackTraceElement e) { - return new StackElement( - e.getMethodName(), - determineFileName(e), - e.getLineNumber()); - } - - public String getMessage() { - return message; - } - - public String getType() { - return type; - } - - public List getStack() { - return stack; - } - - public static class StackElement { - private final String label; - private final String path; - private final int line; - - private StackElement(String label, String path, int line) { - this.label = label; - this.path = path; - this.line = line; - } - - public String getLabel() { - return label; - } - - public String getPath() { - return path; - } - - public int getLine() { - return line; - } - } - } - -} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java new file mode 100644 index 00000000..3fdcb76b --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/DtoSerializers.java @@ -0,0 +1,43 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; +import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory; +import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class DtoSerializers { + /** + * Implementation of + * Initialization-on-demand holder idiom + * This way the serializers will be loaded lazily + */ + private static class SingletonHelper { + private static final PojoSerializer LAMBDA_ERROR_SERIALIZER = GsonFactory.getInstance().getSerializer(LambdaError.class); + private static final PojoSerializer X_RAY_ERROR_CAUSE_SERIALIZER = GsonFactory.getInstance().getSerializer(XRayErrorCause.class); + } + + public static byte[] serialize(LambdaError error) { + return serialize(error, SingletonHelper.LAMBDA_ERROR_SERIALIZER); + } + + public static byte[] serialize(XRayErrorCause xRayErrorCause) { + return serialize(xRayErrorCause, SingletonHelper.X_RAY_ERROR_CAUSE_SERIALIZER); + } + + private static byte[] serialize(T pojo, PojoSerializer serializer) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + serializer.toJson(pojo, outputStream); + return outputStream.toByteArray(); + } catch (IOException e) { + return null; + } + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java new file mode 100644 index 00000000..4f476426 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java @@ -0,0 +1,62 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; + +import java.io.IOException; + +/** + * Java interface for + * Lambda Runtime API + */ +public interface LambdaRuntimeApiClient { + + /** + * Report Init error + * @param error error to report + */ + void reportInitError(LambdaError error) throws IOException; + + /** + * Get next invocation + */ + InvocationRequest nextInvocation() throws IOException; + + /** + * Report invocation success + * @param requestId request id + * @param response byte array representing response + */ + void reportInvocationSuccess(String requestId, byte[] response) throws IOException; + + /** + * Report invocation error + * @param requestId request id + * @param error error to report + */ + void reportInvocationError(String requestId, LambdaError error) throws IOException; + + /** + * Report invocation error + * @param requestId request id + * @param error error to report + * @param xRayErrorCause X-Ray error cause + */ + void reportInvocationError(String requestId, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException; + + /** + * SnapStart endpoint to report that beforeCheckoint hooks were executed + */ + void restoreNext() throws IOException; + + /** + * SnapStart endpoint to report errors during afterRestore hooks execution + * @param error error to report + */ + void reportRestoreError(LambdaError error) throws IOException; +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java new file mode 100644 index 00000000..83ac0b39 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java @@ -0,0 +1,171 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +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; + +public class LambdaRuntimeApiClientImpl implements LambdaRuntimeApiClient { + private final String baseUrl; + private final String invocationEndpoint; + static final String USER_AGENT = String.format( + "aws-lambda-java/%s-%s", + System.getProperty("java.vendor.version"), + LambdaRuntimeApiClientImpl.class.getPackage().getImplementationVersion()); + private static final String DEFAULT_CONTENT_TYPE = "application/json"; + private static final String XRAY_ERROR_CAUSE_HEADER = "Lambda-Runtime-Function-XRay-Error-Cause"; + private static final String ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type"; + private static final int XRAY_ERROR_CAUSE_MAX_HEADER_SIZE = 1024 * 1024; // 1MiB + + public LambdaRuntimeApiClientImpl(String hostnameAndPort) { + Objects.requireNonNull(hostnameAndPort, "hostnameAndPort cannot be null"); + this.baseUrl = "http://" + hostnameAndPort; + this.invocationEndpoint = this.baseUrl + "/2018-06-01/runtime/invocation/"; + NativeClient.init(); + } + + @Override + public void reportInitError(LambdaError error) throws IOException { + String endpoint = this.baseUrl + "/2018-06-01/runtime/init/error"; + reportLambdaError(endpoint, error, null); + } + + @Override + public InvocationRequest nextInvocation() { + return NativeClient.next(); + } + + @Override + public void reportInvocationSuccess(String requestId, byte[] response) { + NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response); + } + + @Override + public void reportInvocationError(String requestId, LambdaError error) throws IOException { + reportInvocationError(requestId, error, null); + } + + @Override + public void reportInvocationError(String requestId, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException { + String endpoint = invocationEndpoint + requestId + "/error"; + reportLambdaError(endpoint, error, xRayErrorCause); + } + + @Override + public void restoreNext() throws IOException { + String endpoint = this.baseUrl + "/2018-06-01/runtime/restore/next"; + int responseCode = doGet(endpoint); + if (responseCode != HTTP_OK) { + throw new LambdaRuntimeClientException(endpoint, responseCode); + } + } + + @Override + public void reportRestoreError(LambdaError error) throws IOException { + String endpoint = this.baseUrl + "/2018-06-01/runtime/restore/error"; + reportLambdaError(endpoint, error, null); + } + + void reportLambdaError(String endpoint, LambdaError error, XRayErrorCause xRayErrorCause) throws IOException { + Map headers = new HashMap<>(); + if (error.errorType != null && !error.errorType.isEmpty()) { + headers.put(ERROR_TYPE_HEADER, error.errorType); + } + if (xRayErrorCause != null) { + byte[] xRayErrorCauseJson = DtoSerializers.serialize(xRayErrorCause); + if (xRayErrorCauseJson != null && xRayErrorCauseJson.length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) { + headers.put(XRAY_ERROR_CAUSE_HEADER, new String(xRayErrorCauseJson)); + } + } + + byte[] payload = DtoSerializers.serialize(error); + int responseCode = doPost(endpoint, headers, payload); + if (responseCode != HTTP_ACCEPTED) { + throw new LambdaRuntimeClientException(endpoint, responseCode); + } + } + + private int doPost(String endpoint, + Map headers, + byte[] payload) throws IOException { + URL url = createUrl(endpoint); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", DEFAULT_CONTENT_TYPE); + conn.setRequestProperty("User-Agent", USER_AGENT); + + 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 int doGet(String endpoint) throws IOException { + URL url = createUrl(endpoint); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("User-Agent", USER_AGENT); + + int responseCode = conn.getResponseCode(); + closeInputStreamQuietly(conn); + + return responseCode; + } + + private URL createUrl(String endpoint) { + try { + return new URL(endpoint); + } catch (MalformedURLException e) { + throw new RuntimeException(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/LambdaRuntimeClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java deleted file mode 100644 index 1d4d3aa5..00000000 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -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 { - - private final String hostname; - private final int port; - private final String invocationEndpoint; - - private static final String DEFAULT_CONTENT_TYPE = "application/json"; - private static final String XRAY_ERROR_CAUSE_HEADER = "Lambda-Runtime-Function-XRay-Error-Cause"; - private static final String ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type"; - private static final int XRAY_ERROR_CAUSE_MAX_HEADER_SIZE = 1024 * 1024; // 1MiB - - public LambdaRuntimeClient(String hostnamePort) { - Objects.requireNonNull(hostnamePort, "hostnamePort cannot be null"); - String[] parts = hostnamePort.split(":"); - this.hostname = parts[0]; - this.port = Integer.parseInt(parts[1]); - this.invocationEndpoint = invocationEndpoint(); - NativeClient.init(); - } - - public InvocationRequest waitForNextInvocation() { - return NativeClient.next(); - } - - public void postInvocationResponse(String requestId, byte[] response) { - NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response); - } - - public void postInvocationError(String requestId, byte[] errorResponse, String errorType) throws IOException { - postInvocationError(requestId, errorResponse, errorType, null); - } - - public void postInvocationError(String requestId, byte[] errorResponse, String errorType, String errorCause) - throws IOException { - String endpoint = invocationErrorEndpoint(requestId); - 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); - } - - private void post(String endpoint, byte[] errorResponse, String errorType, String errorCause) throws IOException { - URL url = createUrl(endpoint); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("POST"); - conn.setRequestProperty("Content-Type", DEFAULT_CONTENT_TYPE); - if (errorType != null && !errorType.isEmpty()) { - conn.setRequestProperty(ERROR_TYPE_HEADER, errorType); - } - if (errorCause != null && errorCause.getBytes().length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) { - conn.setRequestProperty(XRAY_ERROR_CAUSE_HEADER, errorCause); - } - conn.setFixedLengthStreamingMode(errorResponse.length); - conn.setDoOutput(true); - try (OutputStream outputStream = conn.getOutputStream()) { - outputStream.write(errorResponse); - } - - int responseCode = conn.getResponseCode(); - if (responseCode != HTTP_ACCEPTED) { - throw new LambdaRuntimeClientException(endpoint, responseCode); - } - - // don't need to read the response, close stream to ensure connection re-use - closeQuietly(conn.getInputStream()); - } - - private String invocationEndpoint() { - return getBaseUrl() + "/2018-06-01/runtime/invocation/"; - } - - private String invocationErrorEndpoint(String requestId) { - return invocationEndpoint + requestId + "/error"; - } - - private String initErrorEndpoint() { - 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) { - try { - return new URL(endpoint); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - private void closeQuietly(InputStream inputStream) { - if (inputStream == null) return; - try { - inputStream.close(); - } 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/LambdaRuntimeClientException.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java index 1fc52d2f..d9f0341a 100644 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java @@ -1,11 +1,11 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; -/** - * Copyright (c) 2019 Amazon. All rights reserved. - */ public class LambdaRuntimeClientException extends RuntimeException { public LambdaRuntimeClientException(String message, int responseCode) { - super(message + "Response code: '" + responseCode + "'."); + super(message + " Response code: '" + responseCode + "'."); } - } 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 82088c0f..a311ff00 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 @@ -1,7 +1,11 @@ -/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ - +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; + import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.file.Files; @@ -10,17 +14,18 @@ import java.util.ArrayList; import java.util.List; +import static com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClientImpl.USER_AGENT; + /** - * This module defines the native Runtime Interface Client which is responsible for all HTTP + * This module defines the native Runtime Interface Client which is responsible for HTTP * interactions with the Runtime API. */ class NativeClient { 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(); + initializeClient(USER_AGENT.getBytes()); } private static void loadJNILib() { @@ -70,15 +75,6 @@ static void unpackAndLoadNativeLibrary(String[] libsToTry) { 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(); diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/UnknownPlatformException.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/UnknownPlatformException.java deleted file mode 100644 index f0b6d76e..00000000 --- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/UnknownPlatformException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; - -/** - * Copyright (c) 2022 Amazon. All rights reserved. - */ -public class UnknownPlatformException extends RuntimeException { - - public UnknownPlatformException(String message) { - super(message); - } -} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java new file mode 100644 index 00000000..095b2cdc --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/LambdaErrorConverter.java @@ -0,0 +1,31 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters; + +import com.amazonaws.services.lambda.runtime.api.client.UserFault; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError; + +public class LambdaErrorConverter { + private LambdaErrorConverter() { + } + + public static LambdaError fromUserFault(UserFault userFault) { + // Not setting stacktrace for compatibility with legacy/native runtime + return new LambdaError(userFault.msg, userFault.exception, null); + } + + public static LambdaError fromThrowable(Throwable throwable) { + String errorMessage = throwable.getLocalizedMessage() == null ? + throwable.getClass().getName() : throwable.getLocalizedMessage(); + String errorType = throwable.getClass().getName(); + + StackTraceElement[] trace = throwable.getStackTrace(); + String[] stackTrace = new String[trace.length]; + for (int i = 0; i < trace.length; i++) { + stackTrace[i] = trace[i].toString(); + } + return new LambdaError(errorMessage, errorType, stackTrace); + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/XRayErrorCauseConverter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/XRayErrorCauseConverter.java new file mode 100644 index 00000000..f24c3a5d --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/converters/XRayErrorCauseConverter.java @@ -0,0 +1,59 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters; + +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.StackElement; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayException; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class XRayErrorCauseConverter { + private XRayErrorCauseConverter() { + } + + public static XRayErrorCause fromThrowable(Throwable throwable) { + String workingDirectory = System.getProperty("user.dir"); + XRayException xRayException = getXRayExceptionFromThrowable(throwable); + Collection exceptions = Collections.singletonList(xRayException); + Collection paths = Arrays.stream(throwable.getStackTrace()) + .map(XRayErrorCauseConverter::determineFileName) + .collect(Collectors.toSet()); + + return new XRayErrorCause(workingDirectory, exceptions, paths); + } + + static XRayException getXRayExceptionFromThrowable(Throwable throwable) { + String message = throwable.getMessage(); + String type = throwable.getClass().getName(); + List stack = Arrays.stream(throwable.getStackTrace()) + .map(XRayErrorCauseConverter::convertStackTraceElement) + .collect(Collectors.toList()); + return new XRayException(message, type, stack); + } + + static String determineFileName(StackTraceElement e) { + String fileName = null; + if (e.getFileName() != null) { + fileName = e.getFileName(); + } + if (fileName == null) { + String className = e.getClassName(); + fileName = className.substring(className.lastIndexOf('.') + 1) + ".java"; + } + return fileName; + } + + static StackElement convertStackTraceElement(StackTraceElement e) { + return new StackElement( + e.getMethodName(), + determineFileName(e), + e.getLineNumber()); + } +} 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/dto/InvocationRequest.java similarity index 87% rename from aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java rename to aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java index 6a6e7b12..7bdc2500 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/dto/InvocationRequest.java @@ -1,12 +1,11 @@ -package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; /** * An invocation request represents the response of the runtime API's next invocation API. - *

- * Copyright (c) 2019 Amazon. All rights reserved. */ public class InvocationRequest { @@ -63,6 +62,7 @@ public String getInvokedFunctionArn() { return invokedFunctionArn; } + @SuppressWarnings("unused") public void setInvokedFunctionArn(String invokedFunctionArn) { this.invokedFunctionArn = invokedFunctionArn; } @@ -71,6 +71,7 @@ public long getDeadlineTimeInMs() { return deadlineTimeInMs; } + @SuppressWarnings("unused") public void setDeadlineTimeInMs(long deadlineTimeInMs) { this.deadlineTimeInMs = deadlineTimeInMs; } @@ -79,6 +80,7 @@ public String getClientContext() { return clientContext; } + @SuppressWarnings("unused") public void setClientContext(String clientContext) { this.clientContext = clientContext; } @@ -87,12 +89,13 @@ public String getCognitoIdentity() { return cognitoIdentity; } + @SuppressWarnings("unused") public void setCognitoIdentity(String cognitoIdentity) { this.cognitoIdentity = cognitoIdentity; } - public InputStream getContentAsStream() { - return new ByteArrayInputStream(content); + public byte[] getContent() { + return content; } public void setContent(byte[] content) { diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java new file mode 100644 index 00000000..7b5abc10 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/LambdaError.java @@ -0,0 +1,21 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; + +public class LambdaError { + public String errorMessage; + public String errorType; + public String[] stackTrace; + + @SuppressWarnings("unused") + public LambdaError() { + } + + public LambdaError(String errorMessage, String errorType, String[] stackTrace) { + this.errorMessage = errorMessage; + this.errorType = errorType; + this.stackTrace = stackTrace; + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/StackElement.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/StackElement.java new file mode 100644 index 00000000..679f8bf9 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/StackElement.java @@ -0,0 +1,21 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; + +public class StackElement { + public String label; + public String path; + public int line; + + @SuppressWarnings("unused") + public StackElement() { + } + + public StackElement(String label, String path, int line) { + this.label = label; + this.path = path; + this.line = line; + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayErrorCause.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayErrorCause.java new file mode 100644 index 00000000..cc5bee8a --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayErrorCause.java @@ -0,0 +1,24 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; + +import java.util.Collection; + +public class XRayErrorCause { + public String working_directory; + public Collection exceptions; + public Collection paths; + + @SuppressWarnings("unused") + public XRayErrorCause() { + + } + + public XRayErrorCause(String working_directory, Collection exceptions, Collection paths) { + this.working_directory = working_directory; + this.exceptions = exceptions; + this.paths = paths; + } +} diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayException.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayException.java new file mode 100644 index 00000000..2b17fd5f --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/XRayException.java @@ -0,0 +1,23 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto; + +import java.util.List; + +public class XRayException { + public String message; + public String type; + public List stack; + + @SuppressWarnings("unused") + public XRayException() { + } + + public XRayException(String message, String type, List stack) { + this.message = message; + this.type = type; + this.stack = stack; + } +} 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 87fa9f02..02cfeb7e 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 @@ -44,7 +44,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } jclass tempInvocationRequestClassRef; - tempInvocationRequestClassRef = env->FindClass("com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest"); + tempInvocationRequestClassRef = env->FindClass("com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest"); invocationRequestClass = (jclass) env->NewGlobalRef(tempInvocationRequestClassRef); env->DeleteLocalRef(tempInvocationRequestClassRef); 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 index 171985de..76e6f024 100644 --- 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 @@ -1,6 +1,6 @@ package com.amazonaws.services.lambda.runtime.api.client; -import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/FailureTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/FailureTest.java deleted file mode 100644 index d7cc2225..00000000 --- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/FailureTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ - -package com.amazonaws.services.lambda.runtime.api.client; - -import org.junit.jupiter.api.Test; - -import java.io.IOError; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class FailureTest { - static class MySecretException extends ClassNotFoundException {} - - @Test - public void doesNotReportCustomException() { - Throwable throwable = new MySecretException(); - assertEquals(ClassNotFoundException.class, Failure.getReportableExceptionClass(throwable)); - - MySecretException mySecretException = new MySecretException(); - assertEquals(ClassNotFoundException.class, Failure.getReportableExceptionClass(mySecretException)); - } - - @Test - public void correctlyReportsExceptionsWeTrack() { - Throwable ioError = new IOError(new Throwable()); - assertEquals(IOError.class, Failure.getReportableExceptionClass(ioError)); - - Throwable error = new Error(new Throwable()); - assertEquals(Error.class, Failure.getReportableExceptionClass(error)); - - ClassNotFoundException classNotFoundException = new ClassNotFoundException(); - assertEquals(ClassNotFoundException.class, Failure.getReportableExceptionClass(classNotFoundException)); - - VirtualMachineError virtualMachineError = new OutOfMemoryError(); - assertEquals(VirtualMachineError.class, Failure.getReportableExceptionClass(virtualMachineError)); - - Throwable linkageError = new LinkageError(); - assertEquals(LinkageError.class, Failure.getReportableExceptionClass(linkageError)); - - Throwable exceptionInInitializerError = new ExceptionInInitializerError(); - assertEquals(ExceptionInInitializerError.class, Failure.getReportableExceptionClass(exceptionInInitializerError)); - - Throwable noClassDefFoundError = new NoClassDefFoundError(); - assertEquals(NoClassDefFoundError.class, Failure.getReportableExceptionClass(noClassDefFoundError)); - - Throwable invalidHandlerException = new HandlerInfo.InvalidHandlerException(); - assertEquals(HandlerInfo.InvalidHandlerException.class, Failure.getReportableExceptionClass(invalidHandlerException)); - - Throwable throwable = new Throwable(); - assertEquals(Throwable.class, Failure.getReportableExceptionClass(throwable)); - } - - @Test - public void verifyCorrectClassName() { - Throwable ioError = new IOError(new Throwable()); - assertEquals("java.io.IOError", Failure.getReportableExceptionClassName(ioError)); - - Throwable error = new Error(new Throwable()); - assertEquals("java.lang.Error", Failure.getReportableExceptionClassName(error)); - - ClassNotFoundException classNotFoundException = new ClassNotFoundException(); - assertEquals("java.lang.ClassNotFoundException", Failure.getReportableExceptionClassName(classNotFoundException)); - } -} diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCauseTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCauseTest.java index a6ad7577..8de6963a 100644 --- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCauseTest.java +++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCauseTest.java @@ -2,6 +2,9 @@ package com.amazonaws.services.lambda.runtime.api.client; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.XRayErrorCauseConverter; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,25 +50,25 @@ public void xrayErrorCauseTestNoFileName() { } private void assertXrayErrorCause(Throwable t) { - XRayErrorCause xRayErrorCause = new XRayErrorCause(t); + XRayErrorCause xRayErrorCause = XRayErrorCauseConverter.fromThrowable(t); - assertEquals(TEST_WORKING_DIR, xRayErrorCause.getWorking_directory()); + assertEquals(TEST_WORKING_DIR, xRayErrorCause.working_directory); - assertEquals(1, xRayErrorCause.getPaths().size()); - assertTrue(xRayErrorCause.getPaths().contains("StackTraceHelper.java")); + assertEquals(1, xRayErrorCause.paths.size()); + assertTrue(xRayErrorCause.paths.contains("StackTraceHelper.java")); - assertEquals(1, xRayErrorCause.getExceptions().size()); - XRayErrorCause.XRayException xRayException = xRayErrorCause.getExceptions().iterator().next(); - assertEquals("woops", xRayException.getMessage()); - assertEquals("java.lang.RuntimeException", xRayException.getType()); + assertEquals(1, xRayErrorCause.exceptions.size()); + XRayException xRayException = xRayErrorCause.exceptions.iterator().next(); + assertEquals("woops", xRayException.message); + assertEquals("java.lang.RuntimeException", xRayException.type); - assertEquals("throwRuntimeException", xRayException.getStack().get(0).getLabel()); - assertEquals("StackTraceHelper.java", xRayException.getStack().get(0).getPath()); - assertTrue(xRayException.getStack().get(0).getLine() > 0); + assertEquals("throwRuntimeException", xRayException.stack.get(0).label); + assertEquals("StackTraceHelper.java", xRayException.stack.get(0).path); + assertTrue(xRayException.stack.get(0).line > 0); - assertEquals("callThenThrowRuntimeException", xRayException.getStack().get(1).getLabel()); - assertEquals("StackTraceHelper.java", xRayException.getStack().get(1).getPath()); - assertTrue(xRayException.getStack().get(0).getLine() > 0); + assertEquals("callThenThrowRuntimeException", xRayException.stack.get(1).label); + assertEquals("StackTraceHelper.java", xRayException.stack.get(1).path); + assertTrue(xRayException.stack.get(0).line > 0); } private void clearStackTraceElementsFilename(Throwable t) {