OS: Windows 7
JDK: 1.8.0_05
I am working through some simple RMI tutorials including Oracle's "Compute" sample (compute). Starting my server should not require a codebase, and answers to questions similar to this one say that "the codebase is optional." Yet my server can't register a remote object unless its interface is in some codebase.
I make sure my Compute interface is available to the web server running on localhost, start the registry server like this:
set CLASSPATH=
rmiregistry -J-Djava.rmi.server.codebase="http://localhost:80/"
And everything works fine:
Exporting stub
Locating registry
Binding stub
ComputeEngine bound
But if I remove Compute.class from the web server's path I get a 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
I can see from the web server log that an attempt was made to download Compute.class:
GET '/edu/uweo/java2/rmi/compute/server/Compute.class'
I also tried starting the registry server without specifying a codebase:
set CLASSPATH=
rmiregistry
When I do it this way nobody tries to contact my web server (which doesn't surprise me) but I still get the ClassNotFoundException.
My code comes right out of Oracle's tutorial with a couple of extra printed diagnostics thrown in:
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" );
}
Can anyone tell me what I'm missing?
Thanks.
You are correct, you don't need to use the codebase feature. However that means that your remote interface, the classes it depends on, and the stub if you're using one must be available to both the client and the Registry on their classpaths. The exception you're getting indicates that the Registry doesn't have these classes on its CLASSPATH.
The reason it works this way, is that the RMI Registry
is implemented itself using RMI. So when you call bind
in one application, the actual implementation of Registry
within the rmiregistry
process will receive a stub object implementing all of your Remote
interfaces just as with all RMI invocations passing references to remote objects.
Therefore the rmiregistry
process needs access to all remote interfaces implemented by the object you bind
but as you already have discovered, its actual contents is irrelevant as the registry will never invoke any methods on it. All it does is handing the remote stub to callers of lookup
and in this case the stub is serialized and transmitted to the remote caller’s JVM where an equivalent stub is created, again, implementing all remote interfaces the stub (and hence the original object) has implemented.
So all that happens with the interfaces within the rmiregistry
process (if you started it as stand-alone process) is that the information about the remote interfaces implemented by a remote object is recorded, by letting a stub implement them accordingly, and passed to all other remote applications performing a lookup.
In principle it would be possible to implement the entire registry differently by not using RMI and record the implemented interface without the need for their class files, however, that would be a huge effort as you would have to implement all the things you get for free when simply using RMI.
But note that, if you have only a single server, you can simplify everything by letting your server itself create the registry within its own JVM. Then you don’t need to start another process nor think about its classpath/codebase.
来源:https://stackoverflow.com/questions/27671325/why-does-simple-rmi-server-need-codebase