【问题标题】:RMI (JDK 1.6): ClassNotFoundException for <server>_Stub in client-appRMI (JDK 1.6):客户端应用程序中 <server>_Stub 的 ClassNotFoundException
【发布时间】:2011-08-26 21:26:18
【问题描述】:

在google、stackoverflow-ing和在其他论坛寻找答案2天后,我终于放弃了,希望在这里找到答案!

我在运行一个简单的 RMI 客户端-服务器应用程序时遇到了一个烦人的问题,尽管它似乎(或者我只是遗漏了一点点)是 RMI 的一个众所周知的问题。

我正在使用 Eclipse 和 m2eclipse(Maven 集成)进行依赖管理。我的项目结构如下: - 客户端:客户端代码,对共享的依赖 - 服务器:服务器代码,对共享的依赖 - 共享:服务器和客户端之间的远程接口和共享类 - integrationtest:测试项目,同时依赖客户端和服务器

共享:

客户端可以调用的远程接口是:

public interface IServerReceiver extends Remote, Serializable {
  void connect(long clientId) throws RemoteException;
}

此接口位于客户端和服务器都依赖的共享项目中,因此它位于客户端的类路径中。

服务器端:

Server类负责创建注册表并导出远程对象(ServerReceiver实现IServerReceiver):

registry = LocateRegistry.createRegistry(rmiRegistryPort);
serverReceiver = new ServerReceiver(rmiRegistryPort);

registry.rebind(IConstants.RMI_SERVER_RECEIVER_ID, UnicastRemoteObject.exportObject(serverReceiver, 0));

在启动服务器时,通过定义-Djava.security.policy,向服务器的JVM提供一个安全策略文件(虽然我认为这与这里描述的问题无关)。

客户端

负责将客户端连接到服务器的类是ServerConnector类:

...
public ServerConnector() {
 ...
 registry = LocateRegistry.getRegistry(serverRMIHost, serverRMIPort);
 serverReceiver = (IServerReceiver) registry.lookup(IConstants.RMI_SERVER_RECEIVER_ID);
 serverReceiver.connect(client.getId());
 ...
}

Client 类的构造函数如下所示(省略了一些行):

public Client(..) {
 ....
 serverConnector = new ServerConnector(serverRMIHost, serverRMIPort, this);
 ....
}

除了服务器,客户端还获得了一个安全策略文件。

测试

当我启动集成测试创建服务器和客户端时:一切正常,connect() 方法返回预期结果。

但是,当我尝试正常启动服务器实例,然后启动尝试 connect() 到服务器的客户端实例时,会抛出以下异常:

Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: assign2.comm.ServerReceiver_Stub
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at assign2.comm.ServerConnector.<init>(ServerConnector.java:65)

在寻找答案时,最突出的是:客户端必须有权访问服务器接口定义 (IServerReceiver)。但是,对我来说,情况似乎就是这样,因为适当的依赖关系已经到位(客户端->共享,服务器->共享,IServerReceiver 位于共享中)。 我不太了解的异常的另一个方面是:从 JDK 1.5 开始,不再需要通过 rmic 生成存根,而是通过动态代理来实现。因此,客户端只需访问接口 IServerReceiver 即可创建动态代理。那他为什么会抱怨根本找不到 ServerReceiver_Stub 类呢?

由于集成测试成功运行并且在该测试中客户端还可以访问 IServerReceiver 接口的实现,我是否正确假设客户端不仅需要访问远程接口,而且也是实现吗?

我现在很困惑,所以也许你能帮我解释一下这个问题?感谢您阅读了那么长的帖子! ;-)

干杯, 克里斯

【问题讨论】:

  • 我刚刚设法让它工作!我没有足够注意(或没有意识到)异常告诉我的内容:请确保可以找到 ServerReceiver 的存根类 以便客户端能够通过动态加载该类类加载。在启动 server 时使用 -Djava.rmi.server.codebase 参数有帮助: -Djava.rmi.server.codebase=file:///absolute/path/to/server.jar 但是,上面的问题仍然存在:为什么客户端需要 _Stub 类,它是 IServerReceiver 接口的实现?

标签: java rmi classnotfoundexception


【解决方案1】:

我同意,您不需要存根,因为您在导出对象时提供了端口号。唯一的解释是生成的存根被绑定而不是动态存根。我会删除任何存在的存根文件并重新测试。如果服务器需要存根但没有存根,它应该在导出时失败,而不是在有人查找时失败。

我是否正确地假设客户 不仅需要访问远程 界面也是它的 实施?

没有。它只需要存根。实施是远程的。这就是整个想法。但它还需要存根所依赖的任何类,即远程接口和 它 所依赖的任何类,依此类推,直到关闭。

我还有一些问题:

  1. 为什么向新的 ServerReceiver(rmiRegistryPort) 提供注册表端口?
  2. 我假设 ServerReceiver 没有扩展 UnicastRemoteObject?
  3. 您确定这是正在执行的真实服务器代码吗?

【讨论】:

  • 感谢您的快速回答!
  • 关于您的问题: 1. ServerReceiver不需要,只是设置一个变量,现在已经重构了。 2. 正确。 3. 目前正在调查,因为我认为我的类路径中有“旧”存根类。会尽快回复您。
  • 感谢您指出周围可能存在不需要存在的 Stub 类。我生成的源似乎干扰了动态生成的类。
【解决方案2】:

IServerReceiver 应该不是Serializable。我的猜测是您的服务器实例是通过网络而不是代理发送的。

【讨论】:

  • 没有。如果是这种情况,那就是找不到服务器类本身,而不是存根类。远程接口可以扩展Serializable,这没什么不好。也没有意义。
  • 好点 - 立即将其删除。感谢 EJP 的解释。
猜你喜欢
  • 2014-06-14
  • 2017-01-16
  • 2016-10-15
  • 2013-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多