2013-06-18 68 views
1

我想在Java Swing客戶端和Tomcat服務器之間實現RMI工作流。我遇到的困難是創建插件體系結構,以便購買我們產品的用戶可以將新的jar文件放到服務器上的插件文件夾中,然後讓這些類可用於客戶端和服務器。如何使用RMI實現插件體系結構?

現在,我有客戶端部分工作:它向服務器請求插件URL列表,服務器掃描插件文件夾並返回與它在其中找到的jar文件對應的URL,然後客戶端使用一個網絡類加載器與這些提供的URL來動態加載插件類。

當客戶端通過使用插件類的RMI向服務器發送對象時,會發生此問題。服務器的類路徑中沒有這些類(因爲它們只是放置在硬盤驅動器的文件夾中,而不是位於webapp或Tomcat文件夾內),因此會引發錯誤。我需要爲此找到解決方案。

有一些約束需要注意的:

  • 我們不希望用戶能夠在他們放棄新的插件到服務器上的插件文件夾,以重新啓動Tomcat。

  • 我們不想更改Tomcat安裝本身的任何內容,例如啓動參數或其他jar文件,因爲我們的解決方案需要由已經擁有自己的Tomcat的客戶安裝。他們只是想把我們的產品放到他們的webapp目錄下去。

這裏是我已經嘗試了一些可能的解決方案,沒有工作:

  • 我不能用「java.rmi.server.codebase」系統屬性在服務器上解決問題,因爲插件jar列表在運行時發生更改,並且此屬性在啓動時緩存。我還希望不必安裝這種方法所需的SecurityManager。

  • 定義我們自己的類加載器並設置'java.rmi.server.RMIClassLoaderSpi'系統屬性看起來正是我們所需要的,但是當使用這種方法時,類加載器需要由系統類加載器加載。由於我們在使用WebAppClassLoaders的Tomcat中運行,所以不起作用。

  • 我想過使用反射和蠻力設置我們的自定義類加載器到RMIClassLoader類中的'defaultProvider'私有靜態變量,但它是最終的,所以我假設我不能設置一個新的值(還沒有實際測試過)。

- 這裏[UPDATE從服務器顯示堆棧跟蹤]是通過在客戶端嘗試使用該插件的一個類在RMI調用服務器的服務器產生的堆棧跟蹤:

Jun 19, 2013 7:20:58 AM sun.rmi.server.UnicastServerRef logCallException 
FINE: RMI TCP Connection(4)-192.168.1.107: [192.168.1.107] exception: 
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: com.prosc.cps.CPSProperties 
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:294) 
    at sun.rmi.transport.Transport$1.run(Transport.java:159) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155) 
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535) 
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790) 
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) 
    at java.lang.Thread.run(Thread.java:680) 
Caused by: java.lang.ClassNotFoundException: com.prosc.cps.CPSProperties 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 
    at java.lang.Class.forName0(Native Method) 
    at java.lang.Class.forName(Class.java:249) 
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:432) 
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:163) 
    at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620) 
    at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247) 
    at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:201) 
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1589) 
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1494) 
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1748) 
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1327) 
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:349) 
    at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306) 
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:288) 
    ... 9 more 
+0

儘管我完全不瞭解這個問題,但是對於運行時的jar/plugin管理,你有沒有給OSGi一個想法? – happybuddha

+0

不,我完全不知道OSGi –

回答

0

找到答案!我的系統屬性'java.rmi.server。useCodebaseOnly'在服務器上被設置爲true(這很奇怪,因爲它在Java 6中被記錄爲false,這正是我正在使用的)。如果這是真的,服務器會忽略來自客戶端的類註解中嵌入的類路徑URL。我將它設置爲false,現在服務器正在從客戶端讀取註釋並解析類!

1

在客戶端使用RMIClassLoaderSPI方法這相當於在客戶端動態設置codebase屬性:當通過RMI發送到服務器時,這些類會被註釋,以便服務器可以加載它們。

+0

感謝您的回答。目前,我在客戶端上有一個URLClassLoader,它從HTTP URL(該部分不使用RMI)加載插件類。但是,我不明白在這種情況下,客戶端RMIClassLoaderSpi子類需要做什麼。是不是用於解組,而不是編組,RMI參數?不應該基於我已經使用的URLClassLoader編組註釋該類的任何類嗎? –

+0

經過一些調試後,它看起來像是從客戶端正確註釋了類 - 調用'LoaderHandler.getClassAnnotation(theObject.getClass())'正在返回一個有效的HTTP URL,它應該在服務器上正確解析:'http:// apollo.local:8080/MS/downloadExtension/CPSClient.jar'(一切正在發生在一臺計算機上,所以.local是正常的)。這聽起來像服務器忽略註釋?我確實在服務器上安裝了SecurityManager(分類爲允許所有內容)。還有什麼我可能需要做的,讓服務器使用註釋? –

+0

找到答案!我的系統屬性'java.rmi.server.useCodebaseOnly'是真的(這很奇怪,因爲它在Java 6中被記錄爲false,這正是我所使用的)。我將它設置爲false,現在服務器正在從客戶端讀取註釋並解析類!非常感謝您使用註釋將我指向正確的方向。 –