AWS Lambda function handler in Java - AWS Lambda

AWS Lambda function handler in Java

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.

The GitHub repo for this guide provides easy-to-deploy sample applications that demonstrate a variety of handler types. For details, see the end of this topic.

Example handler: Java 17 runtimes

In the following Java 17 example, a class named HandlerIntegerJava17 defines a handler method named handleRequest. The handler method takes in the following inputs:

  • An IntegerRecord, which is a custom Java record that represents event data. In this example, we define IntegerRecord as follows:

    record IntegerRecord(int x, int y, String message) { }
  • A context object, which provides methods and properties that provide information about the invocation, function, and execution environment.

Suppose we want to write a function that logs the message from the input IntegerRecord, and returns the sum of x and y. The following is the function code:

Example HandlerIntegerJava17.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; // Handler value: example.HandlerInteger public class HandlerIntegerJava17 implements RequestHandler<IntegerRecord, Integer>{ @Override /* * Takes in an InputRecord, which contains two integers and a String. * Logs the String, then returns the sum of the two Integers. */ public Integer handleRequest(IntegerRecord event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("String found: " + event.message()); return event.x() + event.y(); } } record IntegerRecord(int x, int y, String message) { }

You specify which method you want Lambda to invoke by setting the handler parameter on your function's configuration. You can express the hander in the following formats:

  • package.Class::method – Full format. For example: example.Handler::handleRequest.

  • package.Class – Abbreviated format for classes that implement a handler interface. For example: example.Handler.

When Lambda invokes your handler, the Lambda runtime receives an event as a JSON-formatted string and converts it into an object. For the previous example, a sample event might look like the following:

Example event.json
{ "x": 1, "y": 20, "message": "Hello World!" }

You can save this file and test your function locally with the following AWS Command Line Interface (CLI) command:

aws lambda invoke --function-name function_name --payload file://event.json out.json

Example handler: Java 11 runtimes and below

Lambda supports records in Java 17 and later runtimes. In all Java runtimes, you can use a class to represent event data. The following example takes a list of integers and a context object as input, and returns the sum of all integers in the list.

Example Handler.java

In the following example, a class named Handler defines a handler method named handleRequest. The handler method takes an event and context object as input and returns a string.

Example HandlerList.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.List; // Handler value: example.HandlerList public class HandlerList implements RequestHandler<List<Integer>, Integer>{ @Override /* * Takes a list of Integers and returns its sum. */ public Integer handleRequest(List<Integer> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("EVENT TYPE: " + event.getClass().toString()); return event.stream().mapToInt(Integer::intValue).sum(); } }

For more examples, see Sample handler code.

Initialization code

Lambda runs your static code and the class constructor during the initialization phase before invoking your function for the first time. Resources created during initialization stay in memory between invocations and can be reused by the handler thousands of times. Thus, you can add initialization code outside of your main handler method to save compute time and reuse resources across multiple invocations.

In the following example, the client initialization code is outside the main handler method. The runtime initializes the client before the function serves its first event. Subsequent events are much faster because Lambda doesn't need to initialize the client again.

Example Handler.java
package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.util.Map; import software.amazon.awssdk.services.lambda.LambdaClient; import software.amazon.awssdk.services.lambda.model.GetAccountSettingsResponse; import software.amazon.awssdk.services.lambda.model.LambdaException; // Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String> { private static final LambdaClient lambdaClient = LambdaClient.builder().build(); @Override public String handleRequest(Map<String,String> event, Context context) { LambdaLogger logger = context.getLogger(); logger.log("Handler invoked"); GetAccountSettingsResponse response = null; try { response = lambdaClient.getAccountSettings(); } catch(LambdaException e) { logger.log(e.getMessage()); } return response != null ? "Total code size for your account is " + response.accountLimit().totalCodeSize() + " bytes" : "Error"; } }

Choosing input and output types

You specify the type of object that the event maps to in the handler method's signature. In the preceding example, the Java runtime deserializes the event into a type that implements the Map<String,String> interface. String-to-string maps work for flat events like the following:

Example Event.json – Weather data
{ "temperatureK": 281, "windKmh": -3, "humidityPct": 0.55, "pressureHPa": 1020 }

However, the value of each field must be a string or number. If the event includes a field that has an object as a value, the runtime can't deserialize it and returns an error.

Choose an input type that works with the event data that your function processes. You can use a basic type, a generic type, or a well-defined type.

Input types
  • Integer, Long, Double, etc. – The event is a number with no additional formatting—for example, 3.5. The runtime converts the value into an object of the specified type.

  • String – The event is a JSON string, including quotes—for example, "My string.". The runtime converts the value (without quotes) into a String object.

  • Type, Map<String,Type> etc. – The event is a JSON object. The runtime deserializes it into an object of the specified type or interface.

  • List<Integer>, List<String>, List<Object>, etc. – The event is a JSON array. The runtime deserializes it into an object of the specified type or interface.

  • InputStream – The event is any JSON type. The runtime passes a byte stream of the document to the handler without modification. You deserialize the input and write output to an output stream.

  • Library type – For events sent by AWS services, use the types in the aws-lambda-java-events library.

If you define your own input type, it should be a deserializable, mutable plain old Java object (POJO), with a default constructor and properties for each field in the event. Keys in the event that don't map to a property as well as properties that aren't included in the event are dropped without error.

The output type can be an object or void. The runtime serializes return values into text. If the output is an object with fields, the runtime serializes it into a JSON document. If it's a type that wraps a primitive value, the runtime returns a text representation of that value.

Handler interfaces

The aws-lambda-java-core library defines two interfaces for handler methods. Use the provided interfaces to simplify handler configuration and validate the handler 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. When you use this interface, the Java runtime deserializes the event into an 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.

Example Handler.java – Handler interface
// Handler value: example.Handler public class Handler implements RequestHandler<Map<String,String>, String>{ @Override public String handleRequest(Map<String,String> event, Context context)

To use your own serialization, 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.

The following example uses buffered reader and writer types to work with the input and output streams.

Example HandlerStream.java
import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.LambdaLogger import com.amazonaws.services.lambda.runtime.RequestStreamHandler ... // Handler value: example.HandlerStream public class HandlerStream implements RequestStreamHandler { @Override /* * Takes an InputStream and an OutputStream. Reads from the InputStream, * and copies all characters to the OutputStream. */ public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { LambdaLogger logger = context.getLogger(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("US-ASCII"))); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, Charset.forName("US-ASCII")))); int nextChar; try { while ((nextChar = reader.read()) != -1) { outputStream.write(nextChar); } } catch (IOException e) { e.printStackTrace(); } finally { reader.close(); String finalString = writer.toString(); logger.log("Final string result: " + finalString); writer.close(); } } }

Sample handler code

The GitHub repository for this guide includes sample applications that demonstrate the use of various handler types and interfaces. Each sample application includes scripts for easy deployment and cleanup, an AWS SAM template, and supporting resources.

Sample Lambda applications in Java
  • java17-examples – A Java function that demonstrates how to use a Java record to represent an input event data object.

  • java-basic – A collection of minimal Java functions with unit tests and variable logging configuration.

  • java-events – A collection of Java functions that contain skeleton code for how to handle events from various services such as Amazon API Gateway, Amazon SQS, and Amazon Kinesis. These functions use the latest version of the aws-lambda-java-events library (3.0.0 and newer). These examples do not require the AWS SDK as a dependency.

  • s3-java – A Java function that processes notification events from Amazon S3 and uses the Java Class Library (JCL) to create thumbnails from uploaded image files.

  • Use API Gateway to invoke a Lambda function – A Java function that scans a Amazon DynamoDB table that contains employee information. It then uses Amazon Simple Notification Service to send a text message to employees celebrating their work anniversaries. This example uses API Gateway to invoke the function.

The java-events and s3-java applications take an AWS service event as input and return a string. The java-basic application includes several types of handlers:

To test different handler types, just change the handler value in the AWS SAM template. For detailed instructions, see the sample application's readme file.