Hudson remoting does not retrieve resources in locally pre-fetched JARs. Though this is a minor performance issue, it causes some Java APIs to break.
For example, a Java API used in a Callable might expect that certain resources exist in the same JAR. So, to make the API happy, the Callable will use the PreloadJarTask to get the JAR pre-loaded on the client. With the current Hudson remoting, each resource retrieval causes the resource to be copied to a local file without considering whether it resides in a pre-loaded JAR.
Consider a callable that uses the Java API called ReallyCool. Assume ReallyCool has a method named execute and we've pre-loaded all JARs containing logo.png:
01: public static void execute() { 02: 03: // This call will cause every resource with the name logo.png 04: // to be downloaded from the server and stored as a local 05: // file. This should be fixed so that it first checks if any 06: // of the pre-loaded JARs have the resource before downloading 07: // them from the server. 08: 09: Enumeration<URL> logo = classLoader.getResources("logo.png"); 10: 11: while (logo.hasMoreElements()) { 12: String urlspec = logo.nextElement().toExternalForm(); 13: urlspec = urlspec.replace("logo.png", "META-INF/MANIFEST.MF"); 14: URL manifest = new URL(urlspec); 15: InputStream stream manifest.openStream(); 16: // Oh, no: stream is null since the URL isn't alongside 17: // logo in the same directory. 18: } 19: }
With the current implementation, the following will happen.
In line 12, logo's URL will be something like: jar:file:/tmp/hudson-remoting1234/logo.png. This is because the RemoteClassLoader findResources() method always downloads resources from the server without considering the pre-loaded JARs.
When the manifest URL is created in lines 13-15, it will be: jar:file:/tmp/hudson-remoting1234/META-INF/MANIFEST.MF. When we attempt to open a stream on it, the stream will be null since that file does not exist.
However, since we pre-loaded it, we expect the lookups of logo.png in line 12 to be like: jar:file:/tmp/hudson-remoting5678/important.jar!/logo.png. Then, the code above that looks up the manifest file in lines 13-15 will work properly since important.jar will exist locally.
I provided a unit test, RemoteClassLoaderTest, that tests this functionality and illustrates the failure.