├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.sbt ├── descriptor.json ├── examples └── simple │ ├── CloudFormation.json │ ├── README.md │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── github │ └── bleshik │ └── example │ ├── JerseyAdapter.java │ ├── PingResource.java │ ├── PingServlet.java │ └── ServletAdapter.java ├── pom.xml └── src ├── main └── java │ └── util │ ├── DummyServletContext.java │ ├── InMemoryHttpServletResponse.java │ ├── JerseyRequestHandler.java │ ├── LambdaHttpServletRequest.java │ ├── ServletRequestHandler.java │ └── SessionManager.java └── test └── java └── util └── JerseyRequestHandlerSpec.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .classpath 6 | .project 7 | .settings/ 8 | .DS_Store 9 | .RData 10 | .Rhistory 11 | .vimtags 12 | .tags 13 | 14 | .gradle/ 15 | build/ 16 | target/ 17 | 18 | third-party/ 19 | node_modules/ 20 | current.js 21 | 22 | *.log 23 | dynamodb-local/ 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | script: mvn install 5 | deploy: 6 | provider: bintray 7 | file: descriptor.json 8 | user: bleshik 9 | key: 10 | secure: ZJB2Nvvqn6TZwX/+fbVSg9VwSNHtaVSWn/91INE3DmQ/hPtsrb6YXw+SEJpd0m9/5Ix/hysmiphhyO0Afmyp9BvRQSXxt2wzk51yLCuqx4k5AWdA4k8d/zfJbyqqjxlvztrtI+UBUwMj/piiz8V+CUW5uw/byitKePiQBkKCYUz+NN9XQIhiZKTbcfpcrRW/WDfCdzmfmofubPv1ljqYjgrLyatyaTj6gv/3x1Vj8FkaQiLL5f3+Fbn7d3T8KIYQClVDPyTUZrNkGb3SIk8nUlis+MuF1eSu2npeajnU8CFQS/QHuN1IOaMfI9YaJ0171jPn/P2F11QIF4mnQ9ZRWMx3Xe+7D+1RXTh7QUw1owbmk6HpH19eMAFlYCQ9YgABe0OspIDQdQH6z5ukfVQa6Z+KX0cwSK7gCEWwCKrWRoMmLAaBROTXf/MCSKH++omEZTxYXS8hIse45Ihk2SH2A+aAxBl+z1ZYiD1BcaafAzmLQaZJNIsyVc/Zge/ciNH9/C6cikPFS+tio4QAyUOpAWPhUiebgiBGvviraIPnUPmfgayfSqW8hB3wKxxF5zAdB9RpGoAYGRIB0NkYbodihgPP/sykioUiT/Yumj+B5AutEVQ2zJd6ZBG2V5KtgXDHAolc5jdmwMDczzzcHSGJ0uDJ5zwwpBFXFzBbTTwAUns= 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/bleshik/aws-lambda-servlet.svg?branch=master)](https://travis-ci.org/bleshik/aws-lambda-servlet) 2 | # AWS Lambda adapter for Java's Servlets 3 | The idea behind this little adapter is to have a way of running APIs implemented in Java on AWS Lambda. This apadater basically takes the incoming Lambda Event and transform it to an HTTP Servet Request and pass it directly to the given servlet. No http server is used in this process. 4 | 5 | Add the project's bintray repository in your pom.xml: 6 | ``` 7 | 8 | 9 | bintray 10 | http://dl.bintray.com/bleshik/maven 11 | 12 | true 13 | 14 | 15 | false 16 | 17 | 18 | 19 | ``` 20 | 21 | Then the dependency itself: 22 | ``` 23 | 24 | com.github.bleshik 25 | aws-lambda-servlet 26 | 1.0 27 | 28 | ``` 29 | 30 | Then you just go like this: 31 | ``` 32 | public class ExampleAdapter extends ServletRequestHandler { 33 | public ExampleAdapter() { 34 | // here you just pass context path and your servlet 35 | super("/", new MyAwesomeServlet()); 36 | } 37 | } 38 | ``` 39 | 40 | Also there is an implementation for Jersey. What you need is to create a class extending JerseyRequestHandler: 41 | ``` 42 | public class ExampleAdapter extends JerseyRequestHandler { 43 | public ExampleAdapter() { 44 | // here you just pass context path and your resources 45 | super("/", TestResource.class); 46 | // or you could use the ResourceConfig directly 47 | // super("/", new ResourceConfig(TestResource.class)); 48 | } 49 | } 50 | ``` 51 | 52 | Then you just deploy ExampleAdapter as a Lambda function and expose it through API Gateway. 53 | 54 | For the complete working example, see the corresponding directory: https://github.com/bleshik/aws-lambda-servlet/tree/master/examples/simple 55 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "AWS Lambda Jersey Adapter" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.8" 6 | 7 | libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" 8 | 9 | libraryDependencies += "org.glassfish.jersey.containers" % "jersey-container-servlet" % "2.23.2" 10 | 11 | libraryDependencies += "com.amazonaws" % "aws-lambda-java-core" % "1.1.0" exclude("commons-logging", "commons-logging") 12 | 13 | libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" 14 | 15 | libraryDependencies += "junit" % "junit" % "4.12" % "test" 16 | 17 | libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.21" 18 | 19 | libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.21" % "test" 20 | -------------------------------------------------------------------------------- /descriptor.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": { 3 | "name": "aws-lambda-servlet", 4 | "repo": "maven", 5 | "subject": "bleshik", 6 | "desc": "AWS Lambda adapter for Java's Servlets (and Jersey in particular, JAX-RS implementation)", 7 | "website_url": "https://github.com/bleshik/aws-lambda-servlet", 8 | "issue_tracker_url": "https://github.com/bleshik/aws-lambda-servlet/issues", 9 | "vcs_url": "https://github.com/bleshik/aws-lambda-servlet.git", 10 | "labels": ["aws", "lambda", "servlet", "java"], 11 | "public_download_numbers": true, 12 | "public_stats": true 13 | }, 14 | "version": { 15 | "name": "1.0", 16 | "desc": "Usable initial version", 17 | "released": "2017-05-26", 18 | "gpgSign": false 19 | }, 20 | "files": [ {"includePattern": "target/aws-lambda-servlet-1.0.jar", "uploadPattern": "/com/github/bleshik/aws-lambda-servlet/1.0/aws-lambda-servlet-1.0.jar"} ], 21 | "publish": true 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples/simple/CloudFormation.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Simple example of usage of aws-lambda-servlet", 4 | "Parameters": { 5 | "JarBucket": { 6 | "Default": "bleshik", 7 | "Description": "The S3 bucket where Jar located", 8 | "Type": "String" 9 | }, 10 | "JarKey": { 11 | "Default": "simple-1.0-jar-with-dependencies.jar", 12 | "Description": "Location of the jar in the bucket (an S3 key)", 13 | "Type": "String" 14 | } 15 | }, 16 | "Resources": { 17 | "Api": { 18 | "Properties": { 19 | "BinaryMediaTypes": [ 20 | "application~1octet-stream", 21 | "image~1png", 22 | "image~1svg+xml", 23 | "image~1*", 24 | "image~1jpeg", 25 | "image~1gif" 26 | ], 27 | "Description": { 28 | "Fn::Sub": " API Gateway for ${JarKey}" 29 | }, 30 | "Name": { 31 | "Fn::Sub": " API for ${JarKey}" 32 | } 33 | }, 34 | "Type": "AWS::ApiGateway::RestApi" 35 | }, 36 | "ApiDeployment": { 37 | "DependsOn": [ 38 | "ProxyANY" 39 | ], 40 | "Properties": { 41 | "Description": { 42 | "Fn::Sub": " API for ${JarKey}" 43 | }, 44 | "RestApiId": { 45 | "Ref": "Api" 46 | }, 47 | "StageName": "api" 48 | }, 49 | "Type": "AWS::ApiGateway::Deployment" 50 | }, 51 | "ApiGatewayAccount": { 52 | "Properties": { 53 | "CloudWatchRoleArn": { 54 | "Fn::GetAtt": [ 55 | "ApiGatewayCloudWatchLogsRole", 56 | "Arn" 57 | ] 58 | } 59 | }, 60 | "Type": "AWS::ApiGateway::Account" 61 | }, 62 | "ApiGatewayCloudWatchLogsRole": { 63 | "Properties": { 64 | "AssumeRolePolicyDocument": { 65 | "Statement": [ 66 | { 67 | "Action": [ 68 | "sts:AssumeRole" 69 | ], 70 | "Effect": "Allow", 71 | "Principal": { 72 | "Service": [ 73 | "apigateway.amazonaws.com" 74 | ] 75 | } 76 | } 77 | ], 78 | "Version": "2012-10-17" 79 | }, 80 | "Policies": [ 81 | { 82 | "PolicyDocument": { 83 | "Statement": [ 84 | { 85 | "Action": [ 86 | "logs:CreateLogGroup", 87 | "logs:CreateLogStream", 88 | "logs:DescribeLogGroups", 89 | "logs:DescribeLogStreams", 90 | "logs:PutLogEvents", 91 | "logs:GetLogEvents", 92 | "logs:FilterLogEvents" 93 | ], 94 | "Effect": "Allow", 95 | "Resource": "*" 96 | } 97 | ], 98 | "Version": "2012-10-17" 99 | }, 100 | "PolicyName": "ApiGatewayLogsPolicy" 101 | } 102 | ] 103 | }, 104 | "Type": "AWS::IAM::Role" 105 | }, 106 | "ApiLambda": { 107 | "DependsOn": [ 108 | "ApiLambdaRole" 109 | ], 110 | "Properties": { 111 | "Code": { 112 | "S3Bucket": { 113 | "Ref": "JarBucket" 114 | }, 115 | "S3Key": { 116 | "Ref": "JarKey" 117 | } 118 | }, 119 | "Handler": "com.github.bleshik.example.ServletAdapter", 120 | "MemorySize": 3008, 121 | "Role": { 122 | "Fn::GetAtt": [ 123 | "ApiLambdaRole", 124 | "Arn" 125 | ] 126 | }, 127 | "Runtime": "java8", 128 | "Timeout": 60 129 | }, 130 | "Type": "AWS::Lambda::Function" 131 | }, 132 | "ApiLambdaPermission": { 133 | "DependsOn": [ 134 | "ApiLambda" 135 | ], 136 | "Properties": { 137 | "Action": "lambda:InvokeFunction", 138 | "FunctionName": { 139 | "Ref": "ApiLambda" 140 | }, 141 | "Principal": "lambda.amazonaws.com", 142 | "SourceArn": { 143 | "Fn::GetAtt": [ 144 | "ApiLambda", 145 | "Arn" 146 | ] 147 | } 148 | }, 149 | "Type": "AWS::Lambda::Permission" 150 | }, 151 | "ApiLambdaRole": { 152 | "Properties": { 153 | "AssumeRolePolicyDocument": { 154 | "Statement": [ 155 | { 156 | "Action": [ 157 | "sts:AssumeRole" 158 | ], 159 | "Effect": "Allow", 160 | "Principal": { 161 | "Service": [ 162 | "lambda.amazonaws.com" 163 | ] 164 | } 165 | } 166 | ], 167 | "Version": "2012-10-17" 168 | }, 169 | "Path": "/" 170 | }, 171 | "Type": "AWS::IAM::Role" 172 | }, 173 | "LambdaPermission": { 174 | "DependsOn": [ 175 | "ApiLambda" 176 | ], 177 | "Properties": { 178 | "Action": "lambda:InvokeFunction", 179 | "FunctionName": { 180 | "Fn::GetAtt": [ 181 | "ApiLambda", 182 | "Arn" 183 | ] 184 | }, 185 | "Principal": "apigateway.amazonaws.com", 186 | "SourceArn": { 187 | "Fn::Join": [ 188 | "", 189 | [ 190 | "arn:aws:execute-api:", 191 | { 192 | "Ref": "AWS::Region" 193 | }, 194 | ":", 195 | { 196 | "Ref": "AWS::AccountId" 197 | }, 198 | ":", 199 | { 200 | "Ref": "Api" 201 | }, 202 | "/*" 203 | ] 204 | ] 205 | } 206 | }, 207 | "Type": "AWS::Lambda::Permission" 208 | }, 209 | "ProxyANY": { 210 | "DependsOn": [ 211 | "Api", 212 | "ApiLambda", 213 | "ProxyResource" 214 | ], 215 | "Properties": { 216 | "AuthorizationType": "NONE", 217 | "HttpMethod": "ANY", 218 | "Integration": { 219 | "IntegrationHttpMethod": "POST", 220 | "Type": "AWS_PROXY", 221 | "Uri": { 222 | "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations" 223 | } 224 | }, 225 | "ResourceId": { 226 | "Ref": "ProxyResource" 227 | }, 228 | "RestApiId": { 229 | "Ref": "Api" 230 | } 231 | }, 232 | "Type": "AWS::ApiGateway::Method" 233 | }, 234 | "ProxyResource": { 235 | "DependsOn": [ 236 | "Api" 237 | ], 238 | "Properties": { 239 | "ParentId": { 240 | "Fn::GetAtt": [ 241 | "Api", 242 | "RootResourceId" 243 | ] 244 | }, 245 | "PathPart": "{proxy+}", 246 | "RestApiId": { 247 | "Ref": "Api" 248 | } 249 | }, 250 | "Type": "AWS::ApiGateway::Resource" 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | For details about this example check out the articles: 2 | 3 | Plain Old Servlet Example: https://medium.com/@AlexeyBalchunas/make-any-java-web-app-serverless-786119ffdf83 4 | 5 | JAX-RS (Jersey) Example: https://medium.com/@AlexeyBalchunas/you-dont-need-frameworks-for-a-serverless-api-in-java-4b01d0b8f4e5 6 | -------------------------------------------------------------------------------- /examples/simple/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.bleshik.aws-lambda-servlet.examples 5 | simple 6 | jar 7 | 1.0 8 | simple 9 | https://github.com/bleshik/aws-lambda-servlet 10 | 11 | 12 | bintray 13 | http://dl.bintray.com/bleshik/maven 14 | 15 | true 16 | 17 | 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-compiler-plugin 27 | 3.6.1 28 | 29 | 1.8 30 | 1.8 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-assembly-plugin 36 | 2.4.1 37 | 38 | 39 | jar-with-dependencies 40 | 41 | 42 | 43 | 44 | make-assembly 45 | package 46 | 47 | single 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | com.github.bleshik 57 | aws-lambda-servlet 58 | 1.0 59 | 60 | 61 | org.slf4j 62 | slf4j-simple 63 | 1.6.1 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/simple/src/main/java/com/github/bleshik/example/JerseyAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.bleshik.example; 2 | 3 | import util.JerseyRequestHandler; 4 | 5 | public class JerseyAdapter extends JerseyRequestHandler { 6 | public JerseyAdapter() { 7 | super("/", PingResource.class); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/simple/src/main/java/com/github/bleshik/example/PingResource.java: -------------------------------------------------------------------------------- 1 | package com.github.bleshik.example; 2 | 3 | import javax.ws.rs.*; 4 | 5 | @Path("ping") 6 | public class PingResource { 7 | @GET 8 | public String get() { 9 | return "OK"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/simple/src/main/java/com/github/bleshik/example/PingServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.bleshik.example; 2 | 3 | import java.io.*; 4 | import javax.servlet.*; 5 | import javax.servlet.http.*; 6 | 7 | public class PingServlet extends HttpServlet { 8 | public void doGet(HttpServletRequest request, HttpServletResponse response) 9 | throws ServletException, IOException { 10 | response.setContentType("text/html"); 11 | response.setStatus(200); 12 | PrintWriter out = response.getWriter(); 13 | out.print("OK"); 14 | out.close(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/simple/src/main/java/com/github/bleshik/example/ServletAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.bleshik.example; 2 | 3 | import util.ServletRequestHandler; 4 | 5 | public class ServletAdapter extends ServletRequestHandler { 6 | public ServletAdapter() { 7 | super("/", new PingServlet()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.bleshik 5 | aws-lambda-servlet 6 | jar 7 | 1.0 8 | aws-lambda-servlet 9 | https://github.com/bleshik/aws-lambda-servlet 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 3.6.1 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | 25 | javax.servlet 26 | javax.servlet-api 27 | 3.1.0 28 | 29 | 30 | org.glassfish.jersey.containers 31 | jersey-container-servlet 32 | 2.23.2 33 | 34 | 35 | com.amazonaws 36 | aws-lambda-java-core 37 | 1.1.0 38 | 39 | 40 | commons-logging 41 | commons-logging 42 | 43 | 44 | 45 | 46 | com.novocode 47 | junit-interface 48 | 0.11 49 | test 50 | 51 | 52 | junit 53 | junit 54 | 4.12 55 | test 56 | 57 | 58 | org.slf4j 59 | slf4j-api 60 | 1.7.21 61 | 62 | 63 | org.slf4j 64 | slf4j-simple 65 | 1.7.21 66 | test 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/util/DummyServletContext.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.InputStream; 4 | import java.net.MalformedURLException; 5 | import java.net.URL; 6 | import java.util.Collections; 7 | import java.util.EnumSet; 8 | import java.util.Enumeration; 9 | import java.util.EventListener; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import javax.servlet.*; 13 | import javax.servlet.descriptor.JspConfigDescriptor; 14 | 15 | @SuppressWarnings("deprecation") 16 | public class DummyServletContext implements ServletContext { 17 | public String getContextPath() { return null; } 18 | public ServletContext getContext(String uripath) { return null; } 19 | public int getMajorVersion() { return 3; } 20 | public int getMinorVersion() { return 0; } 21 | public int getEffectiveMajorVersion() { return 3; } 22 | public int getEffectiveMinorVersion() { return 0; } 23 | public String getMimeType(String file) { return null; } 24 | public Set getResourcePaths(String path) { return Collections.emptySet(); } 25 | public URL getResource(String path) throws MalformedURLException { return null; } 26 | public InputStream getResourceAsStream(String path) { return null; } 27 | public RequestDispatcher getRequestDispatcher(String path) { return null; } 28 | public RequestDispatcher getNamedDispatcher(String name) { return null; } 29 | public Servlet getServlet(String name) throws ServletException { return null; } 30 | public Enumeration getServlets() { return Collections.enumeration(Collections.emptySet()); } 31 | public Enumeration getServletNames() { return Collections.enumeration(Collections.emptySet()); } 32 | public void log(String msg) { } 33 | public void log(Exception exception, String msg) { } 34 | public void log(String message, Throwable throwable) { } 35 | public String getRealPath(String path) { return null; } 36 | public String getServerInfo() { return null; } 37 | public String getInitParameter(String name) { return null; } 38 | public Enumeration getInitParameterNames() { return Collections.enumeration(Collections.emptySet()); } 39 | public boolean setInitParameter(String name, String value) { return false; } 40 | public Object getAttribute(String name) { return null; } 41 | public Enumeration getAttributeNames() { return Collections.enumeration(Collections.emptySet()); } 42 | public void setAttribute(String name, Object object) { } 43 | public void removeAttribute(String name) { } 44 | public String getServletContextName() { return null; } 45 | public ServletRegistration.Dynamic addServlet(String servletName, String className) { return null; } 46 | public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { return null; } 47 | public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { return null; } 48 | public T createServlet(Class clazz) throws ServletException { return null; } 49 | public ServletRegistration getServletRegistration(String servletName) { return null; } 50 | public Map getServletRegistrations() { return null; } 51 | public FilterRegistration.Dynamic addFilter(String filterName, String className) { return null; } 52 | public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { return null; } 53 | public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { return null; } 54 | public T createFilter(Class clazz) throws ServletException { return null; } 55 | public FilterRegistration getFilterRegistration(String filterName) { return null; } 56 | public Map getFilterRegistrations() { return null; } 57 | public SessionCookieConfig getSessionCookieConfig() { return null; } 58 | public void setSessionTrackingModes(Set sessionTrackingModes) { } 59 | public Set getDefaultSessionTrackingModes() { return null; } 60 | public Set getEffectiveSessionTrackingModes() { return null; } 61 | public void addListener(String className) { } 62 | public void addListener(T t) { } 63 | public void addListener(Class listenerClass) { } 64 | public T createListener(Class clazz) throws ServletException { return null; } 65 | public JspConfigDescriptor getJspConfigDescriptor() { return null; } 66 | public ClassLoader getClassLoader() { return getClass().getClassLoader(); } 67 | public void declareRoles(String... roleNames) { } 68 | public String getVirtualServerName() { return null; } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/util/InMemoryHttpServletResponse.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStreamWriter; 6 | import java.io.PrintWriter; 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | import javax.servlet.ServletOutputStream; 10 | import javax.servlet.WriteListener; 11 | import javax.servlet.http.Cookie; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | /** 15 | * HTTP response implementation storing all the data in memory without actually writing anywhere. 16 | */ 17 | @SuppressWarnings({"unchecked", "deprecation"}) 18 | public class InMemoryHttpServletResponse implements HttpServletResponse { 19 | private final Collection cookies = new ArrayList<>(); 20 | private final Map> headers = new HashMap<>(); 21 | private final Set errors = new HashSet<>(); 22 | private final Set messages = new HashSet<>(); 23 | private String redirect; 24 | private int code; 25 | private String encoding = "UTF-8"; 26 | private ByteArrayOutputStream out = new ByteArrayOutputStream(); 27 | private boolean committed; 28 | private Locale locale = Locale.getDefault(); 29 | 30 | public void addCookie(Cookie cookie) { cookies.add(cookie); } 31 | 32 | public boolean containsHeader(String name) { return headers.containsKey(name); } 33 | 34 | public String encodeURL(String url) { return url; } 35 | 36 | public String encodeRedirectURL(String url) { return url; } 37 | 38 | public String encodeUrl(String url) { return url; } 39 | 40 | public String encodeRedirectUrl(String url) { return url; } 41 | 42 | public void sendError(int sc, String msg) throws IOException { 43 | code = sc; 44 | errors.add(msg); 45 | } 46 | 47 | public void sendError(int sc) throws IOException { 48 | code = sc; 49 | } 50 | 51 | public void sendRedirect(String location) throws IOException { 52 | redirect = location; 53 | } 54 | 55 | public void doSetHeader(String name, Object value) { 56 | headers.put(name, new HashSet() {{ add(value); }}); 57 | } 58 | 59 | public void doAddHeader(String name, Object value) { 60 | if (!headers.containsKey(name)) { 61 | doSetHeader(name, value); 62 | } else { 63 | headers.get(name).add(value); 64 | } 65 | } 66 | 67 | public void setDateHeader(String name, long date) { 68 | doSetHeader(name, date); 69 | } 70 | 71 | public void addDateHeader(String name, long date) { 72 | doAddHeader(name, date); 73 | } 74 | 75 | public void setHeader(String name, String value) { 76 | doSetHeader(name, value); 77 | } 78 | 79 | public void addHeader(String name, String value) { 80 | doAddHeader(name, value); 81 | } 82 | 83 | public void setIntHeader(String name, int value) { 84 | doSetHeader(name, value); 85 | } 86 | 87 | public void addIntHeader(String name, int value) { 88 | doAddHeader(name, value); 89 | } 90 | 91 | public void setStatus(int sc) { code = sc; } 92 | 93 | public void setStatus(int sc, String sm) { 94 | code = sc; 95 | messages.add(sm); 96 | } 97 | 98 | public int getStatus() { return code; } 99 | 100 | public String getHeader(String name) { 101 | if (headers.containsKey(name)) { 102 | return headers.get(name).iterator().next().toString(); 103 | } 104 | return null; 105 | } 106 | 107 | public Collection getHeaders(String name) { 108 | if (headers.containsKey(name)) { 109 | return headers.get(name).stream().map((i) -> i.toString()).collect(Collectors.toSet()); 110 | } 111 | return Collections.emptySet(); 112 | } 113 | 114 | public Collection getHeaderNames() { 115 | return headers.keySet(); 116 | } 117 | 118 | public String getCharacterEncoding() { return encoding; } 119 | 120 | public String getContentType() { 121 | return getHeader("Content-Type"); 122 | } 123 | 124 | public ServletOutputStream getOutputStream() throws IOException { 125 | return new ServletOutputStream() { 126 | private WriteListener listener; 127 | @Override 128 | public boolean isReady() { 129 | return true; 130 | } 131 | @Override 132 | public void setWriteListener(WriteListener writeListener) { 133 | listener = writeListener; 134 | try { 135 | writeListener.onWritePossible(); 136 | } catch (Exception e) {} 137 | } 138 | @Override 139 | public void write(int b) throws IOException { 140 | out.write(b); 141 | } 142 | @Override 143 | public void flush() { 144 | committed = true; 145 | } 146 | }; 147 | } 148 | 149 | public PrintWriter getWriter() throws IOException { 150 | return new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding())); 151 | } 152 | 153 | public void setCharacterEncoding(String charset) { this.encoding = charset; } 154 | 155 | public void setContentLength(int len) { doSetHeader("Content-Length", len); } 156 | 157 | public void setContentLengthLong(long len) { doSetHeader("Content-Length", len); } 158 | 159 | public void setContentType(String type) { doSetHeader("Content-Type", type); } 160 | 161 | public void setBufferSize(int size) { out = new ByteArrayOutputStream(size); } 162 | 163 | public int getBufferSize() { return out.size(); } 164 | 165 | public void flushBuffer() throws IOException {} 166 | 167 | public void resetBuffer() { committed = false; out.reset(); } 168 | 169 | public boolean isCommitted() { return committed; } 170 | 171 | public void reset() { resetBuffer(); } 172 | 173 | public void setLocale(Locale loc) { this.locale = loc; } 174 | 175 | public Locale getLocale() { return locale; } 176 | 177 | public String toString() { 178 | try { 179 | return out.toString(getCharacterEncoding()); 180 | } catch(Exception e) { return null; } 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/util/JerseyRequestHandler.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Optional; 8 | import java.util.Set; 9 | import javax.servlet.Filter; 10 | import org.glassfish.jersey.server.ResourceConfig; 11 | import org.glassfish.jersey.servlet.ServletContainer; 12 | 13 | /** 14 | * Lambda handler implementation delegating the request to the Jersey's {@link ServletContainer}. 15 | */ 16 | public class JerseyRequestHandler extends ServletRequestHandler { 17 | 18 | public JerseyRequestHandler(String contextPath, Class... classes) { 19 | this(contextPath, new HashSet>(Arrays.asList(classes))); 20 | } 21 | 22 | public JerseyRequestHandler(String contextPath, Set> classes) { 23 | this(contextPath, new ResourceConfig(classes)); 24 | } 25 | 26 | public JerseyRequestHandler(String contextPath, ResourceConfig rs) { 27 | this(contextPath, rs, Optional.empty(), Collections.emptyList()); 28 | } 29 | 30 | public JerseyRequestHandler(String contextPath, ResourceConfig rs, Optional sm, List filters) { 31 | super(contextPath, new ServletContainer(rs), sm, filters); 32 | } 33 | 34 | public JerseyRequestHandler(String contextPath, ResourceConfig rs, Optional sm, Filter... filters) { 35 | super(contextPath, new ServletContainer(rs), sm, filters); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/util/LambdaHttpServletRequest.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.StringReader; 8 | import java.io.UnsupportedEncodingException; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.Enumeration; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Locale; 17 | import java.util.Map; 18 | import java.util.Optional; 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | import javax.servlet.AsyncContext; 22 | import javax.servlet.DispatcherType; 23 | import javax.servlet.ReadListener; 24 | import javax.servlet.RequestDispatcher; 25 | import javax.servlet.ServletContext; 26 | import javax.servlet.ServletException; 27 | import javax.servlet.ServletInputStream; 28 | import javax.servlet.ServletRequest; 29 | import javax.servlet.ServletResponse; 30 | import javax.servlet.http.Cookie; 31 | import javax.servlet.http.HttpServletRequest; 32 | import javax.servlet.http.HttpServletResponse; 33 | import javax.servlet.http.HttpSession; 34 | import javax.servlet.http.HttpUpgradeHandler; 35 | import javax.servlet.http.Part; 36 | 37 | /** 38 | * Wraps the "pass-through" AWS Lambda parameters into an HTTP request. 39 | */ 40 | @SuppressWarnings("unchecked") 41 | public class LambdaHttpServletRequest implements HttpServletRequest { 42 | 43 | private Map input; 44 | private Map context; 45 | private Map identity; 46 | private Map querystring; 47 | private Map header; 48 | private String body; 49 | private final Optional sm; 50 | 51 | public LambdaHttpServletRequest(Map input) { 52 | this(input, Optional.empty()); 53 | } 54 | 55 | public LambdaHttpServletRequest(Map input, Optional sm) { 56 | this.sm = sm; 57 | this.input = input; 58 | this.context = (Map) input.get("requestContext"); 59 | if (this.context == null) { 60 | this.context = new HashMap<>(); 61 | } 62 | this.identity = (Map) context.get("identity"); 63 | if (this.identity == null) { 64 | this.identity = new HashMap<>(); 65 | } 66 | this.querystring = (Map) input.get("queryStringParameters"); 67 | if (this.querystring == null) { 68 | this.querystring = new HashMap<>(); 69 | } 70 | this.header = (Map) input.get("headers"); 71 | if (this.header == null) { 72 | this.header = new HashMap<>(); 73 | } 74 | this.body = (String) input.get("body"); 75 | if (this.body == null) { 76 | this.body = ""; 77 | } 78 | } 79 | 80 | public String getMethod() { return (String) input.get("httpMethod"); } 81 | 82 | public String getPathInfo() { 83 | return (String) input.get("path"); 84 | } 85 | 86 | public String getPathTranslated() { 87 | return getPathInfo(); 88 | } 89 | 90 | public String getContextPath() { 91 | return ""; 92 | } 93 | 94 | public String getQueryString() { 95 | return querystring.entrySet() 96 | .stream() 97 | .map(entry -> entry.getKey() + "=" + entry.getValue()) 98 | .collect(Collectors.joining("&")); 99 | } 100 | 101 | public String getRemoteUser() { return null; } 102 | 103 | public boolean isUserInRole(String role) { return false; } 104 | 105 | public java.security.Principal getUserPrincipal() { return null; } 106 | 107 | public String getRequestedSessionId() { return null; } 108 | 109 | public String getRequestURI() { return getPathInfo(); } 110 | 111 | public StringBuffer getRequestURL() { return new StringBuffer("http://localhost:80/" + getPathInfo()); } 112 | 113 | public String getServletPath() { return ""; } 114 | 115 | public HttpSession getSession(boolean create) { 116 | return sm.map(m -> m.getSession(this, create)).orElse(null); 117 | } 118 | 119 | public HttpSession getSession() { return getSession(true); } 120 | 121 | public String changeSessionId() { return null; } 122 | 123 | public boolean isRequestedSessionIdValid() { return false; } 124 | 125 | public boolean isRequestedSessionIdFromCookie() { return false; } 126 | 127 | public boolean isRequestedSessionIdFromURL() { return false; } 128 | 129 | public boolean isRequestedSessionIdFromUrl() { return isRequestedSessionIdFromURL(); } 130 | 131 | public boolean authenticate(HttpServletResponse response) throws IOException,ServletException { 132 | return false; 133 | } 134 | 135 | public void login(String username, String password) throws ServletException { } 136 | 137 | public void logout() throws ServletException { } 138 | 139 | public Collection getParts() throws IOException, ServletException { 140 | return Collections.emptySet(); 141 | } 142 | 143 | public Part getPart(String name) throws IOException, ServletException { 144 | return null; 145 | } 146 | 147 | public T upgrade(Class handlerClass) throws IOException, ServletException { 148 | return null; 149 | } 150 | 151 | public String getAuthType() { return null; } 152 | 153 | public Cookie[] getCookies() { 154 | if (!header.containsKey("Cookie")) { 155 | return new Cookie[0]; 156 | } 157 | try { 158 | List cookies = Arrays.asList(header.get("Cookie").split("; ")).stream().map(c -> { 159 | return new Cookie(c.substring(0, c.indexOf("=")), c.substring(c.indexOf("=") + 1)); 160 | }).collect(Collectors.toList()); 161 | return cookies.toArray(new Cookie[cookies.size()]); 162 | } catch (Exception e) { 163 | return new Cookie[0]; 164 | } 165 | } 166 | 167 | public long getDateHeader(String name) { 168 | return System.currentTimeMillis(); 169 | } 170 | 171 | public String getHeader(String name) { 172 | return header.get(name); 173 | } 174 | 175 | public Enumeration getHeaders(String name) { 176 | return Collections.enumeration(Collections.singleton(getHeader(name))); 177 | } 178 | 179 | public Enumeration getHeaderNames() { 180 | return Collections.enumeration(header.keySet()); 181 | } 182 | 183 | public int getIntHeader(String name) { 184 | return Integer.parseInt(getHeader(name)); 185 | } 186 | 187 | public String getRemoteAddr() { 188 | return identity.get("sourceIp"); 189 | } 190 | 191 | public int getContentLength() { 192 | return body.length(); 193 | } 194 | 195 | public String getContentType() { 196 | return header.get("Content-Type"); 197 | } 198 | 199 | public String getCharacterEncoding() { 200 | return "UTF-8"; 201 | } 202 | 203 | public Locale getLocale() { 204 | return Locale.getDefault(); 205 | } 206 | 207 | public String getScheme() { 208 | return "http"; 209 | } 210 | 211 | public int getServerPort() { 212 | return 80; 213 | } 214 | 215 | public String getServerName() { 216 | return "lambda"; 217 | } 218 | 219 | public DispatcherType getDispatcherType() { 220 | return DispatcherType.REQUEST; 221 | } 222 | 223 | public AsyncContext getAsyncContext() { throw new IllegalStateException(); } 224 | 225 | public AsyncContext startAsync() throws IllegalStateException { throw new IllegalStateException(); } 226 | 227 | public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { 228 | throw new IllegalStateException(); 229 | } 230 | 231 | public boolean isAsyncStarted() { return false; } 232 | 233 | public boolean isAsyncSupported() { return false; } 234 | 235 | public ServletContext getServletContext() { return null; } 236 | 237 | public int getLocalPort() { return 80; } 238 | 239 | public String getLocalAddr() { return "127.0.0.1"; } 240 | 241 | public String getLocalName() { return "lo0"; } 242 | 243 | public int getRemotePort() { return 0; } 244 | 245 | public String getRealPath(String path) { return ""; } 246 | 247 | public RequestDispatcher getRequestDispatcher(String path) { 248 | return null; 249 | } 250 | 251 | public boolean isSecure() { return false; } 252 | 253 | public Enumeration getLocales() { 254 | return Collections.enumeration(Collections.singleton(getLocale())); 255 | } 256 | 257 | public Object getAttribute(String name) { return null; } 258 | 259 | public Enumeration getAttributeNames() { 260 | return Collections.enumeration(Collections.emptySet()); 261 | } 262 | 263 | public void setCharacterEncoding(String env) throws UnsupportedEncodingException {} 264 | 265 | public long getContentLengthLong() { return getContentLength(); } 266 | 267 | public ServletInputStream getInputStream() throws IOException { 268 | return new ServletInputStream() { 269 | 270 | private final InputStream bodyStream = new ByteArrayInputStream(body.getBytes()); 271 | private int next; 272 | private ReadListener listener; 273 | private boolean finished; 274 | 275 | public int read() throws IOException { 276 | if (finished) { 277 | return -1; 278 | } 279 | int toReturn; 280 | if (next != -1) { 281 | toReturn = next; 282 | next = -1; 283 | } 284 | toReturn = bodyStream.read(); 285 | if (toReturn == -1) { 286 | finished = true; 287 | if (listener != null) { 288 | listener.onAllDataRead(); 289 | } 290 | } 291 | return toReturn; 292 | } 293 | 294 | public boolean isFinished() { 295 | try { 296 | return finished || next == -1 && (next = bodyStream.read()) == -1; 297 | } catch (IOException e) { 298 | return true; 299 | } 300 | } 301 | 302 | public boolean isReady() { return !isFinished(); } 303 | 304 | public void setReadListener(ReadListener readListener) { 305 | listener = readListener; 306 | try { 307 | readListener.onDataAvailable(); 308 | } catch (IOException e) {} 309 | } 310 | }; 311 | } 312 | 313 | public String getParameter(String name) { 314 | return querystring.get(name); 315 | } 316 | 317 | public Enumeration getParameterNames() { 318 | return Collections.enumeration(querystring.keySet()); 319 | } 320 | 321 | public String[] getParameterValues(String name) { 322 | return new String[] { getParameter(name) }; 323 | } 324 | 325 | public Map getParameterMap() { 326 | Map result = new HashMap<>(); 327 | for (String name : querystring.keySet()) { 328 | result.put(name, getParameterValues(name)); 329 | } 330 | return result; 331 | } 332 | 333 | public String getProtocol() { return "HTTP/1.1"; } 334 | 335 | public BufferedReader getReader() throws IOException { 336 | return new BufferedReader(new StringReader(body)); 337 | } 338 | 339 | public String getRemoteHost() { return getRemoteAddr(); } 340 | 341 | public void setAttribute(String name, Object o) {} 342 | 343 | public void removeAttribute(String name) {} 344 | 345 | } 346 | -------------------------------------------------------------------------------- /src/main/java/util/ServletRequestHandler.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import com.amazonaws.services.lambda.runtime.Context; 4 | import com.amazonaws.services.lambda.runtime.RequestHandler; 5 | import java.io.IOException; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.Enumeration; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | import java.util.Set; 15 | import javax.servlet.Filter; 16 | import javax.servlet.FilterChain; 17 | import javax.servlet.Servlet; 18 | import javax.servlet.ServletConfig; 19 | import javax.servlet.ServletContext; 20 | import javax.servlet.ServletException; 21 | import javax.servlet.ServletRequest; 22 | import javax.servlet.ServletResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * Lambda handler implementation delegating the request to the given Servlet instance. 28 | */ 29 | public class ServletRequestHandler implements RequestHandler, Object> { 30 | protected final Logger logger = LoggerFactory.getLogger(this.getClass()); 31 | 32 | protected final String contextPath; 33 | protected final T servlet; 34 | protected final Optional sm; 35 | protected final List filters; 36 | 37 | public ServletRequestHandler(String contextPath, T servlet) { 38 | this(contextPath, servlet, Optional.empty(), Collections.emptyList()); 39 | } 40 | 41 | public ServletRequestHandler(String contextPath, T servlet, Optional sm, Filter... filters) { 42 | this(contextPath, servlet, sm, Arrays.asList(filters)); 43 | } 44 | 45 | public ServletRequestHandler(String contextPath, T servlet, Optional sm, List filters) { 46 | try { 47 | this.contextPath = contextPath.replaceAll("^/*([^/]*)/*$", "/$1"); 48 | this.filters = filters; 49 | this.sm = sm; 50 | this.servlet = servlet; 51 | this.servlet.init(new ServletConfig() { 52 | public String getServletName() { 53 | return null; 54 | } 55 | public ServletContext getServletContext() { 56 | return new DummyServletContext(); 57 | } 58 | public String getInitParameter(String name) { 59 | return null; 60 | } 61 | public Enumeration getInitParameterNames() { 62 | return Collections.enumeration(Collections.emptySet()); 63 | } 64 | }); 65 | } catch (ServletException e) { 66 | throw new AssertionError("Failed to initialize lambda handler", e); 67 | } 68 | } 69 | 70 | protected FilterChain createFilterChain(List filters) { 71 | return new FilterChain() { 72 | @Override 73 | public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { 74 | if (filters.isEmpty()) { 75 | servlet.service(request, response); 76 | } else { 77 | filters.get(0).doFilter( 78 | request, 79 | response, 80 | createFilterChain( 81 | filters.size() > 1 ? filters.subList(1, filters.size()) : Collections.emptyList() 82 | ) 83 | ); 84 | } 85 | } 86 | }; 87 | } 88 | 89 | protected Map removeContextPath(Map input) { 90 | String path = (String) (input.get("path") != null ? input.get("path") : "/"); 91 | input.put("path", path.replaceAll("^" + this.contextPath + "/?", "/")); 92 | return input; 93 | } 94 | 95 | @Override 96 | public Map handleRequest(Map input, Context context) { 97 | try { 98 | LambdaHttpServletRequest request = new LambdaHttpServletRequest(removeContextPath(input), sm); 99 | InMemoryHttpServletResponse response = new InMemoryHttpServletResponse(); 100 | createFilterChain(filters).doFilter(request, response); 101 | return new HashMap() {{ 102 | int code = response.getStatus(); 103 | put("statusCode", code < 100 ? 200 : code); 104 | put("headers", new HashMap(){{ 105 | for(String h : response.getHeaderNames()) { 106 | put(h, response.getHeader(h)); 107 | } 108 | }}); 109 | put("body", response.toString()); 110 | }}; 111 | } catch (Exception e) { 112 | logger.error("Failed to handle the request", e); 113 | return lambdaHttpResponse(500, "Internal Server Error: " + e.getMessage()); 114 | } 115 | } 116 | 117 | protected Map lambdaHttpResponse(int code, String message) { 118 | return new HashMap() {{ 119 | put("statusCode", code); 120 | put("headers", new HashMap(){{ 121 | put("Content-Type", "text/plain"); 122 | }}); 123 | put("body", message); 124 | }}; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/util/SessionManager.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpSession; 5 | 6 | public interface SessionManager { 7 | HttpSession getSession(HttpServletRequest request); 8 | HttpSession getSession(HttpServletRequest request, boolean create); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/util/JerseyRequestHandlerSpec.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.Enumeration; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | import javax.servlet.Filter; 11 | import javax.servlet.FilterChain; 12 | import javax.servlet.FilterConfig; 13 | import javax.servlet.ServletConfig; 14 | import javax.servlet.ServletContext; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.ServletRequest; 17 | import javax.servlet.ServletResponse; 18 | import javax.ws.rs.GET; 19 | import javax.ws.rs.Path; 20 | import org.glassfish.jersey.server.ResourceConfig; 21 | import org.glassfish.jersey.servlet.ServletContainer; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.junit.runners.JUnit4; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | 28 | @RunWith(JUnit4.class) 29 | @SuppressWarnings("unchecked") 30 | public class JerseyRequestHandlerSpec { 31 | 32 | @Path("/") 33 | public static class TestResource { 34 | @GET 35 | @Path("test") 36 | public String test() { 37 | return "It works!"; 38 | } 39 | } 40 | 41 | private int filterNumber = 41; 42 | 43 | @Test 44 | public void handleRequest() throws Exception { 45 | assertEquals( 46 | new TestResource().test(), 47 | new JerseyRequestHandler( 48 | "root", 49 | new ResourceConfig(TestResource.class), 50 | Optional.empty(), 51 | new Filter() { 52 | @Override 53 | public void init(FilterConfig filterConfig) throws ServletException {} 54 | @Override 55 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 56 | throws IOException, ServletException { 57 | filterNumber = 42; 58 | chain.doFilter(request, response); 59 | } 60 | @Override 61 | public void destroy(){} 62 | } 63 | ).handleRequest(new HashMap() {{ 64 | put("body", null); 65 | put("path", "/root/test"); 66 | put("httpMethod", "GET"); 67 | put("headers", new HashMap()); 68 | put("queryStringParameters", new HashMap()); 69 | put("requestContext", new HashMap(){{ 70 | put("sourceIp", "test-invoke-source-ip"); 71 | }}); 72 | }}, null).get("body") 73 | ); 74 | // check that the filter works 75 | assertEquals(42, filterNumber); 76 | } 77 | } 78 | --------------------------------------------------------------------------------