diff --git a/aws-serverless-java-container-spring/pom.xml b/aws-serverless-java-container-spring/pom.xml index 6ae41bbff..dbcb5b891 100644 --- a/aws-serverless-java-container-spring/pom.xml +++ b/aws-serverless-java-container-spring/pom.xml @@ -45,6 +45,14 @@ ${spring.version} + + + org.springframework + spring-test + ${spring.version} + test + + commons-codec diff --git a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/LambdaSpringApplicationInitializer.java b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/LambdaSpringApplicationInitializer.java index 0c4cfb39a..3e0e6bd46 100644 --- a/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/LambdaSpringApplicationInitializer.java +++ b/aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/LambdaSpringApplicationInitializer.java @@ -12,10 +12,10 @@ */ package com.amazonaws.serverless.proxy.spring; -import com.amazonaws.serverless.exceptions.InvalidResponseObjectException; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.ServletRequestHandledEvent; @@ -44,11 +44,11 @@ public class LambdaSpringApplicationInitializer implements WebApplicationInitial private static final String DEFAULT_SERVLET_NAME = "aws-servless-java-container"; // Configuration variables that can be passed in - private List configurationClasses; + private ConfigurableWebApplicationContext applicationContext; + private boolean refreshContext = true; private List contextListeners; // Dynamically instantiated properties - private AnnotationConfigWebApplicationContext applicationContext; private ServletConfig dispatcherConfig; private DispatcherServlet dispatcherServlet; @@ -57,31 +57,11 @@ public class LambdaSpringApplicationInitializer implements WebApplicationInitial /** * Creates a new instance of the WebApplicationInitializer + * @param applicationContext A custom ConfigurableWebApplicationContext to be used */ - public LambdaSpringApplicationInitializer() { - configurationClasses = new ArrayList<>(); - contextListeners = new ArrayList<>(); - } - - /** - * Adds a configuration class to the Spring startup process. This should be called at least once with an annotated - * class that contains the @Configuration annotations. The simplest possible class uses the @ComponentScan - * annocation to load all controllers. - * - *
-     * {@Code
-     * @Configuration
-     * @ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp")
-     * public class EchoSpringAppConfig {
-     * }
-     * }
-     * 
- * @param configuration A set of configuration classes - */ - public void addConfigurationClasses(Class... configuration) { - for (Class config : configuration) { - configurationClasses.add(config); - } + public LambdaSpringApplicationInitializer(ConfigurableWebApplicationContext applicationContext) { + this.contextListeners = new ArrayList<>(); + this.applicationContext = applicationContext; } /** @@ -94,27 +74,18 @@ public void addListener(ServletContextListener listener) { contextListeners.add(listener); } + public void setRefreshContext(boolean refreshContext) { + this.refreshContext = refreshContext; + } + public void dispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { currentResponse = response; dispatcherServlet.service(request, response); } - /** - * Returns the application context initialized in the library - * @return - */ - public AnnotationConfigWebApplicationContext getApplicationContext() { - return applicationContext; - } - @Override public void onStartup(ServletContext servletContext) throws ServletException { - // Create the 'root' Spring application context - applicationContext = new AnnotationConfigWebApplicationContext(); - for (Class config : configurationClasses) { - applicationContext.register(config); - } applicationContext.setServletContext(servletContext); dispatcherConfig = new DefaultDispatcherConfig(servletContext); @@ -138,7 +109,11 @@ public void onApplicationEvent(ServletRequestHandledEvent servletRequestHandledE // Register and map the dispatcher servlet dispatcherServlet = new DispatcherServlet(applicationContext); - dispatcherServlet.refresh(); + + if (refreshContext) { + dispatcherServlet.refresh(); + } + dispatcherServlet.onApplicationEvent(new ContextRefreshedEvent(applicationContext)); dispatcherServlet.init(dispatcherConfig); 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 44dbcd278..29fa9a223 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 @@ -21,6 +21,8 @@ import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader; import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter; import com.amazonaws.services.lambda.runtime.Context; +import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import javax.servlet.ServletContext; import java.util.concurrent.CountDownLatch; @@ -44,19 +46,34 @@ public class SpringLambdaContainerHandler extends Lam * @return An initialized instance of the `SpringLambdaContainerHandler` * @throws ContainerInitializationException */ - public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) - throws ContainerInitializationException { - SpringLambdaContainerHandler handler = - new SpringLambdaContainerHandler<>( - new AwsProxyHttpServletRequestReader(), - new AwsProxyHttpServletResponseWriter(), - new AwsProxySecurityContextWriter(), - new AwsProxyExceptionHandler() - ); + public static SpringLambdaContainerHandler getAwsProxyHandler(Class... config) throws ContainerInitializationException { + AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); + applicationContext.register(config); - handler.addConfiguration(config); + return new SpringLambdaContainerHandler<>( + new AwsProxyHttpServletRequestReader(), + new AwsProxyHttpServletResponseWriter(), + new AwsProxySecurityContextWriter(), + new AwsProxyExceptionHandler(), + applicationContext + ); + } - return handler; + /** + * Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects + * @param applicationContext A custom ConfigurableWebApplicationContext to be used + * @return An initialized instance of the `SpringLambdaContainerHandler` + * @throws ContainerInitializationException + */ + public static SpringLambdaContainerHandler getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext) + throws ContainerInitializationException { + return new SpringLambdaContainerHandler<>( + new AwsProxyHttpServletRequestReader(), + new AwsProxyHttpServletResponseWriter(), + new AwsProxySecurityContextWriter(), + new AwsProxyExceptionHandler(), + applicationContext + ); } /** @@ -69,20 +86,17 @@ public static SpringLambdaContainerHandler ge * @throws ContainerInitializationException */ public SpringLambdaContainerHandler(RequestReader requestReader, - ResponseWriter responseWriter, - SecurityContextWriter securityContextWriter, - ExceptionHandler exceptionHandler) + ResponseWriter responseWriter, + SecurityContextWriter securityContextWriter, + ExceptionHandler exceptionHandler, + ConfigurableWebApplicationContext applicationContext) throws ContainerInitializationException { super(requestReader, responseWriter, securityContextWriter, exceptionHandler); - initializer = new LambdaSpringApplicationInitializer(); + initializer = new LambdaSpringApplicationInitializer(applicationContext); } - /** - * Registers a set of classes with the underlying Spring application context - * @param config Spring annotated classes to be registered with the application context - */ - public void addConfiguration(Class... config) { - initializer.addConfigurationClasses(config); + public void setRefreshContext(boolean refreshContext) { + this.initializer.setRefreshContext(refreshContext); } @Override @@ -98,6 +112,7 @@ protected void handleRequest(AwsProxyHttpServletRequest containerRequest, AwsHtt initializer.onStartup(context); initialized = true; } + initializer.dispatch(containerRequest, containerResponse); } } 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 aee11abf8..0d19e5884 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 @@ -1,6 +1,5 @@ package com.amazonaws.serverless.proxy.spring; -import com.amazonaws.serverless.exceptions.ContainerInitializationException; import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest; import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse; import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; @@ -12,36 +11,66 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.ConfigurableWebApplicationContext; import java.io.IOException; import java.util.UUID; import static org.junit.Assert.*; +import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; +import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig; +import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel; +import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel; +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.binary.Base64; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.ConfigurableWebApplicationContext; -public class SpringAwsProxyTest { +import java.io.IOException; +import java.util.UUID; +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {EchoSpringAppConfig.class}) +@WebAppConfiguration +@TestExecutionListeners(inheritListeners = false, listeners = {DependencyInjectionTestExecutionListener.class}) +public class SpringAwsProxyTest { private static final String CUSTOM_HEADER_KEY = "x-custom-header"; private static final String CUSTOM_HEADER_VALUE = "my-custom-value"; private static final String AUTHORIZER_PRINCIPAL_ID = "test-principal-" + UUID.randomUUID().toString(); + @Autowired + private ObjectMapper objectMapper; - private static ObjectMapper objectMapper = new ObjectMapper(); - private static SpringLambdaContainerHandler handler = null; + @Autowired + private MockLambdaContext lambdaContext; - private static Context lambdaContext = new MockLambdaContext(); - - @BeforeClass - public static void init() { - try { - handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class); - } catch (ContainerInitializationException e) { - e.printStackTrace(); - fail(); - } - } + @Autowired + private SpringLambdaContainerHandler handler; @Test public void headers_getHeaders_echo() { @@ -188,3 +217,4 @@ private void validateSingleValueModel(AwsProxyResponse output, String value) { } } } + diff --git a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java index a8032a5fa..53ace4766 100644 --- a/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java +++ b/aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java @@ -1,9 +1,39 @@ package com.amazonaws.serverless.proxy.spring.echoapp; +import com.amazonaws.serverless.exceptions.ContainerInitializationException; +import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; +import com.amazonaws.serverless.proxy.spring.LambdaSpringApplicationInitializer; +import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler; +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.web.context.ConfigurableWebApplicationContext; @Configuration @ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp") public class EchoSpringAppConfig { + + @Autowired + private ConfigurableWebApplicationContext applicationContext; + + @Bean + public SpringLambdaContainerHandler springLambdaContainerHandler() throws ContainerInitializationException { + SpringLambdaContainerHandler handler = SpringLambdaContainerHandler.getAwsProxyHandler(applicationContext); + handler.setRefreshContext(false); + return handler; + } + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + @Bean + public MockLambdaContext lambdaContext() { + return new MockLambdaContext(); + } + }