1

この質問は現在、バウンティに寄せられています! この問題を解決する最初の回答が勝ちます。

そのため、最近、OSGI のバンドルが互いに 100% 分離されていないことを発見しました。特に、バンドルがシングルトンを含む共通のバンドルを共有している場合、関連のない 2 つのバンドルがシングルトンを上書きする可能性があります。この問題は、CXF ライブラリで明らかになりました。何が起こっているかの詳細な例を挙げましょう:

バンドル A、B、および共有バンドル CXF がすべて FuseESB ServiceMix (osgi プラットフォーム) に含まれています。CXF の Bus クラスはシングルトンであり、OSGI にはバンドルごとに 1 つのクラスローダーがあるため、CXF を使用する他のすべてのバンドルとこのシングルトンを共有します。したがって、バンドル A とバンドル B に異なるバスを作成することはできないようです。バンドル A は SSL を使用し、バンドル B は SSL を使用すべきではないため、これは重要です。バンドル A とバンドル B が、同じ ServiceMix に一緒にデプロイする必要があること以外は、互いに何の関係もないことを考えると、これはさらに苛立たしいことです。

今、私はしばらくの間(1〜2か月)この問題に直面しており、さまざまな解決策をたくさん読んできました。ただし、問題は、多くのソリューションではソース コードを完全に制御する必要があり、この場合は制御できないことです。私が作成しているバンドル A は、CXF を使用する Xenara と呼ばれる独自のサードパーティの非 osgi ライブラリを使用しています。私の手に負えないビジネス上の理由から、このサードパーティのライブラリを使用する必要があります。幸いなことに、このライブラリが使用する CXF スプリング Bean ファイルにアクセスできます。

この問題を解決するには、バンドル A が CXF の独自の個人用インスタンスを使用できるようにするか、少なくとも他のバンドルと共有されていない CXF バスをインスタンス化できるようにする必要があると思います。私が試したり検討したりした方法は次のとおりです。

  1. CXF をバンドル A に組み込みましたが、残念ながら、クラスローダーはクラスパスを調べる代わりに、バンドル A の外部から CXF をフェッチし続けました。バンドル A の外側を検索する前に、最初にバンドル A 内の CXF を強制的に検索する方法がわかりませんでした。

  2. バンドル A をサービスにする提案がなされました。いくつかの誤解があり、シングルトンは CXF ではなく A にあると考えられていたと思います。とにかく試してみましたが、問題は解決しませんでした。CXF バスは、バンドル A と B の間で共有されていました。

  3. バンドル A が CXF クラスのロードに別のクラスローダーを使用するように、クラスローディングをオーバーライドします。このロジックを完全には理解していませんが、CXF バスと http コンジットを作成するために Spring Bean が使用されていることを考えると、非常にトリッキーになると確信しています。より良いアイデアを得るには、以下の (4) を参照してください。

  4. CXF には、特定のスレッド コンテキストに CXF バスと http-conduit を設定する方法があります。本当にこのソリューションを使用したいのですが、CXF Bean ファイルを同等の Java コードに変換する方法がわかりません。CXF スプリング Bean ファイルを以下に示します。このhttpコンジットを使用してソースコードにアクセスできないことに注意してください。そのため、SOAPService、wsdlにアクセスできないため、このリンクの「Javaコードの使用」に示されている例を使用していませんなど...

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="searchSystemEnvironment" value="true" />
        <property name="ignoreUnresolvablePlaceholders" value="true" />
    </bean>
    
    <cxf:bus>
        <cxf:outInterceptors>           
            <bean class="com.xenara.messaging.security.IdentityAssertingOutInterceptor"
                  scope="singleton" />
        </cxf:outInterceptors>
    
        <cxf:features>
            <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
        </cxf:features>
    </cxf:bus>
    
    <http-conf:conduit name="*.http-conduit">
        <http-conf:client AllowChunking="false" Connection="Keep-Alive" />
        <http-conf:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLS">
            <sec:keyManagers keyPassword="${javax.net.ssl.keyStorePassword}">
                <sec:keyStore type="JKS" password="${javax.net.ssl.keyStorePassword}"
                                         file="${javax.net.ssl.keyStore}" />
            </sec:keyManagers>
            <sec:trustManagers>
                <sec:keyStore type="JKS" password="${javax.net.ssl.trustStorePassword}" file="${javax.net.ssl.trustStore}" />
            </sec:trustManagers>
            <sec:cipherSuitesFilter>
                <sec:include>SSL_RSA_WITH_3DES_EDE_CBC_SHA</sec:include>
                ...
            </sec:cipherSuitesFilter>
        </http-conf:tlsClientParameters>
    </http-conf:conduit>
    
4

2 に答える 2

2

あなたが直面している問題はかなり本質的で基本的なものです。サポートしているライブラリCXFに静的な状態がありますが、CXFを使用するライブラリの共有インスタンスが必要です。共有ライブラリを変更することはできません(サイズが大きいため)。また、CXF(クローズドソース?)を変更することもできません。これらの共有ライブラリをFooとBarと呼びましょう。

次のクラスがあるとします。

CXF#1
Foo#1, using CXF#1
Bar#1, using CXF#1
WebApp#1, using Foo#1 and Bar#1

私が正しく理解していれば、同じ基盤となるライブラリCXF#1を使用せずに、別のアプリケーションでFooとBarの同じインスタンスを使用する必要があります。これは、次のような状況になります。

CXF#2
CXF#1
Foo#1, using CXF#1 when called by App#1, using CXF#2 when called by App#2
Bar#1, using CXF#1 when called by App#1, using CXF#2 when called by App#2
WebApp#1, using Foo#1 and Bar#1
WebApp#2, using Foo#1 and Bar#1

これは不可能です。OSGiにもJavaフレームワークにもありません。既存のクラスは別のクラスに動的にバインドできず、呼び出し元のバンドルに基づいて選択します。ライブラリを変更せずにこれを行う唯一の方法は、サポートするライブラリを複製することです。

CXF#2
CXF#1
Foo#1, using CXF#1
Bar#1, using CXF#1
Foo#2, using CXF#2
Bar#2, using CXF#2
WebApp#1, using Foo#1 and Bar#1
WebApp#2, using Foo#2 and Bar#2

確かに、これは多大な労力であり、ディスク上およびメモリ内のパッケージの数を爆発的に増加させます。CXFパッケージを単一のアプリケーションでのみ使用できる場合、最も論理的な解決策は、パッケージを複製して、使用するすべての場所に埋め込むことです。はい、これにはパッケージが依存するすべてのライブラリが含まれます。

これを解決するためのハッキー/危険な方法は次のとおりです。CXFクラスを逆コンパイルできるはずです。これにより、次のようにクラスを変更できます。

class CXF {
    [...]
    public static CXF getInstance() {
        // based on the current Stack frame, determine which instance to return. Remember, the instance should be based on the WebApp bundle (while you still have shared libraries in between!)
    }
}

これは絶対確実ではありません。WebAppがライブラリAから発信されたコールバックスレッドを開始するとします。このスレッドはCXF.getInstance()->getInstance()メソッドを呼び出します。このメソッドには、どのWebAppがコールバックスレッドを開始したかを判別する方法がありません。

正しい解決策は、シングルトンパターンを使用しないようにすべてのライブラリを変更することです。特別なクラスローダーを実装することで、おそらく問題を回避することができますが、これにより、他のすべてのワームの可能性が開かれます。

-編集-CXFを読んだ後、CXFがシングルトンクラスを公開するのは非常に奇妙に思えます。ものはOSGiのために作られています!おそらく、CXFメーリングリストで質問したほうがよいでしょう。彼らは、シングルトンインスタンスを作成するための特別な砂糖と理由をすべて知っており、おそらくこのユースケースについてすでに考えています。

于 2012-03-27T09:49:48.483 に答える
2

これは OSGi の基本的な前提のように思えます。分離が提供されますが、通常の OSGi でできることの多くを実行できます。たとえば、クラスの静的メンバーを変更すると、全員がそのクラスを共有するため (A はおそらくそれをエクスポートし、B と C はそれをインポートします)、他の人は気付くでしょう。

ほとんどの場合、静的なクラス状態を使用しないことをお勧めします。これは、他のバンドルに何かを台無しにしてしまう可能性があるためです。

あなたの状況では、バンドル A は、フレームワークで共有されている実際の用途がないライブラリであるように思えます。実際の分離が必要な場合は、両方の using バンドル内にライブラリをパッケージ化し、オーバーヘッドをあまり心配しません。

記録のために: この状況は Servicemix とは何の関係もありません。これは基本的な Java です。同じクラスについて話しているときに、誰かが静的プロパティを変更すると、他の人が気付くでしょう。この状況に混乱している場合は、OSGi のクラスのロードと共有のメカニズムについて少し読んでみてください。

于 2012-02-15T08:47:24.343 に答える