依存関係のバージョンの競合をトラブルシューティングする

この記事では、依存関係のバージョンの競合と、それをトラブルシューティングする方法について説明します。

Java 用 Azure クライアント ライブラリは、次のような一般的なサード パーティ製ライブラリに依存しています。

多くの Java アプリケーションとフレームワークでは、これらのライブラリが直接または一時的に使用されています。その結果、バージョンの競合が発生します。 MavenGradle などの依存関係マネージャーでは、すべての依存関係が解決され、クラスパスには各依存関係のバージョンが 1 つのみ存在します。 ただし、解決された依存関係のバージョンが、アプリケーション内のその依存関係のすべてのコンシューマーと互換性があるという保証はありません。 詳細については、Maven ドキュメントの「依存関係メカニズムの概要」と Gradle ドキュメントの「依存関係の解決について」を参照してください。

直接依存関係の API 非互換性により、コンパイル エラーが発生します。 ひし形の依存関係の非互換性により、通常、NoClassDefFoundErrorNoSuchMethodError、その他の LinkageError などのランタイム エラーが発生します。 すべてのライブラリがセマンティック バージョン管理に厳密に従っているわけではないため、同じメジャー バージョン内で破壊的変更が発生する場合があります。

バージョンの不一致の問題を診断する

次のセクションでは、バージョンの不一致の問題を診断する方法について説明します。

Azure SDK for Java ビルド ツールを使用する

Azure SDK と Apache Maven の概要」で導入された Azure SDK for Java ビルド ツールは、よく発生する問題を特定するのに役立ちます。 このビルド ツールをプロジェクトに追加し、azure:run Maven ターゲットを通常のビルド プロセスに追加して実行することをお勧めします。 適切な構成を使用すると、実行時に問題が発生する前に、依存関係の競合を前もって特定し、解決できます。

依存関係ツリーを表示する

mvn dependency:tree または gradle dependencies --scan を実行して、バージョン番号を含むアプリケーションの完全な依存関係ツリーを表示します。 mvn dependency:tree -Dverbose は詳細情報を提供しますが、分かりにくい場合があります。 詳細については、Maven のドキュメントの「Apache Maven 依存関係ツリー」を参照してください。 バージョンの競合が疑われるライブラリごとに、バージョン番号をメモし、どのコンポーネントがそのライブラリに依存しているかを判断します。

開発環境と実稼働環境では、依存関係の解決は、動作が異なる場合があります。 Apache SparkApache FlinkDatabricks、および IDE プラグインでは、カスタム依存関係用の追加の構成が必要です。 また、独自のバージョンの Azure クライアント ライブラリや一般的なコンポーネントが使用されている場合もあります。 詳細については、次の記事を参照してください。

このような環境での競合解決の詳細については、この記事の後半の「ファット JAR を作成する」セクションを参照してください。

Azure Functions の構成

Azure Functions (Java 8 を実行するもののみ) の内部依存関係のバージョンは、ユーザー指定のバージョンよりも優先されます。 この依存関係により、特に、Jackson、Netty、Reactor とのバージョンの競合が発生します。

この問題を解決するには、FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS 環境変数を true または 1 に設定します。 必ず Azure Function Tools (v2 または v3) を最新バージョンに更新してください。

Note

この構成は、Java 8 を実行する Azure Functions にのみ適用されます。Java 11 を実行する Functions には特別な構成は必要ありません。

Apache Spark を構成する

Azure SDK for Java は複数のバージョンの Jackson をサポートしていますが、ビルド ツールとその依存関係の解決順序によっては問題が発生することがあります。 この問題の良い例として、Jackson 2.10 に依存する Apache Spark のバージョン 3.0.0 以降が挙げられます。 これは、Azure SDK for Java と互換性がありますが、開発者は、より新しいバージョンの Jackson が代わりに使用されていることに気付くことが多く、その結果、互換性がなくなります。 この問題を軽減するには、特定のバージョンの Jackson (Spark と互換性のあるもの) をピン留めする必要があります。 詳細については、この記事の「複数の Jackson バージョンのサポート」セクションを参照してください。

以前のバージョンの Spark を使用している場合、または使用している別のライブラリで、Azure SDK for Javaでサポートされていないさらに古いバージョンの Jackson が必要な場合は、引き続きこの記事を読み、考えられる軽減手順を確認してください。

Jackson のランタイム バージョンを検出する

Azure Core 1.21.0 では、Jackson のランタイム バージョンのランタイム検出と、より優れた診断が追加されました。

Jackson API に関連する LinkageError (またはそのサブクラス) が発生する場合は、ランタイム バージョン情報の例外のメッセージを確認します。 例: com.azure.core.implementation.jackson.JacksonVersionMismatchError: com/fasterxml/jackson/databind/cfg/MapperBuilder Package versions: jackson-annotations=2.9.0, jackson-core=2.9.0, jackson-databind=2.9.0, jackson-dataformat-xml=2.9.0, jackson-datatype-jsr310=2.9.0, azure-core=1.19.0-beta.2

JacksonVersion からの警告およびエラー ログを探します。 詳細については、「Azure SDK for Java でログを構成する」をご覧ください。 例: [main] ERROR com.azure.core.implementation.jackson.JacksonVersion - Version '2.9.0' of package 'jackson-core' is not supported (too old), please upgrade.

Note

すべての Jackson パッケージのバージョンが同じであることを確認します。

Azure SDK で使用されるパッケージの一覧とサポートされている Jackson のバージョンについては、「複数の Jackson バージョンのサポート」セクションを参照してください。

バージョンの不一致の問題を軽減する

次のセクションでは、バージョンの不一致の問題を軽減する方法について説明します。

Azure SDK BOM を使用する

最新の安定した Azure SDK BOM を使用し、POM ファイルで Azure SDK と依存関係のバージョンを指定しないようにします。 該当する場合は、Azure Spring Boot BOM を使用します。

Azure SDK BOM に記載されている依存関係は、依存関係の競合を回避するために厳密にテストされています。

不要な依存関係を避ける

可能であれば、依存関係を削除します。 アプリケーションが、基本的に同じ機能を提供する複数のライブラリに依存している場合があります。 そのような不要な依存関係により、アプリケーションはセキュリティの脆弱性、バージョンの競合、サポートとメンテナンスのコストにさらされます。

依存関係のバージョンを更新する

最新の Azure SDK BOM に切り替えても問題が解決しない場合は、競合の原因となっているライブラリとそれらを使用するコンポーネントを特定します。 (詳細については、この記事で前述した「依存関係ツリーを表示する」を参照してください)。より新しいバージョンに更新すると、セキュリティの脆弱性から保護され、多くの場合、新機能、パフォーマンスの向上そして、バグ修正が完了した状態の製品を使用できます。

Azure SDK のバージョンをダウングレードしないようにしてください。アプリケーションが既知の脆弱性や問題にさらされる可能性があります。

ライブラリのシェーディング

一緒に動作するライブラリの組み合わせがない場合は、最後の手段としてシェーディングがあります。

Note

シェーディングには大きな欠点があります。これには、パッケージ サイズとクラスパス上のクラス数の増加、コードのナビゲーションとデバッグの複雑化、JNI コードが配置されない、リフレクションの破損、そしてコード ライセンス違反などが挙げられます。 他のオプションをすべて検討した後にのみ使用してください。

シェーディングを使用すると、ビルド時に JAR 内に依存関係を組み込み、パッケージの名前を変更し、シェーディングされた場所でコードを使用するためにアプリケーション コードを更新することができます。 依存関係の 2 つの異なるコピーが存在するため、ひし形の依存関係の競合は問題ではなくなります。 次の一覧で説明するように、競合する一時的な依存関係または直接のアプリケーション依存関係を持つライブラリをシェーディングすることができます。

  • 一時的な依存関係の競合: 例えば、サードパーティ ライブラリ A では、Azure SDK がサポートしない Jackson 2.9 が必要なので、A を更新できません。 A を含む新しいモジュールを作成し、Jackson 2.9 および必要に応じて A の他の依存関係をシェーディング (再配置) します。
  • アプリケーションの依存関係の競合: アプリケーションは Jackson 2.9 を直接使用します。 コードの更新作業中に、Jackson 2.9 をシェーディングして、代わりに、Jackson クラスを再配置した新しいモジュールに再配置できます。

Note

これら例では、再配置された Jackson クラスを使用して JAR を作成しても、バージョンの競合は解決されていません。これは単に、Jackson のシェーディングされた単一バージョンを強制しているにすぎません。

ファット JAR を作成する

Databricks や Apache Spark などの環境には、カスタム依存関係の管理が用意されており、Jackson のような一般的なライブラリが提供されています。 提供されているライブラリとの競合を避けるために、すべての依存関係を含むファット JAR をビルドすることができます。 詳細については、Apache Maven のシェーディング プラグインに関するページを参照してください。 多くの場合、Jackson クラス (com.fasterxml.jackson) を再配置すると、問題が軽減されます。 このような環境では、独自のバージョンの Azure SDK も使用される場合があります。そのため、バージョンの競合を回避するために com.azure 名前空間を再配置せざるを得ない場合があります。

互換性のある依存関係のバージョンについて

azure-core 固有の依存関係とそのバージョンについては、Maven Central Repository の azure-core を参照してください。 次の表に、一般的な考慮事項を示します。

依存関係 サポートされているバージョン
Jackson 2.10.0 以降のマイナー バージョンには互換性があります。 詳細については、「複数の Jackson バージョンのサポート」セクションを参照してください。
SLF4J 1.7.*
netty-tcnative-boringssl-static 2.0.*
netty-common 4.1.*
reactor-core 3.X.* - メジャーとマイナーのバージョン番号は、お使いの azure-core バージョンが依存しているものと正確に一致する必要があります。 詳細については、Project Reactor の非推奨化に関するポリシーを参照してください。

複数の Jackson バージョンのサポート

Azure SDK for Java は、さまざまな Jackson バージョンでの作業をサポートしています。 サポートされている最も低いバージョンは Jackson 2.10.0 です。 Azure SDK for Java クライアント ライブラリは、実行時に検出されたバージョンに応じて、構成と Jackson の使用を調整します。 この調整により、古いバージョンの Spring framework、Apache Spark、その他の一般的な環境との互換性を向上させることができます。 アプリケーションは、Azure SDK for Java クライアント ライブラリを破壊することなく、Jackson バージョンをダウングレード (2.10.0 以降に) できます。

Note

古いバージョンの Jackson を使用すると、アプリケーションが既知の脆弱性や問題にさらされることがあります。 詳細については、Jackson ライブラリの既知の脆弱性のリストに関するページを参照してください。

特定のバージョンの Jackson をピン留めするときは、次のリストに示すように、Azure SDK で使用されるすべてのモジュールに対して実行してください。

  • jackson-annotations
  • jackson-core
  • jackson-databind
  • jackson-dataformat-xml
  • jackson-datatype-jsr310

次のステップ

依存関係のバージョンの競合と、それをトラブルシューティングする方法について理解したので、それを回避する最適な方法について、「Java での依存関係の管理」を参照してください。