When we last saw our hero, she had fought with RMI for 3 hours only to emerge victorious. Alas, the victory was short-lived. Happy that her code was working, she became busy with another project for awhile and had not tried to run it again until this unfortunate day.
It is a sunny Monday morning, the sounds of the ocean and singing of birds creating a relaxing background music, when our hero sits down to look at her code after a month. She first thinks to herself, "now, what was I doing with this?", and the painful memories of a Friday one month earlier begin to surface. She makes no changes to her code and tries running it again through Netbeans. That still does not work. No problem, she says to herself, unaware that the evil overlord, RMI is waiting to attack with his ultimate weapon, the Exception. She tries to start it from the command line using the exact same command which had finally been successful last time.
The horror, the shock, the emptiness in the pit of her stomach destroyed the peaceful setting as she read:
port is 1099
RMI registry started.
Starting server...
Server failed to start.
java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
How can this be?? How has RMI yet again found a way to destroy our hero's happiness and force her to spend yet another day trying to simply get the server to run?
This sad story is still unfolding. Our hero is right now avoiding pulling her hair out by distracting herself writing silly blog entries. How will it end? Stay tuned!
The story continued:
Our hero's mental stability has begun to crumble. Again, for no reason that can be determined, the code mysteriously started working even though nothing was changed. The command which now works is:
java -Djava.security.manager -Djava.security.policy=policyfile -cp $HOME/nbprojects/TRProto2/dist/TRProto2.jar server.TestRunnerServer
Speed readers may not notice the subtle difference. Last time,
-Djava.security.policy=common/policyfile worked, this time, our hero had to remove the
common/ and use a policyfile outside of the
.jar file. As we leave our hero for the day, she is questioning both her heroness and whether to finally believe that now it does indeed work.
Hopefully there will be no need to check back with our hero. Perhaps RMI has once and for all been defeated. Those who have experienced RMI know better. They know somewhere, somehow, RMI is planning its revenge, waiting to strike when least expected.
...some days I really want to smack some Java engineer... or drop-kick my computer downstairs.
So, of all the things I ever do with Java, RMI has to be the most frustrating. It is frustrating because it is so cool and yet so hard to get running! I've used RMI on several projects and still I cannot seem to ever get it to just run on the first try. I am not talking about anything complex here like by two versions of Java are slightly off so the serialization doesn't work (which has happened to me... and between minor revisions of the same major release, 1.4.2). No, I am talking about something simple like creating a server with one function, which is just a stub still, and just making sure it can start up.
I wasted over three hours on this so I thought I'd blog my experience. Maybe I can save someone else the same waste of time. I wish I could say I got all the problems solved but at least I can start the server.
My first problem was with Netbeans. It is completely not obvious what you need to do to make it so you can run
rmic on your Remote classes. I spent a good 30 minutes trying to figure this out.
To run
rmic on your Remote class:
My second problem was also with Netbeans and I have not solved it yet. Once you have your code compiled and have run rmic, naturally you want to run the code. That didn't work so well for me. The first run resulted in:
Server failed to start.
java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:264)
...
at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
at java.rmi.Naming.rebind(Naming.java:160)
at server.TestRunnerServer.main(TestRunnerServer.java:143)
(Netbeans stack traces are upside down)
Quickly I though to myself that I probably need to add something to the java command since I do when I run RMI code at the command line. Normally I run RMI code with the following:
java -Djava.security.manager -Djava.security.policy=PATH_TO_POLICYFILE -cp MYJAR MYCLASS
So I decided to start there. I had put the policyfile in the common package, the class I want to run is in the server package. I added to "Arguments" in Properties:Run
-Djava.security.manager -Djava.security.policy=common/policyfile, I figured the classpath should not need setting. It did not help, the same stack trace happens. Well, maybe that was the wrong place to put the -D args, so I tried the "VM Options" which seems to be what the
Netbeans Tutorial was saying to do. Still the same problem.
At this point I decided to make sure it was not something with netbeans causing the problem so I tried to run my code from the command line. That did not work either but I did not save what the stack trace was. It was time to start looking on the
Java Forums and on Google for some ideas. The first page I came across was talking about how his problem was the
/etc/hosts on his machine had an entry like:
127.0.0.1 localhost mymachine
This was causing his stuff to try to connect to
mymachine when it should have been
localhost. That was not my problem but it made me start to wonder if maybe VPN or NIS was somehow confusing RMI. So, I moved the code to a machine at work rather than my home machine. The machine at work is the place it will run when deployed so I figured what better place to get it started.
With the code moved to my work machine, I tried to run it again, and still it failed. I did not save this stack trace either but I was wondering if maybe my code that automatically starts the registry was not working.
The code to start the registry:
try
{
Registry registry = LocateRegistry.createRegistry(port);
System.out.println("RMI registry started.");
}
catch (ExportException ee)
{
System.out.println("Failed to find RMI registry: port " + port
+ " already in use.");
}
catch (RemoteException re)
{
System.out.println("Failed to find registry");
if (debug)
{
System.err.println(re);
re.printStackTrace();
}
System.exit(1);
}
So, I started the rmiregistry by hand at the command line. That made the error change, which is progress of a sort. Now I was getting:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: server.TestRunnerServer_Stub
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: server.TestRunnerServer_Stub
So, off to look up that error. It seems people get it pretty often because I found lots of places talking about how to fix it. Here is a summary of the different suggestions:
- Check your files permission carefully. The directory containing the files will be
also required a+rx. Also make sure the path is correct.
- Use the parameter -Dxxx like in the following call:
java -classpath=..
-Djava.rmi.server.codebase=file://<IP>/<Drive-Letter>:/<Path to the
root directory of your packages>/<Class with main>
Do not forget to use the trailing '/'.
- If you have CLASSPATH set to .;xxx;yyy;zzz just start rmiregistry from the directory
where you have server object (to make . work for both rmiregistry and your server
object)
- Explode the jar and call the class file directly.
- Put the .class files in a web directory.
Run rmiregistry.
java -Djava.rmi.server.codebase=http://www.yourmachine.com/~<your-username>/
-Djava.security.policy=java.policy YourClass
rmi://:<port-number>/<a-name>
- You also need to set the classpath for the registry, with -J-Dclasspath=xxx
- Your rmic command needs a -d option specifying the output directory, which should be
the same as the classpath.
- You could try this instead:
javaw -classpath xxx
sun.rmi.registry.RegistryImpl
- I believe the remote class and stub must be visible to the RMIRegistry not just the
remote application. So you will have to unpack at least those two and put them in
a path the rmiregistry can find.
- Important! No classes shall be visible for rmiregistry.
You might get problems later by having it like that.
I think your problem is the codebase, set the codebase to the stubs and I think it
will work.
-Djava.rmi.server.codebase=file:/myhome/classes
- The solution: do NOT use a COMPRESSED jar file with an RMI server.
- I put my Server in a package and when running with "java" the stub could not be found.
java -cp .
java -classpath %CLASSPATH%;d:\my\class\path;
sun.rmi.registry.RegistryImpl
- I've taken to starting the registry in code in my server using
LocateRegistry.createRegistry() and documenting in my server documentation that any
already running registry MUST have the server jar (which contains the stub) on its
classpath.
java -classpath /jars/x.jar:/jars/y.jar
-Djava.rmi.codebase=file:/export/home/guy_t/
-
-Djava.rmi.server.codebase=file:/home/crease/java/basic/rmi_first/first.jar
- If the client and server do not share the same file system (even via NFS), then -Djava.rmi.server.codebase=file:///xyz will not work.
Using ftp instead, should work, but I have never used it. Read the doc, as I understand FTP always need a user name and password .. I do not know how that is shared.
com.foo.bar.MainClass
Woah, is your head spinning yet? Ok, has anyone developing Java thought that if there can be this many ways to mess up just finding the
_Stub that maybe something is not right here? So, here I am trying every one of these "solutions" (some really can't be called anything less than cheesy hack). And in the end, none of them worked.
Now, keep in mind through all of this, I have not changed a line of actual code, just played with my CLASSPATH, the args I am sending to the java command, extracting my class files out of the jar, etc.. I do not even know why, perhaps the gods finally took pity on me after 3 hours of this torture, but I decided to kill rmiregistry and then use just the following command:
java -Djava.security.manager -Djava.security.policy=common/policyfile -cp $HOME/nbprojects/TRProto2/dist/TRProto2.jar server.TestRunnerServer
And it worked. W??!?!?!?! I know what the problem was, the rmiregistry process I started did not have the classpath. But, I tried running it a few times before I started running rmiregistry by hand, and it didn't work. What is more, I did the same thing on my home machine where this started, and it worked there too!
Sadly Netbeans is still not working, but at least it runs. If I weren't so ready to be done for the weekend and enjoy my Friday I might be upset that there seems to be no reason why it suddenly works now.
Hey, yea, it is friday night... what the heck am I doing blogging? Enjoy your weekend!