Java + Gradle で Lambda 関数用のデプロイパッケージを作成する

Java は新卒時以降全く使っていなかったのですが、最近 Java で Lambda を作成する時色々ハマったので、作成手順をまとめました。

Java + Gradle で Lambda 関数用のデプロイパッケージを作成する環境

  • macOS: Catalina (10.15.7)
  • JDK: Amazon Corretto 11 (Java 11 JDK)
  • Gradle: 7.4.1

Java + Gradle で Lambda 関数用のデプロイパッケージを作成する手順

作業用ディレクトリの作成とプロジェクトの初期化

作業ディレクトリを作成して、gradle init コマンドを実行します。
gradle init コマンド実行時の各設問は最初の 2 つ以外はデフォルト値にしています。

Copied!
$ mkdir ~/java-sample
$ cd ~/java-sample
$ gradle init
Starting a Gradle Daemon (subsequent builds will be faster)

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

Project name (default: java-sample):

> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples at https://docs.gradle.org/7.4.1/samples

BUILD SUCCESSFUL in 16s
2 actionable tasks: 2 executed

gradle init コマンド実行後、ディレクトリ構成は以下のようになります。

Copied!
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

Lambda 関数ハンドラーの作成

今回は、Lambda のドキュメントに記載されているこちらのサンプルを実装してみます。

まず、build.gradle ファイルを以下のように修正します。
dependencies に今回使用するライブラリを定義します。
また、task でデプロイパッケージ(.zip ファイルアーカイブ)を作成するタスクを定義します。

Copied!
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.code.gson:gson:2.9.0'

    implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
}

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

build.dependsOn buildZip

次に、Lambda ハンドラー関数用の Java ファイルを作成してこちらのサンプルを作成します。

Copied!
mkdir src/main/java/sample
touch src/main/java/sample/App.java
src/main/java/sample/App.java
Copied!
package sample;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.LambdaLogger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.Map;

public class App implements RequestHandler<Map<String, String>, String> {
  Gson gson = new GsonBuilder().setPrettyPrinting().create();

  @Override
  public String handleRequest(Map<String, String> event, Context context) {
    String response = new String("200 OK");
    LambdaLogger logger = context.getLogger();
    // log execution details
    logger.log("ENVIRONMENT VARIABLES: " + gson.toJson(System.getenv()));
    logger.log("CONTEXT: " + gson.toJson(context));
    // process event
    logger.log("EVENT: " + gson.toJson(event));
    logger.log("EVENT TYPE: " + event.getClass().toString());
    return response;
  }
}

デプロイパッケージの作成

gradle build コマンドを実行してデプロイパッケージを作成します。

Copied!
gradle build

build/distributions ディレクトリに zip 形式のデプロイパッケージが作成されます。
作成されたデプロイパッケージの中身は以下のようになっているかと思います。

Copied!
$ unzip -l build/distributions/java-sample.zip
Archive:  build/distributions/java-sample.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  03-21-2022 22:51   sample/
     2686  03-21-2022 22:51   sample/App.class
     1381  03-21-2022 22:51   previous-compilation-data.bin
        0  03-21-2022 22:51   lib/
   249277  03-20-2022 23:20   lib/gson-2.9.0.jar
     7515  03-20-2022 22:45   lib/aws-lambda-java-core-1.2.1.jar
---------                     -------
   260859                     6 files

Lambda 関数の作成と実行

作成したデプロイパッケージを使って AWS マネジメントコンソールから Lambda 関数を作成することもできますが、今回は AWS CLI を使って Lambda 関数の作成をします。

Copied!
aws lambda create-function --function-name java-sample \
--zip-file fileb://build/distributions/java-sample.zip --handler sample.App::handleRequest --runtime java11 \
--role <IAM ロール arn>

Lambda 関数を実行して、エラーが出ず以下のように出力されれば成功です。
また、Lambda 関数の CloudWatch Logs のロググループに payload の値などが出力されているはずです。

Copied!
$ aws lambda invoke --function-name java-sample  \
--payload '{"text":"Hello"}' response.txt --cli-binary-format raw-in-base64-out
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

参考情報