63

XML ベースの Java アプリケーションを開発していると、最近 Ubuntu Linux で興味深い問題に遭遇しました。

Java Plugin Frameworkを使用する私のアプリケーションは、 dom4jで作成された XML ドキュメントをBatik のSVG 仕様の実装に変換できないようです。

コンソールで、エラーが発生したことがわかります。

スレッド "AWT-EventQueue-0" java.lang.LinkageError での例外: インターフェイス itable 初期化でのローダー制約違反: メソッド "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg の解決時/w3c/dom/Attr;" 現在のクラス org/apache/batik/dom/svg/SVGOMDocument のクラス ローダー (org/java/plugin/standard/StandardPluginClassLoader のインスタンス)、およびインターフェイス org/w3c/ のクラス ローダー (<bootloader> のインスタンス) dom/Document には、署名で使用されるタイプ org/w3c/dom/Attr の異なる Class オブジェクトがあります
    org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149) で
    org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361) で
    org.dom4j.io.DOMWriter.write(DOMWriter.java:138) で

この問題は、JVM の元のクラスローダーと、プラグイン フレームワークによってデプロイされたクラスローダーとの間の競合が原因であると考えています。

私の知る限り、フレームワークが使用するクラスローダーを指定することはできません。ハッキングすることは可能かもしれませんが、(何らかの理由で) Linux システムでのみ発生するため、この問題を解決するためのより積極的なアプローチを好みません。

あなたの 1 人がそのような問題に遭遇し、それを修正する方法、または少なくとも問題の核心にたどり着く方法を知っていますか?

4

7 に答える 7

73

LinkageError は、クラス C が複数のクラスローダーによってロードされ、それらのクラスが同じコード (比較、キャストなど) で一緒に使用されているという古典的なケースで得られるものです。同じクラス名であるか、同一の jar からロードされたものであるかは関係ありません。あるクラスローダーからのクラスは、別のクラスローダーからロードされた場合、常に別のクラスとして扱われます。

メッセージ(長年にわたって大幅に改善されています)は次のように述べています。

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: 
loader constraint violation in interface itable initialization: 
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" 
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) 
of the current class, org/apache/batik/dom/svg/SVGOMDocument, 
and the class loader (instance of ) for interface org/w3c/dom/Document 
have different Class objects for the type org/w3c/dom/Attr used in the signature

したがって、ここでの問題は、org.w3c.dom.Attr (標準 DOM ライブラリの一部) を使用する SVGOMDocument.createAttribute() メソッドの解決にあります。しかし、Batik でロードされた Attr のバージョンは、メソッドに渡した Attr のインスタンスとは異なるクラスローダーからロードされました。

Batik のバージョンが Java プラグインからロードされているように見えることがわかります。そして、あなたのものは " " からロードされています。これはおそらく組み込みの JVM ローダー (ブート クラスパス、ESOM、またはクラスパス) の 1 つです。

3 つの主要なクラスローダー モデルは次のとおりです。

  • 委譲 (JDK のデフォルト - 親に聞いてから私に聞いてください)
  • 委任後 (プラグイン、サーブレット、および分離が必要な場所で一般的です - 私に聞いてから、親)
  • 兄弟 (OSGi、Eclipse などの依存関係モデルで一般的)

JPF クラスローダーが使用する委任戦略はわかりませんが、重要なのは、dom ライブラリの 1 つのバージョンをロードし、全員が同じ場所からそのクラスをソースするようにすることです。これは、クラスパスから削除してプラグインとしてロードするか、Batik がロードしないようにするか、またはその他のことを意味する場合があります。

于 2008-10-28T22:00:50.907 に答える
22

クラスローダー階層の問題のように聞こえます。アプリケーションがどのタイプの環境にデプロイされているかはわかりませんが、この問題はWeb環境で発生する場合があります。この場合、アプリケーションサーバーは次のようなクラスローダーの階層を作成します。

javahome/lib-ルート
appserver/libとして-ルートの子として
webapp/WEB-INF/lib-ルートの子の子として
など

通常、クラスローダーはロードを親クラスローダー(これは「parent-first」と呼ばれます)に委任し、そのクラスローダーがクラスを見つけられない場合、子クラスローダーはそれを試みます。たとえば、webapp / WEB-INF / libにJARとしてデプロイされたクラスがクラスをロードしようとすると、最初にappserver / libに対応するクラスローダーにクラスのロードを要求します(次に、javahome/libに対応するクラスローダーに要求しますクラスをロードするため)、このルックアップが失敗した場合、WEB-INF/libはこのクラスに一致するものを検索します。

Web環境では、この階層で問題が発生する可能性があります。たとえば、私が以前に遭遇した1つの間違い/問題は、WEB-INF/libのクラスがappserver/libにデプロイされたクラスに依存し、次にWEB-INF/libにデプロイされたクラスに依存した場合でした。クラスローダーは親クラスローダーに委任できますが、ツリーを下に委任することはできないため、これによりエラーが発生しました。したがって、WEB-INF/libクラスローダーはappserver/libクラスローダーにクラスを要求し、appserver / libクラスローダーはそのクラスをロードして依存クラスをロードしようとしますが、appserver/libまたはjavahomeでそのクラスが見つからなかったため失敗します。 /lib。

そのため、Web /アプリサーバー環境にアプリをデプロイしていない場合でも、環境にクラスローダーの階層が設定されている場合は、長すぎる説明が当てはまる可能性があります。しますか?JPFは、プラグイン機能を実装できるようにするために、ある種のクラスローダーの魔法を実行していますか?

于 2008-10-28T20:22:46.033 に答える
5

クラスローダーを指定できますか?そうでない場合は、次のようにコンテキストクラスローダーを指定してみてください。

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

私はJavaプラグインフレームワークに精通していませんが、Eclipseのコードを記述しており、同様の問題が発生することがあります。私はそれがそれを修正することを保証しません、しかしそれはおそらく一撃の価値があります。

于 2008-10-28T20:28:20.360 に答える
3

アレックスとマットからの回答は非常に役に立ちます。私も彼らの分析から恩恵を受けることができました。

Netbeans RCP フレームワークで Batik ライブラリを使用しているときに同じ問題が発生しました。Batik ライブラリは「ライブラリ ラッパー モジュール」として含まれています。他のモジュールが XML API を使用し、そのモジュールに必要な Batik への依存関係が確立されていない場合、クラス ローダーの制約違反の問題が発生し、同様のエラー メッセージが表示されます。

Netbeans では、個々のモジュールは専用のクラス ローダーを使用し、モジュール間の依存関係は適切なクラス ローダー委任ルーティングを意味します。

この問題は、Batik ライブラリ バンドルから xml-apis jar ファイルを削除するだけで解決できました。

于 2009-11-23T16:56:06.780 に答える