AWS Lambda Handler with Java for AWS API Gateway
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 :
SIZE | SI | BINARY |
---|---|---|
0 | 0 B | 0 B |
27 | 27 B | 27 B |
999 | 999 B | 999 B |
1000 | 1.0 kB | 1000 B |
1023 | 1.0 kB | 1023 B |
1024 | 1.0 kB | 1.0 KiB |
1728 | 1.7 kB | 1.7 KiB |
110592 | 110.6 kB | 108.0 KiB |
7077888 | 7.1 MB | 6.8 MiB |
452984832 | 453.0 MB | 432.0 MiB |
28991029248 | 29.0 GB | 27.0 GiB |
1855425871872 | 1.9 TB | 1.7 TiB |
Create Maven project
Create maven project using Intellij or Eclipse.
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:
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.
After that, create an REST API gateway with this Tutorial, Please make sure you have created a resource and HTTP method POST
.
Once you've done that, deploy your API and test it with Postman or another tool of your choice.
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!