Summary
Java is one of the most popular language in use today. However, I have not run across a lot of people using Java with AWS Lambda or CDK. In this post, I will demonstrate how to deploy Java to AWS Lambda using AWS CDK and provide a sample repo structure to copy for your own purposes.Java
Java has a long history of running high performant applications for massive customers. Java and the JVM continue to be very popular today. AWS has made significant investments into the Java ecosystem. This includes maintaining their own OpenJDK distribution, Corretto. This is due to Java’s popularity, particularly in large companies. So it’s no surprise that AWS would want to support these customers.
AWS Lambda currently supports the following Java versions:
- Java 11
- Java 8 Corretto
- Java 8 OpenJDK
For this post, we will be using the Java 8 OpenJDK runtime.
Bundling Java for AWS Lambda
Much like other languages supported by Lambda, your Java applications can be bundled in a .jar or zip file to be deployed
to AWS Lambda. The handler can be any class implementing the RequestHandler
interface.
This means we are not limited to Java the language on AWS Lambda. We can, in fact, use any language capable of compiling
for the JVM, including Kotlin.
The com.amazonaws.services.lambda.runtime.RequestHandler
interface is included in the Maven dependency:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>${lambda.version}</version>
</dependency>
The full class and method name will be designated as the handler for AWS Lambda. When lambda executes, it will call the method designated, passing in the event that was used to invoke the lambda.
Using Lambda Layers
Layers are a feature of AWS lambda that allows multiple lambda functions to share assets. Using layers we can bundle all the dependencies of an application into a single .zip file and any lambda’s can use it. This means they do not need to contain all the external dependencies, keeping the deployment package small.
In the sample repository, we have a submodule, layer that is responsible for bundling the dependencies for layer creation. The layer is created in CDK using:
// Create a layer from the layer module
final LayerVersion layer = new LayerVersion(this, "layer", LayerVersionProps.builder()
.code(Code.fromAsset("../layer/target/bundle"))
.compatibleRuntimes(Arrays.asList(Runtime.JAVA_8))
.build()
);
The maven assembly plugin bundles all the dependencies into a single jar and outputs to a directory. For AWS Lambda,
java dependencies must be in the java/lib
directory of a layer. The code above will create a .zip file and upload it
to S3. Then, we specify the layer in the lambda function.
Using the repository
The structure of the sample repository includes a parent pom.xml
where we can add any dependencies and setup the base
project structure. Personally, I tend to ignore the Maven convention of holding sources under src/main/java
and opt for
a flat repository structure where source code is simply put under src
. This is defined in the parent pom.
In addition, the repository has three maven modules:
infra
- Contains CDK implementation for deploying AWS resourceslambdas
- Contains the lambdas to be deployedlayer
- Creates the layer bundle
In order to work with CDK in Java, you will need to install CDK. This requires NodeJS, which can be install with nvm.
In the sample repository, the code for deploying the lambdas resides in cdk.Stack
. This class, creates
the lambda and nothing else. However, if you have other AWS resources that you would like to deploy, they
can be place in here.
// Example of creating a lambda in CDK java
new Function(this, "JavaFn", FunctionProps.builder()
.runtime(Runtime.JAVA_8)
.code(Code.fromAsset("../lambdas/target/lambdas.jar"))
.handler("lambdas.ExampleLambda")
.layers(Arrays.asList(layer))
.memorySize(1024)
.timeout(Duration.seconds(30))
.logRetention(RetentionDays.ONE_WEEK)
.build());
Now, venturing into the lambdas
module, we see a single ExampleLambda
class. This class implements the
RequestHandler
interface. If you wanted to deploy more than one lambda, you could simply define and additional class
and add the lambda as above.
For the purposes of this sample, the implementation is pretty simple:
package lambdas;
import java.util.Map;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.google.common.base.Joiner;
public class ExampleLambda implements RequestHandler<Map<String, String>, String> {
@Override
public String handleRequest(final Map<String, String> event, final Context context) {
System.out.println("Received event: " + event);
// using an external dependency provided by layer
final String msg = Joiner.on(" ").join("Hello", "from", "Java!");
return null;
}
}
Deployment
To deploy the sample, you should be logged into your AWS account utilizing the AWS CLI. There is a Makefile
present in
the root of the repository. This file will run the relevant commands:
make deploy
This target will build the submodules and deploy the lambda using CDK. The sample repo is located in Github. You can use it to get started deploying your own lambdas using Java.