OSGiバンドルのClassNotFoundExceptionおよびNoClassDefFoundErrorの解決

ClassNotFoundExceptionNoClassDefFoundErrorは、よく知られた一般的な例外です。

  • ClassNotFoundExceptionは、クラスパスにないクラスを検索するか、無効な名前を使用してランタイムクラスパスにないクラスを検索するとスローされます。
  • NoClassDefFoundErrorは、コンパイルされたクラスがランタイムクラスパス上にない別のクラスを参照するときに発生します。

ただし、OSGi環境では、ClassNotFoundExceptionまたはNoClassDefFoundErrorが発生する可能性がある追加のケースがあります。

  1. 欠落しているクラスが、OSGiモジュールであるモジュール依存関係に属している。
  2. 欠落しているクラスが、OSGiモジュールではないモジュール依存関係に属している。
  3. 欠落しているクラスが、Liferay DXP webappスコープまたはアプリケーションサーバースコープのいずれかのグローバルライブラリに属している。
  4. 欠落しているクラスが、Javaランタイムパッケージに属している。

このチュートリアルでは、各ケースの処理方法について説明します。

ケース1:欠落しているクラスがOSGIモジュールに属している

この場合、2つの原因が考えられます。

  1. モジュールがクラスのパッケージをインポートしていない:モジュール(またはWAB)が別のモジュールのエクスポートされたクラスを使用するには、使用するモジュールがそのクラスを含むエクスポートされたパッケージをインポートする必要があります。これを行うには、使用するモジュールのbnd.bndファイルにImport-Packageヘッダーを追加します。使用するモジュールがパッケージをインポートせずにクラスにアクセスしようとすると、ClassNotFoundExceptionまたはNoClassDefFoundErrorが発生します。

    パッケージ名を確認し、使用するモジュールが正しいパッケージをインポートしていることを確認してください。インポートは正しくされているが、それでも例外またはエラーが発生する場合、クラスがパッケージに存在していない可能性があります。

  2. クラスがインポートされたパッケージに存在していない:モジュールはOSGiランタイム環境で頻繁に変更されます。開発者によって削除された別のモジュールのクラスを参照すると、NoClassDefFoundErrorまたはClassNotFoundExceptionが発生します。Semantic Versioningによって、エクスポートされたパッケージからクラスを削除するとそのパッケージの新しいメジャーバージョンが構成されるというシナリオを回避できます。パッケージのメジャーバージョンをインクリメントしないと、依存モジュールが壊れます。

    たとえば、クラスcom.foo.Barを使用するモジュールがパッケージインポートcom.foo;version=[1.0.0, 2.0.0)を指定するとします。このモジュールは、1.0.0から2.0.0まで(ただし、2.0.0は含まない)のcom.fooバージョンを使用します。バージョン番号の最初の部分(1.0.01)は、 メジャーバージョンを表しています。使用するモジュールは、クラスの削除など、重大な互換性を破る変更を想定していません。パッケージを新しいメジャーバージョン(2.0.0など)にインクリメントせずにcom.fooからcom.foo.Barを削除すると、他のモジュールがそのクラスを検索または参照したときにClassNotFoundExceptionまたはNoClassDefFoundErrorが発生します。

    クラスがパッケージに存在しない場合は、選択肢が制限されます。

    • 新しいAPIに適合させます。これを行う方法については、パッケージ/モジュールのJavadoc、リリースノート、および/または正式なドキュメントを参照してください。 著者に質問したり、フォーラムを検索したりすることもできます。

    • 以前に使用していたモジュールバージョンに戻します。デプロイされたモジュールバージョンは[Liferay_Home]/osgi/にあります。詳細については、Backing up Liferay Installationsを参照してください。 モジュールを適切に動作させるために適切な方法を実行してください。

これで、ClassNotFoundExceptionまたはNoClassDefFoundErrorに関連する一般的な状況を解決する方法がわかりました。NoClassDefFoundErrorの追加情報については、OSGi Enrouteの記事What is NoClassDefFoundError?を参照してください。

ケース2:欠落しているクラスがOSGIモジュールに属していない

この場合、次の2つのオプションがあります。

  1. 依存関係をOSGiモジュールに変換して、欠落しているクラスをエクスポートできるようにします。OSGi以外のJARファイルの依存関係を、アプリケーションと一緒にデプロイできるOSGiモジュールに変換することが理想的なソリューションであるため、まずはこの方法を選択するのがベストです。

  2. 依存関係JARファイルのパッケージをプライベートパッケージとしてモジュールに埋め込むことにより、モジュールに依存関係を埋め込みます。アプリケーションにOSGi以外のJARファイルを埋め込む場合は、チュートリアルAdding Third Party Libraries to a Moduleを参照してください。

ケース3:欠落しているクラスがグローバルライブラリに属している

この場合、OSGiシステムモジュールが欠落しているクラスのパッケージをエクスポートするように、Liferay DXPを構成できます。その後、モジュールはパッケージをインポートできます。ただし、これは気軽に行うべきではありません。Liferayがグローバルライブラリを開発者が使用できるように設計している場合、システムモジュールにはこのライブラリがすでにエクスポートされています。他に解決策がない場合にのみ続行し、意図しない結果に注意してください。パッケージをエクスポートするには2つの方法があります。

  1. portal-ext.propertiesファイルで、プロパティmodule.framework.system.packages.extraを使用して、エクスポートするパッケージを指定します。プロパティの現在のリストを保持します。

  2. 必要なパッケージがLiferay DXP JARからのものである場合、[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jarMETA-INF/system.packages.extra.bndファイルにあるエクスポートされたパッケージのリストにモジュールを追加できます。このオプションは、最初のオプションが機能しない場合にのみ試してください。

必要なパッケージがLiferay DXPモジュールからのものである場合(つまり、グローバルライブラリからのものではない)、そのモジュールのbnd.bndエクスポートにパッケージを追加できます。ただし、これは気軽に行うべきではありません。Liferayがパッケージを使用できるように設計している場合、パッケージはすでにエクスポートされています。

ケース4:欠落しているクラスがJavaランタイムパッケージに属している

この場合、クラスはJavaのrt.jarに属していますが、パッケージはOSGiフレームワークのブート委任リストで指定されていません。rt.jarjava.*パッケージは、クラスパスで自動的に使用できる唯一のパッケージです。 クラスパスにアクセスするには、ブート委任リストで他のパッケージを指定する必要があります。

パッケージをブート委任リストに追加する方法は次のとおりです。

  1. portal-ext.propertiesファイルで、portal property org.osgi.framework.bootdelegationをオーバーライドします。 プロパティの現在のリストを保持します。

  2. 欠落しているパッケージをリストに追加します。

関連トピック

Backing up Liferay Installations

Adding Third Party Libraries to a Module

Bundle Classloading Flow

« Bundle-SymbolicName構文問題の解決Liferayアーティファクトバージョンの依存関係の特定 »
この記事は役に立ちましたか?
0人中0人がこの記事が役に立ったと言っています