Creating Your First Application

Learn how to create a Hello World Microlam app. This guide covers:

  • Bootstrapping an application

  • Testing the application

  • Packaging of the application

  • Deploying to AWS

1. Prerequisites

To complete this guide, you need:

  • from 15 minutes to 1 hour

  • an IDE

  • JDK 11+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.8.1+

  • Docker

  • AWS CLI

  • AWS CDK CLI

Verify Maven is using the Java you expect

If you have multiple JDK’s installed it is not certain Maven will pick up the expected java and you could end up with unexpected results. You can verify which JDK Maven uses by running mvn --version.

2. Architecture

In this guide, we create a straightforward application serving 2 endpoints mult and sum with a REST API:

  • POST /mult

  • POST /sum

Architecture

The 2 endpoints are connected to the same lambda via Proxy integration.

Both receive a json body of the form:

{
"arguments" : [ 2, 4, 6]
}

The expected response will be a json of the form:

{
"result" : 12
}

The result will be the sum or the product of the arguments depending on the respective endpoints /sum or /mult.

3. Bootstrapping the project

The easiest way to create a new Microlam project is to open a terminal and run the following command:

In your CLI:

mvn archetype:generate -DarchetypeGroupId=io.microlam -DarchetypeArtifactId=microlam-lambda-quickstart -DarchetypeVersion=0.9.1

You will have to answer a few questions:

  • Choose a groupId, artifactId, version and package for your new project

  • Choose your awsBucket : this is the name of an S3 bucket where the lambda artifacts will be deployed

  • Choose your awsProfile : this is the name of your AWS profile

  • Choose your awsRegion: this is the name of an AWS region (ex: eu-west-1)

  • Choose your lambdaName: this is the name of your Lambda must be also suitable as the name of the Java class for the Lambda.

In case you provided incorrect values for awsBucket, awsProfile or awsRegion, no problem!

It is very easy to set these values afterwards: Change the file [package].devops.Aws in src/test/java and eventually also samconfig.toml if you need to deploy with AWS SAM

4. Discovering and working with the generated project

4.1. The Java code in src/main/java

Let’t take a concrete example:

  • groupId = tech.solusoft

  • artifactId = microlam-lambda

  • version = 1.0-SNAPSHOT

  • package = tech.solusoft.example

  • awsProfile = premiumpay

  • awsRegion = eu-west-1

  • lambdaName = MicrolamLambda

Once generated, import the project into your prefered IDE, this is what you will find in src/main/java:

Files in src/main/java
  • In the [package].bs folder, you can find the business services (separating the business operations from the AWS code is a good thing).

  • In the [package].lambda folder you can see 3 classes providing the Lambda entry points, in fact, only one (here MicrolamLambda) will be deployed to AWS (One good reason for that is to ensure this lambda will be more hot). MultLambda and SumLambda are really doing the work.

  • In the [package].body folder you can see LambdaBodyIn and LambdaBodyOut, they are POJO representing what expect the lambda as Input and what to expect from its output.

Let’s see the code for one of our Lambda (MultLambda):

MultLambda code

You can see the MultLambda code is very simple (as SumLambda), using the business service as a singleton (line 15), implementing the interface APIGatewayProxyLambda only method handleRequest. Note the use of the library google Gson to convert from/to json to/from our POJOs (lines 24 and 31).

As we said, we use MicrolamLambda as a proxy to redirect the call to MultLambda and SumLambda depending on the target resource /mult or /sum:

MicrolamLambda code

As you can see, MicrolamLambda implements the registerAllLambda() method from its abstract mother class and map each endpoint to a given lambda (it may be the same), the mapping is:

[METHOD][SPACE][RESOURCE] -> Lambda

Where:

  • [METHOD] is POST or GET or PUT etc…​ (the Http Method)

  • [SPACE] is the space character

  • [RESOURCE] is the resource for AWS, or endpoint (here /mult or /sum)

Solving the aggregated log problem

As only the MicrolamLambda is deployed to AWS, the logs from MultLambda and SumLambda will be available in Cloudwatch at the same place inside MicrolamLambda logs. The MicrolamLambda mother class is logging at the beginning in which Lambda it is redirecting. Be careful to always map any new Lambda, but even if you forgot to map it, the logs will tell you exactly which mapping was not found.

4.2. The pom.xml

You will find the import of the Microlam BOM, allowing you to omit the version on the different Microlam dependencies, but that’s not the main point.

What is original is that, you will not find any microlam-maven-plugin that will hide all the work that is made magically to compile or configure your native compilation.

One of Microlam value is to be easily hackable, because on a simple example, the magical solution may work, but what happens when it does not ? Microlam is designed with the idea that your project is enough complex to not work with a magical solution. In fact, this Quickstart project will not compile magically because of the usage of Gson (using reflection) to Map from/to json to/from the POJOs for example…​ So Microlam will help you generate the configuration for the compilation.

Microlam is providing everything inside this pom.xml, helping you compile, generate the Java Deployment package for your Lambdas and also help you compile natively your project with GraalVM Native Image. In order to offer an uniform development experience (for linux, MacOS or Windows developer), Microlam is using Docker for compiling natively and building the Lambda Custom deployment package containing your compiled code.

The pom.xml allow you to achieve different goals:

4.2.1. Generating the Lambda Java Deployment package

Use simply the java maven profile:

mvn package -Pjava

This command build the Java deployment package in target/ folder with the name [xxx]-aws-lambda.jar.

Which Java version to choose ?

Currently AWS supports only Java 8 or Java 11 Runtimes, but it is possible to use a Custom Runtime to deploy also for Java 17. Microlam is providing the maven argument -Djava17layer=axx64 for bringing the layer, see next TIP for detailed instructions and this project (AWS Lambda Java 17 Runtime. By default, Microlam is configured by default to use Java 11, but you can change this to Java 17 by setting the property release.version to 17.

How to use Java 17 ?

First modify the pom.xml property: release.version to 17. Then use the command mvn package -Pjava -Djava17layer=axx64 where axx64 is amd64 or arm64. This brings the java 17 layer in your target/ folder. You need to deploy the Java 17 Layer and the simplest way is to use the CDK (but using the AWS console is also possible), see and update as necessary the Class [xxx].devops.cdk.CreateApp (from src/test folder).

Why we need the Java Lambda if we can compile to native ?

It is important to reduce the developement cycle develop-test-develop as much as possible. It may be a good idea to develop and test your Java Lambda because it is very fast and to compile only when needed. This is the same code, so it should be ok.

4.2.2. Configure, Compile and Generate the Native Lambda Deployment Package

For an introduction to the subject of Native compilation for Java, you can refer to this article.

The native build is depending on the java version (java11 or java17) and the target architecture (amd64 or arm64).

You need to provide this information in the maven command line using -Dnative=javaXX-axx64 (by replacing XX and xx with the correct values).

Let’s say your target is java11-amd64, if everything is ready, you just have to use this maven command:

mvn install -Djava11-amd64 -Pcompile

In case the build is successful, the Native deployment package is in the target folder with the name [xxx]-aws-lambda-native.zip.

But generally, this build will not work immediately and if it works, the resulting compiled code will not work at runtime. Actually, the GraalVM native-image tool is compiling only the code that is necessary using a static analysis, which is not covering the cases of usage of Java Reflection for example, in our case, our use of Gson…​ that’s why we need to provide an additional configuration to the tool.

The solution proposed by Microlam is to generate this configuration automatically by the GraalVM Tracing Agent while you are running your integration tests on it.

This can be done by this maven command:

mvn install -Djava11-amd64 -Pconfig

At the end of the build, a container is running, letting you run your tests on it :

In our case, you can run the Junit Tests in the class in [xxx].devops.LocalLambdaTests.

The generated configuration is updated (and merged) every 30s in the folder: src/main/resources/META-INF/native-image/[groupId]/[artifactId]/.

Then it is a good idea, to compile and test locally the Native lambda which can be done with this command activating the dev mode:

mvn install -Djava11-amd64 -Pcompile,dev

At the end of the build, a container is running, letting you try your native lambda locally.

In our case, you can run again the Junit Tests in the class in [xxx].devops.LocalLambdaTests.

If everything is working as expected, you are ready to generate your deployment package.

In another console, while the container is running, run this command:

mvn docker:copy -Dnative=java11-amd64 -Pcompile

This will copy the Native deployment package to the target folder with the name [xxx]-aws-lambda-native.zip.

You can now stop the running container from the first console, with CTRL-C.

4.3. The Native configuration

It is found in the src/main/resources folder:

Files in src/main/java

This configuration will be merged automatically when using the config profile.

4.4. The Java code in src/test/java

This is what you will find in src/test/java:

Files in src/main/java
  • In the [package].devops folder, you can find code to help you test and deploy your Lambda.

  • In the [package].devops.cdk folder, you can find the class CreateApp including all what is necessary to deploy the complete Application to AWS using AWS CDK.

  • In the [package] folder, you can find the Class LambdaTest, which show you how to test your Lambda code without testing the Lambda Machinery for Unit testing.

Using the AWS CDK is the recommended way to deploy your App and to maintain it during development.

As you develop, modify CreateApp if necessary, for our example, you need to apply a few changes to set the function Runtime, Architecture and Code according to your needs, because by default it deploys the Java Lambda Package.

Then use this command to deploy or synchronize your deployment:

cdk deploy

The Aws class is used for holding the Aws configuration used by the CreateApp class for the CDK deployment. When you develop your lambdas, it is possible to run simply the unit test in UploadAndUpdateLambda to upload your java deployment or UploadAndUpdateLambdaNative to upload your native deployment.

In case you want to clean everything on your aws account, you just need to run:

cdk destroy

5. Logging

By default, the project is using slf4j-simple-lambda which is a fork of slf4j-simple dedicated to AWS Lambda with these features:

  • Support for AWSRequestId

  • Allow env properties with underscore instead of point to be compatible with the AWS lambda env property naming rules

You can change the configuration by updating the file simplelogger.properties.

All the logs will be found in CloudWatch as usual.

For understanding how slf4j-simple-lambda solves the Java Aws Lambda Logging problem you can refer to this article.

6. Microlam

Microlam is only beginning and needs your feedback and your help to improve.

If you have any questions or need some support, click here.