diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java index 7eef6764f..f98c13b11 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSLambdaUtils.java @@ -29,7 +29,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; import org.springframework.cloud.function.json.JsonMapper; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -37,6 +36,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; /** * @@ -52,21 +52,20 @@ final class AWSLambdaUtils { } public static Message generateMessage(byte[] payload, MessageHeaders headers, - FunctionInvocationWrapper function, JsonMapper mapper) { - return generateMessage(payload, headers, function, mapper, null); + Type inputType, JsonMapper mapper) { + return generateMessage(payload, headers, inputType, mapper, null); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static Message generateMessage(byte[] payload, MessageHeaders headers, - FunctionInvocationWrapper function, JsonMapper mapper, @Nullable Context awsContext) { + Type inputType, JsonMapper mapper, @Nullable Context awsContext) { if (logger.isInfoEnabled()) { - logger.info("Incoming JSON for ApiGateway Event: " + new String(payload)); + logger.info("Incoming JSON Event: " + new String(payload)); } MessageBuilder messageBuilder = null; Object request = mapper.fromJson(payload, Object.class); - Type inputType = function.getInputType(); if (FunctionTypeUtils.isMessage(inputType)) { inputType = FunctionTypeUtils.getImmediateGenericType(inputType, 0); } @@ -81,7 +80,7 @@ final class AWSLambdaUtils { } else if (requestMap.containsKey("httpMethod")) { // API Gateway logger.info("Incoming request is API Gateway"); - if (inputType.getTypeName().endsWith(APIGatewayProxyRequestEvent.class.getSimpleName())) { + if (isTypeAnApiGatewayRequest(inputType)) { APIGatewayProxyRequestEvent gatewayEvent = mapper.fromJson(requestMap, APIGatewayProxyRequestEvent.class); messageBuilder = MessageBuilder.withPayload(gatewayEvent); } @@ -108,7 +107,8 @@ final class AWSLambdaUtils { public static byte[] generateOutput(Message requestMessage, Message responseMessage, JsonMapper mapper) { byte[] responseBytes = responseMessage.getPayload(); - if (requestMessage.getHeaders().containsKey("httpMethod") || requestMessage.getPayload() instanceof APIGatewayProxyRequestEvent) { // API Gateway + if (requestMessage.getHeaders().containsKey("httpMethod") + || isPayloadAnApiGatewayRequest(responseMessage.getPayload())) { // API Gateway Map response = new HashMap(); response.put("isBase64Encoded", false); @@ -136,6 +136,23 @@ final class AWSLambdaUtils { return responseBytes; } + private static boolean isPayloadAnApiGatewayRequest(Object payload) { + return isAPIGatewayProxyRequestEventPresent() + ? payload instanceof APIGatewayProxyRequestEvent + : false; + } + + private static boolean isTypeAnApiGatewayRequest(Type type) { + return isAPIGatewayProxyRequestEventPresent() + ? type.getTypeName().endsWith(APIGatewayProxyRequestEvent.class.getSimpleName()) + : false; + } + + private static boolean isAPIGatewayProxyRequestEventPresent() { + return ClassUtils.isPresent("com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent", + ClassUtils.getDefaultClassLoader()); + } + private static void logEvent(List> records) { if (isKinesisEvent(records.get(0))) { logger.info("Incoming request is Kinesis Event"); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java index 5fa888a44..29847c775 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java @@ -95,7 +95,7 @@ public class CustomRuntimeEventLoop { FunctionInvocationWrapper function = locateFunction(functionCatalog, response.getHeaders().getContentType()); Message eventMessage = AWSLambdaUtils.generateMessage(response.getBody().getBytes(StandardCharsets.UTF_8), - fromHttp(response.getHeaders()), function, mapper); + fromHttp(response.getHeaders()), function.getInputType(), mapper); if (logger.isDebugEnabled()) { logger.debug("Event message: " + eventMessage); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java index a83697ae6..7e8c2f488 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeInitializer.java @@ -16,6 +16,9 @@ package org.springframework.cloud.function.adapter.aws; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.boot.CommandLineRunner; import org.springframework.cloud.function.context.config.ContextFunctionCatalogInitializer; import org.springframework.cloud.function.web.source.DestinationResolver; @@ -31,18 +34,33 @@ import org.springframework.util.StringUtils; @Order(0) public class CustomRuntimeInitializer implements ApplicationContextInitializer { + private static Log logger = LogFactory.getLog(CustomRuntimeInitializer.class); + @Override public void initialize(GenericApplicationContext context) { - Boolean enabled = context.getEnvironment().getProperty("spring.cloud.function.web.export.enabled", - Boolean.class); - if (enabled == null || !enabled) { - if (StringUtils.hasText(System.getenv("AWS_LAMBDA_RUNTIME_API"))) { - if (context.getBeanFactory().getBeanNamesForType(CustomRuntimeEventLoop.class, false, false).length == 0) { - context.registerBean(StringUtils.uncapitalize(CustomRuntimeEventLoop.class.getSimpleName()), - CommandLineRunner.class, () -> args -> CustomRuntimeEventLoop.eventLoop(context)); - } + if (logger.isDebugEnabled()) { + logger.debug("AWS Environment: " + System.getenv()); + } + + // the presence of AWS_LAMBDA_RUNTIME_API signifies Custom Runtime + if (!this.isWebExportEnabled(context) && StringUtils.hasText(System.getenv("AWS_LAMBDA_RUNTIME_API"))) { + if (context.getBeanFactory().getBeanNamesForType(CustomRuntimeEventLoop.class, false, false).length == 0) { + context.registerBean(StringUtils.uncapitalize(CustomRuntimeEventLoop.class.getSimpleName()), + CommandLineRunner.class, () -> args -> CustomRuntimeEventLoop.eventLoop(context)); } } + + +// Boolean enabled = context.getEnvironment() +// .getProperty("spring.cloud.function.web.export.enabled", Boolean.class); +// if (enabled == null || !enabled) { +// if (StringUtils.hasText(System.getenv("AWS_LAMBDA_RUNTIME_API"))) { +// if (context.getBeanFactory().getBeanNamesForType(CustomRuntimeEventLoop.class, false, false).length == 0) { +// context.registerBean(StringUtils.uncapitalize(CustomRuntimeEventLoop.class.getSimpleName()), +// CommandLineRunner.class, () -> args -> CustomRuntimeEventLoop.eventLoop(context)); +// } +// } +// } else if (ContextFunctionCatalogInitializer.enabled && context.getEnvironment().getProperty("spring.functional.enabled", Boolean.class, false)) { if (context.getBeanFactory().getBeanNamesForType(DestinationResolver.class, false, false).length == 0) { @@ -53,4 +71,10 @@ public class CustomRuntimeInitializer implements ApplicationContextInitializer - - org.springframework.boot - spring-boot-starter-webflux - - - org.springframework.boot - spring-boot-starter-logging - - - org.springframework.boot - spring-boot-starter-json - - - org.hibernate.validator - hibernate-validator - - - org.synchronoss.cloud - nio-multipart-parser - - - - - org.springframework.cloud - spring-cloud-function-web - org.springframework.cloud spring-cloud-function-adapter-aws diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties b/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties index 9e8695a2c..48d1c32ec 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties +++ b/spring-cloud-function-samples/function-sample-aws-custom/src/main/resources/application.properties @@ -1,4 +1,4 @@ -spring.cloud.function.web.export.enabled=true -spring.cloud.function.web.export.debug=true +#spring.cloud.function.web.export.enabled=true +#spring.cloud.function.web.export.debug=true spring.main.web-application-type=none logging.level.org.springframework.cloud=DEBUG \ No newline at end of file diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/ContainerTests.java b/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/ContainerTests.java index 764c39166..20ec4834b 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/ContainerTests.java +++ b/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/ContainerTests.java @@ -18,6 +18,7 @@ package com.example; import java.util.concurrent.TimeUnit; import org.awaitility.Awaitility; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.ToStringConsumer; @@ -35,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class ContainerTests { @Test + @Disabled void test() throws Exception { ToStringConsumer consumer = new ToStringConsumer(); try (@SuppressWarnings("resource") diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java b/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java deleted file mode 100644 index 8e4bba7e5..000000000 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/test/java/com/example/LambdaApplicationTests.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example; - -import org.junit.jupiter.api.Test; - -import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; - -@FunctionalSpringBootTest -public class LambdaApplicationTests { - - @Test - public void contextLoads() { - } - -} - diff --git a/spring-cloud-function-samples/pom.xml b/spring-cloud-function-samples/pom.xml index 10616ca0b..d6af37d48 100644 --- a/spring-cloud-function-samples/pom.xml +++ b/spring-cloud-function-samples/pom.xml @@ -19,6 +19,7 @@ function-sample-pojo function-sample-aws function-sample-aws-custom + function-sample-aws-custom-bean function-sample-supplier-exporter function-sample-azure function-sample-spring-integration