为什么RMI注册表忽略了java.rmi.server.codebase属性

8

我正在运行一个Java RMI的Hello World示例。

1) 我在一个空文件夹中运行注册表。

motta@motta-laptop ~/tmp $ rmiregistry

2) 我启动HTTP服务器以在运行时检索类。下载文件夹包含客户端-服务器的远程接口。

motta@motta-laptop ~/download $ java NanoHTTPD 8080

3) 按照Java RMI教程建议,我通过传递java.rmi.server.codebase属性来启动服务器。

motta@motta-laptop ~/server $ java -Djava.rmi.server.codebase="http://localhost:8080" WarehouseServer

RMI注册表无法联系HTTP服务器并抛出异常(详见问题后的细节)。但如果按照以下步骤操作: 1)使用java.rmi.server.codebase属性启动rmi注册表。
motta@motta-laptop ~/tmp $ rmiregistry -J-Djava.rmi.server.codebase="http://localhost:8080/"

2) 如之前一样启动HTTP服务器

3) 以默认选项启动服务器

motta@motta-laptop ~/server $ java WarehouseServer
它能工作,但是为什么?似乎在第一个过程中,RMI注册表忽略了java.rmi.server.codebase属性。

谢谢

=================================

我正在运行

java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

RMI注册表的异常

Constructing server implementation...
Binding server implementation to registry...
Exception in thread "main" java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
java.lang.ClassNotFoundException: Warehouse
at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:419)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:267)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:553)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:808)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:667)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:273)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:251)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:377)
at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
at WarehouseServer.main(WarehouseServer.java:14)

非常奇怪。您能否尝试再次执行,而不要在代码库URL周围加上引号? - user207421
1
嗨EJP,我已经尝试过不加引号了,但行为仍然相同。 - mottalrd
我仍在处理这个问题,它看起来确实像是一个 bug。上述问题出现在我的 Linux Mint 虚拟机中,而如果我在 Windows 机器上运行完全相同的代码(和配置),一切都很好。 - mottalrd
我确认在Windows中,我不需要向rmiregistry传递codebase参数,而在Linux Mint中我需要。 我已记录下所有内容,并设置了一个存储库,其中包含示例和运行说明。 https://github.com/mottalrd/RMITutorial (Warehouse v1项目集) - mottalrd
在Linux中,当您启动注册表并指向您的服务器代码时,是否可能设置了CLASSPATH环境变量? - user207421
我已经检查过,classpath是空的。我从控制台和使用eclipse外部工具命令启动了RMI注册表(提供一个没有项目相关类的工作目录)。行为是相同的 :/ - mottalrd
1个回答

16
似乎在第一个过程中,RMI注册表正在忽略java.rmi.server.codebase属性。
是的,这是正确的。原因是,在JDK 7u21之后,java.rmi.server.useCodebaseOnly属性默认为true,而在之前的版本中默认为false
useCodebaseOnlyfalse时,RMI注册表(以及RMI客户端)使用从服务器传递给它们的代码库。现在默认值为true,注册表和客户端将忽略服务器的codebase属性。注册表和客户端必须设置自己的codebase属性以匹配服务器的属性,或者(不建议)可以将useCodebaseOnly设置回false。有关详细信息,请参见JDK 7中的RMI增强功能RMI教程尚未更新以反映此更改。抱歉。我们将确保更新它。

非常非常有用的答案!非常感谢,你节省了我很多时间!不管怎样,Oracle在这个修改上做得很烂。首先,它没有任何理由(甚至连Oracle自己都没有提供)。其次,正如他们自己所写:“默认值的更改可能会导致基于RMI的应用程序意外中断”。是的!这就是我们如此痛苦地认识到的Oracle!SUN,我们非常想念你! - wrzasa
@wrzasa:可能的理由是这个漏洞。提到的“其他供应商”很可能是这个 - mins
RMI教程尚未提到useCodebaseOnly属性。即使只有一点提示,也可以帮助人们避免与示例代码不兼容的困惑 - 就像我一样。请更新教程以保持高质量。 - chris
2
先生,非常抱歉地通知您RMI代码仍未更新。看起来您忘记了您的承诺!@StuartMarks - asad
2
教程仍未更新至Java 8。我刚刚使用以下命令使其运行:rmiregistry -J-Djava.rmi.server.codebase=file:///<path-to-remote-classes>/ - Paul Uszak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接