(Full source code: step2 directory)
The QuantumMaid initialization code will be exactly the same whether we run the code as a local HTTP endpoint or as an AWS Lambda function.
To share code between the local mode and the Lambda mode, we extract it to a new method in the Main class:
private static QuantumMaid quantumMaidConfig() {
return QuantumMaid.quantumMaid()
.get("/helloworld", (request, response) -> response.setBody("Hello World!"));
}Lambda integration is provided through an additional HttpMaid dependency:
<dependency>
<groupId>de.quantummaid.httpmaid.integrations</groupId>
<artifactId>httpmaid-awslambda</artifactId>
<version>0.9.143</version>
</dependency>Once the httpmaid-lambda dependency is added, a new class is available to bridge the QuantumMaid world and the AWS Lambda world: AwsLambdaEndpoint.
We should initialize an instance of AwsLambdaEndpoint in a static field of the Main class, so that:
- The time taken to initialize QuantumMaid does not count towards the execution time of the Lambda function. In plain English, it will not be billed by AWS.
- The QuantumMaid initialization delay is experienced only once per Lambda instance. For our purpose, a Lambda instance is a Java Virtual Machine (JVM) process, with some notable differences.
Here are the imports required:
import de.quantummaid.httpmaid.awslambda.AwsLambdaEndpoint;
import static de.quantummaid.httpmaid.awslambda.AwsLambdaEndpoint.awsLambdaEndpointFor;And the AwsLambdaEndpoint static initialization code:
public final class Main {
private static final AwsLambdaEndpoint ADAPTER = awsLambdaEndpointFor(quantumMaidConfig().httpMaid());
//...The request handling method is the method that will be invoked by the AWS Lambda Java runtime, and must forward all calls to the AwsLambdaEndpoint adapter we just added.
public Map<String, Object> handleRequest(final Map<String, Object> request) {
return ADAPTER.delegate(request);
}While the method's parameter type and return type are fixed (both must be Map<String, Object>), the method can be named whatever we like. We will reference this method name in the SAM template (➊).
Regular CloudFormation templates are rather verbose when deploying AWS Lambda functions, so we will use an AWS Serverless Application Model (SAM) template instead.
AWSTemplateFormatVersion: 2010-09-09
Description: quantummaid tutorials lambda function
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Handler: de.quantummaid.tutorials.Main::handleRequest # ➊
Runtime: java11
MemorySize: 256
Events:
HelloWorldHttpApi:
Type: HttpApi # ➋
Properties:
Path: /{proxy+} # ➌
Method: ANY # ➍
➊ The Handler property is [fully qualified request handler class name]::[request handler method name].
➋ Use Type: Api if you want to use REST API instead of HTTP API. We use HTTP API because it's faster, lower cost and simpler to use.
➌➍ This means that requests to ➌ any path depth (/, /helloworld, /hello/...), using ➍ any method (GET, HEAD, PUT, POST, etc.), will be handled by our HttpMaid function. These parameters are fixed and required for a so-called Lambda proxy integration.
Are we there yet? Almost.
The AWS Lambda Java runtime instantiates the request handler class (Main) by calling its default constructor,
which must be public and take no arguments:
public Main() {
// the AWS Lambda Java runtime requires a public no-args constructor
}If you don't, the AWS Lambda Java runtime will fail while looking up the constructor:
java.lang.Exception: Class de.quantummaid.tutorials.Main has no public zero-argument constructor
Caused by: java.lang.NoSuchMethodException: de.quantummaid.tutorials.Main.<init>()
Next, we are going to deploy our function to AWS Lambda.