Java Lambda 関数を使用した「ClassNotFoundException」エラーのトラブルシューティング方法を教えてください。

最終更新日: 2020 年 2 月 28 日

Java で記述された AWS Lambda 関数を実行すると、「ClassNotFoundException」または「NoSuchMethodError」エラーが発生します。これらのエラーを解決する方法を教えてください。

簡単な説明

Java ランタイムは、完全修飾名 (クラス名を持つパッケージ) でクラスのロードを試みた時にクラスを見つけることができないと、「ClassNotFoundException」エラーを生成します。参照される依存関係バージョンがパッケージ化されたバージョンと異なる場合、ランタイムは「NoSuchMethodError」エラーを生成します。

Java 関数デプロイパッケージの構造については、Java 関数の ZIP デプロイパッケージの作成を参照してください。

解決方法

Java Lambda 関数のデプロイパッケージの構造と内容を確認して、エラーの原因を特定します。ベストプラクティスとして、zipinfo を使用してビルドシステムの出力 Java ファイルを表示します。または、次のいずれかを実行してパッケージをダウンロードできます。

  • AWS コマンドラインインターフェイス (AWS CLI) から Lambda get-function コマンドを実行します。コマンド出力には、ファイルのダウンロードに使用できる署名付き URL が表示されます。詳細については、Lambda 関数の取得を参照してください。
    注意: このオプションを使用する場合、出力で関数ハンドラー値の設定も確認します。この値は、次の手順で必要になります。
  • Lambda コンソールの関数ページで関数を選択します。アクション関数をエクスポートの順に選択します。関数をエクスポートダイアログボックスで、デプロイパッケージをダウンロードを選択します。

次の問題のいずれかを探します。問題を特定して修正したら、Lambda 関数を手動でバンドルしてアップロードします。次に、引き続きエラーが発生するかどうかを確認します。

関数ハンドラーメソッド名を確認

次のいずれかを実行して、Lambda 関数ハンドラーの名前を確認します。

詳細については、Java での AWS Lambda 関数ハンドラーを参照してください。

デプロイパッケージ構造を表示

次のコマンドを実行して、Lambda 関数デプロイパッケージのファイル構造を表示します。

注意: これらのコマンドは Linux/Unix/macOS の各システムにのみ有効です。my-deployment-package.zip をデプロイパッケージのファイル名に置き換えます。

$ zipinfo my-deployment-package.zip

CI/CD パイプラインを使用するときの問題を確認

継続的インテグレーションと継続的デリバリー (CI/CD) パイプラインを使用して関数をパッケージ化してデプロイする場合は、次を確認します。

  • 必要な依存関係はすべて、関数をパッケージ化する時にバンドルされています。
  • 参照される依存関係のバージョンはすべて正しいです。
  • Amazon Simple Storage Service (Amazon S3) バケットソースを使用し、バケットのバージョニングが有効になっている場合、S3 バケット URL が存在し、ファイルの最新バージョンを指します。
  • ハンドラー設定の変更をデプロイする前に、コードの変更がデプロイされました。(Lambda には、1 つのアトミック変更でコードと設定を更新するメカニズムはありません)。

クラスファイルの問題を確認

「ClassNotFoundException」エラーという名前のクラスについて、次を確認します。

  • デプロイパッケージに含まれています。クラスが見つからない場合は、デプロイパッケージの作成時にバンドルされていない可能性があります。
  • バンドルされたクラス名は、関数のハンドラー値と同じです。たとえば、関数ハンドラー値が com.amazonaws.lambda.demo.LambdaFunctionHandler の場合、Lambda は関数ディレクトリ「/com/amazonaws/demo/」で「LambdaFunctionHandler.class」のクラス名を想定します。詳細については、Java での AWS Lambda 関数ハンドラーを参照してください。
  • これは、/lib またはルートディレクトリにあります。
  • 場所は Java CLASSPATH 環境変数で指定されます。
  • Lambda レイヤーとして参照されている場合、その内容は java/lib, 以外のディレクトリに抽出されていません。
  • 関数にパッケージ化されたクラスと同じバージョンです。そうでない場合は、ローカルマシンにパッケージしたバージョンとは異なるバージョンがあるかどうかを確認します。

Java 関数の詳細なパッケージ化については、Java の AWS Lambda デプロイパッケージを参照してください。

JAR ファイルの問題を確認

関数がローカルマシンで、または AWS サーバーレスアプリケーションモデル (AWS SAM) アプリケーションで、正常に実行されるかどうかを確認します。Lambda から呼び出された時にのみ関数が失敗する場合は、参照される依存関係 (JAR ファイル) に問題がある可能性があります。

ヒント: Eclipse 統合開発環境 (IDE) を使用して Java Lambda 関数を構築することを検討してください。Eclipse で使用可能なプラグインを使ってプロジェクトを作成すると、自動的にプロジェクトが適切なビルド用に設定されます。詳細については、AWS Toolkit for Eclipse で Lambda を使用するを参照してください。

ローカルディレクトリに存在し、Java CLASSPATH 環境変数で指定されている JAR ファイルについて、次を確認します。

  • ファイルは関数のデプロイパッケージに含まれています。参照されている JAR ファイルが見つからない場合は、デプロイパッケージの作成時にバンドルされていない可能性があります。
  • ファイルバージョンは、デプロイパッケージ内のファイルと同じです。

ファイルが見つからないか、バージョンが正しくない場合は、すべての依存関係 (JAR ファイル) を /lib またはルートディレクトリにコピーします。必ず正しいバージョンを参照してください。次に、圧縮したコンテンツをアップロードします。

注意: Apache Maven や Gradle などのビルドツールを使用している場合は、デプロイアーティファクトのビルド時に必要なすべての依存関係をバンドルするために必要なプラグイン (Apache Maven Shade プラグインなど) を使用してください。

Java 関数の詳細なパッケージ化については、Java の AWS Lambda デプロイパッケージを参照してください。

権限問題を確認

Lambda では、zip パッケージファイルにグローバルな読み取り権限が付与される必要があります。詳細については、デプロイパッケージをアップロードするときに Lambda で発生する「権限が拒否されました」または「モジュールをインポートできません」というエラーを解決するには、どうすれば良いですか? を参照してください。