diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java
index b35bba92c..f3976ab29 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java
@@ -121,11 +121,13 @@ public ServletContext getServletContext() {
* Sets the ServletContext in the handler and initialized a new FilterChainManager
* @param context An initialized ServletContext
*/
- protected void setServletContext(final ServletContext context) {
+ public void setServletContext(final ServletContext context) {
servletContext = context;
// We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
// the existing AwsServletContext object since it has no dependencies other than the Lambda context
- filterChainManager = new AwsFilterChainManager((AwsServletContext)servletContext);
+ if (context instanceof AwsServletContext) {
+ filterChainManager = new AwsFilterChainManager((AwsServletContext)servletContext);
+ }
}
protected FilterChain getFilterChain(HttpServletRequest req, Servlet servlet) {
diff --git a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java
index 075db0ac3..4ee168491 100644
--- a/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java
+++ b/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/model/AwsProxyRequest.java
@@ -32,7 +32,7 @@ public class AwsProxyRequest {
private String resource;
private AwsProxyRequestContext requestContext;
private MultiValuedTreeMap multiValueQueryStringParameters;
- private Map queryStringParameters;
+ private Map queryStringParameters;
private Headers multiValueHeaders;
private SingleValueHeaders headers;
private Map pathParameters;
@@ -41,12 +41,13 @@ public class AwsProxyRequest {
private String path;
private boolean isBase64Encoded;
- public AwsProxyRequest() {
- multiValueHeaders = new Headers();
- multiValueQueryStringParameters = new MultiValuedTreeMap<>();
- pathParameters = new HashMap<>();
- stageVariables = new HashMap<>();
- }
+ public AwsProxyRequest() {
+ this.headers = new SingleValueHeaders();
+ multiValueHeaders = new Headers();
+ multiValueQueryStringParameters = new MultiValuedTreeMap<>();
+ pathParameters = new HashMap<>();
+ stageVariables = new HashMap<>();
+ }
//-------------------------------------------------------------
@@ -131,17 +132,21 @@ public Headers getMultiValueHeaders() {
return multiValueHeaders;
}
- public void setMultiValueHeaders(Headers multiValueHeaders) {
- this.multiValueHeaders = multiValueHeaders;
- }
+ public void setMultiValueHeaders(Headers multiValueHeaders) {
+ if (multiValueHeaders != null) {
+ this.multiValueHeaders = multiValueHeaders;
+ }
+ }
public SingleValueHeaders getHeaders() {
return headers;
}
- public void setHeaders(SingleValueHeaders headers) {
- this.headers = headers;
- }
+ public void setHeaders(SingleValueHeaders headers) {
+ if (headers != null) {
+ this.headers = headers;
+ }
+ }
public Map getPathParameters() {
diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml
index c734576a4..bae45b1d3 100644
--- a/aws-serverless-java-container-spring/pom.xml
+++ b/aws-serverless-java-container-spring/pom.xml
@@ -21,6 +21,11 @@
+
+ org.springframework.cloud
+ spring-cloud-function-serverless-web
+ 4.0.3
+
com.amazonaws.serverless
@@ -57,13 +62,6 @@
test
-
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version}
- test
-
-
jakarta.activation
jakarta.activation-api
diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
index 5e56dba62..41dff16a3 100644
--- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
+++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java
@@ -20,6 +20,7 @@
import com.amazonaws.serverless.proxy.internal.servlet.*;
import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest;
import com.amazonaws.services.lambda.runtime.Context;
+
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
@@ -49,13 +50,19 @@ public class SpringLambdaContainerHandler extends Aws
* @return An initialized instance of the `SpringLambdaContainerHandler`
* @throws ContainerInitializationException When the Spring framework fails to start.
*/
- public static SpringLambdaContainerHandler getAwsProxyHandler(Class>... config) throws ContainerInitializationException {
- return new SpringProxyHandlerBuilder()
- .defaultProxy()
- .initializationWrapper(new InitializationWrapper())
- .configurationClasses(config)
- .buildAndInitialize();
- }
+ public static SpringLambdaContainerHandler getAwsProxyHandler(Class>... config)
+ throws ContainerInitializationException {
+ // Temporary flag. Should be removed once spring-cloud-function delegation model becomes the only path.
+ boolean delegateToSpringCloudFunction = Boolean.parseBoolean(System.getenv().getOrDefault("spring.cloud.function.enable",
+ (String) System.getProperties().getOrDefault("spring.cloud.function.enable", "true")));
+ if (delegateToSpringCloudFunction) {
+ return getSpringNativeHandler(config);
+ } else {
+ return new SpringProxyHandlerBuilder().defaultProxy()
+ .initializationWrapper(new InitializationWrapper()).configurationClasses(config)
+ .buildAndInitialize();
+ }
+ }
/**
* Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects and sets the given profiles as active
@@ -188,4 +195,11 @@ protected void registerServlets() {
reg.addMapping("/");
reg.setLoadOnStartup(1);
}
+
+ private static SpringLambdaContainerHandler getSpringNativeHandler(Class>... config) throws ContainerInitializationException {
+ SpringLambdaContainerHandler handler = new SpringProxyHandlerBuilder()
+ .defaultProxy().configurationClasses(config).buildSpringProxy();
+
+ return handler;
+ }
}
diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
index 5614b94df..4db08159a 100644
--- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
+++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java
@@ -13,12 +13,19 @@
package com.amazonaws.serverless.proxy.spring;
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
+import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
+import com.amazonaws.services.lambda.runtime.Context;
+
+import org.springframework.cloud.function.serverless.web.ProxyMvc;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.function.BiFunction;
+
public class SpringProxyHandlerBuilder extends ServletLambdaContainerHandlerBuilder<
RequestType,
@@ -73,6 +80,33 @@ public SpringLambdaContainerHandler build() throw
return handler;
}
+ /**
+ * Builds an instance of SpringLambdaContainerHandler with "delegate" to Spring provided ProxyMvc. The delegate
+ * is provided via BiFunction which takes HttpServletRequest and HttpSerbletResponse as input parameters.
+ * The AWS context is set as attribute of HttpServletRequest under `AWS_CONTEXT` key.
+ *
+ * @return instance of SpringLambdaContainerHandler
+ */
+ SpringLambdaContainerHandler buildSpringProxy() {
+ ProxyMvc mvc = ProxyMvc.INSTANCE(this.configurationClasses);
+ BiFunction handlerDelegate = new BiFunction() {
+ @Override
+ public Void apply(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ mvc.service(request, response);
+ response.flushBuffer();
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ return null;
+ }
+ };
+ SpringLambdaContainerHandler handler = createHandler(mvc.getApplicationContext(), handlerDelegate);
+ handler.setServletContext(mvc.getServletContext());
+ return handler;
+ }
+
protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx) {
return new SpringLambdaContainerHandler<>(
requestTypeClass, responseTypeClass, requestReader, responseWriter,
@@ -80,6 +114,20 @@ protected SpringLambdaContainerHandler createHand
);
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected SpringLambdaContainerHandler createHandler(ConfigurableWebApplicationContext ctx,
+ BiFunction handler) {
+ return new SpringLambdaContainerHandler(requestTypeClass, responseTypeClass, requestReader, responseWriter,
+ securityContextWriter, exceptionHandler, ctx, initializationWrapper) {
+ @Override
+ protected void handleRequest(HttpServletRequest containerRequest, AwsHttpServletResponse containerResponse,
+ Context lambdaContext) throws Exception {
+ containerRequest.setAttribute("AWS_CONTEXT", lambdaContext);
+ handler.apply(containerRequest, containerResponse);
+ }
+ };
+ }
+
@Override
public SpringLambdaContainerHandler buildAndInitialize() throws ContainerInitializationException {
SpringLambdaContainerHandler handler = build();
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
index 1101efc8c..19378a650 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringAwsProxyTest.java
@@ -3,7 +3,6 @@
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler;
-import com.amazonaws.serverless.proxy.internal.servlet.AwsServletRegistration;
import com.amazonaws.serverless.proxy.model.*;
import com.amazonaws.serverless.proxy.internal.servlet.AwsServletContext;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
@@ -21,13 +20,19 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
-import org.springframework.web.servlet.DispatcherServlet;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterRegistration;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletRegistration;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
+
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.servlet.DispatcherServlet;
+
import java.io.IOException;
+import java.lang.reflect.Field;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
@@ -57,8 +62,8 @@ public class SpringAwsProxyTest {
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/echo/*");
// servlet name mappings are disabled and will throw an exception
- //handler.getApplicationInitializer().getDispatcherServlet().setThrowExceptionIfNoHandlerFound(true);
- ((DispatcherServlet)((AwsServletRegistration)c.getServletRegistration("dispatcherServlet")).getServlet()).setThrowExceptionIfNoHandlerFound(true);
+ DispatcherServlet dServlet = extractDispatcherServletFromContext(c);
+ dServlet.setThrowExceptionIfNoHandlerFound(true);
});
private String type;
@@ -503,5 +508,17 @@ private void validateSingleValueModel(AwsProxyResponse output, String value) {
fail("Exception while parsing response body: " + e.getMessage());
}
}
+
+ private static DispatcherServlet extractDispatcherServletFromContext(ServletContext servletContext) {
+ ServletRegistration servletRegistration = servletContext.getServletRegistration("dispatcherServlet");
+ Field field = ReflectionUtils.findField(servletRegistration.getClass(), "servlet");
+ field.setAccessible(true);
+ try {
+ return (DispatcherServlet) field.get(servletRegistration);
+ }
+ catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
index 7742db7f6..3d251d2f8 100644
--- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
+++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/profile/SpringProfileTest.java
@@ -55,6 +55,7 @@ void profile_defaultProfile() throws Exception {
@Test
void profile_overrideProfile() throws Exception {
+ System.setProperty("spring.cloud.function.enable", "false");
AwsProxyRequest request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
.build();
SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
@@ -67,5 +68,6 @@ void profile_overrideProfile() throws Exception {
assertEquals("override-profile", response.getValues().get("profileTest"));
assertEquals("not-overridden", response.getValues().get("noOverride"));
assertEquals("override-profile-from-bean", response.getValues().get("beanInjectedValue"));
+ System.setProperty("spring.cloud.function.enable", "true");
}
}
\ No newline at end of file
diff --git a/samples/spring/pet-store/pom.xml b/samples/spring/pet-store/pom.xml
index 9958e007c..91843c33f 100644
--- a/samples/spring/pet-store/pom.xml
+++ b/samples/spring/pet-store/pom.xml
@@ -32,6 +32,12 @@
+
+ io.github.crac
+ org-crac
+ 0.1.3
+
+
com.amazonaws.serverless
aws-serverless-java-container-spring