开发者

RMI (JDK 1.6): ClassNotFoundException for <server>_Stub in client-app

开发者 https://www.devze.com 2023-03-06 14:55 出处:网络
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!

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 serve开发者_如何转开发r, 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


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?


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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号