在发帖之前搜索了一下,也看了一些关于RMI的使用的讨论,可是这些帖子都是很早的了,差不多都在2003年以前的了.所以想再次提起这个问题问一下.
★这个codebase属性到底指明了什么东西?是指明了这个rmiregistry服务应该去哪里找client程序所需要的类吗?还是别的什么东西?我使用的例子如下:
一下的例子都是基于这个tutorial说明的。
http://java./docs/books/tutorial/rmi/index.html##首先创建了两个接口:Compoute和Task。
package compute;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Compute extends Remote {
Object executeTask(Task t) throws RemoteException;
}
package compute;
import java.io.Serializable;
public interface Task extends Serializable {
Object execute();
}
然后ComputeEngine实现了这个接口:
package engine;
import java.rmi.*;
import java.rmi.server.*;
import compute.*;
public class ComputeEngine extends UnicastRemoteObject
implements Compute
{
public ComputeEngine() throws RemoteException {
super();
}
public Object executeTask(Task t) {
System.out.println("got a task.");
return t.execute();
}
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
String name = "//"
args[0]"/Compute";
try {
Compute engine = new ComputeEngine();
Naming.rebind(name, engine);
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception: " + e.getMessage());
e.printStackTrace();
}
}
}
这样server端的工作算是做完了。
在client端用类Pi实现了一个Task接口:
package client;
import compute.*;
import java.math.*;
public class Pi implements Task {
/** constants used in pi computation */
private static final BigDecimal ZERO =
BigDecimal.valueOf(0);
private static final BigDecimal ONE =
BigDecimal.valueOf(1);
private static final BigDecimal FOUR =
BigDecimal.valueOf(4);
/** rounding mode to use during pi computation */
private static final int roundingMode =
BigDecimal.ROUND_HALF_EVEN;
/** digits of precision after the decimal point */
private int digits;
/**
* Construct a task to calculate pi to the specified
* precision.
*/
public Pi(int digits) {
this.digits = digits;
}
/**
* Calculate pi.
*/
public Object execute() {
return computePi(digits);
}
…………
…………
}
再创建了一个ComputePi的类来使用远程接口:
package client;
import java.rmi.*;
import java.math.*;
import compute.*;
public class ComputePi {
public static void main(String args[]) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
String name = "//" + args[0] + "/Compute";
System.out.println("try to look up the Registry...");
Compute comp = (Compute) Naming.lookup(name);
System.out.println("Remote access succeed");
Pi task = new Pi(Integer.parseInt(args[1]));
BigDecimal pi = (BigDecimal) (comp.executeTask(task));
System.out.println(pi);
} catch (Exception e) {
System.err.println("ComputePi exception: " + e.getMessage());
e.printStackTrace();
}
}
}
我原本认为作为远程的方法调用,那么只要对client发布Compute和Task接口就可以了,于是在client端我的文件组织如下:
g:\classes\ 下有:client\Pi.class ,client\ComputePi.class; compute\Compute.class ,compute\Task.class;
而在server端我的文件组织如下:
c:\classes\ 下有:engine\ComputeEngine_Stub.class,ComputeEngine_Skel.class; C:\app下有:compute\Compute.class ,compute\Task.class; engine\ComputeEngine.class
然后在两台机器之间做实验:server:192.168.14.113 ;client:192.168.14.118均为Windows
在server上的操作为:
>start rmiregistry //这时没有classpath环境变量
>set classpath=c:\app> java -Djava.rmi.server.codebase=file:///C:\classes/ -Djava.security.policy=java.policy engne.ComputeEngine 192.168.14.113 //最后的参数是用来指明rmiregistry所在的机器,用在 String name = "//"
args[0]"/Compute"; …… Naming.rebind(name, engine);
java.policy文件如下:
grant {
permission java.net.SocketPermission "*:1024-65535",
"connect,accept";
permission java.net.SocketPermission "*:80", "connect";
};
然后再client端操作如下:
>set classpath=g:\classes>java -Djava.rmi.server.codebase=file:///g:\classes/ -Djava.security.policy=java.policy client.ComputePi 192.168.14.113 15//两个参数,第一个是指明rmiregistry的位置,第二个指明Pi的计算精度。
如此执行下来之后,结果server端可以起来,但是client端抛出异常,提示找不到engine.ComputeEngine_Stub,可是如果把ComputeEngine_Stub.class拷贝到client的 g:\classes\engine目录下,通过为调试而加入的语句,知道已经可以访问到CmputeEngnie这个类了,可是有提示 client.Pi找不到,可是client.Pi明明就在client的codebase里,
就以上的实验,有几个疑问:
1.在server的codebase中已经包含了ComputeEngine_Stub,可是为什么不能自己下载到client呢?是因为我的java.policy文件有问题吗?
2.既然Pi这个类的对象也要传递到server端,那么为什么这个类却不需要通过rmic来编译一下?
3.policy文件在使用有没有放在什么目录下的位置限制?
再后来我就在server和client上启动了IIS,然后将两个机器上的classes文件加分别拷贝到各自的网页根目录下,然后在server的codebase中使用了HTTP://192.168.14.118/classes/ 在client的codebase上使用了HTTP://192.168.14.113/classes/ 这样终于成功地完整执行了一次。
这样看来,难道RMI的运行还必须得借助HTTP服务?可是在JDK 文档中RMI的FAQ中说明不必需要HTTP服务的,内容如下:
A.2 Do I have to install the _Stub file in the client's CLASSPATH? I thought it could be downloaded. A stub class can be downloaded, if the server that is exporting the remote object annotates the marshalled stub instance with the java.rmi.server.codebase property, which indicates the location from where the stub class can be loaded. You should set the java.rmi.server.codebase property on the server exporting a remote object. While remote clients could set this property, they would then be limited to only getting remote objects from the specified codebase. You should not assume that any client VM will have specified a codebase that resolves to the location of your object.
When a remote object is marshalled by Java RMI (whether as an argument to a remote call or as a return value), the codebase for the stub class is retrieved by Java RMI and used to annotate the serialized stub. When the stub is unmarshalled, the codebase is used to load the stub classfile using the RMIClassLoader, unless the class can already be found in the CLASSPATH or by the context classloader for the receiving object, such as an applet codebase.
If the _Stub class was loaded by an RMIClassLoader, then Java RMI already knows which codebase to use for its annotation. If the _Stub class was loaded from the CLASSPATH, then there is no obvious codebase, and Java RMI consults the java.rmi.server.codebase system property to find the codebase. If the system property is not set, then the stub is marshalled with a null codebase, which means that it cannot be used unless the client has a matching copy of the _Stub classfile in the client's CLASSPATH.
It is easy to forget to specify the codebase property. One way to detect this error is to start the rmiregistry separately and without access to the application classes. This will force Naming.rebind to fail if the codebase is omitted.
这段话我没有看明白,到底是不是只要在server的codebase中包含了ComputeEngine_Stub就可以了呢?
到底是我的codebase给的不对呢还是policy文件写得有问题?抑或是别的问题?
如果有对这一部分熟悉的programmer,希望能对于这个例子,给出详细一点的解释,最好能给出正确的执行命令,如果我上面说的不清楚,那就请具体指出,我在加以叙述。