rmi有两个主要问题:
1. 调用如何从客户端传输到服务器端
这个问题的是通过stub来解决的,stub负责和服务器通信,将调用传输到服务器并接收
返回值
2. 由于stub类可以通过工具生成,但初始化必须在服务器端完成,所有如何将一个可用的
stub传输到客户端就是我们最关心的问题
注:在1.4中stub类是必须的,
在5.0中使用UnicastRemoteObject类可以不需要stub类,
而由动态生成的Proxy类(实现远程接口,InvocationHandler是
RemoteObjectInvocationHandler)代替,具体见5.0的文档。如果不使用
UnicastRemoteObject类,则stub类在服务器端是必须的
rmi的核心问题是如何将一个可用的stub传输到客户端,大致有两种方法:
1. 最直观的方法,服务器和客户端直接建立连接,用任何可用的协议传输stub,缺点显而
易见,客户端必须明确的知道服务器的地址及相关信息
2. 使用注册中心,如:rmiregistry和JNDI。这里主要讨论使用rmiregistry的情况
有一点需要注意,这里传输的只是stub的实例,而不是stub类的定义。所有stub类文件必须
在客户端和服务器端的classpath中,否则会抛出ClassNotFoundException。这样要求客户
端确实有些过分,我们希望客户端只要远程接口的定义就够了,这实际上很容易办到,下面
我们就来讨论这个问题
Dynamic code downloading using RMI
(http://java.sun.com/j2se/1.4.2/docs/guide/rmi/codebase.html)
要下载类必须设置RMISecurityManager,默认是禁止下载的
客户端可以通过网络自动下载stub,此时stub不需要在客户端的classpath中,只需要远程
接口即可。
由rmiregistry作为注册中心的远程调用
在服务器绑定远程对象到rmiregistry时,rmiregistry必须能够找到该对象的stub,它在三
个地方寻找:
1. 启动它时的CLASSPATH环境变量
2. 启动它时所在的文件夹作为classpath寻找
3. 该stub中的codebase,这个值由服务器通过系统属性 java.rmi.server.codebase 设置
若找不到则无法绑定
客户端通过rmiregistry lookup到stub对象,虚拟机从两个位置寻找stub类的定义:
1. classpath中寻找
2. stub中的codebase,这个值由rmi服务器通过系统属性 java.rmi.server.codebase 设置
由以上分析可见,
服务器端只要正确设置了codebase,无论何种情况,远程调用都能顺利完成
不仅客户端可以从服务器下载类,服务器也可以从客户端下载类,只需要在客户端设置
codebase,安装RMISecurityManager即可
若服务器允许下载类,则客户端有可能在服务器上运行任何他希望的代码,如以下代码:
// 远程接口定义
package rmi.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Service extends Remote {
public void hello() throws RemoteException;
public void runThread(Runnable thread) throws RemoteException;
}
// 远程接口实现,main方法作为服务器
package rmi.server;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.RemoteStub;
import java.rmi.server.UnicastRemoteObject;
import java.security.Permission;
public class ServiceImpl implements Service {
private RemoteStub stub;
private int count;
public ServiceImpl() throws RemoteException {
stub = UnicastRemoteObject.exportObject(this);
count = 0;
}
public void hello() throws RemoteException {
System.out.println("Hello World: count = " + (++count));
}
public void runThread(Runnable thread) throws RemoteException {
Thread t = new Thread(thread);
t.start();
}
public RemoteStub getStub() {
return stub;
}
public static void main(String[] args) {
System.setProperty("java.rmi.server.ignoreStubClasses", "true");
System.setProperty("java.rmi.server.codebase",
"file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiserver\\\\classes\\\\");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager() {
public void checkPermission(Permission p) {
}
});
}
try {
ServiceImpl service = new ServiceImpl();
Naming.rebind("service", service);
System.out.println("Server ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 客户端
package rmi.client;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.security.Permission;
import rmi.server.Service;
public class ServiceClient {
/**
* @param args
*/
public static void main(String[] args) {
System.setProperty("java.rmi.server.codebase",
"file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiclient\\\\classes\\\\");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager() {
public void checkPermission(Permission p) {
}
});
}
try {
Service service = (Service) Naming.lookup("service");
service.hello();
Runnable runner = new Runner();
service.runThread(runner);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package rmi.client;
import java.io.Serializable;
public class Runner implements Runnable, Serializable {
public void run() {
System.out.println("i'm runner");
}
}
服务器端生成stub并放在classpath中,codebase设置为能找到stub的路径,运行rmiregistry,
运行服务器。
客户端只需要Service类在classpath中,设置codebase为能找到Runner的路径,运行客户端。
客户端会从服务器下载stub,服务器会从客户端下载Runner。Runner可以执行任何操作甚至破坏
服务器上的数据。
多次运行客户端可以发现count在增长,也就是说对于export出的远程对象,状态是始终保存的
posted on 2005-11-11 19:49
JBahamut 阅读(1537)
评论(1) 编辑 收藏