So far, I have made AWS Lambda mainly in Python, sometimes Node.js, but for a change I investigated how to develop in Java, so I will summarize it.
This time, I built the source code of the Lambda function written in Java with Gradle and deployed it with AWS SAM. The AWS Toolkit for Eclipse (https://docs.aws.amazon.com/ja_jp/toolkit-for-eclipse/v1/user-guide/welcome.html) is also provided for development in Eclipse, I usually develop with Vim and deploy with CLI, so I will match that.
~~ As you can see in the official documentation below, the only Java runtime provided is Java 8
( JDK 8
).
If you use any other version, you will have to wait until it is supported or use a custom runtime. ~~
Java 11 was added on November 19, 2019, and the runtime can now be selected between Java 8 (JDK 8) and Java 11 (JDK 11).
Building Lambda Functions with Java-AWS Lambda
Package and deploy an application written in Java in a zip file or standalone jar.
The official documentation shows how to deploy to a standalone jar using Maven and a zip file using Gradle. This time, I tried the method of packaging and deploying to a zip file with Gradle.
Java AWS Lambda Deployment Package-AWS Lambda
There are multiple methods for receiving the request of the handler function and returning the response as follows.
How to write the handler function is different for each. In other words, there are three ways to write a handler function.
It seems that it is used properly from the viewpoint of what kind of data is sent, rather than how to decide "If you use this event trigger, this type". There seems to be no such thing as this in this situation, so it is important to consider which method to use when actually developing a Lambda function in Java.
This time, we adopted the "POJO type".
Handler I / O Type (Java) --AWS Lambda
I've created a simple web application that uses ʻAPI Gateway` as a trigger for a Lambda function. It's a simple API that just sends a POST request and returns "Hello".
I put the created code here. https://github.com/mmclsntr/awslambda-javagradle
.
├── build/
│ ├── distributions/
│ │ └── awslambda-javagradle.zip #Deploy package generated by build
│ └── ...
├── build.gradle #Gradle build configuration file
├── src/main/java/awslambda/javagradle
│ ├── Greeting.java #The core part of the app
│ ├── Handler.java #Store Lambda handler function
│ └── Response.java #Format Lambda response
└── template.yml #CloudFormation template file
└── Other files for Gradle
Handler.java
package awslambda.javagradle;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class Handler implements RequestHandler<Map<String, Object>, Response> {
private static final Logger LOG = Logger.getLogger(Handler.class.getName());
@Override
public Response handleRequest(Map<String, Object> input, Context context) {
LOG.info("received: " + input);
LOG.setLevel(Level.INFO);
Greeting greetingBody = new Greeting("Hello");
return Response.builder()
.setStatusCode(200)
.setObjectBody(greetingBody)
.build();
}
}
Response.java
package awslambda.javagradle;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.Map;
import org.apache.log4j.Logger;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Response {
private final int statusCode;
private final String body;
private final Map<String, String> headers;
private final boolean isBase64Encoded;
public Response(int statusCode, String body, Map<String, String> headers, boolean isBase64Encoded) {
this.statusCode = statusCode;
this.body = body;
this.headers = headers;
this.isBase64Encoded = isBase64Encoded;
}
...
}
Create a super simple application that returns Hello as a sample.
Greeting.java
package awslambda.javagradle;
public class Greeting {
private String greetings;
public Greeting(String greetings) {
this.greetings = greetings;
}
public String getGreetings() {
return greetings;
}
public void setGreetings(String greetings) {
this.greetings = greetings;
}
}
template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
AWS Lambda Java with Gradle
Globals:
Function:
Timeout: 20
Resources:
PostGreetingFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: build/distributions/awslambda-javagradle.zip
Handler: awslambda.javagradle.Handler::handleRequest
Runtime: java8
Events:
GetOrder:
Type: Api
Properties:
Path: /
Method: post
Outputs:
ApiEndpoint:
Description: "API Gateway endpoint URL for Prod stage"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
PostGreetingFunction:
Description: "PostGreeting Lambda Function ARN"
Value: !GetAtt PostGreetingFunction.Arn
build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile (
'com.amazonaws:aws-lambda-java-core:1.1.0',
'com.amazonaws:aws-lambda-java-log4j:1.0.0',
'com.amazonaws:aws-lambda-java-events:1.1.0',
'com.fasterxml.jackson.core:jackson-core:2.8.5',
'com.fasterxml.jackson.core:jackson-databind:2.8.5',
'com.fasterxml.jackson.core:jackson-annotations:2.8.5'
)
}
// Task for building the zip file for upload
task buildZip(type: Zip) {
from compileJava
from processResources
into('lib') {
from configurations.runtime
}
}
build.dependsOn buildZip
Build with the Gradle command.
gradle build
aws s3 mb s3://<Deploy to S3 bucket> --aws-profile=<AWS profile>
With sam package
, upload the executable file to the S3 bucket created above & generate a template file for deployment.
sam package \
--s3-bucket <Deploy destination S3 bucket name> \
--s3-prefix <Deployment destination S3 folder name(Prefix)※Any> \
--output-template-file output.yml \
--profile <AWS profile>
As an output, a ʻoutput.yml` file is created.
Deploy Lambda and API Gateway with sam deploy
.
sam deploy \
--template-file output.yml \
--stack-name awslambda-javagradle-greeting \
--capabilities CAPABILITY_IAM \
--profile <AWS profile>
Now that we have a Lambda function and API Gateway on AWS, let's throw a POST request to the endpoint.
request
curl -X POST https://xxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/
response
{"greetings":"Hello"}
I myself don't usually use Java, so it took me a while, but I have the impression that it was easy to make. Of course, the amount of code is larger than writing in a scripting language such as Python, but it is attractive to be able to do rigorous serverless development.
I couldn't grasp the specific specifications for writing in Java, such as multiple types of handler function writing, just this time. When actually using it, I would like to design it in detail while considering that area.
Also, as rumored, the initial startup is slow (about 5 seconds until it responds). Reference: https://cero-t.hatenadiary.jp/entry/20160106/1452090214
I was able to grasp the characteristics and coding sense of developing Lambda functions using Java. If you're migrating from a Servlet, I think it will require a fairly large-scale refurbishment, but I have the impression that it can be used as a substitute.
I'm a little worried about the support there, whether the runtime is just Java 8 as it is. .. I used API Gateway as a trigger, but I would like to try other services as well.
https://qiita.com/kamata1729/items/8d88ea10dd3bb61fa6cc https://qiita.com/riversun/items/7fcc06617b469aed8f27
Recommended Posts