codeWithYoha logo
Code with Yoha
AWS Lambda

AWS Lambda Handler with Java for AWS API Gateway

AWS Lambda Handler with Java for AWS API Gateway
7 min read
#AWS Lambda

With AWS, Lambda provides server computation with no infrastructure hassle. It also provides the runtime for many programming languages like Java, Python, Javascript, Golang, etc...

Today, we will see how to create a Lambda function with the Java triggered by an API Gateway throught an endpoint.

Prerequisites

To Continue this tutorial, there are a few prerequisites that you need to fulfill:

  • An AWS account to deploy Lambda function.
  • JDK 17 and Maven 3.5 or higher
  • Java Editor (Intellij or Eclipse)

Idea to build

We want to build an app that exposes an endpoint where the user can send the file size in bytes and type (SI or BINARY), and the function will convert the size to a human readable format and return the result (String format). The endpoint will accept POST requests.
The table below contains size of file and the result after convert the size to a human readable format :

SIZESIBINARY
00 B0 B
2727 B27 B
999999 B999 B
10001.0 kB1000 B
10231.0 kB1023 B
10241.0 kB1.0 KiB
17281.7 kB1.7 KiB
110592110.6 kB108.0 KiB
70778887.1 MB6.8 MiB
452984832453.0 MB432.0 MiB
2899102924829.0 GB27.0 GiB
18554258718721.9 TB1.7 TiB

Create Maven project

Create maven project using Intellij or Eclipse.

maven project for lambda api gateway image

Add the packages needed for the project in our case we will have handler, service and enums packages. The maven project structure will look like the screenshot below:

maven project for lambda api gateway created structure

Open the pom.xml file and replace the content with the code below:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.codewithyaho.lambda</groupId>
    <artifactId>lambda-readable-file-size-format</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <maven-assembly.version>3.3.0</maven-assembly.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>${maven-assembly.version}</version>
                <configuration>
                    <!-- get all project dependencies -->
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>com.codewithyaho.lambda.handler.FileSizeHandler</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <!-- bind to the packaging phase -->
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Install the Lambda package for Java

lambda-readable-file-size-format is just a classic Java maven project, In order to make it act as a Lambda Function, we need to install the Java package for AWS Lambda. Let's update the pom.xml to add the dependency:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.2.2</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-events</artifactId>
    <version>3.11.1</version>
</dependency>

Here are the links to the Maven repository:

Update the Maven dependencies with following command:

mvn dependency:resolve

Lambda function logic

We will need a library to parse the request body from a string to an object; we will use GSON Library. Update the pom.xml to add these dependencies:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

Update the Maven dependencies:

mvn dependency:resolve

Let's create an enum called FileSizeTypeEnum under the package com.codewithyaho.lambda.enums that will contain the type of size:

public enum FileSizeTypeEnum {
    SI, BINARY
}

Let's create a file called RequestInput.java under the package com.codewithyaho.lambda.model that will contain the request body:

import com.codewithyaho.lambda.enums.FileSizeTypeEnum;

public class RequestInput {
    private FileSizeTypeEnum type;
    private long size;

    public RequestInput() {
    }

    public RequestInput(FileSizeTypeEnum type, long size) {
        this.type = type;
        this.size = size;
    }

    public FileSizeTypeEnum getType() {
        return type;
    }

    public void setType(FileSizeTypeEnum type) {
        this.type = type;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }
}

Let's now create a service FileSizeFormatService.java under the package com.codewithyaho.lambda.service to convert the file size by type to a human readable format.

package com.codewithyaho.lambda.service;

import com.codewithyaho.lambda.model.RequestInput;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;

public class FileSizeFormatService {

    private FileSizeFormatService() {
    }

    public static String convertFileSizeToReadableFormat(final RequestInput requestInput) {
        return switch (requestInput.getType()) {
            case SI -> humanReadableByteCountSI(requestInput.getSize());
            case BINARY -> humanReadableByteCountBinary(requestInput.getSize());
            default -> throw new Error("FileSizeFormatService:: Unsupported file size type :::" + requestInput.getSize());
        };
    }


    public static String humanReadableByteCountSI(long bytes) {
        if (-1000 < bytes && bytes < 1000) {
            return bytes + " B";
        }
        CharacterIterator ci = new StringCharacterIterator("kMGTPE");
        while (bytes <= -999_950 || bytes >= 999_950) {
            bytes /= 1000;
            ci.next();
        }
        return String.format("%.1f %cB", bytes / 1000.0, ci.current());
    }

    public static String humanReadableByteCountBinary(long bytes) {
        long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
        if (absB < 1024) {
            return bytes + " B";
        }
        long value = absB;
        CharacterIterator ci = new StringCharacterIterator("KMGTPE");
        for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
            value >>= 10;
            ci.next();
        }
        value *= Long.signum(bytes);
        return String.format("%.1f %ciB", value / 1024.0, ci.current());
    }
}

Let's now create the handler FileSizeHandler.java under the package com.codewithyaho.lambda.handler which will be the entry point of the AWS Lambda:

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.codewithyaho.lambda.model.RequestInput;
import com.codewithyaho.lambda.service.FileSizeFormatService;
import com.google.gson.Gson;

import java.util.HashMap;
import java.util.Map;

public class FileSizeHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) {
        final Gson gson = new Gson();
        LambdaLogger logger = context.getLogger();
        logger.log("FileSizeHandler:: start lambda");
        String request = gson.toJson(apiGatewayProxyRequestEvent);
        logger.log("FileSizeHandler:: received request : " + request);

        String httpMethod = apiGatewayProxyRequestEvent.getHttpMethod();
        String requestEventBody = apiGatewayProxyRequestEvent.getBody();
        return switch (httpMethod) {
            case "POST" -> {
                RequestInput requestInput = gson.fromJson(requestEventBody, RequestInput.class);
                yield createAPIResponse(FileSizeFormatService.convertFileSizeToReadableFormat(requestInput), 200);
            }
            default -> throw new Error("FileSizeHandler:: Unsupported Methods:::" + httpMethod);
        };
    }

    private static APIGatewayProxyResponseEvent createAPIResponse(String body, int statusCode) {
        APIGatewayProxyResponseEvent responseEvent = new APIGatewayProxyResponseEvent();
        responseEvent.setBody(body);
        responseEvent.setHeaders(createHeaders());
        responseEvent.setStatusCode(statusCode);
        return responseEvent;
    }

    private static Map<String, String> createHeaders() {
        Map<String, String> headers = new HashMap();
        headers.put("Content-Type", "application/json");
        headers.put("Access-Control-Allow-Headers", "*");
        headers.put("Access-Control-Allow-Origin", "*");
        headers.put("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
        return headers;
    }
}

NB: createAPIResponse and createHeaders() are utils methods, the first one is to create APIGatewayProxyResponseEvent based on the body and statusCode parameters. The second method is used to create headers in order to avoid CORS issue.

Creates JAR file for the project to convert it into a distributable format with the command below:

mvn clean package

Create & Deploy AWS Lambda Function

Create AWS Lambda function with this Tutorial, You can then adjust the settings and configuration to fit your needs.
Next, Upload the jar generated and adjust the lambda handler.

AWS Lambda deploy and update handler

After that, create an REST API gateway with this Tutorial, Please make sure you have created a resource and HTTP method POST.

AWS Api gateway create method with lambda integration

Once you've done that, deploy your API and test it with Postman or another tool of your choice.

Test With Postman

Here's the GitHub repo for the AWS lambda handler : Github Repo

That's all for this tutorial, I hope you enjoyed it, and until our next tutorial, take care.
Thank you!