Define Lambda function handler in Java (original) (raw)

The Lambda function handler is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method. Your function runs until the handler returns a response, exits, or times out.

This page describes how to work with Lambda function handlers in Java, including options for project setup, naming conventions, and best practices. This page also includes an example of a Java Lambda function that takes in information about an order, produces a text file receipt, and puts this file in an Amazon Simple Storage Service (Amazon S3) bucket. For information about how to deploy your function after writing it, seeDeploy Java Lambda functions with .zip or JAR file archives or Deploy Java Lambda functions with container images.

Sections

Setting up your Java handler project

When working with Lambda functions in Java, the process involves writing your code, compiling it, and deploying the compiled artifacts to Lambda. You can initialize a Java Lambda project in various ways. For instance, you can use tools like theMaven Archetype for Lambda functions, the AWS SAM CLI sam init command, or even a standard Java project setup in your preferred IDE, such as IntelliJ IDEA or Visual Studio Code. Alternatively, you can create the required file structure manually.

A typical Java Lambda function project follows this general structure:

/project-root
    └ src
        └ main
            └ java
                └ example
                    └ OrderHandler.java (contains main handler)
                    └ <other_supporting_classes>
     └ build.gradle OR pom.xml

You can use either Maven or Gradle to build your project and manage dependencies.

The main handler logic for your function resides in a Java file under thesrc/main/java/example directory. In the example on this page, we name this fileOrderHandler.java. Apart from this file, you can include additional Java classes as needed. When deploying your function to Lambda, make sure you specify the Java class that contains the main handler method that Lambda should invoke during an invocation.

Example Java Lambda function code

The following example Java 21 Lambda function code takes in information about an order, produces a text file receipt, and puts this file in an Amazon S3 bucket.

Example OrderHandler.java Lambda function
package example;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;

import java.nio.charset.StandardCharsets;

/**
 * Lambda handler for processing orders and storing receipts in S3.
 */
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {

    private static final S3Client S3_CLIENT = S3Client.builder().build();

    /**
     * Record to model the input event.
     */
    public record Order(String orderId, double amount, String item) {}

    @Override
    public String handleRequest(Order event, Context context) {
        try {
            // Access environment variables
            String bucketName = System.getenv("RECEIPT_BUCKET");
            if (bucketName == null || bucketName.isEmpty()) {
                throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
            }

            // Create the receipt content and key destination
            String receiptContent = String.format("OrderID: %s\nAmount: $%.2f\nItem: %s",
                    event.orderId(), event.amount(), event.item());
            String key = "receipts/" + event.orderId() + ".txt";

            // Upload the receipt to S3
            uploadReceiptToS3(bucketName, key, receiptContent);

            context.getLogger().log("Successfully processed order " + event.orderId() +
                    " and stored receipt in S3 bucket " + bucketName);
            return "Success";

        } catch (Exception e) {
            context.getLogger().log("Failed to process order: " + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private void uploadReceiptToS3(String bucketName, String key, String receiptContent) {
        try {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .build();

            // Convert the receipt content to bytes and upload to S3
            S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
        } catch (S3Exception e) {
            throw new RuntimeException("Failed to upload receipt to S3: " + e.awsErrorDetails().errorMessage(), e);
        }
    }
}

This OrderHandler.java file contains the following sections of code:

The following build.gradle or pom.xml file accompanies this function.

build.gradle

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.3'
    implementation 'software.amazon.awssdk:s3:2.28.29'
    implementation 'org.slf4j:slf4j-nop:2.0.16'
}

task buildZip(type: Zip) {
    from compileJava
    from processResources
    into('lib') {
        from configurations.runtimeClasspath
    }
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

build.dependsOn buildZip

pom.xml

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>example-java</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>example-java-function</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.28.29</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>2.0.16</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.2</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.4.1</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                            <excludes>
                                <exclude>META-INF/*</exclude>
                                <exclude>META-INF/versions/**</exclude>
                            </excludes>
                        </filter>
                    </filters>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <release>21</release>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

For this function to work properly, its execution role must allow the s3:PutObject action. Also, ensure that you define the RECEIPT_BUCKET environment variable. After a successful invocation, the Amazon S3 bucket should contain a receipt file.

Note

This function may require additional configuration settings to run successfully without timing out. We recommend configuring 256 MB of memory, and a 10 second timeout. The first invocation may take extra time due to a cold start. Subsequent invocations should run much faster due to reuse of the execution environment.

Valid class definitions for Java handlers

To define your class, the aws-lambda-java-core library defines two interfaces for handler methods. Use the provided interfaces to simplify handler configuration and validate the method signature at compile time.

The RequestHandler interface is a generic type that takes two parameters: the input type and the output type. Both types must be objects. In this example, ourOrderHandler class implements RequestHandler<OrderHandler.Order, String>. The input type is the Order record we define within the class, and the output type is String.

public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
    ...
}

When you use this interface, the Java runtime deserializes the event into the object with the input type, and serializes the output into text. Use this interface when the built-in serialization works with your input and output types.

To use your own serialization, you can implement the RequestStreamHandler interface. With this interface, Lambda passes your handler an input stream and output stream. The handler reads bytes from the input stream, writes to the output stream, and returns void. For an example of this using the Java 21 runtime, see HandlerStream.java.

If you’re working only with basic and generic types (i.e. String, Integer,=List, or Map) in your Java function , you don’t need to implement an interface. For example, if your function takes in a Map<String, String> input and returns aString, your class definition and handler signature may look like the following:

public class ExampleHandler {
    public String handleRequest(Map<String, String> input, Context context) {
        ...
    }
}

In addition, when you don’t implement an interface, thecontext object is optional. For example, your class definition and handler signature may look like the following:

public class NoContextHandler {
   public String handleRequest(Map<String, String> input) {
        ...
   }
}

Handler naming conventions

For Lambda functions in Java, if you are implementing either the RequestHandler orRequestStreamHandler interface, your main handler method must be namedhandleRequest. Also, include the @Override tag above yourhandleRequest method. When you deploy your function to Lambda, specify the main handler in your function’s configuration in the following format:

For Lambda functions in Java that don’t implement the RequestHandler orRequestStreamHandler interface, you can use any name for the handler. When you deploy your function to Lambda, specify the main handler in your function’s configuration in the following format:

Defining and accessing the input event object

JSON is the most common and standard input format for Lambda functions. In this example, the function expects an input similar to the following:

{
    "orderId": "12345",
    "amount": 199.99,
    "item": "Wireless Headphones"
}

When working with Lambda functions in Java 17 or newer, you can define the shape of the expected input event as a Java record. In this example, we define a record within the OrderHandler class to represent an Order object:

public record Order(String orderId, double amount, String item) {}

This record matches the expected input shape. After you define your record, you can write a handler signature that takes in a JSON input that conforms to the record definition. The Java runtime automatically deserializes this JSON into a Java object. You can then access the fields of the object. For example, event.orderId retrieves the value of orderId from the original input.

Note

Java records are a feature of Java 17 runtimes and newer only. In all Java runtimes, you can use a class to represent event data. In such cases, you can use a library likejackson to deserialize JSON inputs.

Other input event types

There are many possible input events for Lambda functions in Java:

Accessing and using the Lambda context object

The Lambda context object contains information about the invocation, function, and execution environment. In this example, the context object is of typecom.amazonaws.services.lambda.runtime.Context, and is the second argument of the main handler function.

public String handleRequest(Order event, Context context) {
    ...
}

If your class implements either the RequestHandler or RequestStreamHandler interface, the context object is a required argument. Otherwise, the context object is optional. For more information about valid accepted handler signatures, seeValid class definitions for Java handlers.

If you make calls to other services using the AWS SDK, the context object is required in a few key areas. For example, to produce function logs for Amazon CloudWatch, you can use thecontext.getLogger() method to get a LambdaLogger object for logging. In this example, we can use the logger to log an error message if processing fails for any reason:

context.getLogger().log("Failed to process order: " + e.getMessage());

Outside of logging, you can also use the context object for function monitoring. For more information about the context object, see Using the Lambda context object to retrieve Java function information.

Using the AWS SDK for Java v2 in your handler

Often, you’ll use Lambda functions to interact with or make updates to other AWS resources. The simplest way to interface with these resources is to use the AWS SDK for Java v2.

Note

The AWS SDK for Java (v1) is in maintenance mode, and will reach end-of-support on December 31, 2025. We recommend that you use only the AWS SDK for Java v2 going forward.

To add SDK dependencies to your function, add them in your build.gradle for Gradle or pom.xml file for Maven. We recommend only adding the libraries that you need for your function. In the example code earlier, we used thesoftware.amazon.awssdk.services.s3 library. In Gradle, you can add this dependency by adding the following line in the dependencies section of your build.gradle:

implementation 'software.amazon.awssdk:s3:2.28.29'

In Maven, add the following lines in the <dependencies> section of yourpom.xml:

    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
        <version>2.28.29</version>
    </dependency>
Note

This may not be the most recent version of the SDK. Choose the appropriate version of the SDK for your application.

Then, import the dependencies directly in your Java class:

import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;

The example code then initializes an Amazon S3 client as follows:

private static final S3Client S3_CLIENT = S3Client.builder().build();

In this example, we initialized our Amazon S3 client outside of the main handler function to avoid having to initialize it every time we invoke our function. After you initialize your SDK client, you can then use it to interact with other AWS services. The example code calls the Amazon S3 PutObject API as follows:

PutObjectRequest putObjectRequest = PutObjectRequest.builder()
    .bucket(bucketName)
    .key(key)
    .build();

// Convert the receipt content to bytes and upload to S3
S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));

Accessing environment variables

In your handler code, you can reference any environment variables by using the System.getenv() method. In this example, we reference the defined RECEIPT_BUCKET environment variable using the following line of code:

String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
    throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}

Using global state

Lambda runs your static code and the class constructor during theinitialization phase before invoking your function for the first time. Resources created during initialization stay in memory between invocations, so you can avoid having to create them every time you invoke your function.

In the example code, the S3 client initialization code is outside the main handler method. The runtime initializes the client before the function handles its first event, and the client remains available for reuse across all invocations.

Code best practices for Java Lambda functions

Adhere to the guidelines in the following list to use best coding practices when building your Lambda functions:

public class MyHandler {  
  // first set TTL property  
  static{  
   java.security.Security.setProperty("networkaddress.cache.ttl" , "0");  
  }  
 // then instantiate logger  
  var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class);  
}