Skip to content

Commit

Permalink
Merge pull request #2 from mattcorey/profile-support
Browse files Browse the repository at this point in the history
Profile support
  • Loading branch information
mattcorey authored Feb 25, 2017
2 parents 1682ba8 + 1af0f7c commit 7205220
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 7 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyRe
```

### Spring support
The library supports Spring applications that are configured using annotations (in code) rather than in an XML file. The simplest possible configuration uses the `@ComponentScan` annotation to load all controller classes from a package. For example, our unit test application has the following configuuration class.
The library supports Spring applications that are configured using annotations (in code) rather than in an XML file. The simplest possible configuration uses the `@ComponentScan` annotation to load all controller classes from a package. For example, our unit test application has the following configuration class.

```java
@Configuration
Expand All @@ -57,6 +57,9 @@ public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyRe
}
```

#### Spring Profiles
You can enable Spring Profiles (as defined with the `@Profile` annotation) by deploying your Lambda under a stage name that matches the profile name. This will allow you to create a single package with support for different resources for dev, test, prod, etc. See [@Profile documentation](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html) for details.

### Spark support
The library also supports applications written with the [Spark framework](http://sparkjava.com/). When using the library with Spark, it's important to initialize the `SparkLambdaContainerHandler` before defining routes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,9 @@ public String getVirtualServerName() {
return null;
}

public String getStage() {
return awsProxyRequest.getRequestContext().getStage();
}

public static ServletContext getInstance(AwsProxyRequest request, Context lambdaContext) {
if (instance == null) {
Expand All @@ -418,4 +421,8 @@ public static ServletContext getInstance(AwsProxyRequest request, Context lambda

return instance;
}

public static void clearServletContextCache() {
instance = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class LambdaSpringApplicationInitializer implements WebApplicationInitial
private ConfigurableWebApplicationContext applicationContext;
private boolean refreshContext = true;
private List<ServletContextListener> contextListeners;
private String lambdaStage;

// Dynamically instantiated properties
private ServletConfig dispatcherConfig;
Expand Down Expand Up @@ -84,8 +85,13 @@ public void dispatch(HttpServletRequest request, HttpServletResponse response)
dispatcherServlet.service(request, response);
}

public void setLambdaStage(String lambdaStage) {
this.lambdaStage = lambdaStage;
}

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
applicationContext.getEnvironment().setActiveProfiles(lambdaStage);
applicationContext.setServletContext(servletContext);

dispatcherConfig = new DefaultDispatcherConfig(servletContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@
import com.amazonaws.serverless.proxy.internal.*;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest;
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader;
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
import com.amazonaws.serverless.proxy.internal.servlet.*;
import com.amazonaws.services.lambda.runtime.Context;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
Expand Down Expand Up @@ -112,6 +109,7 @@ protected void handleRequest(AwsProxyHttpServletRequest containerRequest, AwsHtt

// wire up the application context on the first invocation
if (!initialized) {
initializer.setLambdaStage(((AwsProxyServletContext) containerRequest.getServletContext()).getStage());
ServletContext context = containerRequest.getServletContext();
initializer.onStartup(context);
initialized = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

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.context.annotation.PropertySource;
import org.springframework.web.context.ConfigurableWebApplicationContext;

@Configuration
@ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp")
@PropertySource("classpath:application.properties")
public class EchoSpringAppConfig {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.amazonaws.serverless.proxy.spring.echoapp;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;

/**
* Created by matthewcorey on 2/20/17.
*/
@Configuration
@Profile("override")
@PropertySource("classpath:/application-override.properties")
public class ProfileOverrideConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.amazonaws.serverless.proxy.spring.echoapp.profile;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DefaultProfileConfiguration {
@Bean
public String beanInjectedValue() {
return "default-profile-from-bean";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.amazonaws.serverless.proxy.spring.echoapp.profile;

import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/profile")
public class DefaultProfileResource {
@Value("${spring-proxy.profile-test}")
private String profileTest;

@Value("${spring-proxy.not-overridden-test}")
private String noOverride;

@Autowired
private String beanInjectedValue;

@RequestMapping(path = "/spring-properties", method = RequestMethod.GET)
public MapResponseModel loadProperties() {
MapResponseModel model = new MapResponseModel();

model.addValue("profileTest", profileTest);
model.addValue("noOverride", noOverride);
model.addValue("beanInjectedValue", beanInjectedValue);
return model;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.amazonaws.serverless.proxy.spring.echoapp.profile;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("override")
public class OverrideProfileConfiguration {
@Bean
public String beanInjectedValue() {
return "override-profile-from-bean";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.amazonaws.serverless.proxy.spring.profile;

import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyServletContext;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
import com.fasterxml.jackson.databind.ObjectMapper;
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 static org.junit.Assert.assertEquals;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EchoSpringAppConfig.class)
@WebAppConfiguration
@TestExecutionListeners(inheritListeners = false, listeners = {DependencyInjectionTestExecutionListener.class})
public class SpringProfileTest {
@Autowired
private ObjectMapper objectMapper;

@Autowired
private MockLambdaContext lambdaContext;

@Before
public void clearServletContextCache() {
AwsProxyServletContext.clearServletContextCache();
}

@Test
public void profile_defaultProfile() throws Exception {
AwsProxyRequest request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
.build();

SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
AwsProxyResponse output = handler.proxy(request, lambdaContext);
assertEquals(200, output.getStatusCode());

MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
assertEquals(3, response.getValues().size());
assertEquals("default-profile", response.getValues().get("profileTest"));
assertEquals("not-overridden", response.getValues().get("noOverride"));
assertEquals("default-profile-from-bean", response.getValues().get("beanInjectedValue"));
}

@Test
public void profile_overrideProfile() throws Exception {
AwsProxyRequest request = new AwsProxyRequestBuilder("/profile/spring-properties", "GET")
.stage("override")
.build();

SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
AwsProxyResponse output = handler.proxy(request, lambdaContext);
assertEquals(200, output.getStatusCode());

MapResponseModel response = objectMapper.readValue(output.getBody(), MapResponseModel.class);
assertEquals(3, response.getValues().size());
assertEquals("override-profile", response.getValues().get("profileTest"));
assertEquals("not-overridden", response.getValues().get("noOverride"));
assertEquals("override-profile-from-bean", response.getValues().get("beanInjectedValue"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spring-proxy.profile-test=override-profile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
spring-proxy.profile-test=default-profile
spring-proxy.not-overridden-test=not-overridden

0 comments on commit 7205220

Please sign in to comment.