From a3d63aab9bfaac1386a64a051c6dfbe17b7a2c53 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Wed, 20 Jun 2018 17:05:27 +0200 Subject: [PATCH 01/11] Provide first module to run Struts2 based applications as AWS Lambda functions https://github.com/awslabs/aws-serverless-java-container/issues/149 --- aws-serverless-java-container-struts2/pom.xml | 178 ++++++++++ .../Struts2LambdaContainerHandler.java | 112 ++++++ .../proxy/struts2/Struts2LambdaHandler.java | 45 +++ .../proxy/struts2/Struts2AwsProxyTest.java | 326 ++++++++++++++++++ .../proxy/struts2/echoapp/EchoAction.java | 53 +++ .../echoapp/EchoRequestInfoAction.java | 85 +++++ .../src/test/resources/struts.xml | 29 ++ pom.xml | 1 + 8 files changed, 829 insertions(+) create mode 100644 aws-serverless-java-container-struts2/pom.xml create mode 100644 aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaContainerHandler.java create mode 100644 aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaHandler.java create mode 100644 aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/Struts2AwsProxyTest.java create mode 100644 aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoAction.java create mode 100644 aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoRequestInfoAction.java create mode 100644 aws-serverless-java-container-struts2/src/test/resources/struts.xml diff --git a/aws-serverless-java-container-struts2/pom.xml b/aws-serverless-java-container-struts2/pom.xml new file mode 100644 index 000000000..621587cec --- /dev/null +++ b/aws-serverless-java-container-struts2/pom.xml @@ -0,0 +1,178 @@ + + + 4.0.0 + + aws-serverless-java-container-struts2 + AWS Serverless Java container support - Struts2 implementation + Allows Java applications written for the Struts2 framework to run in AWS Lambda + https://aws.amazon.com/lambda + 1.2-SNAPSHOT + + + com.amazonaws.serverless + aws-serverless-java-container + 1.2-SNAPSHOT + + + + 2.5.16 + 2.9.5 + + + + + + com.amazonaws.serverless + aws-serverless-java-container-core + 1.2-SNAPSHOT + + + + org.apache.struts + struts2-core + ${struts2.version} + true + + + commons-io + commons-io + + + + + + org.apache.struts + struts2-json-plugin + ${struts2.version} + test + + + + org.apache.struts + struts2-junit-plugin + ${struts2.version} + test + + + + + commons-codec + commons-codec + 1.10 + test + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + org.apache.logging.log4j + log4j-to-slf4j + 2.10.0 + + + + javax.el + javax.el-api + 2.2.4 + test + + + + javax.el + javax.el-api + 2.2.4 + test + + + + javax.servlet + jsp-api + 2.0 + test + + + + org.glassfish.web + javax.el + 2.2.4 + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.9 + + always + + + + com.github.spotbugs + spotbugs-maven-plugin + 3.1.1 + + + Max + + Low + + true + + ${project.build.directory}/spotbugs + + + + com.h3xstream.findsecbugs + findsecbugs-plugin + 1.7.1 + + + + + + + analyze-compile + compile + + check + + + + + + org.owasp + dependency-check-maven + 3.1.1 + + true + + ${project.basedir}/../owasp-suppression.xml + + 7 + + + + + check + + + + + + + diff --git a/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaContainerHandler.java b/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaContainerHandler.java new file mode 100644 index 000000000..003390ec0 --- /dev/null +++ b/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaContainerHandler.java @@ -0,0 +1,112 @@ +package com.amazonaws.serverless.proxy.struts2; + +import com.amazonaws.serverless.exceptions.ContainerInitializationException; +import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler; +import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter; +import com.amazonaws.serverless.proxy.ExceptionHandler; +import com.amazonaws.serverless.proxy.RequestReader; +import com.amazonaws.serverless.proxy.ResponseWriter; +import com.amazonaws.serverless.proxy.SecurityContextWriter; +import com.amazonaws.serverless.proxy.internal.servlet.AwsHttpServletResponse; +import com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler; +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.testutils.Timer; +import com.amazonaws.serverless.proxy.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.services.lambda.runtime.Context; +import org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.DispatcherType; +import javax.servlet.FilterRegistration; +import java.util.EnumSet; +import java.util.concurrent.CountDownLatch; + +/** + * A Lambda handler to initialize the Struts2 filter and proxy the requests. + * + * @param request type + * @param response type + */ +public class Struts2LambdaContainerHandler extends AwsLambdaServletContainerHandler { + + private static final Logger log = LoggerFactory.getLogger(Struts2LambdaContainerHandler.class); + + public static final String HEADER_STRUTS_STATUS_CODE = "X-Struts-StatusCode"; + + private static final String TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR = "STRUTS2_CONTAINER_CONSTRUCTOR"; + private static final String TIMER_STRUTS_2_HANDLE_REQUEST = "STRUTS2_HANDLE_REQUEST"; + private static final String TIMER_STRUTS_2_COLD_START_INIT = "STRUTS2_COLD_START_INIT"; + private static final String STRUTS_FILTER_NAME = "Struts2Filter"; + + private boolean initialized; + + public static Struts2LambdaContainerHandler getAwsProxyHandler() { + return new Struts2LambdaContainerHandler( + AwsProxyRequest.class, + AwsProxyResponse.class, + new AwsProxyHttpServletRequestReader(), + new AwsProxyHttpServletResponseWriter(), + new AwsProxySecurityContextWriter(), + new AwsProxyExceptionHandler()); + } + + public Struts2LambdaContainerHandler(Class requestTypeClass, + Class responseTypeClass, + RequestReader requestReader, + ResponseWriter responseWriter, + SecurityContextWriter securityContextWriter, + ExceptionHandler exceptionHandler) { + + super(requestTypeClass, responseTypeClass, requestReader, responseWriter, securityContextWriter, exceptionHandler); + Timer.start(TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR); + this.initialized = false; + Timer.stop(TIMER_STRUTS_2_CONTAINER_CONSTRUCTOR); + } + + protected AwsHttpServletResponse getContainerResponse(AwsProxyHttpServletRequest request, CountDownLatch latch) { + return new AwsHttpServletResponse(request, latch); + } + + @Override + protected void handleRequest(AwsProxyHttpServletRequest httpServletRequest, + AwsHttpServletResponse httpServletResponse, + Context lambdaContext) throws Exception { + Timer.start(TIMER_STRUTS_2_HANDLE_REQUEST); + if (!this.initialized) { + initialize(); + } + + httpServletRequest.setServletContext(this.getServletContext()); + this.doFilter(httpServletRequest, httpServletResponse, null); + String responseStatusCode = httpServletResponse.getHeader(HEADER_STRUTS_STATUS_CODE); + if (responseStatusCode != null) { + httpServletResponse.setStatus(Integer.parseInt(responseStatusCode)); + } + Timer.stop(TIMER_STRUTS_2_HANDLE_REQUEST); + } + + @Override + public void initialize() throws ContainerInitializationException { + log.info("Initialize Struts2 Lambda Application ..."); + Timer.start(TIMER_STRUTS_2_COLD_START_INIT); + try { + if (this.startupHandler != null) { + this.startupHandler.onStartup(this.getServletContext()); + } + StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter(); + FilterRegistration.Dynamic filterRegistration = this.getServletContext() + .addFilter(STRUTS_FILTER_NAME, filter); + filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); + } catch (Exception e) { + throw new ContainerInitializationException("Could not initialize Struts2", e); + } + + this.initialized = true; + Timer.stop(TIMER_STRUTS_2_COLD_START_INIT); + log.info("... initialize of Struts2 Lambda Application completed!"); + } +} diff --git a/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaHandler.java b/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaHandler.java new file mode 100644 index 000000000..e2afdfb47 --- /dev/null +++ b/aws-serverless-java-container-struts2/src/main/java/com/amazonaws/serverless/proxy/struts2/Struts2LambdaHandler.java @@ -0,0 +1,45 @@ +package com.amazonaws.serverless.proxy.struts2; + +import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler; +import com.amazonaws.serverless.proxy.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * The lambda handler to handle the requests. + *

+ * + * com.amazonaws.serverless.proxy.struts2.Struts2LambdaHandler::handleRequest + * + */ +public class Struts2LambdaHandler implements RequestStreamHandler { + + private static final Logger log = LoggerFactory.getLogger(Struts2LambdaHandler.class); + + private final Struts2LambdaContainerHandler handler = Struts2LambdaContainerHandler + .getAwsProxyHandler(); + + @Override + public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) { + + try { + AwsProxyRequest request = LambdaContainerHandler.getObjectMapper() + .readValue(inputStream, AwsProxyRequest.class); + + AwsProxyResponse response = handler.proxy(request, context); + LambdaContainerHandler.getObjectMapper().writeValue(outputStream, response); + + // just in case it wasn't closed by the mapper + outputStream.close(); + } catch (IOException e) { + log.error("An unexpected exception happened while handling request", e); + } + } +} diff --git a/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/Struts2AwsProxyTest.java b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/Struts2AwsProxyTest.java new file mode 100644 index 000000000..f1e4b4b8f --- /dev/null +++ b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/Struts2AwsProxyTest.java @@ -0,0 +1,326 @@ +/* + * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +package com.amazonaws.serverless.proxy.struts2; + + +import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder; +import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext; +import com.amazonaws.serverless.proxy.model.AwsProxyRequest; +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; +import com.amazonaws.serverless.proxy.struts2.echoapp.EchoAction; +import com.amazonaws.services.lambda.runtime.Context; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.codec.binary.Base64; +import org.apache.struts2.StrutsJUnit4TestCase; +import org.junit.Test; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Unit test class for the Struts2 AWS_PROXY default implementation + */ +public class Struts2AwsProxyTest extends StrutsJUnit4TestCase { + 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(); + private static final String QUERY_STRING_KEY = "message"; + private static final String QUERY_STRING_ENCODED_VALUE = "Hello Struts2"; + private static final String USER_PRINCIPAL = "user1"; + private static final String CONTENT_TYPE_APPLICATION_JSON = "application/json;charset=UTF-8"; + + + private static ObjectMapper objectMapper = new ObjectMapper(); + private final Struts2LambdaContainerHandler handler = Struts2LambdaContainerHandler + .getAwsProxyHandler(); + private static Context lambdaContext = new MockLambdaContext(); + + @Test + public void headers_getHeaders_echo() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "headers") + .header(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE) + .json() + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertEquals(CONTENT_TYPE_APPLICATION_JSON, output.getHeaders().get("Content-Type")); + + validateMapResponseModel(output); + } + + @Test + public void context_servletResponse_setCustomHeader() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "GET") + .queryString("customHeader", "true") + .json() + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertTrue(output.getHeaders().containsKey("XX")); + } + + @Test + public void context_serverInfo_correctContext() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "GET") + .queryString(QUERY_STRING_KEY, "Hello Struts2") + .header("Content-Type", "application/json") + .queryString("contentType", "true") + .build(); + AwsProxyResponse output = handler.proxy(request, lambdaContext); + for (String header : output.getHeaders().keySet()) { + System.out.println(header + ": " + output.getHeaders().get(header)); + } + assertEquals(200, output.getStatusCode()); + assertEquals(CONTENT_TYPE_APPLICATION_JSON, output.getHeaders().get("Content-Type")); + System.out.println("Body: " + output.getBody()); + + validateSingleValueModel(output, "Hello Struts2"); + } + + @Test + public void queryString_uriInfo_echo() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "query-string") + .queryString(CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE) + .json() + .build(); + + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertEquals(CONTENT_TYPE_APPLICATION_JSON, output.getHeaders().get("Content-Type")); + + validateMapResponseModel(output); + } + + @Test + public void requestScheme_valid_expectHttps() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "scheme") + .queryString(QUERY_STRING_KEY, QUERY_STRING_ENCODED_VALUE) + .json() + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertEquals(CONTENT_TYPE_APPLICATION_JSON, output.getHeaders().get("Content-Type")); + + validateSingleValueModel(output, "https"); + } + + @Test + public void authorizer_securityContext_customPrincipalSuccess() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "principal") + .json() + .authorizerPrincipal(AUTHORIZER_PRINCIPAL_ID) + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertEquals(CONTENT_TYPE_APPLICATION_JSON, output.getHeaders().get("Content-Type")); + + validateSingleValueModel(output, AUTHORIZER_PRINCIPAL_ID); + } + + @Test + public void errors_unknownRoute_expect404() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/unknown", "GET").build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(404, output.getStatusCode()); + } + + @Test + public void error_contentType_invalidContentType() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "POST") + .queryString("mode", "content-type") + .header("Content-Type", "application/octet-stream") + .body("asdasdasd") + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(415, output.getStatusCode()); + } + + @Test + public void error_statusCode_methodNotAllowed() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "POST") + .queryString("mode", "not-allowed") + .json() + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(405, output.getStatusCode()); + } + + + @Test + public void responseBody_responseWriter_validBody() throws JsonProcessingException { + Map value = new HashMap<>(); + value.put(QUERY_STRING_KEY, CUSTOM_HEADER_VALUE); + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "POST") + .json() + .body(objectMapper.writeValueAsString(value)) + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + assertNotNull(output.getBody()); + + validateSingleValueModel(output, "{\"message\":\"my-custom-value\"}"); + } + + @Test + public void statusCode_responseStatusCode_customStatusCode() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "custom-status-code") + .queryString("status", "201") + .json() + .build(); + + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(201, output.getStatusCode()); + } + + @Test + public void base64_binaryResponse_base64Encoding() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "GET").build(); + + AwsProxyResponse response = handler.proxy(request, lambdaContext); + assertNotNull(response.getBody()); + assertTrue(Base64.isBase64(response.getBody())); + } + + @Test + public void exception_mapException_mapToNotImplemented() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "POST") + .queryString("mode", "not-implemented") + .build(); + + AwsProxyResponse response = handler.proxy(request, lambdaContext); + assertNotNull(response.getBody()); + assertEquals("null", response.getBody()); + assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), response.getStatusCode()); + } + + @Test + public void stripBasePath_route_shouldRouteCorrectly() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/custompath/echo", "GET") + .json() + .queryString(QUERY_STRING_KEY, "stripped") + .build(); + handler.stripBasePath("/custompath"); + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(200, output.getStatusCode()); + validateSingleValueModel(output, "stripped"); + handler.stripBasePath(""); + } + + @Test + public void stripBasePath_route_shouldReturn404() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/custompath/echo/status-code", "GET") + .json() + .queryString("status", "201") + .build(); + handler.stripBasePath("/custom"); + AwsProxyResponse output = handler.proxy(request, lambdaContext); + assertEquals(404, output.getStatusCode()); + handler.stripBasePath(""); + } + + @Test + public void securityContext_injectPrincipal_expectPrincipalName() { + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo-request-info", "GET") + .queryString("mode", "principal") + .authorizerPrincipal(USER_PRINCIPAL).build(); + + AwsProxyResponse resp = handler.proxy(request, lambdaContext); + assertEquals(200, resp.getStatusCode()); + validateSingleValueModel(resp, USER_PRINCIPAL); + } + + @Test + public void queryParam_encoding_expectUnencodedParam() { + String paramValue = "p%2Fz%2B3"; + String decodedParam = ""; + try { + decodedParam = URLDecoder.decode(paramValue, "UTF-8"); + System.out.println(decodedParam); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + fail("Could not decode parameter"); + } + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "GET").queryString(QUERY_STRING_KEY, decodedParam) + .build(); + + AwsProxyResponse resp = handler.proxy(request, lambdaContext); + assertEquals(200, resp.getStatusCode()); + validateSingleValueModel(resp, decodedParam); + } + + @Test + public void queryParam_encoding_expectEncodedParam() { + String paramValue = "p%2Fz%2B3"; + AwsProxyRequest request = new AwsProxyRequestBuilder("/echo", "GET").queryString(QUERY_STRING_KEY, paramValue) + .build(); + + AwsProxyResponse resp = handler.proxy(request, lambdaContext); + assertEquals(200, resp.getStatusCode()); + validateSingleValueModel(resp, paramValue); + } + + + private void validateMapResponseModel(AwsProxyResponse output) { + validateMapResponseModel(output, CUSTOM_HEADER_KEY, CUSTOM_HEADER_VALUE); + } + + private void validateMapResponseModel(AwsProxyResponse output, String key, String value) { + try { + TypeReference> typeRef + = new TypeReference>() { + }; + HashMap response = objectMapper.readValue(output.getBody(), typeRef); + assertNotNull(response.get(key)); + assertEquals(value, response.get(key)); + } catch (IOException e) { + fail("Exception while parsing response body: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void validateSingleValueModel(AwsProxyResponse output, String value) { + try { + assertNotNull(output.getBody()); + assertEquals(value, objectMapper.readerFor(String.class).readValue(output.getBody())); + } catch (Exception e) { + fail("Exception while parsing response body: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoAction.java b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoAction.java new file mode 100644 index 000000000..bfaa0a69f --- /dev/null +++ b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoAction.java @@ -0,0 +1,53 @@ +package com.amazonaws.serverless.proxy.struts2.echoapp; + +import com.opensymphony.xwork2.ActionSupport; +import org.apache.commons.io.IOUtils; +import org.apache.struts2.ServletActionContext; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +public class EchoAction extends ActionSupport { + + private String message; + + public String execute() throws IOException { + HttpServletRequest request = ServletActionContext.getRequest(); + + if (message == null && requestHasBody(request)) { + message = IOUtils.toString(request.getReader()); + } + + return SUCCESS; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setCustomHeader(boolean customHeader) { + if (customHeader) { + HttpServletResponse response = ServletActionContext.getResponse(); + response.setHeader("XX", "FOO"); + } + } + + + public void setContentType(boolean contentType) { + if (contentType) { + HttpServletResponse response = ServletActionContext.getResponse(); + response.setContentType("application/json"); + } + } + + private boolean requestHasBody(HttpServletRequest request) throws IOException { + return ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())) && request.getReader() != null; + } + +} diff --git a/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoRequestInfoAction.java b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoRequestInfoAction.java new file mode 100644 index 000000000..42f7f3c15 --- /dev/null +++ b/aws-serverless-java-container-struts2/src/test/java/com/amazonaws/serverless/proxy/struts2/echoapp/EchoRequestInfoAction.java @@ -0,0 +1,85 @@ +package com.amazonaws.serverless.proxy.struts2.echoapp; + +import com.amazonaws.serverless.proxy.RequestReader; +import com.amazonaws.serverless.proxy.model.ApiGatewayRequestContext; +import com.opensymphony.xwork2.ActionSupport; +import org.apache.struts2.ServletActionContext; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + + +public class EchoRequestInfoAction extends ActionSupport { + + private String mode = "principal"; + private Object result = null; + + public String execute() { + + HttpServletRequest request = ServletActionContext.getRequest(); + ApiGatewayRequestContext apiGatewayRequestContext = + (ApiGatewayRequestContext) request + .getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY); + + switch (mode) { + case "principal": + result = apiGatewayRequestContext.getAuthorizer().getPrincipalId(); + break; + case "scheme": + result = request.getScheme(); + break; + case "content-type": + if (request.getContentType().contains("application/octet-stream")) { + ServletActionContext.getResponse().setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + } + result = request.getContentType(); + break; + case "not-allowed": + ServletActionContext.getResponse().setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + break; + case "custom-status-code": + ServletActionContext.getResponse().setStatus(HttpServletResponse.SC_CREATED); + break; + case "not-implemented": + ServletActionContext.getResponse().setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); + break; + case "headers": + Map headers = new HashMap<>(); + + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, request.getHeader(headerName)); + } + + result = headers; + break; + case "query-string": + Map params = new HashMap<>(); + + Enumeration parameterNames = request.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String parameterName = parameterNames.nextElement(); + params.put(parameterName, request.getParameter(parameterName)); + } + + result = params; + break; + default: + throw new IllegalArgumentException("Invalid mode requested: " + mode); + } + + return SUCCESS; + } + + public Object getResult() { + return result; + } + + public void setMode(String mode) { + this.mode = mode; + } +} diff --git a/aws-serverless-java-container-struts2/src/test/resources/struts.xml b/aws-serverless-java-container-struts2/src/test/resources/struts.xml new file mode 100644 index 000000000..0c5dd8328 --- /dev/null +++ b/aws-serverless-java-container-struts2/src/test/resources/struts.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + message + + + + + + + + result + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index c36feaa9f..2a950d60a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ aws-serverless-java-container-jersey aws-serverless-java-container-spark aws-serverless-java-container-spring + aws-serverless-java-container-struts2 archetypes/jersey archetypes/spark archetypes/spring From 69306cea5002ff242d2911fdb2af7a533e9b8524 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Wed, 20 Jun 2018 22:18:47 +0200 Subject: [PATCH 02/11] Remove optional from struts2-core dependency and remove duplicate dependency https://github.com/awslabs/aws-serverless-java-container/issues/149 https://github.com/awslabs/aws-serverless-java-container/pull/159 --- aws-serverless-java-container-struts2/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/aws-serverless-java-container-struts2/pom.xml b/aws-serverless-java-container-struts2/pom.xml index 621587cec..60c75baed 100644 --- a/aws-serverless-java-container-struts2/pom.xml +++ b/aws-serverless-java-container-struts2/pom.xml @@ -32,7 +32,6 @@ org.apache.struts struts2-core ${struts2.version} - true commons-io @@ -82,13 +81,6 @@ test - - javax.el - javax.el-api - 2.2.4 - test - - javax.servlet jsp-api From 6af00229d8ba8aa7f2e3f518bcdc14fb59238c58 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Sat, 18 Aug 2018 13:53:39 +0200 Subject: [PATCH 03/11] Add link to Apache Struts project and to quick start guide for lambdas based on Struts2 https://github.com/awslabs/aws-serverless-java-container/issues/149 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 752dca60f..389f2af8f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # Serverless Java container [![Build Status](https://travis-ci.org/awslabs/aws-serverless-java-container.svg?branch=master)](https://travis-ci.org/awslabs/aws-serverless-java-container) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.amazonaws.serverless/aws-serverless-java-container/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.amazonaws.serverless/aws-serverless-java-container) [![Help](http://img.shields.io/badge/help-gitter-E91E63.svg?style=flat-square)](https://gitter.im/awslabs/aws-serverless-java-container) -The `aws-serverless-java-container` makes it easy to run Java applications written with frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). +The `aws-serverless-java-container` makes it easy to run Java applications written with frameworks such as [Spring](https://spring.io/), [Spring Boot](https://projects.spring.io/spring-boot/), [Apache Struts](http://struts.apache.org/), [Jersey](https://jersey.java.net/), or [Spark](http://sparkjava.com/) in [AWS Lambda](https://aws.amazon.com/lambda/). Serverless Java Container natively supports API Gateway's proxy integration models for requests and responses, you can create and inject custom models for methods that use custom mappings. Follow the quick start guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: * [Spring quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring) * [Spring Boot quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot) +* [Apache Struts quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Struts-Boot) * [Jersey quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Jersey) * [Spark quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spark) From fbf4810b05c107d29ac5becd1e7dcd654c8c6b1b Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Sat, 18 Aug 2018 13:54:42 +0200 Subject: [PATCH 04/11] Correct link for quick start guide for lambdas based on Struts2 https://github.com/awslabs/aws-serverless-java-container/issues/149 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 389f2af8f..f938a9464 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Serverless Java Container natively supports API Gateway's proxy integration mode Follow the quick start guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: * [Spring quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring) * [Spring Boot quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot) -* [Apache Struts quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Struts-Boot) +* [Apache Struts quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Struts) * [Jersey quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Jersey) * [Spark quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spark) From cd9917f62a564a87443bb82a961f57cb6bf2498e Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Sat, 18 Aug 2018 14:18:21 +0200 Subject: [PATCH 05/11] Correct link for quick start guide for lambdas based on Struts2 https://github.com/awslabs/aws-serverless-java-container/issues/149 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f938a9464..3823b65ba 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Serverless Java Container natively supports API Gateway's proxy integration mode Follow the quick start guides in [our wiki](https://github.com/awslabs/aws-serverless-java-container/wiki) to integrate Serverless Java Container with your project: * [Spring quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring) * [Spring Boot quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spring-Boot) -* [Apache Struts quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Struts) +* [Apache Struts quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start-Struts) * [Jersey quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Jersey) * [Spark quick start](https://github.com/awslabs/aws-serverless-java-container/wiki/Quick-start---Spark) From e9e474f37193c793c6a72e569799274844bd1a47 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Thu, 23 Aug 2018 09:09:22 +0200 Subject: [PATCH 06/11] Switch to latest Struts2 version 2.5.17 with important security fixes https://github.com/awslabs/aws-serverless-java-container/issues/149 --- aws-serverless-java-container-struts2/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-serverless-java-container-struts2/pom.xml b/aws-serverless-java-container-struts2/pom.xml index 60c75baed..f72d6cf66 100644 --- a/aws-serverless-java-container-struts2/pom.xml +++ b/aws-serverless-java-container-struts2/pom.xml @@ -16,7 +16,7 @@ - 2.5.16 + 2.5.17 2.9.5 From 85c9a4a18036ead8384877bbced67d33b47daecf Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Sat, 8 Sep 2018 13:26:22 +0200 Subject: [PATCH 07/11] Provide a pet store example for lambdas based on apache struts2 framework https://github.com/awslabs/aws-serverless-java-container/issues/149 --- samples/struts/pet-store/README.md | 62 +++++++ samples/struts/pet-store/pom.xml | 167 ++++++++++++++++++ samples/struts/pet-store/sam.yaml | 32 ++++ .../pet-store/src/main/assembly/dist.xml | 31 ++++ .../sample/struts/actions/PetsController.java | 92 ++++++++++ .../serverless/sample/struts/model/Pet.java | 69 ++++++++ .../sample/struts/model/PetData.java | 111 ++++++++++++ .../pet-store/src/main/resources/log4j2.xml | 19 ++ .../pet-store/src/main/resources/struts.xml | 44 +++++ 9 files changed, 627 insertions(+) create mode 100644 samples/struts/pet-store/README.md create mode 100644 samples/struts/pet-store/pom.xml create mode 100644 samples/struts/pet-store/sam.yaml create mode 100644 samples/struts/pet-store/src/main/assembly/dist.xml create mode 100644 samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/actions/PetsController.java create mode 100644 samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/Pet.java create mode 100644 samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/PetData.java create mode 100644 samples/struts/pet-store/src/main/resources/log4j2.xml create mode 100644 samples/struts/pet-store/src/main/resources/struts.xml diff --git a/samples/struts/pet-store/README.md b/samples/struts/pet-store/README.md new file mode 100644 index 000000000..9203dd896 --- /dev/null +++ b/samples/struts/pet-store/README.md @@ -0,0 +1,62 @@ +# Serverless Struts2 example +A basic pet store written with the [Apache Struts framework](https://struts.apache.org). The `Struts2LambdaHandler` object provided by the `aws-serverless-java-container-struts2` is the main entry point for Lambda. + +The application can be deployed in an AWS account using the [Serverless Application Model](https://github.com/awslabs/serverless-application-model). The `sam.yaml` file in the root folder contains the application definition + +## Installation +To build and install the sample application you will need [Maven](https://maven.apache.org/) and the [AWS CLI](https://aws.amazon.com/cli/) installed on your computer. + +In a shell, navigate to the sample's folder and use maven to build a deployable jar. +``` +$ mvn package +``` + +This command should generate a `serverless-struts-example-1.0-SNAPSHOT.jar` in the `target` folder. Now that we have generated the zip file, we can use the AWS CLI to package the template for deployment. + +You will need an S3 bucket to store the artifacts for deployment. Once you have created the S3 bucket, run the following command from the sample's folder: + +``` +$ aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket +Uploading to xxxxxxxxxxxxxxxxxxxxxxxxxx 6464692 / 6464692.0 (100.00%) +Successfully packaged artifacts and wrote output template to file output-sam.yaml. +Execute the following command to deploy the packaged template +aws cloudformation deploy --template-file /your/path/output-sam.yaml --stack-name +``` + +As the command output suggests, you can now use the cli to deploy the application. Choose a stack name and run the `aws cloudformation deploy` command from the output of the package command. + +``` +$ aws cloudformation deploy --template-file output-sam.yaml --stack-name ServerlessSpringSample --capabilities CAPABILITY_IAM +``` + +Once the application is deployed, you can describe the stack to show the API endpoint that was created. The endpoint should be the `SparkPetStoreApi` key of the `Outputs` property: + +``` +$ aws cloudformation describe-stacks --stack-name ServerlessSpringSample +{ + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:xxxxxxxx:stack/JerseySample/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx", + "Description": "Example Pet Store API written with Apache Struts with the aws-serverless-java-container library", + "Tags": [], + "Outputs": [ + { + "Description": "URL for application", + "OutputKey": "PetStoreApi", + "OutputValue": "https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/pets" + } + ], + "CreationTime": "2016-12-13T22:59:31.552Z", + "Capabilities": [ + "CAPABILITY_IAM" + ], + "StackName": "StrutsSample", + "NotificationARNs": [], + "StackStatus": "UPDATE_COMPLETE" + } + ] +} + +``` + +Copy the `OutputValue` into a browser to test a first request. diff --git a/samples/struts/pet-store/pom.xml b/samples/struts/pet-store/pom.xml new file mode 100644 index 000000000..5500cbdf8 --- /dev/null +++ b/samples/struts/pet-store/pom.xml @@ -0,0 +1,167 @@ + + + 4.0.0 + + com.amazonaws.serverless.sample + serverless-struts-example + 1.0-SNAPSHOT + Struts2 example for the aws-serverless-java-container library + Simple pet store written with the Apache Struts framework + https://aws.amazon.com/lambda/ + + + https://github.com/awslabs/aws-serverless-java-container.git + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + 1.8 + 1.8 + 2.5.17 + 2.9.5 + 4.12 + 2.8.2 + + + + + com.amazonaws.serverless + aws-serverless-java-container-struts2 + [0.1,) + + + + com.amazonaws + aws-lambda-java-core + 1.2.0 + + + + org.apache.struts + struts2-convention-plugin + ${struts2.version} + + + + org.apache.struts + struts2-rest-plugin + ${struts2.version} + + + + org.apache.struts + struts2-bean-validation-plugin + ${struts2.version} + + + + org.apache.struts + struts2-junit-plugin + ${struts2.version} + test + + + + + com.jgeppert.struts2 + struts2-aws-lambda-support-plugin + 1.0.0 + + + + + org.hibernate + hibernate-validator + 4.3.2.Final + + + + com.fasterxml.jackson.core + jackson-core + 2.9.4 + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.4 + + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + + com.amazonaws + aws-lambda-java-log4j2 + 1.1.0 + + + + junit + junit + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + src/main/assembly/dist.xml + + + + + lambda + package + + single + + + + + + + + diff --git a/samples/struts/pet-store/sam.yaml b/samples/struts/pet-store/sam.yaml new file mode 100644 index 000000000..e91f9d597 --- /dev/null +++ b/samples/struts/pet-store/sam.yaml @@ -0,0 +1,32 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Example Pet Store API written with Apache Struts based on the aws-serverless-java-container library + +Globals: + Api: + # API Gateway regional endpoints + EndpointConfiguration: REGIONAL + +Resources: + PetStoreFunction: + Type: AWS::Serverless::Function + Properties: + Handler: com.amazonaws.serverless.proxy.struts2.Struts2LambdaHandler::handleRequest + Runtime: java8 + CodeUri: serverless-struts-example-1.0-SNAPSHOT-lambda.zip + MemorySize: 256 + Policies: AWSLambdaBasicExecutionRole + Timeout: 30 + Events: + GetResource: + Type: Api + Properties: + Path: /{proxy+} + Method: any + +Outputs: + SpringPetStoreApi: + Description: URL for application + Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/pets' + Export: + Name: Struts2PetStoreApi diff --git a/samples/struts/pet-store/src/main/assembly/dist.xml b/samples/struts/pet-store/src/main/assembly/dist.xml new file mode 100644 index 000000000..029ec01c7 --- /dev/null +++ b/samples/struts/pet-store/src/main/assembly/dist.xml @@ -0,0 +1,31 @@ + + lambda + + zip + + false + + + lib + false + + + + + ${basedir}/src/main/resources + / + + * + + + + ${project.build.directory}/classes + / + + **/*.class + + + + \ No newline at end of file diff --git a/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/actions/PetsController.java b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/actions/PetsController.java new file mode 100644 index 000000000..bc2ad8bbe --- /dev/null +++ b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/actions/PetsController.java @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +package com.amazonaws.serverless.sample.struts.actions; + +import com.amazonaws.serverless.sample.struts.model.Pet; +import com.amazonaws.serverless.sample.struts.model.PetData; +import com.opensymphony.xwork2.ModelDriven; +import org.apache.struts2.rest.DefaultHttpHeaders; +import org.apache.struts2.rest.HttpHeaders; +import org.apache.struts2.rest.RestActionSupport; + +import java.util.Collection; +import java.util.UUID; +import java.util.stream.Collectors; + + +public class PetsController extends RestActionSupport implements ModelDriven { + + private Pet model = new Pet(); + private String id; + private Collection list = null; + + // GET /pets/1 + public HttpHeaders show() { + return new DefaultHttpHeaders("show"); + } + + // GET /pets + public HttpHeaders index() { + list = PetData.getNames() + .stream() + .map(petName -> new Pet( + UUID.randomUUID() + .toString(), PetData.getRandomBreed(), petName, PetData.getRandomDoB())) + .collect(Collectors.toList()); + return new DefaultHttpHeaders("index") + .disableCaching(); + } + + // POST /pets + public HttpHeaders create() { + if (model.getName() == null || model.getBreed() == null) { + return null; + } + + Pet dbPet = model; + dbPet.setId(UUID.randomUUID().toString()); + return new DefaultHttpHeaders("success") + .setLocationId(model.getId()); + + } + + // PUT /pets/1 + public String update() { + //TODO: UPDATE LOGIC + return SUCCESS; + } + + // DELETE /petsr/1 + public String destroy() { + //TODO: DELETE LOGIC + return SUCCESS; + } + + public void setId(String id) { + if (id != null) { + this.model = new Pet(id, PetData.getRandomBreed(), PetData.getRandomName(), PetData.getRandomDoB()); + } + this.id = id; + } + + public Object getModel() { + if (list != null) { + return list; + } else { + if (model == null) { + model = new Pet(); + } + return model; + } + } +} diff --git a/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/Pet.java b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/Pet.java new file mode 100644 index 000000000..c9d420ca8 --- /dev/null +++ b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/Pet.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +package com.amazonaws.serverless.sample.struts.model; + +import org.hibernate.validator.constraints.NotBlank; + +import java.util.Date; + +public class Pet { + + private String id; + private String breed; + + @NotBlank + private String name; + private Date dateOfBirth; + + public Pet() { + } + + public Pet(String id, String breed, String name, Date dateOfBirth) { + this.id = id; + this.breed = breed; + this.name = name; + this.dateOfBirth = dateOfBirth; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBreed() { + return breed; + } + + public void setBreed(String breed) { + this.breed = breed; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getDateOfBirth() { + return dateOfBirth; + } + + public void setDateOfBirth(Date dateOfBirth) { + this.dateOfBirth = dateOfBirth; + } +} diff --git a/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/PetData.java b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/PetData.java new file mode 100644 index 000000000..84be64eab --- /dev/null +++ b/samples/struts/pet-store/src/main/java/com/amazonaws/serverless/sample/struts/model/PetData.java @@ -0,0 +1,111 @@ +/* + * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +package com.amazonaws.serverless.sample.struts.model; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +public class PetData { + private static List breeds = new ArrayList<>(); + static { + breeds.add("Afghan Hound"); + breeds.add("Beagle"); + breeds.add("Bernese Mountain Dog"); + breeds.add("Bloodhound"); + breeds.add("Dalmatian"); + breeds.add("Jack Russell Terrier"); + breeds.add("Norwegian Elkhound"); + } + + private static List names = new ArrayList<>(); + static { + names.add("Bailey"); + names.add("Bella"); + names.add("Max"); + names.add("Lucy"); + names.add("Charlie"); + names.add("Molly"); + names.add("Buddy"); + names.add("Daisy"); + names.add("Rocky"); + names.add("Maggie"); + names.add("Jake"); + names.add("Sophie"); + names.add("Jack"); + names.add("Sadie"); + names.add("Toby"); + names.add("Chloe"); + names.add("Cody"); + names.add("Bailey"); + names.add("Buster"); + names.add("Lola"); + names.add("Duke"); + names.add("Zoe"); + names.add("Cooper"); + names.add("Abby"); + names.add("Riley"); + names.add("Ginger"); + names.add("Harley"); + names.add("Roxy"); + names.add("Bear"); + names.add("Gracie"); + names.add("Tucker"); + names.add("Coco"); + names.add("Murphy"); + names.add("Sasha"); + names.add("Lucky"); + names.add("Lily"); + names.add("Oliver"); + names.add("Angel"); + names.add("Sam"); + names.add("Princess"); + names.add("Oscar"); + names.add("Emma"); + names.add("Teddy"); + names.add("Annie"); + names.add("Winston"); + names.add("Rosie"); + } + + public static List getBreeds() { + return breeds; + } + + public static List getNames() { + return names; + } + + public static String getRandomBreed() { + return breeds.get(ThreadLocalRandom.current().nextInt(0, breeds.size() - 1)); + } + + public static String getRandomName() { + return names.get(ThreadLocalRandom.current().nextInt(0, names.size() - 1)); + } + + public static Date getRandomDoB() { + GregorianCalendar gc = new GregorianCalendar(); + + int year = ThreadLocalRandom.current().nextInt( + Calendar.getInstance().get(Calendar.YEAR) - 15, + Calendar.getInstance().get(Calendar.YEAR) + ); + + gc.set(Calendar.YEAR, year); + + int dayOfYear = ThreadLocalRandom.current().nextInt(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR)); + + gc.set(Calendar.DAY_OF_YEAR, dayOfYear); + return gc.getTime(); + } +} diff --git a/samples/struts/pet-store/src/main/resources/log4j2.xml b/samples/struts/pet-store/src/main/resources/log4j2.xml new file mode 100644 index 000000000..6afd44ed1 --- /dev/null +++ b/samples/struts/pet-store/src/main/resources/log4j2.xml @@ -0,0 +1,19 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/struts/pet-store/src/main/resources/struts.xml b/samples/struts/pet-store/src/main/resources/struts.xml new file mode 100644 index 000000000..038370d3d --- /dev/null +++ b/samples/struts/pet-store/src/main/resources/struts.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c3de905581c7af7e6fc86cc5ecf554410b9a9f49 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Mon, 10 Sep 2018 08:17:30 +0200 Subject: [PATCH 08/11] Correct some typos https://github.com/awslabs/aws-serverless-java-container/issues/149 --- samples/struts/pet-store/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/struts/pet-store/pom.xml b/samples/struts/pet-store/pom.xml index 5500cbdf8..9aab925d0 100644 --- a/samples/struts/pet-store/pom.xml +++ b/samples/struts/pet-store/pom.xml @@ -25,7 +25,7 @@ 1.8 - 1.8 + 1.8 2.5.17 2.9.5 4.12 @@ -70,7 +70,7 @@ test - + com.jgeppert.struts2 struts2-aws-lambda-support-plugin From d48ca490dafb78b88c910bca54dcc9714df7ccb7 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Mon, 10 Sep 2018 08:20:52 +0200 Subject: [PATCH 09/11] Adjust struts2 pet store log4j config https://github.com/awslabs/aws-serverless-java-container/issues/149 --- samples/struts/pet-store/src/main/resources/log4j2.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/struts/pet-store/src/main/resources/log4j2.xml b/samples/struts/pet-store/src/main/resources/log4j2.xml index 6afd44ed1..55ed0d21c 100644 --- a/samples/struts/pet-store/src/main/resources/log4j2.xml +++ b/samples/struts/pet-store/src/main/resources/log4j2.xml @@ -10,8 +10,6 @@ - - From 1d89996b389e6840040c33b65e8fcf510e99741c Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Mon, 10 Sep 2018 09:05:40 +0200 Subject: [PATCH 10/11] Provide archetype for struts2 based lambdas https://github.com/awslabs/aws-serverless-java-container/issues/149 --- archetypes/struts/pom.xml | 47 ++++++ .../META-INF/maven/archetype-metadata.xml | 33 ++++ .../resources/archetype-resources/README.md | 129 +++++++++++++++ .../resources/archetype-resources/pom.xml | 153 ++++++++++++++++++ .../resources/archetype-resources/sam.yaml | 48 ++++++ .../src/main/assembly/dist.xml | 31 ++++ .../src/main/java/actions/PingController.java | 74 +++++++++ .../src/main/resources/application.properties | 3 + .../src/main/resources/log4j2.xml | 17 ++ .../src/main/resources/struts.xml | 45 ++++++ pom.xml | 1 + 11 files changed, 581 insertions(+) create mode 100644 archetypes/struts/pom.xml create mode 100644 archetypes/struts/src/main/resources/META-INF/maven/archetype-metadata.xml create mode 100644 archetypes/struts/src/main/resources/archetype-resources/README.md create mode 100644 archetypes/struts/src/main/resources/archetype-resources/pom.xml create mode 100644 archetypes/struts/src/main/resources/archetype-resources/sam.yaml create mode 100644 archetypes/struts/src/main/resources/archetype-resources/src/main/assembly/dist.xml create mode 100644 archetypes/struts/src/main/resources/archetype-resources/src/main/java/actions/PingController.java create mode 100644 archetypes/struts/src/main/resources/archetype-resources/src/main/resources/application.properties create mode 100644 archetypes/struts/src/main/resources/archetype-resources/src/main/resources/log4j2.xml create mode 100644 archetypes/struts/src/main/resources/archetype-resources/src/main/resources/struts.xml diff --git a/archetypes/struts/pom.xml b/archetypes/struts/pom.xml new file mode 100644 index 000000000..9175a40d2 --- /dev/null +++ b/archetypes/struts/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + + com.amazonaws.serverless + aws-serverless-java-container + 1.2-SNAPSHOT + + + com.amazonaws.serverless.archetypes + aws-serverless-struts-archetype + 1.2-SNAPSHOT + maven-archetype + + + https://github.com/awslabs/aws-serverless-java-container.git + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + org.apache.maven.archetype + archetype-packaging + 3.0.1 + + + + + + + org.apache.maven.plugins + maven-archetype-plugin + 3.0.1 + + + + + \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/struts/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 000000000..7aa1c89bd --- /dev/null +++ b/archetypes/struts/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,33 @@ + + + + src/main/java + + **/*.java + + + + src/main/resources + + **/*.properties + **/*.xml + + + + src/main/assembly + + **/*.xml + + + + + + sam.yaml + README.md + + + + \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/archetype-resources/README.md b/archetypes/struts/src/main/resources/archetype-resources/README.md new file mode 100644 index 000000000..4b646a46e --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/README.md @@ -0,0 +1,129 @@ +#set($resourceName = $artifactId) +#macro(replaceChar $originalName, $char) + #if($originalName.contains($char)) + #set($tokens = $originalName.split($char)) + #set($newResourceName = "") + #foreach($token in $tokens) + #set($newResourceName = $newResourceName + $token.substring(0,1).toUpperCase() + $token.substring(1).toLowerCase()) + #end + ${newResourceName} + #else + #set($newResourceName = $originalName.substring(0,1).toUpperCase() + $originalName.substring(1)) + ${newResourceName} + #end +#end +#set($resourceName = "#replaceChar($resourceName, '-')") +#set($resourceName = "#replaceChar($resourceName, '.')") +#set($resourceName = $resourceName.replaceAll("\n", "").trim()) +# ${artifactId} serverless API +The ${artifactId} project, created with [`aws-serverless-java-container`](https://github.com/awslabs/aws-serverless-java-container). + +The starter project defines a simple `/ping` resource that can accept `GET` requests with its tests. + +The project folder also includes a `sam.yaml` file. You can use this [SAM](https://github.com/awslabs/serverless-application-model) file to deploy the project to AWS Lambda and Amazon API Gateway or test in local with [SAM Local](https://github.com/awslabs/aws-sam-local). + +## Building the project +Using [Maven](https://maven.apache.org/), you can create an AWS Lambda-compatible jar file simply by running the maven package command from the projct folder. + +```bash +$ mvn archetype:generate -DartifactId=my-spring-api -DarchetypeGroupId=com.amazonaws.serverless.archetypes -DarchetypeArtifactId=aws-serverless-spring-archetype -DarchetypeVersion=1.0-SNAPSHOT -DgroupId=com.sapessi.spring -Dversion=0.1 -Dinteractive=false +$ cd my-spring-api +$ mvn clean package + +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 6.546 s +[INFO] Finished at: 2018-02-15T08:39:33-08:00 +[INFO] Final Memory: XXM/XXXM +[INFO] ------------------------------------------------------------------------ +``` + +## Testing locally with SAM local +You can use [AWS SAM Local](https://github.com/awslabs/aws-sam-local) to start your project. + +First, install SAM local: + +```bash +$ npm install -g aws-sam-local +``` + +Next, from the project root folder - where the `sam.yaml` file is located - start the API with the SAM Local CLI. + +```bash +$ sam local start-api --template sam.yaml + +... +Mounting ${groupId}.StreamLambdaHandler::handleRequest (java8) at http://127.0.0.1:3000/{proxy+} [OPTIONS GET HEAD POST PUT DELETE PATCH] +... +``` + +Using a new shell, you can send a test ping request to your API: + +```bash +$ curl -s http://127.0.0.1:3000/ping | python -m json.tool + +{ + "pong": "Hello, World!" +} +``` + +## Deploying to AWS +You can use the [AWS CLI](https://aws.amazon.com/cli/) to quickly deploy your application to AWS Lambda and Amazon API Gateway with your SAM template. + +You will need an S3 bucket to store the artifacts for deployment. Once you have created the S3 bucket, run the following command from the project's root folder - where the `sam.yaml` file is located: + +``` +$ aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket +Uploading to xxxxxxxxxxxxxxxxxxxxxxxxxx 6464692 / 6464692.0 (100.00%) +Successfully packaged artifacts and wrote output template to file output-sam.yaml. +Execute the following command to deploy the packaged template +aws cloudformation deploy --template-file /your/path/output-sam.yaml --stack-name +``` + +As the command output suggests, you can now use the cli to deploy the application. Choose a stack name and run the `aws cloudformation deploy` command from the output of the package command. + +``` +$ aws cloudformation deploy --template-file output-sam.yaml --stack-name ServerlessSpringApi --capabilities CAPABILITY_IAM +``` + +Once the application is deployed, you can describe the stack to show the API endpoint that was created. The endpoint should be the `ServerlessSpringApi` key of the `Outputs` property: + +``` +$ aws cloudformation describe-stacks --stack-name ServerlessSpringApi +{ + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:xxxxxxxx:stack/ServerlessSpringApi/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx", + "Description": "AWS Serverless Spring API - ${groupId}::${artifactId}", + "Tags": [], + "Outputs": [ + { + "Description": "URL for application", + "ExportName": "${resourceName}Api", + "OutputKey": "${resourceName}Api", + "OutputValue": "https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping" + } + ], + "CreationTime": "2016-12-13T22:59:31.552Z", + "Capabilities": [ + "CAPABILITY_IAM" + ], + "StackName": "ServerlessSpringApi", + "NotificationARNs": [], + "StackStatus": "UPDATE_COMPLETE" + } + ] +} + +``` + +Copy the `OutputValue` into a browser or use curl to test your first request: + +```bash +$ curl -s https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping | python -m json.tool + +{ + "pong": "Hello, World!" +} +``` \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/archetype-resources/pom.xml b/archetypes/struts/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 000000000..bb05d5edf --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + ${groupId} + ${artifactId} + ${version} + jar + + Serverless Spring Boot API + https://github.com/awslabs/aws-serverless-java-container + + + org.springframework.boot + spring-boot-starter-parent + 1.5.10.RELEASE + + + + 1.8 + 1.8 + 2.5.17 + 2.9.5 + 4.12 + 2.8.2 + + + + + com.amazonaws.serverless + aws-serverless-java-container-struts2 + 1.2.0 + + + + com.amazonaws + aws-lambda-java-core + 1.2.0 + + + + org.apache.struts + struts2-convention-plugin + ${struts2.version} + + + + org.apache.struts + struts2-rest-plugin + ${struts2.version} + + + + org.apache.struts + struts2-bean-validation-plugin + ${struts2.version} + + + + org.apache.struts + struts2-junit-plugin + ${struts2.version} + test + + + + + com.jgeppert.struts2 + struts2-aws-lambda-support-plugin + 1.0.0 + + + + + org.hibernate + hibernate-validator + 4.3.2.Final + + + + com.fasterxml.jackson.core + jackson-core + 2.9.4 + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.4 + + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + + com.amazonaws + aws-lambda-java-log4j2 + 1.1.0 + + + + junit + junit + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + src/main/assembly/dist.xml + + + + + lambda + package + + single + + + + + + + diff --git a/archetypes/struts/src/main/resources/archetype-resources/sam.yaml b/archetypes/struts/src/main/resources/archetype-resources/sam.yaml new file mode 100644 index 000000000..d17e69de7 --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/sam.yaml @@ -0,0 +1,48 @@ +#set($resourceName = $artifactId) +#macro(replaceChar $originalName, $char) + #if($originalName.contains($char)) + #set($tokens = $originalName.split($char)) + #set($newResourceName = "") + #foreach($token in $tokens) + #set($newResourceName = $newResourceName + $token.substring(0,1).toUpperCase() + $token.substring(1).toLowerCase()) + #end + ${newResourceName} + #else + #set($newResourceName = $originalName.substring(0,1).toUpperCase() + $originalName.substring(1)) + ${newResourceName} + #end +#end +#set($resourceName = "#replaceChar($resourceName, '-')") +#set($resourceName = "#replaceChar($resourceName, '.')") +#set($resourceName = $resourceName.replaceAll("\n", "").trim()) +#macro(regionVar) + AWS::Region +#end +#set($awsRegion = "#regionVar()") +#set($awsRegion = $awsRegion.replaceAll("\n", "").trim()) +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: AWS Serverless Apache Struts2 API - ${groupId}::${artifactId} +Resources: + ${resourceName}Function: + Type: AWS::Serverless::Function + Properties: + Handler: com.amazonaws.serverless.proxy.struts2.Struts2LambdaHandler::handleRequest + Runtime: java8 + CodeUri: target/${artifactId}-${version}-lambda.zip + MemorySize: 512 + Policies: AWSLambdaBasicExecutionRole + Timeout: 30 + Events: + GetResource: + Type: Api + Properties: + Path: /{proxy+} + Method: any + +Outputs: + ${resourceName}Api: + Description: URL for application + Value: !Sub 'https://${ServerlessRestApi}.execute-api.${${awsRegion}}.amazonaws.com/Prod/ping' + Export: + Name: ${resourceName}Api diff --git a/archetypes/struts/src/main/resources/archetype-resources/src/main/assembly/dist.xml b/archetypes/struts/src/main/resources/archetype-resources/src/main/assembly/dist.xml new file mode 100644 index 000000000..029ec01c7 --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/src/main/assembly/dist.xml @@ -0,0 +1,31 @@ + + lambda + + zip + + false + + + lib + false + + + + + ${basedir}/src/main/resources + / + + * + + + + ${project.build.directory}/classes + / + + **/*.class + + + + \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/archetype-resources/src/main/java/actions/PingController.java b/archetypes/struts/src/main/resources/archetype-resources/src/main/java/actions/PingController.java new file mode 100644 index 000000000..b8a67e00a --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/src/main/java/actions/PingController.java @@ -0,0 +1,74 @@ +package ${groupId}.controller; + + +import com.opensymphony.xwork2.ModelDriven; +import org.apache.struts2.rest.DefaultHttpHeaders; +import org.apache.struts2.rest.HttpHeaders; +import org.apache.struts2.rest.RestActionSupport; + + +import java.util.Collection; +import java.util.UUID; + + +public class PingController extends RestActionSupport implements ModelDriven { + + private String model = new String(); + private String id; + private Collection list = null; + + + // GET /ping/1 + public HttpHeaders show() { + return new DefaultHttpHeaders("show"); + } + + // GET /ping + public HttpHeaders index() { + this.model = "Hello, World!"; + return new DefaultHttpHeaders("index") + .disableCaching(); + } + + // POST /ping + public HttpHeaders create() { + if (model.getName() == null || model.getBreed() == null) { + return null; + } + + this.model = UUID.randomUUID().toString(); + return new DefaultHttpHeaders("success") + .setLocationId(model.getId()); + + } + + // PUT /ping/1 + public String update() { + //TODO: UPDATE LOGIC + return SUCCESS; + } + + // DELETE /ping/1 + public String destroy() { + //TODO: DELETE LOGIC + return SUCCESS; + } + + public void setId(String id) { + if (id != null) { + this.model = "New model instance"; + } + this.id = id; + } + + public Object getModel() { + if (list != null) { + return list; + } else { + if (model == null) { + model = "Pong"; + } + return model; + } + } +} diff --git a/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/application.properties b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/application.properties new file mode 100644 index 000000000..ec1cb9792 --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/application.properties @@ -0,0 +1,3 @@ +# Reduce logging level to make sure the application works with SAM local +# https://github.com/awslabs/aws-serverless-java-container/issues/134 +logging.level.root=WARN \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/log4j2.xml b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/log4j2.xml new file mode 100644 index 000000000..55ed0d21c --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1}:%L - %m%n + + + + + + + + + + + \ No newline at end of file diff --git a/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/struts.xml b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/struts.xml new file mode 100644 index 000000000..d0a1c31d2 --- /dev/null +++ b/archetypes/struts/src/main/resources/archetype-resources/src/main/resources/struts.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b5829f99a..ec032e28a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ archetypes/spark archetypes/spring archetypes/springboot + archetypes/struts From 7e0a5ab1d87208b5cc896d6eb16fff8d2de737c7 Mon Sep 17 00:00:00 2001 From: Johannes Geppert Date: Wed, 17 Oct 2018 11:38:46 +0200 Subject: [PATCH 11/11] Switch to latest Struts2 version 2.5.18 https://github.com/awslabs/aws-serverless-java-container/issues/149 --- aws-serverless-java-container-struts2/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-serverless-java-container-struts2/pom.xml b/aws-serverless-java-container-struts2/pom.xml index f72d6cf66..13401b81c 100644 --- a/aws-serverless-java-container-struts2/pom.xml +++ b/aws-serverless-java-container-struts2/pom.xml @@ -16,7 +16,7 @@ - 2.5.17 + 2.5.18 2.9.5