The aws-serverless-java-container
is collection of interfaces and their implementations that let you run Java application written with frameworks such as Jersey or Spark in AWS Lambda.
The library contains a core artifact called aws-serverless-java-container-core
that defines the interfaces and base classes required as well as default implementation of the Java servlet HttpServletRequest
and HttpServletResponse
.
The library also includes two initial implementations of the interfaces to support Jersey apps (aws-serverless-java-container-jersey
) and Spark (aws-serverless-java-container-spark
).
To include the library in your Maven project, add the desired implementation to your pom.xml
file, for example:
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-jersey</artifactId>
<version>0.2</version>
</dependency>
The simplest way to run your application serverlessly is to configure API Gateway to use the
AWS_PROXY
integration type and
configure your desired LambdaContainerHandler
implementation to use AwsProxyRequest
/AwsProxyResponse
readers and writers. Both Spark and Jersey implementations provide static helper methods that
pre-configure this for you.
The library expects to receive a valid JAX-RS application object. For the Jersey implementation this is the ResourceConfig
object.
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
private ResourceConfig jerseyApplication = new ResourceConfig().packages("my.jersey.app.package");
private JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler
= JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
return handler.proxy(awsProxyRequest, context);
}
}
The library supports Spring applications that are configured using annotations (in code) rather than in an XML file. The simplest possible configuration uses the @ComponentScan
annotation to load all controller classes from a package. For example, our unit test application has the following configuuration class.
@Configuration
@ComponentScan("com.amazonaws.serverless.proxy.spring.echoapp")
public class EchoSpringAppConfig {
}
Once you have declared a configuration class, you can initialize the library with the class name:
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
SpringLambdaContainerHandler.getAwsProxyHandler(EchoSpringAppConfig.class);
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
return handler.proxy(awsProxyRequest, context);
}
}
The library also supports applications written with the Spark framework. When using the library with Spark, it's important to initialize the SparkLambdaContainerHandler
before defining routes.
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
private SparkLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
SparkLambdaContainerHandler.getAwsProxyHandler();
private boolean initialized = false;
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
if (!initialized) {
defineRoutes();
initialized = true;
}
return handler.proxy(awsProxyRequest, context);
}
private void defineRoutes() {
get("/hello", (req, res) -> "Hello World");
}
}
The aws-serverless-java-container-core
contains a default implementation of the SecurityContextWriter
that supports API Gateway's proxy integration. The generated security context uses the API Gateway $context
object to establish the request security context. The context looks for the following values in order and returns the first matched type:
- Cognito My User Pools
- Custom authorizers
- IAM auth.
The String values for these are exposed as static variables in the AwsProxySecurityContext
object.
AUTH_SCHEME_COGNITO_POOL
AUTH_SCHEME_CUSTOM
AUTH_SCHEME_IAM
The RequestReader
and ResponseWriter
interfaces in the core package can be used to support event types and generate different responses. For example, ff you have configured mapping templates in
API Gateway to create a custom event body or response you can create your own implementation of the RequestReader
and ResponseWriter
to handle these.
The LambdaContainerHandler
also requires a SecurityContextWriter
and an ExceptionHandler
. You can also create custom implementations of these interfaces.
The RequestReader
, ResponseWriter
, SecurityContextWriter
, and ExceptionHandler
objects are passed to the constructor of the LambdaContainerHandler
implementation:
JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
new JerseyLambdaContainerHandler<>(new MyCustomRequestReader(),
new MyCustomResponseWriter(),
new MyCustomSecurityContextWriter(),
new MyCustomExceptionHandler(),
jaxRsApplication);
The aws-serverless-java-container-jersey
includes a Jersey factory class to produce HttpServletRequest
objects for your methods. First, you will need to register the factory with your Jersey application.
ResourceConfig app = new ResourceConfig()
.packages("com.amazonaws.serverless.proxy.test.jersey")
.register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(JerseyAwsProxyServletRequestFactory.class)
.to(HttpServletRequest.class)
.in(RequestScoped.class);
}
});
Once the factory is registered, you can receive HttpServletRequest
objects in your methods.
@Path("/my-servlet") @GET
public String echoServletHeaders(@Context HttpServletRequest context) {
Enumeration<String> headerNames = context.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
}
return "servlet";
}