1

这个问题现在要交给赏金了!解决此问题的第一个答案获胜。

所以我最近发现 OSGI 中的包不是 100% 相互隔离的,尤其是当你的包共享一个包含单例的公共包时,这可能导致两个不相关的包覆盖单例。这个问题已经在 CXF 库中体现出来了。让我举一个详细的例子来说明正在发生的事情:

我们在 FuseESB ServiceMix(一个 osgi 平台)中有包 A、B 和共享包 CXF。CXF 的 Bus 类是一个单例,因为 OSGI 每个包都有一个类加载器,它会与使用 CXF 的所有其他包共享这个单例。所以我似乎无法为包 A 和包 B 创建不同的总线,这很重要,因为包 A 应该使用 SSL,而包 B 不应该使用 SSL。这更令人沮丧,因为 bundle A 和 bundle B 除了必须一起部署在同一个 ServiceMix 上之外,彼此没有任何关系。

现在我已经解决这个问题一段时间了(1-2 个月),我已经阅读了很多不同的解决方案。然而问题是很多解决方案都要求我完全控制源代码,而在这种情况下我不需要。我正在创建的 Bundle A 使用了一些专有的第三方非 osgi 库,称为 Xenara,它使用 CXF。出于我无法控制的商业原因,我必须使用这个第三方库。幸运的是,我确实可以访问该库使用的 CXF spring bean 文件。

我对解决这个问题的猜测是,我需要一些方法,以便包 A 可以使用它自己的个人 CXF 实例,或者至少让它实例化不与其他包共享的 CXF 总线。以下是我尝试或考虑过的方法:

  1. 我将 CXF 嵌入到包 A 中,但不幸的是,类加载器一直从包 A 外部获取 CXF,而不是查看类路径。从来没有想过如何强制它在包 A 之外搜索之前先在包 A 中搜索 CXF。

  2. 有人建议将捆绑包 A 变成一项服务。我认为存在一些误解,人们认为单例在 A 中而不是在 CXF 中。无论如何我尝试了它并没有解决问题。CXF 总线仍然在包 A 和 B 之间共享。

  3. 覆盖类加载,以便包 A 使用不同的类加载器来加载 CXF 类。我不完全理解这其中的逻辑,但我确信这将是非常棘手的,因为使用了一个 spring bean 来创建 CXF 总线和 http-conduit。请参阅下面的 (4) 以获得更好的想法。

  4. 在 CXF 中,有一种方法可以为给定的线程上下文设置 CXF 总线和 http-conduit。我真的很想使用这个解决方案,但我不知道如何将 CXF bean 文件转换为等效的 java 代码。下面提供了 CXF spring bean 文件。请注意,我无权使用此 http-conduit 访问源代码,这就是为什么我没有使用“使用 Java 代码”的此链接中显示的示例,因为我无权访问 SOAPService、wsdl , ETC...

    <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

如果我理解正确,您现在希望另一个应用程序使用相同的 Foo 和 Bar 实例,而不使用相同的底层库 CXF#1。这相当于以下情况。

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 框架中。现有类不能动态绑定到另一个类,根据调用 Bundle 进行选择。在不修改库的情况下执行此操作的唯一方法是复制支持库:

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 公开了一个 Singleton 类似乎很奇怪。这东西是为 OSGi 设计的!您最好在 CXF 邮件列表上提问;他们将知道所有特殊的糖和制作单例实例的原因,并且可能已经考虑过这个用例。

于 2012-03-27T09:49:48.483 回答
2

在我看来,这听起来像是 OSGi 的基本前提:提供了隔离,但您可以在常规 OSGi 中做很多事情;例如,修改一个类的静态成员,并且由于你们都共享该类(A 大概导出它,B 和 C 导入它),其他人会注意到。

在大多数情况下,我建议您不要使用静态类状态,因为它必然会为其他捆绑包造成混乱。

在您的情况下,在我看来,bundle A 是一个在框架中共享并没有实际用途的库。如果您需要真正的隔离,我会将库打包在两个使用包中,而不必担心过多的开销。

记录一下:这种情况与 Servicemix 无关,它是基本的 Java:如果我们谈论的是同一个类,并且有人更改了静态属性,其他人会注意到。如果这种情况让您感到困惑,您可以阅读一些有关 OSGi 中的类加载和共享机制的信息。

于 2012-02-15T08:47:24.343 回答