URLClassLoader, RMI & Equinox [message #91059] |
Fri, 22 June 2007 14:03 |
Brett Humphreys Messages: 17 Registered: July 2009 |
Junior Member |
|
|
All,
I ran into a problem with equinox this week and I thought I'd share my experience and solution.
Important Environment Notes:
I start equinox programatically through the eclipse starter
The eclipse starter is loaded within a URLClassLoader
This client calls an RMI method and passes a subclass of a method parameter
(similar to figure 4 in http://java.sun.com/j2se/1.5.0/docs/guide/rmi/codebase.html)
Problem:
When I would make an RMI call and would pass in a subclassed as a method param, in windows, I
would get a MalformedURLException (description was "No protocol: and "). I was receiving this
because some of my jars (and bundles for that matter) were in C:\Documents and Settings. I thought
I could get around this by moving the jar location to a temp directory, however in windows the
standard temp directory is not C:\WINNT\TEMP it is C:\Documents and Settings\<User>\Temp. Which
left me with the same problem I had started with. Incidentally, if I updated my home directory in
linux to a path with a space in it (/temp home/<user>) this problem would exist as well.
Solution:
For a number of reasons recreating this in linux allowed me to get further along on a solution.
What I found was the problem was with equinox. Any bundle that I had installed was producing a bad
file:/ url. A url that was not properly encoded. I thought the solution would be to update equinox
to call file.toURI().toURL() instead of just file.toURL(). Well, i updated equinox and the problem
went away, but after further digging, I realized that this was not an acceptable solution. For one,
I'd much rather use an off-the-shelf build of equinox than to something I had to patch. But the
bigger problem was the fact that equinox does not have toURI available to it in all execution
environments.
So I started digging into RMI trying to figure out how it was building this path. Long story made
short is this:
When you install a bundle in equinox and you're using a URL class loader it calls (via reflection to
get around some of the protection) 'addURL' on the classloader. Further, in RMI's default
RMIClassLoaderSPI it looks to see if the class that it is trying to serialize is loaded from a URL
classloader, if it is, it asks the URL classloader for its URLs and produces a space separated
string of URLs. This is where the problem lies, equinox was adding urls (via addURL) that had
spaces in them, and the URL class loader was trusting that there were no spaces in the URLs that it had.
The simple solution was to create a subclass of URLClassLoader which when addURL is called it tries
to call 'toURI', if this does not throw then the URL was properly encoded, if it does throw then I
build a new URI from the URL's pieces parts (proto, host, port, path etc), call 'toURL' on that URI,
and add that, not what was passed in.
This obviously works iff you have control of the classloader's type. If you're in an environment
where you do not have control of the classloader's type, and it is a plain URLClassLoader the
solution gets a bit more complex. To solve this you need to create your own implementation of an
RMIClassLoaderSPI which can clean up any non-encoded URIs when an RMI call is made. I did this by
looking closely at what the default does (java.rmi.server.RMIClassLoaderSPI which basically
delegates to a sun.* implementation), and looking closely at the OpenJDK to see the sun.*
implementation.
At anyrate, my problem is solved now (by solving via the URLClassLoader subclass method), and
hopefully no one else will have to go through all this pain just to make an RMI call.
-Brett
|
|
|
Powered by
FUDForum. Page generated in 0.04368 seconds