【问题标题】:Why Does Simple RMI Server Need Codebase?为什么简单 RMI 服务器需要代码库?
【发布时间】:2014-12-27 20:46:08
【问题描述】:

操作系统:Windows 7
JDK:1.8.0_05

我正在学习一些简单的 RMI 教程,包括 Oracle 的“计算”示例 (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

当我这样做时,没有人会尝试联系我的 Web 服务器(这并不让我感到惊讶),但我仍然会收到 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" );
}

谁能告诉我我错过了什么?

谢谢。

【问题讨论】:

    标签: java rmi


    【解决方案1】:

    您是对的,您不需要使用代码库功能。然而,这意味着您的远程接口、它所依赖的类和存根(如果您正在使用一个)必须在其类路径上对客户端和注册表都可用。您收到的异常表明注册表在其 CLASSPATH 上没有这些类。

    【讨论】:

    • 根据文档,UnicastRemoteObject.exportObject() 创建存根,而注册表只是将它绑定到一个名称。为什么注册中心需要接口声明?
    • 因为它是通过 RMI 本身访问的。
    • 存根似乎是在绑定之前由 RMI 创建的。如果这是真的,注册服务器只需要知道它在哪里,而不是它是什么类型。
    • @JackStraub 你一边走一边编。 RMI 要求两个对等方都可以访问这些类。注册表也不例外。例外就是证明。
    【解决方案2】:

    它以这种方式工作的原因是 RMI Registry 是使用 RMI 自己实现的。因此,当您在一个应用程序中调用bind 时,rmiregistry 进程中Registry 的实际实现将接收一个实现所有Remote 接口的存根对象,就像所有RMI 调用传递对远程对象的引用一样。

    因此rmiregistry 进程需要访问由bind 对象实现的所有远程接口,但正如您已经发现的那样,它的实际内容是无关紧要的,因为注册表永远不会调用它上面的任何方法。它所做的只是将远程存根交给 lookup 的调用者,在这种情况下,存根被序列化并传输到远程调用者的 JVM,在那里创建等效的存根,再次实现存根的所有远程接口(因此是原始对象) 已实施。

    因此,rmiregistry 进程(如果您将其作为独立进程启动)中的接口所发生的所有事情都是记录有关由远程对象实现的远程接口的信息,方法是让存根相应地实现它们,并传递给所有其他执行查找的远程应用程序。

    原则上,可以通过不使用 RMI 以不同方式实现整个注册表并记录实现的接口而不需要它们的类文件,但是,这将是一项巨大的工作,因为您必须实现所有您需要的东西只需使用 RMI 即可免费获得。

    但请注意,如果您只有一个服务器,您可以通过让服务器本身 create the registry 在其自己的 JVM 中来简化一切。然后你不需要启动另一个进程,也不需要考虑它的类路径/代码库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 2012-09-25
      • 2017-06-23
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多