Skip to content

Commit a3d63aa

Browse files
committed
Provide first module to run Struts2 based applications as AWS Lambda functions
aws#149
1 parent 7c754eb commit a3d63aa

File tree

8 files changed

+829
-0
lines changed

8 files changed

+829
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<artifactId>aws-serverless-java-container-struts2</artifactId>
7+
<name>AWS Serverless Java container support - Struts2 implementation</name>
8+
<description>Allows Java applications written for the Struts2 framework to run in AWS Lambda</description>
9+
<url>https://aws.amazon.com/lambda</url>
10+
<version>1.2-SNAPSHOT</version>
11+
12+
<parent>
13+
<groupId>com.amazonaws.serverless</groupId>
14+
<artifactId>aws-serverless-java-container</artifactId>
15+
<version>1.2-SNAPSHOT</version>
16+
</parent>
17+
18+
<properties>
19+
<struts2.version>2.5.16</struts2.version>
20+
<jackson.version>2.9.5</jackson.version>
21+
</properties>
22+
23+
<dependencies>
24+
<!-- Core interfaces for the aws-serverless-java-container project -->
25+
<dependency>
26+
<groupId>com.amazonaws.serverless</groupId>
27+
<artifactId>aws-serverless-java-container-core</artifactId>
28+
<version>1.2-SNAPSHOT</version>
29+
</dependency>
30+
31+
<dependency>
32+
<groupId>org.apache.struts</groupId>
33+
<artifactId>struts2-core</artifactId>
34+
<version>${struts2.version}</version>
35+
<optional>true</optional>
36+
<exclusions>
37+
<exclusion>
38+
<groupId>commons-io</groupId>
39+
<artifactId>commons-io</artifactId>
40+
</exclusion>
41+
</exclusions>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.apache.struts</groupId>
46+
<artifactId>struts2-json-plugin</artifactId>
47+
<version>${struts2.version}</version>
48+
<scope>test</scope>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>org.apache.struts</groupId>
53+
<artifactId>struts2-junit-plugin</artifactId>
54+
<version>${struts2.version}</version>
55+
<scope>test</scope>
56+
</dependency>
57+
58+
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
59+
<dependency>
60+
<groupId>commons-codec</groupId>
61+
<artifactId>commons-codec</artifactId>
62+
<version>1.10</version>
63+
<scope>test</scope>
64+
</dependency>
65+
66+
<dependency>
67+
<groupId>com.fasterxml.jackson.core</groupId>
68+
<artifactId>jackson-databind</artifactId>
69+
<version>${jackson.version}</version>
70+
</dependency>
71+
72+
<dependency>
73+
<groupId>org.apache.logging.log4j</groupId>
74+
<artifactId>log4j-to-slf4j</artifactId>
75+
<version>2.10.0</version>
76+
</dependency>
77+
78+
<dependency>
79+
<groupId>javax.el</groupId>
80+
<artifactId>javax.el-api</artifactId>
81+
<version>2.2.4</version>
82+
<scope>test</scope>
83+
</dependency>
84+
85+
<dependency>
86+
<groupId>javax.el</groupId>
87+
<artifactId>javax.el-api</artifactId>
88+
<version>2.2.4</version>
89+
<scope>test</scope>
90+
</dependency>
91+
92+
<dependency>
93+
<groupId>javax.servlet</groupId>
94+
<artifactId>jsp-api</artifactId>
95+
<version>2.0</version>
96+
<scope>test</scope>
97+
</dependency>
98+
99+
<dependency>
100+
<groupId>org.glassfish.web</groupId>
101+
<artifactId>javax.el</artifactId>
102+
<version>2.2.4</version>
103+
<scope>test</scope>
104+
</dependency>
105+
</dependencies>
106+
107+
<build>
108+
<plugins>
109+
<!-- fork JVM before each struts2 test to make sure we have a clean context -->
110+
<plugin>
111+
<groupId>org.apache.maven.plugins</groupId>
112+
<artifactId>maven-surefire-plugin</artifactId>
113+
<version>2.9</version>
114+
<configuration>
115+
<forkMode>always</forkMode>
116+
</configuration>
117+
</plugin>
118+
<plugin>
119+
<groupId>com.github.spotbugs</groupId>
120+
<artifactId>spotbugs-maven-plugin</artifactId>
121+
<version>3.1.1</version>
122+
<configuration>
123+
<!--
124+
Enables analysis which takes more memory but finds more bugs.
125+
If you run out of memory, changes the value of the effort element
126+
to 'Low'.
127+
-->
128+
<effort>Max</effort>
129+
<!-- Reports all bugs (other values are medium and max) -->
130+
<threshold>Low</threshold>
131+
<!-- Produces XML report -->
132+
<xmlOutput>true</xmlOutput>
133+
<!-- Configures the directory in which the XML report is created -->
134+
<spotbugsXmlOutputDirectory>${project.build.directory}/spotbugs</spotbugsXmlOutputDirectory>
135+
136+
<plugins>
137+
<plugin>
138+
<groupId>com.h3xstream.findsecbugs</groupId>
139+
<artifactId>findsecbugs-plugin</artifactId>
140+
<version>1.7.1</version>
141+
</plugin>
142+
</plugins>
143+
</configuration>
144+
<executions>
145+
<!--
146+
Ensures that SpotBug inspects source code when project is compiled.
147+
-->
148+
<execution>
149+
<id>analyze-compile</id>
150+
<phase>compile</phase>
151+
<goals>
152+
<goal>check</goal>
153+
</goals>
154+
</execution>
155+
</executions>
156+
</plugin>
157+
<plugin>
158+
<groupId>org.owasp</groupId>
159+
<artifactId>dependency-check-maven</artifactId>
160+
<version>3.1.1</version>
161+
<configuration>
162+
<skipProvidedScope>true</skipProvidedScope>
163+
<suppressionFiles>
164+
<suppressionFile>${project.basedir}/../owasp-suppression.xml</suppressionFile>
165+
</suppressionFiles>
166+
<failBuildOnCVSS>7</failBuildOnCVSS>
167+
</configuration>
168+
<executions>
169+
<execution>
170+
<goals>
171+
<goal>check</goal>
172+
</goals>
173+
</execution>
174+
</executions>
175+
</plugin>
176+
</plugins>
177+
</build>
178+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.amazonaws.serverless.proxy.struts2;
2+
3+
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
4+
import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler;
5+
import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
6+
import com.amazonaws.serverless.proxy.ExceptionHandler;
7+
import com.amazonaws.serverless.proxy.RequestReader;
8+
import com.amazonaws.serverless.proxy.ResponseWriter;
9+
import com.amazonaws.serverless.proxy.SecurityContextWriter;
10+
import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse;
11+
import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler;
12+
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest;
13+
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequestReader;
14+
import com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletResponseWriter;
15+
import com.amazonaws.serverless.proxy.internal.testutils.Timer;
16+
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
17+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
18+
import com.amazonaws.services.lambda.runtime.Context;
19+
import org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import javax.servlet.DispatcherType;
24+
import javax.servlet.FilterRegistration;
25+
import java.util.EnumSet;
26+
import java.util.concurrent.CountDownLatch;
27+
28+
/**
29+
* A Lambda handler to initialize the Struts2 filter and proxy the requests.
30+
*
31+
* @param <RequestType> request type
32+
* @param <ResponseType> response type
33+
*/
34+
public class Struts2LambdaContainerHandler<RequestType, ResponseType> extends AwsLambdaServletContainerHandler<RequestType, ResponseType, AwsProxyHttpServletRequest, AwsHttpServletResponse> {
35+
36+
private static final Logger log = LoggerFactory.getLogger(Struts2LambdaContainerHandler.class);
37+
38+
public static final String HEADER_STRUTS_STATUS_CODE = "X-Struts-StatusCode";
39+
40+
private static final String TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR = "STRUTS2_CONTAINER_CONSTRUCTOR";
41+
private static final String TIMER_STRUTS_2_HANDLE_REQUEST = "STRUTS2_HANDLE_REQUEST";
42+
private static final String TIMER_STRUTS_2_COLD_START_INIT = "STRUTS2_COLD_START_INIT";
43+
private static final String STRUTS_FILTER_NAME = "Struts2Filter";
44+
45+
private boolean initialized;
46+
47+
public static Struts2LambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> getAwsProxyHandler() {
48+
return new Struts2LambdaContainerHandler(
49+
AwsProxyRequest.class,
50+
AwsProxyResponse.class,
51+
new AwsProxyHttpServletRequestReader(),
52+
new AwsProxyHttpServletResponseWriter(),
53+
new AwsProxySecurityContextWriter(),
54+
new AwsProxyExceptionHandler());
55+
}
56+
57+
public Struts2LambdaContainerHandler(Class<RequestType> requestTypeClass,
58+
Class<ResponseType> responseTypeClass,
59+
RequestReader<RequestType, AwsProxyHttpServletRequest> requestReader,
60+
ResponseWriter<AwsHttpServletResponse, ResponseType> responseWriter,
61+
SecurityContextWriter<RequestType> securityContextWriter,
62+
ExceptionHandler<ResponseType> exceptionHandler) {
63+
64+
super(requestTypeClass, responseTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler);
65+
Timer.start(TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR);
66+
this.initialized = false;
67+
Timer.stop(TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR);
68+
}
69+
70+
protected AwsHttpServletResponse getContainerResponse(AwsProxyHttpServletRequest request, CountDownLatch latch) {
71+
return new AwsHttpServletResponse(request, latch);
72+
}
73+
74+
@Override
75+
protected void handleRequest(AwsProxyHttpServletRequest httpServletRequest,
76+
AwsHttpServletResponse httpServletResponse,
77+
Context lambdaContext) throws Exception {
78+
Timer.start(TIMER_STRUTS_2_HANDLE_REQUEST);
79+
if (!this.initialized) {
80+
initialize();
81+
}
82+
83+
httpServletRequest.setServletContext(this.getServletContext());
84+
this.doFilter(httpServletRequest, httpServletResponse, null);
85+
String responseStatusCode = httpServletResponse.getHeader(HEADER_STRUTS_STATUS_CODE);
86+
if (responseStatusCode != null) {
87+
httpServletResponse.setStatus(Integer.parseInt(responseStatusCode));
88+
}
89+
Timer.stop(TIMER_STRUTS_2_HANDLE_REQUEST);
90+
}
91+
92+
@Override
93+
public void initialize() throws ContainerInitializationException {
94+
log.info("Initialize Struts2 Lambda Application ...");
95+
Timer.start(TIMER_STRUTS_2_COLD_START_INIT);
96+
try {
97+
if (this.startupHandler != null) {
98+
this.startupHandler.onStartup(this.getServletContext());
99+
}
100+
StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter();
101+
FilterRegistration.Dynamic filterRegistration = this.getServletContext()
102+
.addFilter(STRUTS_FILTER_NAME, filter);
103+
filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
104+
} catch (Exception e) {
105+
throw new ContainerInitializationException("Could not initialize Struts2", e);
106+
}
107+
108+
this.initialized = true;
109+
Timer.stop(TIMER_STRUTS_2_COLD_START_INIT);
110+
log.info("... initialize of Struts2 Lambda Application completed!");
111+
}
112+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.amazonaws.serverless.proxy.struts2;
2+
3+
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
4+
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
5+
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
6+
import com.amazonaws.services.lambda.runtime.Context;
7+
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.io.OutputStream;
14+
15+
/**
16+
* The lambda handler to handle the requests.
17+
* <p>
18+
* <code>
19+
* com.amazonaws.serverless.proxy.struts2.Struts2LambdaHandler::handleRequest
20+
* </code>
21+
*/
22+
public class Struts2LambdaHandler implements RequestStreamHandler {
23+
24+
private static final Logger log = LoggerFactory.getLogger(Struts2LambdaHandler.class);
25+
26+
private final Struts2LambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = Struts2LambdaContainerHandler
27+
.getAwsProxyHandler();
28+
29+
@Override
30+
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
31+
32+
try {
33+
AwsProxyRequest request = LambdaContainerHandler.getObjectMapper()
34+
.readValue(inputStream, AwsProxyRequest.class);
35+
36+
AwsProxyResponse response = handler.proxy(request, context);
37+
LambdaContainerHandler.getObjectMapper().writeValue(outputStream, response);
38+
39+
// just in case it wasn't closed by the mapper
40+
outputStream.close();
41+
} catch (IOException e) {
42+
log.error("An unexpected exception happened while handling request", e);
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)