ClassNotFoundException
とNoClassDefFoundError
は、よく知られた一般的な例外です。
ClassNotFoundException
は、クラスパスにないクラスを検索するか、無効な名前を使用してランタイムクラスパスにないクラスを検索するとスローされます。NoClassDefFoundError
は、コンパイルされたクラスがランタイムクラスパス上にない別のクラスを参照するときに発生します。
ただし、OSGi環境では、ClassNotFoundException
またはNoClassDefFoundError
が発生する可能性がある追加のケースがあります。
- 欠落しているクラスが、OSGiモジュールであるモジュール依存関係に属している。
- 欠落しているクラスが、OSGiモジュールではないモジュール依存関係に属している。
- 欠落しているクラスが、Liferay DXP webappスコープまたはアプリケーションサーバースコープのいずれかのグローバルライブラリに属している。
- 欠落しているクラスが、Javaランタイムパッケージに属している。
このチュートリアルでは、各ケースの処理方法について説明します。
ケース1:欠落しているクラスがOSGIモジュールに属している
この場合、2つの原因が考えられます。
-
モジュールがクラスのパッケージをインポートしていない:モジュール(またはWAB)が別のモジュールのエクスポートされたクラスを使用するには、使用するモジュールがそのクラスを含むエクスポートされたパッケージをインポートする必要があります。これを行うには、使用するモジュールの
bnd.bnd
ファイルにImport-Package
ヘッダーを追加します。使用するモジュールがパッケージをインポートせずにクラスにアクセスしようとすると、ClassNotFoundException
またはNoClassDefFoundError
が発生します。パッケージ名を確認し、使用するモジュールが正しいパッケージをインポートしていることを確認してください。インポートは正しくされているが、それでも例外またはエラーが発生する場合、クラスがパッケージに存在していない可能性があります。
-
クラスがインポートされたパッケージに存在していない:モジュールは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.0
の1
)は、 メジャーバージョンを表しています。使用するモジュールは、クラスの削除など、重大な互換性を破る変更を想定していません。パッケージを新しいメジャーバージョン(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つのオプションがあります。
-
依存関係をOSGiモジュールに変換して、欠落しているクラスをエクスポートできるようにします。OSGi以外の
JAR
ファイルの依存関係を、アプリケーションと一緒にデプロイできるOSGiモジュールに変換することが理想的なソリューションであるため、まずはこの方法を選択するのがベストです。 -
依存関係
JAR
ファイルのパッケージをプライベートパッケージとしてモジュールに埋め込むことにより、モジュールに依存関係を埋め込みます。アプリケーションにOSGi以外のJAR
ファイルを埋め込む場合は、チュートリアルAdding Third Party Libraries to a Moduleを参照してください。
ケース3:欠落しているクラスがグローバルライブラリに属している
この場合、OSGiシステムモジュールが欠落しているクラスのパッケージをエクスポートするように、Liferay DXPを構成できます。その後、モジュールはパッケージをインポートできます。ただし、これは気軽に行うべきではありません。Liferayがグローバルライブラリを開発者が使用できるように設計している場合、システムモジュールにはこのライブラリがすでにエクスポートされています。他に解決策がない場合にのみ続行し、意図しない結果に注意してください。パッケージをエクスポートするには2つの方法があります。
-
portal-ext.properties
ファイルで、プロパティmodule.framework.system.packages.extra
を使用して、エクスポートするパッケージを指定します。プロパティの現在のリストを保持します。 -
必要なパッケージがLiferay DXP JARからのものである場合、
[LIFERAY_HOME]/osgi/core/com.liferay.portal.bootstrap.jar
のMETA-INF/system.packages.extra.bnd
ファイルにあるエクスポートされたパッケージのリストにモジュールを追加できます。このオプションは、最初のオプションが機能しない場合にのみ試してください。
必要なパッケージがLiferay DXPモジュールからのものである場合(つまり、グローバルライブラリからのものではない)、そのモジュールのbnd.bnd
エクスポートにパッケージを追加できます。ただし、これは気軽に行うべきではありません。Liferayがパッケージを使用できるように設計している場合、パッケージはすでにエクスポートされています。
ケース4:欠落しているクラスがJavaランタイムパッケージに属している
この場合、クラスはJavaのrt.jar
に属していますが、パッケージはOSGiフレームワークのブート委任リストで指定されていません。rt.jar
のjava.*
パッケージは、クラスパスで自動的に使用できる唯一のパッケージです。 クラスパスにアクセスするには、ブート委任リストで他のパッケージを指定する必要があります。
パッケージをブート委任リストに追加する方法は次のとおりです。
-
portal-ext.properties
ファイルで、portal propertyorg.osgi.framework.bootdelegation
をオーバーライドします。 プロパティの現在のリストを保持します。 -
欠落しているパッケージをリストに追加します。
関連トピック
Backing up Liferay Installations