Skip to content

Commit 2599a0b

Browse files
authored
Extract interface for Runtime API client (#471)
Extract interface for Runtime API client
1 parent d6b3b8b commit 2599a0b

25 files changed

+544
-627
lines changed

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java

+37-83
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
//
2-
// AWSLambda.java
3-
//
4-
// Copyright (c) 2013 Amazon. All rights reserved.
5-
//
1+
/*
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: Apache-2.0
4+
*/
65
package com.amazonaws.services.lambda.runtime.api.client;
76

87
import com.amazonaws.services.lambda.crac.Core;
@@ -12,23 +11,25 @@
1211
import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger;
1312
import com.amazonaws.services.lambda.runtime.api.client.logging.LogSink;
1413
import com.amazonaws.services.lambda.runtime.api.client.logging.StdOutLogSink;
15-
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
16-
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClient;
14+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClient;
15+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeApiClientImpl;
16+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.LambdaErrorConverter;
17+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.converters.XRayErrorCauseConverter;
18+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest;
19+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.LambdaError;
20+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause;
1721
import com.amazonaws.services.lambda.runtime.api.client.util.LambdaOutputStream;
1822
import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
1923
import com.amazonaws.services.lambda.runtime.logging.LogFormat;
2024
import com.amazonaws.services.lambda.runtime.logging.LogLevel;
21-
import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
22-
import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
23-
import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory;
2425
import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
2526

2627
import java.io.ByteArrayOutputStream;
2728
import java.io.File;
2829
import java.io.FileDescriptor;
2930
import java.io.FileInputStream;
31+
import java.io.IOError;
3032
import java.io.IOException;
31-
import java.io.OutputStream;
3233
import java.io.PrintStream;
3334
import java.lang.reflect.Constructor;
3435
import java.net.URLClassLoader;
@@ -67,6 +68,10 @@ public class AWSLambda {
6768

6869
private static final String AWS_LAMBDA_INITIALIZATION_TYPE = System.getenv(ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_INITIALIZATION_TYPE);
6970

71+
protected static URLClassLoader customerClassLoader;
72+
73+
private static LambdaRuntimeApiClient runtimeClient;
74+
7075
static {
7176
// Override the disabledAlgorithms setting to match configuration for openjdk8-u181.
7277
// This is to keep DES ciphers around while we deploying security updates.
@@ -143,17 +148,6 @@ public static void setupRuntimeLogger(LambdaLogger lambdaLogger)
143148
);
144149
}
145150

146-
public static String getEnvOrExit(String envVariableName) {
147-
String value = System.getenv(envVariableName);
148-
if (value == null) {
149-
System.err.println("Could not get environment variable " + envVariableName);
150-
System.exit(-1);
151-
}
152-
return value;
153-
}
154-
155-
protected static URLClassLoader customerClassLoader;
156-
157151
/**
158152
* convert an integer into a FileDescriptor object using reflection to access private members.
159153
*/
@@ -207,8 +201,7 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge
207201
System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8"));
208202
setupRuntimeLogger(lambdaLogger);
209203

210-
String runtimeApi = getEnvOrExit(ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_RUNTIME_API);
211-
LambdaRuntimeClient runtimeClient = new LambdaRuntimeClient(runtimeApi);
204+
runtimeClient = new LambdaRuntimeApiClientImpl(LambdaEnvironment.RUNTIME_API);
212205

213206
String taskRoot = System.getProperty("user.dir");
214207
String libRoot = "/opt/java";
@@ -223,17 +216,18 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge
223216
requestHandler = findRequestHandler(handler, customerClassLoader);
224217
} catch (UserFault userFault) {
225218
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
226-
reportInitError(new Failure(userFault), runtimeClient);
219+
LambdaError error = LambdaErrorConverter.fromUserFault(userFault);
220+
runtimeClient.reportInitError(error);
227221
System.exit(1);
228222
return;
229223
}
230224
if (INIT_TYPE_SNAP_START.equals(AWS_LAMBDA_INITIALIZATION_TYPE)) {
231-
onInitComplete(runtimeClient, lambdaLogger);
225+
onInitComplete(lambdaLogger);
232226
}
233227
boolean shouldExit = false;
234228
while (!shouldExit) {
235229
UserFault userFault = null;
236-
InvocationRequest request = runtimeClient.waitForNextInvocation();
230+
InvocationRequest request = runtimeClient.nextInvocation();
237231
if (request.getXrayTraceId() != null) {
238232
System.setProperty(LAMBDA_TRACE_HEADER_PROP, request.getXrayTraceId());
239233
} else {
@@ -243,26 +237,23 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge
243237
ByteArrayOutputStream payload;
244238
try {
245239
payload = requestHandler.call(request);
246-
runtimeClient.postInvocationResponse(request.getId(), payload.toByteArray());
240+
runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray());
247241
boolean ignored = Thread.interrupted(); // clear interrupted flag in case if it was set by user's code
248242
} catch (UserFault f) {
243+
shouldExit = f.fatal;
249244
userFault = f;
250245
UserFault.filterStackTrace(f);
251-
payload = new ByteArrayOutputStream(1024);
252-
Failure failure = new Failure(f);
253-
GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
254-
shouldExit = f.fatal;
255-
runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType());
246+
247+
LambdaError error = LambdaErrorConverter.fromUserFault(f);
248+
runtimeClient.reportInvocationError(request.getId(), error);
256249
} catch (Throwable t) {
250+
shouldExit = t instanceof VirtualMachineError || t instanceof IOError;
257251
UserFault.filterStackTrace(t);
258252
userFault = UserFault.makeUserFault(t);
259-
payload = new ByteArrayOutputStream(1024);
260-
Failure failure = new Failure(t);
261-
GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
262-
// These two categories of errors are considered fatal.
263-
shouldExit = Failure.isInvokeFailureFatal(t);
264-
runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType(),
265-
serializeAsXRayJson(t));
253+
254+
LambdaError error = LambdaErrorConverter.fromThrowable(t);
255+
XRayErrorCause xRayErrorCause = XRayErrorCauseConverter.fromThrowable(t);
256+
runtimeClient.reportInvocationError(request.getId(), error, xRayErrorCause);
266257
} finally {
267258
if (userFault != null) {
268259
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
@@ -271,23 +262,22 @@ private static void startRuntime(String handler, LambdaContextLogger lambdaLogge
271262
}
272263
}
273264

274-
static void onInitComplete(final LambdaRuntimeClient runtimeClient, final LambdaContextLogger lambdaLogger) throws IOException {
265+
static void onInitComplete(final LambdaContextLogger lambdaLogger) throws IOException {
275266
try {
276267
Core.getGlobalContext().beforeCheckpoint(null);
277-
// Blocking call to RAPID /restore/next API, will return after taking snapshot.
278-
// This will also be the 'entrypoint' when resuming from snapshots.
279-
runtimeClient.getRestoreNext();
268+
runtimeClient.restoreNext();
280269
} catch (Exception e1) {
281270
logExceptionCloudWatch(lambdaLogger, e1);
282-
reportInitError(new Failure(e1), runtimeClient);
271+
LambdaError error = LambdaErrorConverter.fromThrowable(e1);
272+
runtimeClient.reportInitError(error);
283273
System.exit(64);
284274
}
285275
try {
286276
Core.getGlobalContext().afterRestore(null);
287277
} catch (Exception restoreExc) {
288278
logExceptionCloudWatch(lambdaLogger, restoreExc);
289-
Failure errorPayload = new Failure(restoreExc);
290-
reportRestoreError(errorPayload, runtimeClient);
279+
LambdaError error = LambdaErrorConverter.fromThrowable(restoreExc);
280+
runtimeClient.reportRestoreError(error);
291281
System.exit(64);
292282
}
293283
}
@@ -297,40 +287,4 @@ private static void logExceptionCloudWatch(LambdaContextLogger lambdaLogger, Exc
297287
UserFault userFault = UserFault.makeUserFault(exc, true);
298288
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
299289
}
300-
301-
static void reportInitError(final Failure failure,
302-
final LambdaRuntimeClient runtimeClient) throws IOException {
303-
304-
ByteArrayOutputStream payload = new ByteArrayOutputStream(1024);
305-
JacksonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
306-
runtimeClient.postInitError(payload.toByteArray(), failure.getErrorType());
307-
}
308-
309-
static int reportRestoreError(final Failure failure,
310-
final LambdaRuntimeClient runtimeClient) throws IOException {
311-
312-
ByteArrayOutputStream payload = new ByteArrayOutputStream(1024);
313-
JacksonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
314-
return runtimeClient.postRestoreError(payload.toByteArray(), failure.getErrorType());
315-
}
316-
317-
private static PojoSerializer<XRayErrorCause> xRayErrorCauseSerializer;
318-
319-
/**
320-
* @param throwable throwable to convert
321-
* @return json as string expected by XRay's web console. On conversion failure, returns null.
322-
*/
323-
private static String serializeAsXRayJson(Throwable throwable) {
324-
try {
325-
final OutputStream outputStream = new ByteArrayOutputStream();
326-
final XRayErrorCause cause = new XRayErrorCause(throwable);
327-
if (xRayErrorCauseSerializer == null) {
328-
xRayErrorCauseSerializer = JacksonFactory.getInstance().getSerializer(XRayErrorCause.class);
329-
}
330-
xRayErrorCauseSerializer.toJson(cause, outputStream);
331-
return outputStream.toString();
332-
} catch (Exception e) {
333-
return null;
334-
}
335-
}
336290
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ private static String pathToClassName(final String path) {
3535
private static void loadClass(String name) {
3636
try {
3737
Class.forName(name, true, SYSTEM_CLASS_LOADER);
38+
System.out.println("Loaded " + name);
3839
} catch (ClassNotFoundException e) {
3940
System.err.println("[WARN] Failed to load " + name + ": " + e.getMessage());
4041
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import com.amazonaws.services.lambda.runtime.api.client.api.LambdaCognitoIdentity;
1414
import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
1515
import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger;
16-
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
16+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest;
1717
import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
1818
import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
1919
import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
@@ -22,6 +22,7 @@
2222
import com.amazonaws.services.lambda.runtime.serialization.util.Functions;
2323
import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
2424

25+
import java.io.ByteArrayInputStream;
2526
import java.io.ByteArrayOutputStream;
2627
import java.io.IOException;
2728
import java.io.InputStream;
@@ -902,7 +903,8 @@ public ByteArrayOutputStream call(InvocationRequest request) throws Error, Excep
902903
}
903904
}
904905

905-
handler.handleRequest(request.getContentAsStream(), output, context);
906+
ByteArrayInputStream bais = new ByteArrayInputStream(request.getContent());
907+
handler.handleRequest(bais, output, context);
906908
return output;
907909
}
908910
};

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java

-88
This file was deleted.

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ public class LambdaEnvironment {
1616
public static final String LAMBDA_LOG_FORMAT = ENV_READER.getEnvOrDefault(AWS_LAMBDA_LOG_FORMAT, "TEXT");
1717
public static final String FUNCTION_NAME = ENV_READER.getEnv(AWS_LAMBDA_FUNCTION_NAME);
1818
public static final String FUNCTION_VERSION = ENV_READER.getEnv(AWS_LAMBDA_FUNCTION_VERSION);
19+
public static final String RUNTIME_API = ENV_READER.getEnv(AWS_LAMBDA_RUNTIME_API);
1920
}

aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
package com.amazonaws.services.lambda.runtime.api.client;
44

5-
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
5+
import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.InvocationRequest;
66

77
import java.io.ByteArrayOutputStream;
88

0 commit comments

Comments
 (0)