AWS Lambda Functions in Java with SAM
SAM (Serverless Application Model) is an alleviate version of CloudFormation and is one of the preferred methods to describe AWS infrastructures in a IaC (Infrastructure as Code) manner.
The AWS console allows the user to create and update, in an user-friendly manner, cloud infrastructure resources. Despite all the advantages that such a high level tool might have, using it is repetitive and error prone. For example, each time we create a Lambda Function using the AWS console, we need to repeat the same operations, again and again and, even if these operations are intuitive and easy as graphical widget manipulations, the whole process is time consuming and laborious. This working mode is convenient for the rapid prototyping but, as soon as we have to work on real projects, with relatively large scope and duration, it doesn't meet anymore the team's goals and wishes. In such a case, the preferred solution is IaC.
IaC essentially consists in using a declarative notation in order to specify infrastructure resources. In the case of AWS, this notation, expressed as a formalism based on a JSON or YAML syntax, is captured in configuration files and submitted to the CloudFormation IaC utility.
CloudFormation is a vast topic that couldn't be detailed in a blog ticket. The important point that we need to retain here is that, this service is able to process input configuration files and to guarantee the creation and the update of the associated AWS cloud infrastructure resources. While the benefits of the CloudFormation IaC approach are obvious, this tool has the reputation of being verbose, unwieldy and inflexible. Fortunatelly, AWS Lambda developers have the choice of using SAM, a superset of CloudFormation which includes some special commands and shortcuts aiming at easing the development, testing and deployment of the Java serverless code.
Installing SAM
Installing SAM is very simple, one only has to follow the guide here. For example, installing it on Ubuntu 22.04 LTS is as simple as shown below:
Creating AWS Lambda Functions in Java with SAM
Now that SAM is installed on your workstation, you can write and deploy your first Java serverless function. Of course, we assume here that your AWS account has been created and that your environment is configured such that you can run AWS CLI commands, as explained here.
Like CloudFormation, SAM is based on the notion of template which is a YAML formatted text file that describes an AWS infrastructure. This template file, which name by default is template.yaml
, has to be authored manually, such that to be aligned with the SAM template anatomy, which complete specifications can be found here. But writing a template.yaml
file from scratch is difficult. Hence the idea of automatically generating it. Enters CookieCutter.
CookieCutter is an open source project allowing the automatic code generation. It is very used in the Python world but, here, we'll use it in the Java world. Its modus operandi is very similar to the one of the Maven archetypes, in the sense that it is able to automatically generate full Java projects, including but not limited to packages, classes, configuration files, etc. The generation process is highly customizable and is able to replace string occurrences, flagged by placeholders expressed in a dedicated syntax, by values defined in an external JSON formatted file.
This GitHub repository provides such a CookieCutter based generator able to generate a simple but complete Java project, ready to be deployed as an AWS Lambda serverless function. The listing below shows how:
The command sam init
above mentions the location of the CookieCutter based template used to generate a new Java project. This generation process takes the form of a dialog where the utility is asking questions and accepts answers. Each question has default responses and, in order to accept them, the user just need to type Enter.
Everything starts by asking the project name and we chose aws-lambda-simple
. Further information to be entered are:
the AWS resource name;
the Maven GAV (GroupId, ArtifactId, Version);
the Java package name;
the Java class name;
the processor architecture;
the timeout value;
the tracing profile.
As soon as the command terminates, you may open the new project in your preferred IDE and inspect the generated code. Once finished, you may proceed with a first build, as follows:
Our new Java project has been built and packaged as a JAR (Java ARchive). The generated template.yaml
file defines the required AWS cloud infrastructure, as shown below:
This file has been created based on the value entered during the generation process. Things like the AWS template version and the transformation version are constants and should be use as such. All the other elements are known as they mirror the input data. A special consideration have to be given to the CodeUri
element which specifies the location of the JAR to be deployed as the Lambda function. It contains the class AwsLambdaSimple
below:
A Lambda function in Java can be ran in the following two modes:
a synchronous or
RequestResponse
mode in which the caller waits for whatever response the Lambda function returns;an asynchronous or
Event
mode in which the caller is responded without waiting, by the Lambda platform itself, while the function proceeds with the request processing, without returning any further response.
In both cases, the method handleRequest() above is processing the request, as its name implies. This request is an event implemented as a Map<String, String>
. If you need more details and explanations concerning the Lambda functions invocation, please don't hesitate to read my book (in French).
All right, now our new Java project is generated and, while the class AwsLambdaSimple,
presented above, which will be deployed in fine as an AWS Lambda function, doesn't do much, it is sufficiently complete in order to demonstrate our use case. So let's deploy our cloud infrastructure. But first, we need to create an AWS S3 bucket in order to store in it our Lambda function. The simplest way to do that is shown below:
Here we just created an S3 bucket having the name of bucket-18468
. The AWS S3 buckets are constrained to have a unique name across regions. And since it's difficult to guarantee the uniqueness of a name, we use here the Linux $$
function which generates a random number.
Now we can deploy our SAM template as follows:
Our Java class has been successfully deployed as an AWS Lambda function, let's test it using the two invocation methods presented above.
The listing above demonstrates the synchronous or RequestResponse invocation. We pass a JSON formatted payload as the input event and, since its default format is base64, we need to convert it first. Since the invocation is synchronous, the caller waits for the response which is captured in the file outputfile.txt
. The returned status code is HTTP 200, as expected, meaning that the request has been correctly processed. Let's see now the asynchronous or Event invocation.
This time the --invocation-type
is Event
and, consequently, the returned status code is HTTP 202, meaning that the request has been accepted, but not yet processed. The file output.txt
is empty, as there is no result.
This concludes our use case showing the AWS Lambda functions deployment in Java via the SAM tool. Don't forget to clean-up your environment before leaving by running:
Enjoy !
Last updated