エンジニアのはしがき

プログラミングの日々の知見を書き連ねているブログです

Serverless Framework+AWS Lambda(Java8)でHello worldしようとしたらNoClassDefFoundError

少しずつですがJavaのビルドツール周りも分かりかけてきたなという矢先、エラーで数時間ハマりました…😭

開発環境

  • OS: Windows11 Home 21H2
$ node -v
v16.14.2

$ npm -v
8.5.0

$ sls --version
Framework Core: 2.34.0
Plugin: 4.5.3
SDK: 4.2.2
Components: 3.8.2

$ java --version
openjdk 17.0.2 2022-01-18
OpenJDK Runtime Environment (build 17.0.2+8-86)
OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)

$ aws --version
aws-cli/2.1.19 Python/3.7.9 Windows/10 exe/AMD64 prompt/off

経緯

  • Serverless Frameworkのテンプレートaws-java-mavenを使って、プロジェクトを新規作成。(sls create -t aws-java-maven)
  • pom.xmlに記載のライブラリに脆弱性のあるものが含まれているとIDEに怒られていた為、バージョンを修正。(かの有名なlog4j脆弱性修正前のバージョンがテンプレートとして使われていました。これ普通に危ない…。)
  • mvn install, mvn packageでビルド。
  • sls deployAWS Lambdaへデプロイを実行。結果は成功。
  • ローカルからsls invokeを実行し、Lambdaのテストをしてみたが下記のエラーが発生。
$ sls invoke -f hello
{
    "errorMessage": "com/fasterxml/jackson/core/util/JacksonFeature",
    "errorType": "java.lang.NoClassDefFoundError",
    "stackTrace": [
        "com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:673)",
        "com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:576)",
        "com.serverless.ApiGatewayResponse$Builder.<clinit>(ApiGatewayResponse.java:53)",
        "com.serverless.ApiGatewayResponse.builder(ApiGatewayResponse.java:46)",
        "com.serverless.Handler.handleRequest(Handler.java:20)",
        "com.serverless.Handler.handleRequest(Handler.java:12)"
    ],
    "cause": {
        "errorMessage": "com.fasterxml.jackson.core.util.JacksonFeature",
        "errorType": "java.lang.ClassNotFoundException",
        "stackTrace": [
            "java.net.URLClassLoader.findClass(URLClassLoader.java:387)",
            "java.lang.ClassLoader.loadClass(ClassLoader.java:418)",
            "java.lang.ClassLoader.loadClass(ClassLoader.java:351)",
            "com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:673)",
            "com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:576)",
            "com.serverless.ApiGatewayResponse$Builder.<clinit>(ApiGatewayResponse.java:53)",
            "com.serverless.ApiGatewayResponse.builder(ApiGatewayResponse.java:46)",
            "com.serverless.Handler.handleRequest(Handler.java:20)",
            "com.serverless.Handler.handleRequest(Handler.java:12)"
        ]
    }
}
 ...

エラー発生時点のpom.xml

上述のエラー発生前にlog4j-core, jackson-databind脆弱性があるとIDEから指摘されていた為、推奨のバージョンに修正をしていましたが、どうもこれではうまく動作しませんでした。

...
  <dependencies>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-log4j2</artifactId>
      <version>1.5.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <!-- ↓バージョン変更箇所 -->
      <version>2.17.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.10</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <!-- ↓バージョン変更箇所 -->
      <version>2.13.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.10</version>
    </dependency>
  </dependencies>
...

原因

  • pom.xmlの依存関係にあるライブラリ同士のバージョンに差異があり、実行時にエラーが発生していた。

具体的には以下のように修正をすることで、正常にLambdaが動作しました。

対応

pom.xmlの修正

groupIdが同じライブラリ同士は、バージョンを揃えてあげる必要があったようで、今回はorg.apache.logging.log4j, com.fasterxml.jackson.coreを修正しました。

...
  <dependencies>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-log4j2</artifactId>
      <version>1.5.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.17.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.17.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.13.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.13.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.13.2</version>
    </dependency>
  </dependencies>
...

再度デプロイ

pom.xmlを修正したらもう1度ビルド+デプロイを実行。

$ mvn install
$ mvn package
$ sls deploy

動作のテスト

$ sls invoke -f hello
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.x! Your function executed successfully!\",\"input\":{}}",
    "headers": {
        "X-Powered-By": "AWS Lambda & serverless"
    },
    "isBase64Encoded": false
}

JSONのレスポンスが返ってきました!どうやらLambdaが正しく動作してくれたようです😊

Node.jsでも依存関係で辛いことがたまにありますが、Javaでも注意が必要ですね。