非常にXMLベースのJavaアプリケーションを開発中、最近Ubuntu Linuxで興味深い問題に遭遇しました。
Java Plugin Framework を使用する私のアプリケーションは、SVG仕様の dom4j -created XMLドキュメントを Batik's 実装に変換できないようです。
コンソールで、エラーが発生することを学びます。
スレッド "AWT-EventQueue-0"の例外Java.lang.LinkageError:インターフェースのイタブル初期化におけるローダー制約違反:メソッド "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.Apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.Java:149)[.____の署名 で使用されるorg/w3c/dom/Attrタイプの異なるClassオブジェクトがあります。 。] org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.Java:361) at org.dom4j.io.DOMWriter.write(DOMWriter.Java:138)
この問題は、JVMの元のクラスローダーとプラグインフレームワークによってデプロイされたクラスローダーとの競合が原因であると考えられます。
私の知る限り、フレームワークが使用するクラスローダーを指定することはできません。それをハッキングすることは可能かもしれませんが、Linuxシステムでのみ発生するため(何らかの理由で)この問題を解決するために、それほど積極的ではないアプローチを好むでしょう。
そのような問題に遭遇した人がいて、それを修正する方法、または少なくとも問題の核心にたどり着く方法を知っていますか?
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プラグインからロードされているように見えます。そして、あなたのは ''からロードされています。 ESOM、またはクラスパス)。
3つの顕著なクラスローダーモデルは次のとおりです。
JPFクラスローダーが使用する委任戦略はわかりませんが、重要なのは、domライブラリの1つのバージョンをロードし、全員が同じ場所からそのクラスを調達することです。それは、クラスパスからそれを削除してプラグインとしてロードするか、Batikがそれをロードすること、または何か他のものを防ぐことを意味するかもしれません。
クラスローダーの階層の問題のように聞こえます。アプリケーションがどのタイプの環境にデプロイされているのかわかりませんが、この問題は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は、プラグイン機能を実装できるように、何らかのクラスローダーマジックを実行していますか?
これは私にとって非常に良い結果が得られるため、誰かの助けになるかもしれません。この問題は、独自の依存関係を統合することで解決できます。この簡単な手順に従ってください
強調表示された2つのクラスを参照してください。 「StaticLoggerBinder.class jar download」および「LoggeraFactory.class jar download」のように、Googleで検索します。これにより、プロジェクトに含まれているjarバージョンの1つである最初のリンクまたは場合によっては2番目のリンク(サイトは http://www.Java2s.com )が表示されます。自分でスマートに識別できますが、Googleにはまっています;)
その後、jarファイル名がわかります。私の場合は、slf4j-log4j12-1.5.6.jar&slf4j-api-1.5.8のようになります。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
クラスローダーを指定できますか?そうでない場合は、次のようにコンテキストクラスローダーを指定してみてください。
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(yourClassLoader);
callDom4j();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
私はJava Plugin Frameworkに精通していませんが、Eclipseのコードを作成し、時々同様の問題に遭遇します。それを修正する保証はありませんが、おそらく一見の価値があります。
アレックスとマットからの答えは非常に役立ちます。私も彼らの分析から恩恵を受けることができました。
Netbeans RCPフレームワークでBatikライブラリを使用するときに同じ問題が発生しました。Batikライブラリは「Library Wrapper Module」として含まれています。他のモジュールがXML APIを使用し、そのモジュールに対してBatikに依存する必要がなく、確立されていない場合、同様のエラーメッセージでクラスローダー制約違反の問題が発生します。
Netbeansでは、個々のモジュールは専用のクラスローダーを使用し、モジュール間の依存関係は適切なクラスローダー委任ルーティングを意味します。
Batikライブラリバンドルからxml-apis jarファイルを省略するだけで問題を解決できました。
この質問 で指定されているように、-verbose:class
は、ロードされるすべてのクラスに関するJVMログ情報を作成します。これは、より複雑なシナリオとアプリケーションでクラスがどこから来ているかを理解するのに非常に役立ちます。
取得される出力は、おおよそ次のようになります(その質問からコピーされます)。
[Opened /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/Java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded Java.lang.Object from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.io.Serializable from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.Comparable from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.CharSequence from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded Java.lang.String from /usr/Java/j2sdk1.4.1/jre/lib/rt.jar]