1. 程式人生 > >RMI總結及可能產生的幾種異常及解決

RMI總結及可能產生的幾種異常及解決

RMI技術的總結

一、概要

主要包括5個檔案:IRMI(介面),RMIImp, RMIServer, RMIClient, RMIImp_stub

在和cn統一目錄級別下執行: rmic cn.cnic.osg.RMIImp

二、安全機制

在Think In Java裡有一個示例 RMI 的程式碼 PerfectTime, 可是其中並沒有把有些執行細節說明了,也就可以造成新手執行它會出某些異常而不知所措。下面將列出可能產生的異常,並說明解決辦法,如讀者在執行當中還遇到其他未列出的異常,可留言告知,吾將盡力而為。

首先把程式碼PerfectTime和DisplayPerfectTime 中的//colossus:2005/PerfectTime

改為//localhost:2005/PerfectTime ,因為colossus為機器名,所以改為localhost指向本機,不然找不到主機colossus的。

已經用命令 RMIC 生成PerfectTime_Stub.class,並且執行了命令 rmiregistry 2005

1. 執行java PerfectTime出現異常 java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:2005 connect,resolve)

無法解析和連線到127.0.0.1的2005埠上,原因是在PerfectTime中設定了安全管理器<System.setSecurityManager(new RMISecurityManager());>,可是又沒有設定訪問的策略,解決辦法有四(解決這種異常的辦法同樣適用於DisplayPerfectTime):

(1) 可以把程式碼System.setSecurityManager(new RMISecurityManager());去掉,不設定安全管理器
(2) 修改JRE的安全策略檔案,這就要求你能確定執行時是用的哪個JRE,比如在Eclipse中用JDK是c:/Java/jdk1.5.0_06,相應的安全策略檔案就是c:/java/jdk1.5.0_06/jre/lib/security/java.policy,如果是Applet中的java程式就應該是在 jre 目錄中,如檔案C:/Java/jre1.5.0_06/lib/security/java.policy。修改安全策略檔案,在grant {},大括號中加上permission java.net.SocketPermission “localhost:2005″,”connect,resolve”;
(3) 建立自己的策略檔案,如c:/MyPolicy.policy ,內容為:

        grant {
             permission java.net.SocketPermission “localhost:2005″,”connect,resolve”;
        }

    執行PerfectTime時用命令 java -Djava.security.policy=c:/MyPolicy.policy PerfectTime  指定了安全策略檔案,我對此進行了測試,伺服器端不需要加-Djava****=***MyPolicy.policy。這個在客戶端加上就可以了。

(4) 把 System.setSecurityManager (new RMISecurityManager()) 改為匿名類實現,覆蓋兩個方法

        System.setSecurityManager (new RMISecurityManager() {
            public void checkConnect (String host, int port) {}
            public void checkConnect (String host, int port, Object context) {}
       });

當然最簡單的解決方法莫過於第一種。

2. 同樣是執行 PerfectTime 出現的異常
    java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
       java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
       java.lang.ClassNotFoundException: PerfectTime_Stub

   很多人對這個問題有些莫名其妙,因為明明看到 PerfectTime_Stub 和 PerfectTime 這兩個類是在同一個目錄中,並且classpath 也有設定當前目錄,按理既然能載入 PerfectTime 類執行,就能載入到 PerfectTime_Stub吧,為什麼還提示ClassNotFound呢?其實類 PerfectTime_Stub並非由PerfectTime執行行直接載入,而是PerfectTime在向RMI註冊時,要求rmiregistry去載入 PerfectTime_Stub類的,理解了這一層次上的意義就會知道其實 PerfectTime_Stub是為 rmiregistry所用的。所以解決辦法是:

(1) 在執行 rmiregistry 之前,設定classpath讓能查詢到PerfectTime_Stub類,如在同一Dos視窗中,假設 PerfectTime_Stub類是在E:/workspace/TestRMI/bin目錄中,執行過程那就是

C:/Documents and Settings/unmi>set classpath=%classpath%;E:/workspace/TestRMI/bin

C:/Documents and Settings/unmi>rmiregistry 2005

(2) 或者在命令列中先進入到 PerfectTime_Stub類所在的目錄,然後再執行 rmiregistry (這種方法實質是與上面一樣的,只是恰當的應用的classpath中的當前目錄 “.” ),執行過程如下

C:/Documents and Settings/unmi>e:

E:/>cd E:/workspace/TestRMI/bin

E:/workspace/TestRMI/bin>rmiregistry 2005

3. 執行客戶端程式 DisplayPerfectTime 出現異常 java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1276 connect,resolve),同時在伺服器端也產生異常 Exception in thread “RMI TCP Connection(6)-127.0.0.1″ java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1296 accept,resolve)

直接能想到的解決辦法是把127.0.0.1:1276,127.0.0.1:1276的解析連線許可權也加上,方法可取第 1 種異常所列的方法,但這個埠是隨機的。在此解析一下這些埠的用途,2005是直接指定的供客戶端查詢註冊的服務物件引用的埠,這是固定的,而上面產生的在客戶端和伺服器上的1276和1296的埠,是隨機的,是在方法呼叫時真正的客戶端與提供服務的伺服器(而非註冊伺服器)之間的資料通訊的埠。

為了滿足上面的埠應用,可以在安全策略檔案中只加上 permission java.net.SocketPermission “localhost:*”,”accept,connect,resolve”; 允許在所有埠上的接受,連線,解析。再如果要訪問的IP很多,又要寫成 permission java.net.SocketPermission “*:*”,”accept,connect,resolve”; 方便。

4. 執行客戶端程式 DisplayPerfectTime出現異常  java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is:  java.io.EOFException,這種異常應該比較少見,出現情況是 客戶端有許可權訪問服務提供端的某個埠,而服務提供端卻無許可權在某個埠上或給那個客戶端提供服務造成的,解決辦法把客戶端和伺服器的安全策略檔案都改為能訪問任何埠就行。

總結:上面1、3、4三種情況都是因為許可權不足所造成的,如果安全控制的粒度不要求太細的化,在伺服器端和客戶端可以不用設定定全管理器,或者策略檔案中設定為能接受、連線、解析任何IP及埠:permission java.net.SocketPermission “*:*”,”accept,connect,resolve”; 或者用1(4)的方法忽略所有IP及埠的檢測。

寫完之後,我也是感覺好象沒怎麼理清那些問題,希望不會讓讀者越看越迷糊。

三、注意事項:

1)做了修改後,RMIServer需要重啟.

今天由於忘了宣告要傳輸的物件是Serializable的,總是丟擲

後來補上後,還是有問題,看了很多帖子,說所有ExeResult內的類也都要是實現了Serializable,於是把CpuusgInfo也序列化了還是不行。最後,想是不是應該重新啟動一下RMIServer,果然搞定。我估計NMServer啟動後,其已經將類載入到了記憶體空間,及時你更新了新的類檔案,內從空間中的類沒有更新,於是就有了上述現象。

2)所有類都要序列化:為了驗證上述這句話,還故意把CpuusgInfo不序列化,結果果然丟擲:

java.io.NotSerializableException: cn.cnic.osg.datadefine.perfmonintor.CpuusgInfo

very good。餘不欺我。