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 f679c217c..9eb7efe7b 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 @@ -5,7 +5,6 @@ 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; @@ -38,11 +37,9 @@ 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; @@ -51,16 +48,10 @@ 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 ThreadLocal>> typeCache = ThreadLocal.withInitial(HashMap::new); private static final Comparator methodPriority = new Comparator() { public int compare(Method lhs, Method rhs) { @@ -97,16 +88,14 @@ private EventHandlerLoader() { } /** - * returns the appropriate serializer for the class based on platform and whether the class is a supported event + * returns the appropriate serializer for the class based on whether the class is a supported event * - * @param platform enum platform - * @param type Type of object used + * @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 getSerializer(Platform platform, Type type) { + private static PojoSerializer getSerializer(Type type) { PojoSerializer customSerializer = PojoSerializerLoader.getCustomerSerializer(type); if (customSerializer != null) { return customSerializer; @@ -119,24 +108,15 @@ private static PojoSerializer getSerializer(Platform platform, Type type return LambdaEventSerializers.serializerFor(clazz, AWSLambda.getCustomerClassLoader()); } } - // else platform dependent (Android uses GSON but all other platforms use Jackson) - if (Objects.requireNonNull(platform) == Platform.ANDROID) { - return GsonFactory.getInstance().getSerializer(type); - } return JacksonFactory.getInstance().getSerializer(type); } - private static PojoSerializer getSerializerCached(Platform platform, Type type) { - EnumMap>> threadTypeCache = typeCache.get(); - Map> cache = threadTypeCache.get(platform); - if (cache == null) { - cache = new HashMap<>(); - threadTypeCache.put(platform, cache); - } + private static PojoSerializer getSerializerCached(Type type) { + Map> cache = typeCache.get(); PojoSerializer serializer = cache.get(type); if (serializer == null) { - serializer = getSerializer(platform, type); + serializer = getSerializer(type); cache.put(type, serializer); } @@ -158,31 +138,6 @@ private static PojoSerializer getCognitoSerializer() { } - private static Platform getPlatform(Context context) { - ClientContext cc = context.getClientContext(); - if (cc == null) { - return Platform.UNKNOWN; - } - - Map env = cc.getEnvironment(); - if (env == null) { - return Platform.UNKNOWN; - } - - String platform = env.get("platform"); - if (platform == null) { - return Platform.UNKNOWN; - } - - if ("Android".equalsIgnoreCase(platform)) { - return Platform.ANDROID; - } else if ("iPhoneOS".equalsIgnoreCase(platform)) { - return Platform.IOS; - } else { - return Platform.UNKNOWN; - } - } - private static boolean isVoid(Type type) { return Void.TYPE.equals(type) || (type instanceof Class) && Void.class.isAssignableFrom((Class) type); } @@ -629,11 +584,11 @@ public PojoHandlerAsStreamHandler( if (inputType.isPresent()) { - getSerializerCached(Platform.UNKNOWN, inputType.get()); + getSerializerCached(inputType.get()); } if (outputType.isPresent()) { - getSerializerCached(Platform.UNKNOWN, outputType.get()); + getSerializerCached(outputType.get()); } } @@ -642,10 +597,9 @@ public PojoHandlerAsStreamHandler( public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { final Object input; - final Platform platform = getPlatform(context); try { if (inputType.isPresent()) { - input = getSerializerCached(platform, inputType.get()).fromJson(inputStream); + input = getSerializerCached(inputType.get()).fromJson(inputStream); } else { input = null; } @@ -662,7 +616,7 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co try { if (outputType.isPresent()) { - PojoSerializer serializer = getSerializerCached(platform, outputType.get()); + PojoSerializer serializer = getSerializerCached(outputType.get()); serializer.toJson(output, outputStream); } else { outputStream.write(_JsonNull); 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 aae2f1afe..4d39b6696 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 @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class EventHandlerLoaderTest { @@ -59,6 +60,22 @@ void PojoHandlerTest_twoParams() throws Exception { assertSuccessfulInvocation(lambdaRequestHandler); } + @Test + void PojoHandler_outputSerializer_ignoresClientContextPlatform() throws Exception { + LambdaRequestHandler handler = + getLambdaRequestHandler("test.lambda.handlers.POJOHanlderImpl::pojoOutputHandler"); + + InvocationRequest request = getTestInvocationRequest(); + request.setClientContext("{\"env\":{\"platform\":\"Android\"}}"); + + String result = handler.call(request).toString(); + + assertTrue(result.contains("beanProperty"), + "expected property-based (Jackson) serialization, got: " + result); + assertFalse(result.contains("internalField"), + "caller ClientContext must not switch serialization to field-based (Gson), got: " + result); + } + private LambdaRequestHandler getLambdaRequestHandler(String handler) throws ClassNotFoundException { ClassLoader cl = this.getClass().getClassLoader(); HandlerInfo handlerInfo = HandlerInfo.fromString(handler, cl); diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java index ca1a6bd4f..7b2c7cea9 100644 --- a/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java +++ b/aws-lambda-java-runtime-interface-client/src/test/java/test/lambda/handlers/POJOHanlderImpl.java @@ -23,4 +23,18 @@ public String oneParamHandler_context(Context context) { public String twoParamsHandler(String event, Context context) { return "success"; } + + @SuppressWarnings("unused") + public PojoOutput pojoOutputHandler(String event) { + return new PojoOutput(); + } + + @SuppressWarnings("unused") + public static class PojoOutput { + private final String internalField = "field-based-value"; + + public String getBeanProperty() { + return "property-based-value"; + } + } }