Hi all,
  
 I've long had an impression that all class loaders in an loadClass() chain 
should be recorded as initiating loaders. That knowledge comes from Inside the 
Java Virtual Machine, 2nd Edition [1]:
 But it looks like that's an incorrect impression.
  
 According to the JVMS2e 5.3.2 [2], the "Otherwise" paragraph, only when the 
JVM initiates a class loading process, the loader directly invoked by the JVM 
will be recorded as the initiating loader; the ones in the middle of the 
delegation chain won't be recorded. A loader whose loadClass() is called 
explicitly (and successfully completed) from Java code isn't necessarily 
recorded as an initiating loader.
  
 Using the example in [1] in the "Now imagine" paragraph, only Cindy should be 
an initiating loader of java.io.FileReader; neither Mom nor Grandma should be 
recorded as an initiating loader.
 I've tried writing an actual code to emulate the example, and on JDK6, it 
shows the description in [1] is wrong.
  
 -----------------------------------------------
 TestInitiatingLoader.java
  
 import java.io.*;
import java.net.*;
  
 public class TestInitiatingLoader {
  public static void main(String[] args) throws Exception {
    Grandma grandma = new Grandma();
    Mom mom = new Mom(grandma);
    Cindy cindy = new Cindy(mom);
    cindy.loadClass("Dummy").newInstance(); // force class init
    
    final String reader = "java.io.FileReader";
    printStats(grandma, reader); // false
    printStats(mom, reader);     // false
    printStats(cindy, reader);   // true
  }
  
  private static void printStats(LoadedClassQueryable loader, String name) {
    System.out.printf("Is %s an initiating loader of %s: %b\n",
      loader.getClass().getName(),
      name,
      loader.foundLoadedClass(name));
  }
}
  
 interface LoadedClassQueryable {
  boolean foundLoadedClass(String name);
}
  
 class Cindy
    extends URLClassLoader
    implements LoadedClassQueryable {
  public Cindy(ClassLoader parent) {
    super(makeArgs(), parent);
  }
  
  private static URL[] makeArgs() {
    try {
      return new URL[] { new File(".").toURI().toURL() };
    } catch (Exception e) {
      e.printStackTrace();
      return new URL[] { };
    }
  }
  
  @Override
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    if ("Dummy".equals(name)) {
      // force Cindy to load this Dummy class
      return findClass("Dummy");
    }
    return super.loadClass(name);
  }
  
  @Override
  public boolean foundLoadedClass(String name) {
    return findLoadedClass(name) != null;
  }
}
  
 class Mom
    extends ClassLoader
    implements LoadedClassQueryable {
  public Mom(ClassLoader parent) {
    super(parent);
  }
  
  @Override
  public boolean foundLoadedClass(String name) {
    return findLoadedClass(name) != null;
  }
}
  
 // use system class loader as parent
class Grandma
    extends ClassLoader
    implements LoadedClassQueryable {
  @Override
  public boolean foundLoadedClass(String name) {
    return findLoadedClass(name) != null;
  }
}
-----------------------------------------------
 Dummy.java
  
 import java.io.*;
  
 public class Dummy {
  static {
    // Dummy should be loaded by Cindy.
    // So Cindy will be recorded as an initiating loader for java.io.FileReader
    try {
      new FileReader("Dummy.class").close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
-----------------------------------------------
  
 Could anybody clarify which loaders are supposed to be recorded as an 
initiating loader?
  
 [1]: http://www.artima.com/insidejvm/ed2/linkmod3.html
 [2]: 
http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#79441

Reply via email to