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:

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.

Microlam is running docker using the exec-maven-plugin and the maven-toolchains-plugin. You need to configure your paths (so docker command can be found) in the toolchains.xml file in ~/.m2/toolchains.xml like this:

<?xml version="1.0" encoding="UTF8"?>
<toolchains>
    <toolchain>
        <type>paths</type>
        <provides>
            <id>binaries</id>
        </provides>
        <configuration>
            <!-- Defines the folders to search for binaries of the "paths" toolset -->
            <paths>
                <path>/usr/local/bin</path>
            </paths>
        </configuration>
    </toolchain>
</toolchains>

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.10

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 pom.xml and [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-demo

  • version = 1.0-SNAPSHOT

  • package = tech.solusoft.demo

  • awsProfile = microlam

  • awsRegion = eu-west-1

  • lambdaName = MicrolamDemoLambda

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.

  • In the [package].params folder you can find the class SystemParameters giving you access to System Manager/Parameters, there is only one parameter currently : DEBUG

  • In the [package].log folder you can find the class LoggingConfiguration where you can configure your Logging.

  • In the [package].aws folder you can find the class AwsClients where you can put all your AWS Clients as singletons (they are thread safe). Currently there is only the SSM Client (for System Manager).

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 22), implementing the interface APIGatewayProxyLambda only method handleRequest. Note the use of the Jsonb and Jsonp to convert from/to json to/from our POJOs (lines 31 and 35).

As we said, we use MicrolamDemoLambda 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, MicrolamDemoLambda 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)

Also you can find the static bloc where AwsClients and LoggingConfiguration are configured lines 14-17.

Lines 25-29, this code allow to update the LogLevel using the SystemParameter DEBUG. this code needs to be there to avoid SnapStart for freezing it in its snapshot.

Solving the aggregated log problem

As only the MicrolamDemoLambda is deployed to AWS, the logs from MultLambda and SumLambda will be available in Cloudwatch at the same place inside MicrolamDemoLambda logs. The MicrolamDemoLambda 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

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

Profile -Pjava

Generating the Lambda Java deployment package is coming from the profile -Pjava which is now activated by default. You may disable it with -P-java.

Which Java version to choose ?

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

How to use Java 20 or Java 19?

First modify the pom.xml property: release.version to 20 or 19. Then use the command mvn package -Djava20layer=axx64 (or -Djava19layer=axx64) where axx64 is amd64 or arm64. This brings the java 20/19 layer in your target/ folder. You need to deploy the Java 20/19 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, java17, java19 , Java20 or Java21) 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 java17-amd64, if everything is ready, you just have to use this maven command:

mvn package -Dnative=java17-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 package -Dnative=java17-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: target/[artifactId]-[version]-native-config/java11-amd64/function/config/.

You can manually copy the files to the folder src/main/resources/META-INF/native-image/[groupId]/[artifactId]/.

Then it is a good idea, to compile again the Native lambda which can be done with this command:

mvn package -Dnative=java17-amd64 -Pcompile

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

Then test locally the result of your compilation:

mvn package -Dnative=java17-amd64 -Prun

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.

Using Oracle GraalVM instead of GraalVM CE

You may use Oracle GraalVM instead of GraalVM CE by using the parameter -Dnative=oracle-javaXX-axx64 where XX = 17 , 20 or 21

4.3. The Native configuration

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

Files in src/main/java

See how to generate this configuration 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.

4.5. Deploying your Application with the CDK

Bootstrapping your project for the CDK

You need to bootstrap your project before using the CDK only the first time. For this, inside the project root folder, use the command: cdk bootstrap

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 now using SLF4J with slf4j-api-2.0.7.jar and slf4j-jdk14-2.0.7.jar with a custom Handler for AWS Lambda from microlam-java-logging with these features:

  • Support for Default Runtime & Custom Runtime (without configuration change)

  • Possible to opt for Json Logging by changing a flag to true (LoggingConfiguration class)

  • Use of System Manager/Parameter DEBUG set to "true"` to dynamically log to DEBUG with current deployment.

You can change the configuration by updating the class LoggingConfiguration.

All the logs will be found in CloudWatch as usual.

For understanding 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.