操作系统:Windows 7
JDK:1.8.0_05
我正在编写一些简单的RMI教程,包括Oracle" Compute"样本(compute)。启动我的服务器不应该需要代码库,并且类似于这个问题的答案说"代码库是可选的。"然而,我的服务器无法注册远程对象,除非它的接口位于某个代码库中。
我确保我的Compute接口可用于在localhost上运行的Web服务器,启动注册表服务器,如下所示:
set CLASSPATH=
rmiregistry -J-Djava.rmi.server.codebase="http://localhost:80/"
一切正常:
Exporting stub
Locating registry
Binding stub
ComputeEngine bound
但是如果我从Web服务器的路径中删除Compute.class,我会得到一个ClassNotFoundException:
Exporting stub
Locating registry
Binding stub
java.rmi.ServerException: RemoteException occurred ...:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: edu.uweo.java2.rmi.compute.server.Compute
我可以从Web服务器日志中看到尝试下载Compute.class:
GET '/edu/uweo/java2/rmi/compute/server/Compute.class'
我还尝试启动注册表服务器而不指定代码库:
set CLASSPATH=
rmiregistry
当我这样做时,没有人试图联系我的网络服务器(这并不让我感到惊讶),但我仍然得到ClassNotFoundException。
我的代码来自Oracle教程,其中包含几个额外的打印诊断信息:
try
{
String name = "Compute";
ComputeEngine engine = new ComputeEngine();
System.out.println( "Exporting stub" );
Compute stub =
(Compute)UnicastRemoteObject.exportObject( engine, 0 );
System.out.println( "Locating registry " );
Registry registry = LocateRegistry.getRegistry();
System.out.println( "Binding stub" );
registry.rebind( name, stub );
System.out.println( "ComputeEngine bound" );
}
谁能告诉我我失踪了什么?
感谢。
答案 0 :(得分:1)
您是对的,您不需要使用代码库功能。但是,这意味着您的远程接口,它依赖的类以及存根(如果您正在使用它)必须可以在其类路径上同时对客户端和注册表使用。您获得的异常表明注册表在其CLASSPATH上没有这些类。
答案 1 :(得分:0)
它以这种方式工作的原因是RMI Registry
是使用RMI实现的。因此,当您在一个应用程序中调用bind
时,Registry
进程中rmiregistry
的实际实现将收到实现所有Remote
接口的存根对象,就像所有RMI一样调用传递对远程对象的引用。
因此rmiregistry
进程需要访问由bind
对象实现的所有远程接口,但正如您已经发现的那样,其实际内容无关紧要,因为注册表永远不会调用其上的任何方法。所有这一切都是将远程存根交给lookup
的调用者,在这种情况下,存根被序列化并传输到远程调用者的JVM,在JVM中创建等效存根,再次实现存根的所有远程接口(因此原始对象)已经实现。
因此,rmiregistry
进程中的接口(如果您将其作为独立进程启动)所发生的一切就是通过使用存根实现来记录远程对象实现的远程接口的信息相应地,它们被传递给执行查找的所有其他远程应用程序。
原则上可以通过不使用RMI并记录实现的接口而不需要他们的类文件来实现整个注册表,但是,这将是一项巨大的工作,因为您必须实现所有的事情只需使用RMI就可以免费获得。
但请注意,如果您只有一台服务器,则可以通过让服务器本身create the registry在自己的JVM中来简化所有事情。然后你不需要启动另一个进程,也不需要考虑它的classpath / codebase。