简体   繁体   中英

Does URLClassLoader traverse MANIFEST.MF Class-Path headers properly?

UPDATE 1: Indeed, the URL format differences cause the error. Here is a unit test (cut and obfuscated by hand; hope I didn't miss anything) showing the problem:

@Test
public void wheresWaldo2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException, NoSuchMethodException {
  // Find Waldo from file:/someLocation/waldo.jar.  Prove that works.
  URL waldosJar = new File("/someLocation/waldo.jar").toURI().toURL();
  assertNotNull(waldosJar);
  assertEquals("file", waldosJar.getProtocol());
  String waldosPath = waldosJar.getPath();
  assertNotNull(waldosPath);
  assertTrue(waldosPath.endsWith("/waldo.jar"));

  ClassLoader cl = new URLClassLoader(new URL[] { waldosJar }, this.getClass().getClassLoader());

  Class<?> waldosClass = cl.loadClass("com.foobar.Waldo");
  assertNotNull(waldosClass);
  assertEquals("com.foobar.Waldo", waldosClass.getName());
  assertSame(cl, waldosClass.getClassLoader());

  Class<?> jimbosClass = cl.loadClass("com.foobar.Jimbo"); // Note: works
  assertNotNull(jimbosClass);

  // Find Waldo from jar:file:/someLocation/waldo.jar!/.  Prove that works.
  // This URL, when passed to a URLClassLoader, should result in the same
  // functionality as the first one.  But it doesn't.
  waldosJar = new URL("jar:" + waldosJar.toExternalForm() + "!/");
  assertEquals("jar", waldosJar.getProtocol());
  assertEquals("file:" + waldosPath + "!/", waldosJar.getPath());

  cl = new URLClassLoader(new URL[] { waldosJar }, this.getClass().getClassLoader());

  waldosClass = cl.loadClass("com.foobar.Waldo");
  assertNotNull(waldosClass);
  assertEquals("com.foobar.Waldo", waldosClass.getName());
  assertSame(cl, waldosClass.getClassLoader());

  jimbosClass = cl.loadClass("com.foobar.Jimbo"); // XXX FAILS
}

UPDATE 0: The problem may have to do with the supposed, but not actual, equivalence between two URL s referring to a jar file. For example, the following two URLs are supposed to refer to the same file:

  • file:/myjar.jar
  • jar:file:/myjar.jar!/

When I pass a URL built from the first format to my machinery, I think things are working. When I pass a URL built from the second format, I get the results described below. I am testing more to confirm all this beyond a doubt.

ORIGINAL QUESTION

(I am aware of this question .)

I have a jar file, waldo.jar , that contains a META-INF/MANIFEST.MF that looks like this:

Manifest-Version: 1.0
Class-Path: jimbo.jar

It also has a class at the following location within it:

com/foobar/Waldo.class

The class' source code is essentially:

package com.foobar;

public class Waldo {
  public Jimbo getJimbo() {
    return null;
  }
}

Next, in the same directory, I have a jar file, jimbo.jar , that contains a class at the following location within it:

com/foobar/Jimbo.class

That class' source code is essentially:

package com.foobar;

public class Jimbo {

}

Now I construct a URLClassLoader with a URL to waldo.jar . To review: jimbo.jar contains Jimbo and is "next to" waldo.jar , and is listed in waldo.jar 's META-INF/MANIFEST-MF 's Class-Path header appropriately. waldo.jar contains Waldo , that has a code reference to Jimbo . With me so far?

I can load com.foobar.Waldo just fine. But if I do something with Waldo that involves com.foobar.Jimbo , like, for example, calling Waldo.class.getDeclaredMethod("getJimbo") , I get a NoClassDefFoundError . Here is a sample stack:

java.lang.NoClassDefFoundError: com/foobar/Jimbo
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethod(Class.java:2128)
    at com.foobar.TestClassLoadingProblems.wheresWaldo(TestClassLoadingProblems.java:115)

    Caused by:
    java.lang.ClassNotFoundException: com.foobar.Jimbo
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at com.foobar.MyClassLoader.findClass(...) // calls super.findClass()
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

This suggests to me that URLClassLoader is not consulting the Class-Path header properly in all cases (I'm aware of how it works in general ).

Can anyone shed light on what is happening here?

这是由于JDK中错误所致。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM