2012-02-14 59 views
1

這個問題現在是賞金!解決這個問題的第一個答案獲勝。OSGI CXF單身班加載問題

所以我最近發現OSGI中的bundle並不是100%相互隔離的,特別是當你的bundle共享一個包含單例的bundle時,這可能導致兩個不相關的bundle重寫singleton。這個問題已經在CXF庫中體現出來了。讓我詳細說明發生了什麼:

我們在FuseESB ServiceMix(An osgi平臺)中擁有bundle A,B和共享bundle CXF。 CXF的Bus類是一個單例,由於OSGI每個bundle有一個類加載器,它將與使用CXF的其他bundle共享這個單例。所以我似乎無法爲捆綁包A和捆綁包B創建不同的總線,這一點很重要,因爲捆綁包A應該使用SSL,而捆綁包B不應該使用SSL。這更令人沮喪,因爲捆綁包A和捆綁包B除了必須一起部署在同一個ServiceMix上以外,沒有任何關係。

現在我已經在這個問題一段時間了(1-2個月),我已經讀了很多不同的解決方案。但問題是,很多解決方案都要求我完全控制源代碼,在這種情況下,我不這樣做。 Bundle我創建的是使用一些專有的第三方非osgi庫,名爲Xenara,它使用CXF。出於無法控制的商業原因,我必須使用這個第三方庫。幸運的是,我可以訪問該庫使用的CXF spring bean文件。

我對解決這個問題的猜測是,我需要做一些如何讓包A可以使用自己的CXF個人實例,或者至少使其實例化與其他包不共享的CXF總線。下面是我嘗試或考慮的方法:

  1. 我嵌入到CXF一個捆綁可惜的類加載器不停地從包A的外取CXF,而不是看在classpath。從未想出如何強制它在捆綁A中搜索CXF之前,先搜索捆綁A之外的內容。

  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訪問源代碼,這就是爲什麼我沒有使用this link here中的示例在「使用Java代碼」中顯示的原因,因爲我無法訪問SOAPService,wsdl等...

    <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> 
    
+0

在這個問題上浪費了大量時間,最終的解決方案是在我們的FuseESB ServiceMix環境之外可悲地運行該程序,並通過ActiveMQ與FuseESB Servicemix通話。 – Thirlan 2012-03-27 16:46:19

回答

2

這聽起來像OSGi的對我的基本premisse:提供隔離,但你可以做很多的東西,你可以在常規的OSGi;比如修改一個類的靜態成員,並且由於你們都共享那個類(A大概是出口它,B和C導入它),其他人會注意到。

在大多數情況下,我建議你不要使用靜態類的狀態,因爲它必然會讓其他包變混亂。

在你的情況下,在我看來,捆綁包A是一個沒有真正用於在框架中共享的庫。如果您需要真正的隔離,並且不用擔心太多的開銷,我會將使用包中的庫中的庫打包到中。對於記錄:這種情況與Servicemix無關,它是基本的Java:如果我們談論的是同一個類,並且某人改變了一個靜態屬性,其他人就會注意到。如果這種情況讓你感到困惑,那麼你可以在OSGi中瞭解一下類加載和共享機制。

+0

我知道java靜態是如何工作的,當第一次使用Servicemix時,我剛剛感受到捆綁在他們自己的上下文中並且完全與其他上下文分離。這是一廂情願的想法,Servicemix框架以一種特殊的方式將所有東西粘合在一起。 – Thirlan 2012-02-15 13:55:42

+0

所以我嘗試了這一點,但這是不可能的。一旦我嵌入了jar,它就開始尋求各種其他的軟件包。我們使用Maven來構建和管理我們的OSGI包並進行嵌入,導致Manifest文件的導入大小增加了三倍,並開始要求提供各種包,從bean shell包到junit ......另一篇文章建議將它轉換爲服務。有空的時候我會試試看。 – Thirlan 2012-02-16 15:55:45

+0

誠然,包裝'巨大的圖書館'往往是棘手的努力。最近還有一個關於這個問題的問題,這可能會給你一些幫助。 http://stackoverflow.com/questions/9292642/problems-with-maven-built-osgi-including-dependencies/9292749#9292749。確實把它變成服務是另一種可能性,取決於圖書館的類型。 – 2012-02-16 17:51:39

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和酒吧的相同的情況下,不使用相同的基礎庫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啓動了回調線程。

正確的解決方案是修改所有庫不使用Singleton模式。你可以通過實現一個特殊的類加載器來解決這個問題,但是這會打開整個蠕蟲。

- 編輯 - 讀完CXF後,CXF暴露Singleton類似乎很奇怪。這件事是爲OSGi做的!您最好在CXF郵件列表上提問;他們會知道所有的特殊糖和單身實例的原因,並可能已經考慮過這個用例。

+0

我會給你作爲唯一一個在賞金時被迴應的人的賞金。對你的編輯來說,我已經去過CXF論壇,可惜修正涉及到修改Foo和Bar的來源,我不能這樣做。如上所述,嵌入CXF庫已經嘗試了好幾次,並帶來了很多問題。僞單身,如果你很好奇,可以在這裏找到,http://cxf.apache.org/javadoc/latest/org/apache/cxf/BusFactory.html。如果您沒有考慮編程,那麼您的軟件包可以/將修改總線,並影響以相同方式編寫的其他軟件包。 – Thirlan 2012-03-27 16:42:23

+0

非常討厭!祝好運,說服原始開發人員他們的代碼不應該使用有問題的類... CXF不應該公佈這樣的靜態類,或者至少使它成爲捆綁特定/ OSGi服務! – parasietje 2012-03-28 07:06:40