RMI (JDK 1.6):客户端应用程序中_Stub 的 ClassNotFoundException

发布于 2024-11-08 11:10:00 字数 2355 浏览 2 评论 0原文

经过两天的谷歌搜索、stackoverflow-ing以及在其他论坛寻找答案,我终于放弃了,希望能在这里找到答案!

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

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

共享:

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

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 接口的实现,我是否正确地假设客户端不仅需要访问远程接口,还需要访问它的实现?

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

干杯, 克里斯

After 2 days of googling, stackoverflow-ing and looking for answers in other forums, I finally gave up and hope to find an answer here!

I have an annoying problem in getting a simple RMI client-server application to run although it seems (or maybe I'm simply missing a tiny bit) to be a well-known issue with RMI.

I'm using Eclipse and m2eclipse (Maven integration) for dependency management. My project structure is as follows:
- client: the client code, dependency to shared
- server: server code, dependency to shared
- shared: remote interfaces and shared classes between server and client
- integrationtest: test project, having both dependencies on client and server

Shared:

The remote interface clients can call is:

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

This interface is in the shared project where both client and server have dependencies on so it is in the client's classpath.

Server-side:

The Server class is responsible for creating a registry and exporting the remote object (ServerReceiver implements IServerReceiver):

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

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

When starting the server, a security policy file is provided to the server's JVM by defining -Djava.security.policy (although I don't think that this has to do with the issue described here).

Client-side

The class responsible for connecting the client to the server is the ServerConnector class:

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

The Client class' constructor looks like this (some lines omitted):

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

As well as the server, the client also gets a security policy file.

Testing

When I fire up an integration test creating a server and then a client: all works fine as expected and the connect()-method returns the expected result.

However, when I try to normally start a Server instance, then starting the client instance which tries to connect() to the server, the following exception gets throws:

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)

While looking for answers, the most prominent was: the client must have access to the servers interface definition (IServerReceiver). However, to me it seems like that's the case here as proper dependencies are in place (client->shared, server->shared, IServerReceiver place in shared).
Another aspect of the exception I don't quite understand is: since JDK 1.5, stub generation through rmic is no longer necessary and is achieved through dynamic proxyies. The client therefore only needs access to the interface IServerReceiver in order to create the dynamic proxy. Why would he be complaining about not finding the ServerReceiver_Stub class at all then?

Since the integration test runs successfully and within that test the client also has access to the implementation of the IServerReceiver interface, am I right in assuming that the client needs not only access to the remote interface but also it's implementation?

I'm pretty confused by now so maybe you can help me shed some light on this matter? Thanks for having read that long post! ;-)

Cheers,
Chris

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一身软味 2024-11-15 11:10:00

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

我的假设正确吗?
不仅需要访问远程
接口而且还有它的
实施?

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

我还有一些进一步的问题:

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

I agree, you don't need a stub, as you are providing a port number when exporting the object. The only explanation is that a generated stub got bound instead of a dynamic stub. I would delete any stub files that exist and re-test. If the server needs a stub and doesn't have one it should fail at export time, not when someone looks it up.

Am I right in assuming that the client
needs not only access to the remote
interface but also its
implementation?

No. It only needs the stub. The implementation is remote. That's the whole idea. But it also needs whatever classes the stub depends on, i.e. the remote interface and any classes it depends on, and so on until closure.

I have some further questions:

  1. Why is the registry port being provided to the new ServerReceiver(rmiRegistryPort)?
  2. I assume ServerReceiver doesn't extend UnicastRemoteObject?
  3. Are you sure this is the real server code that is executing?
梦亿 2024-11-15 11:10:00

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

IServerReceiver should not be Serializable. my guess is that your server instance is being sent over the network instead of the proxy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文