Skip to content

Commit ce7136c

Browse files
committed
Improve extensibility, allow other servlets besides DispatcherServlet
1 parent a70de22 commit ce7136c

File tree

7 files changed

+208
-18
lines changed

7 files changed

+208
-18
lines changed

aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringLambdaContainerHandler.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* @param <ResponseType> The expected return type
3838
*/
3939
public class SpringLambdaContainerHandler<RequestType, ResponseType> extends AwsLambdaServletContainerHandler<RequestType, ResponseType, HttpServletRequest, AwsHttpServletResponse> {
40-
private ConfigurableWebApplicationContext appContext;
40+
protected final ConfigurableWebApplicationContext appContext;
4141
private String[] profiles;
4242

4343
// State vars
@@ -172,12 +172,20 @@ public void initialize()
172172
appContext.getEnvironment().setActiveProfiles(profiles);
173173
}
174174
appContext.setServletContext(getServletContext());
175+
registerServlets();
176+
// call initialize on AwsLambdaServletContainerHandler to initialize servlets that are set to load on startup
177+
super.initialize();
178+
Timer.stop("SPRING_COLD_START");
179+
}
180+
181+
/**
182+
* Overriding this method allows to customize the standard Spring DispatcherServlet
183+
* or to register additional servlets
184+
*/
185+
protected void registerServlets() {
175186
DispatcherServlet dispatcher = new DispatcherServlet(appContext);
176187
ServletRegistration.Dynamic reg = getServletContext().addServlet("dispatcherServlet", dispatcher);
177188
reg.addMapping("/");
178189
reg.setLoadOnStartup(1);
179-
// call initialize on AwsLambdaServletContainerHandler to initialize servlets that are set to load on startup
180-
super.initialize();
181-
Timer.stop("SPRING_COLD_START");
182190
}
183191
}

aws-serverless-java-container-spring/src/main/java/com/amazonaws/serverless/proxy/spring/SpringProxyHandlerBuilder.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,14 @@
1313
package com.amazonaws.serverless.proxy.spring;
1414

1515
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
16-
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest;
1716
import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
18-
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
1917
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
20-
import org.springframework.web.WebApplicationInitializer;
2118
import org.springframework.web.context.ConfigurableWebApplicationContext;
2219
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
2320

2421
import javax.servlet.http.HttpServletRequest;
2522

26-
public final class SpringProxyHandlerBuilder<RequestType> extends ServletLambdaContainerHandlerBuilder<
23+
public class SpringProxyHandlerBuilder<RequestType> extends ServletLambdaContainerHandlerBuilder<
2724
RequestType,
2825
AwsProxyResponse,
2926
HttpServletRequest,
@@ -69,22 +66,20 @@ public SpringLambdaContainerHandler<RequestType, AwsProxyResponse> build() throw
6966
}
7067
}
7168

72-
SpringLambdaContainerHandler<RequestType, AwsProxyResponse> handler = new SpringLambdaContainerHandler<RequestType, AwsProxyResponse>(
73-
requestTypeClass,
74-
responseTypeClass,
75-
requestReader,
76-
responseWriter,
77-
securityContextWriter,
78-
exceptionHandler,
79-
ctx,
80-
initializationWrapper
81-
);
69+
SpringLambdaContainerHandler<RequestType, AwsProxyResponse> handler = createHandler(ctx);
8270
if (profiles != null) {
8371
handler.activateSpringProfiles(profiles);
8472
}
8573
return handler;
8674
}
8775

76+
protected SpringLambdaContainerHandler<RequestType, AwsProxyResponse> createHandler(ConfigurableWebApplicationContext ctx) {
77+
return new SpringLambdaContainerHandler<>(
78+
requestTypeClass, responseTypeClass, requestReader, responseWriter,
79+
securityContextWriter, exceptionHandler, ctx, initializationWrapper
80+
);
81+
}
82+
8883
@Override
8984
public SpringLambdaContainerHandler<RequestType, AwsProxyResponse> buildAndInitialize() throws ContainerInitializationException {
9085
SpringLambdaContainerHandler<RequestType, AwsProxyResponse> handler = build();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.amazonaws.serverless.proxy.spring.extensibility;
2+
3+
import org.springframework.context.ApplicationContext;
4+
5+
import javax.servlet.http.HttpServlet;
6+
import javax.servlet.http.HttpServletRequest;
7+
import javax.servlet.http.HttpServletResponse;
8+
import java.io.IOException;
9+
10+
public class CustomServlet extends HttpServlet {
11+
private ApplicationContext appCtx;
12+
13+
@Override
14+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
15+
resp.getWriter().print("Unittest " + (appCtx!=null ? appCtx.getDisplayName() : ""));
16+
}
17+
18+
public void setAppCtx(ApplicationContext appCtx) {
19+
this.appCtx = appCtx;
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.amazonaws.serverless.proxy.spring.extensibility;
2+
3+
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
4+
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
5+
import org.junit.Test;
6+
7+
import javax.ws.rs.HttpMethod;
8+
import java.io.ByteArrayOutputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
12+
import static org.junit.Assert.assertTrue;
13+
14+
public class CustomServletTest {
15+
16+
@Test
17+
public void customServlet() throws IOException {
18+
StreamLambdaHandler lambdaHandler = new StreamLambdaHandler();
19+
InputStream requestStream = new AwsProxyRequestBuilder("/test", HttpMethod.GET)
20+
.buildStream();
21+
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
22+
lambdaHandler.handleRequest(requestStream, responseStream, new MockLambdaContext());
23+
assertTrue("response should contain value set in CustomServlet",
24+
responseStream.toString().contains("Unittest"));
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.amazonaws.serverless.proxy.spring.extensibility;
2+
3+
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
4+
import com.amazonaws.serverless.proxy.*;
5+
import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
6+
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
7+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
8+
import com.amazonaws.serverless.proxy.model.HttpApiV2ProxyRequest;
9+
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
10+
import org.springframework.web.context.ConfigurableWebApplicationContext;
11+
12+
import javax.servlet.ServletRegistration;
13+
import javax.servlet.http.HttpServletRequest;
14+
15+
public class CustomSpringLambdaContainerHandler<RequestType, ResponseType> extends SpringLambdaContainerHandler<RequestType, ResponseType> {
16+
17+
/**
18+
* Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects
19+
* @param config A set of classes annotated with the Spring @Configuration annotation
20+
* @return An initialized instance of the `SpringLambdaContainerHandler`
21+
* @throws ContainerInitializationException When the Spring framework fails to start.
22+
*/
23+
public static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> getAwsProxyHandler(Class<?>... config) throws ContainerInitializationException {
24+
return new CustomSpringProxyHandlerBuilder<AwsProxyRequest>()
25+
.defaultProxy()
26+
.initializationWrapper(new InitializationWrapper())
27+
.configurationClasses(config)
28+
.buildAndInitialize();
29+
}
30+
31+
/**
32+
* Creates a default SpringLambdaContainerHandler initialized with the `AwsProxyRequest` and `AwsProxyResponse` objects and sets the given profiles as active
33+
* @param applicationContext A custom ConfigurableWebApplicationContext to be used
34+
* @param profiles The spring profiles to activate
35+
* @return An initialized instance of the `SpringLambdaContainerHandler`
36+
* @throws ContainerInitializationException When the Spring framework fails to start.
37+
*/
38+
public static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> getAwsProxyHandler(ConfigurableWebApplicationContext applicationContext, String... profiles)
39+
throws ContainerInitializationException {
40+
return new CustomSpringProxyHandlerBuilder<AwsProxyRequest>()
41+
.defaultProxy()
42+
.initializationWrapper(new InitializationWrapper())
43+
.springApplicationContext(applicationContext)
44+
.profiles(profiles)
45+
.buildAndInitialize();
46+
}
47+
48+
/**
49+
* Creates a default SpringLambdaContainerHandler initialized with the `HttpApiV2ProxyRequest` and `AwsProxyResponse` objects
50+
* @param config A set of classes annotated with the Spring @Configuration annotation
51+
* @return An initialized instance of the `SpringLambdaContainerHandler`
52+
* @throws ContainerInitializationException When the Spring framework fails to start.
53+
*/
54+
public static SpringLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse> getHttpApiV2ProxyHandler(Class<?>... config) throws ContainerInitializationException {
55+
return new CustomSpringProxyHandlerBuilder<HttpApiV2ProxyRequest>()
56+
.defaultHttpApiV2Proxy()
57+
.initializationWrapper(new InitializationWrapper())
58+
.configurationClasses(config)
59+
.buildAndInitialize();
60+
}
61+
62+
/**
63+
* Creates a new container handler with the given reader and writer objects
64+
*
65+
* @param requestTypeClass The class for the incoming Lambda event
66+
* @param requestReader An implementation of `RequestReader`
67+
* @param responseWriter An implementation of `ResponseWriter`
68+
* @param securityContextWriter An implementation of `SecurityContextWriter`
69+
* @param exceptionHandler An implementation of `ExceptionHandler`
70+
*/
71+
public CustomSpringLambdaContainerHandler(Class<RequestType> requestTypeClass,
72+
Class<ResponseType> responseTypeClass,
73+
RequestReader<RequestType, HttpServletRequest> requestReader,
74+
ResponseWriter<AwsHttpServletResponse, ResponseType> responseWriter,
75+
SecurityContextWriter<RequestType> securityContextWriter,
76+
ExceptionHandler<ResponseType> exceptionHandler,
77+
ConfigurableWebApplicationContext applicationContext,
78+
InitializationWrapper init) {
79+
super(requestTypeClass, responseTypeClass, requestReader, responseWriter, securityContextWriter,
80+
exceptionHandler, applicationContext, init);
81+
}
82+
83+
@Override
84+
protected void registerServlets() {
85+
CustomServlet customServlet = new CustomServlet();
86+
customServlet.setAppCtx(appContext);
87+
ServletRegistration.Dynamic reg = getServletContext().addServlet("customServlet", customServlet);
88+
reg.addMapping("/");
89+
reg.setLoadOnStartup(1);
90+
}
91+
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.amazonaws.serverless.proxy.spring.extensibility;
2+
3+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
4+
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
5+
import com.amazonaws.serverless.proxy.spring.SpringProxyHandlerBuilder;
6+
import org.springframework.web.context.ConfigurableWebApplicationContext;
7+
8+
public class CustomSpringProxyHandlerBuilder<RequestType> extends SpringProxyHandlerBuilder<RequestType> {
9+
10+
@Override
11+
protected SpringLambdaContainerHandler<RequestType, AwsProxyResponse> createHandler(ConfigurableWebApplicationContext ctx) {
12+
return new CustomSpringLambdaContainerHandler<>(requestTypeClass, responseTypeClass, requestReader, responseWriter,
13+
securityContextWriter, exceptionHandler, ctx, initializationWrapper);
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.amazonaws.serverless.proxy.spring.extensibility;
2+
3+
4+
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
5+
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
6+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
7+
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
8+
import com.amazonaws.services.lambda.runtime.Context;
9+
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
10+
import org.springframework.web.context.support.GenericWebApplicationContext;
11+
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.io.OutputStream;
15+
16+
17+
public class StreamLambdaHandler implements RequestStreamHandler {
18+
private static final SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
19+
static {
20+
try {
21+
handler = CustomSpringLambdaContainerHandler.getAwsProxyHandler(new GenericWebApplicationContext());
22+
} catch (ContainerInitializationException e) {
23+
throw new RuntimeException("Could not initialize Spring framework", e);
24+
}
25+
}
26+
27+
@Override
28+
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
29+
throws IOException {
30+
handler.proxyStream(inputStream, outputStream, context);
31+
}
32+
}

0 commit comments

Comments
 (0)