I'm trying to make a Java RMI client/server app. I'm running into problems starting up the server side of my app, as it keeps running into a ClassNotFoundException during the call to the Registry.bind() method when I attempt to start up the server side of the app.
I started with the simple tutorial here: http://docs.oracle.com/javase/1.5.0/docs/guide/rmi/hello/hello-world.html. After following those instructions, it was initially throwing a ClassNotFoundException complaining that it couldn't find "example.hello.Hello". I was able to resolve that by starting the rmiregistry FROM the destDir directory in the tutorial, since rmiregistry, apparently, uses its initial starting directory as part of its classpath.
I started on my other test app after that, and I was fine until I started to use third-party jar files in my server class. Now Registry.bind() throws a ClassNotFoundException if my server class references anything in any jar file since the rmiregistry app doesn't know about those jar files.
As far as I can tell, rmiregistry does not accept any sort of classpath startup arg, so I'm wondering how I can tell it what classpath I want it to acknowledge. According to the tutorial here: http://docs.oracle.com/javase/tutorial/rmi/running.html, "you must make sure that the shell or window in which you will run rmiregistry either has no CLASSPATH environment variable set or has a CLASSPATH environment variable that does not include the path to any classes that you want downloaded to clients of your remote objects." That sounds like the opposite of what I need... or am I reading it incorrectly? Has anyone had any success starting up a RMI client/server that uses third-party jars (commons-io, commons-logging, and rmiio, in my case)?
This is on Windows, by they way.
Update
I found a way around it. See my answer below.
The remarks about unsetting the CLASSPATH only apply if you're using the RMI codebase feature, as the cryptic remark about downloading classes is intended to imply.
If you're not using the codebase feature, just either:
- Set the CLASSPATH environment variable as required before starting
rmiregistry,
as shown elsewhere here, or
- Start
rmiregistry
with -J-Djava.class.path=...
- Start the Registry inside your server JVM, with
LocateRegistry.createRegistry().
This is in many ways the best solution, as it lives and dies with the JVM, shares its classpath, and incidentally complies with the requirements for the codebase feature as well.
If you do (3), you must store the return value into a static variable to prevent it from being garbage-collected, which causes the registry to be unexported, which can lead to DGC of the remote object, which can lead to it being GC'd, which will lead to the listening thread exiting, which can lead to the whole JVM exiting.
There is no RMI Registry specific java class path. It should use the same classpath as the rest of the Java system. You can set your classpath by specifying it on the command line, or in your OS environment. Though a little dated, this doc should point you in the right direction.
(I had originally posted this answer as an update to my question. Per Zecas's suggestion in one of the comments, I'm moving it to the answer section.)
I was able to get the server part to start up by disobeying the suggestion in the second tutorial referenced in my question above:
"you must make sure that the shell or window in which you will run
rmiregistry either has no CLASSPATH environment variable set or
has a CLASSPATH environment variable that does not include the path to
any classes that you want downloaded to clients of your remote
objects."
I created a CLASSPATH environment variable and added my .class output directory and each of the third-party jar files to that. I thought I had tried that before, but I guess not... Just thought I'd leave my solution in case someone else has the same problem.
An example of how to set classpath for rmiregistry
Win32:
set CLASSPATH=c:\home\ann\src;c:\home\ann\public_html\classes\compute.jar
UNIX:
setenv CLASSPATH /home/ann/src:/home/ann/public_html/classes/compute.jar