好了,言歸正傳。
因 為種種點點點我不知道的原因(理論上是因為 Java 沒有多重繼承,然後 RMI 又希望溝通的物件是 parent class 是 java.rmi.server.UnicastRemoteObject,不過這純屬猜測... API 懶得看 XD),所以真正要讓 client 可以遠端呼叫的物件,必須讓它 implement 自己訂做的 interface,而且這個 interface 還得 extend java.rmi.Remote。所以,就先來定義一下 interface 吧:
FooInterface.java
package test.remoteInterface;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
public interface FooInterface extends Remote {
public Date getServerDate() throws RemoteException;
public String getServerMessage() throws RemoteException;
public void setServerMessage(String s) throws RemoteException;
}
這 裡留了幾個惡搞的空間。只要 Server 跟 client 不同機器,且時間調的不一樣,那麼 getServerDate() 就可以顯現出差異。而 serverMessage 的 setter 跟 getter 是用來測試 server 端的物件如果被改變,能不能 keep 住。下面這個實做就變得沒啥好講了... [茶]
FooObject.java
package test.remoteObject;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
import test.remoteInterface.FooInterface;
public class FooObject extends UnicastRemoteObject implements FooInterface {
/**
* 為了減少 warning 而加的
*/
private static final long serialVersionUID = 1L;
private String message;
public FooObject() throws RemoteException {
super();
message = "Default Message";
}
public Date getServerDate() throws RemoteException {
return new Date();
}
public String getServerMessage() throws RemoteException {
return message;
}
public void setServerMessage(String s) throws RemoteException {
message = s;
}
}
然後追加一個 server,執行這個就可以讓他接客了 \囧/
FooServer.java
package test;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import test.remoteObject.FooObject;
public class FooServer {
public static void main(String[] args) {
FooObject foo;
try {
foo = new FooObject();
Naming.rebind("rmi://127.0.0.1/FooServer", foo);
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
System.out.println("Foo Server is ready");
}
}
最後,當然是來寫 client 端啊:
FooClient.java
package test;給 我等一下![指],並不是這樣子 compile 完,分別執行 FooServer 跟 FooClient 就能用。你還得針對 FooObject 另外作 rmi compile 的處理。在這個 foo 的例子當中,就要下(classpath 等問題請自己搞定)
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Date;
import test.remoteInterface.FooInterface;
public class FooClient {
/**
* 1: ip 位址
* 2: 設定字串
* @param args 參數
*/
public static void main(String[] args) {
try {
FooInterface foo = (FooInterface) Naming.lookup("rmi://" + args[0]
+ "/FooServer");
System.out.println("Server Date : " + foo.getServerDate());
System.out.println("Client Date : " + new Date());
System.out
.println("Before set message : " + foo.getServerMessage());
foo.setServerMessage(args[1]);
System.out.println("After set message : " + foo.getServerMessage());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
rmic test.remoteObject.FooObject這會另外產生 FooObject_Stub.class,在包 client 的執行環境,這個檔也要加進去。
另外,在執行 FooServer 之前,得先執行一個程式 rmiregistry(在 {JDK}/bin/)。如果你也不幸是用 Windows,那在 command line 的環境下打「start rmiregistry」,會另外自動開個黑窗。
一切都順利的話,就可以來真正來玩玩看了。[茶]
錯誤排除區:
java.net.ConnectException→咪的,rmiregistry 啟動了沒?
備註區:
網路上的範例幾乎都有在 client/server 端的程式碼來這行:
System.setSecurityManager(newRMISecurityManager())但是... 我用了反而問題一大堆(有設定 java.policy 了),還搞出一堆靈異現象。拿掉那行反而一點事情也沒有 [爆]。