Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8202620

5.3: Clarify handling of exceptions thrown by ClassLoaders

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: specification
    • Labels:
      None
    • Subcomponent:
      vm

      Description

      The ClassLoader.loadClass method allows arbitrary user-defined behavior to occur during loading of classes. Notably, the method can throw any exception type, including RuntimeExceptions and Errors.

      5.3 claims that, if any error occurs, "an instance of a subclass of LinkageError" may be thrown. It does not account for the possibility of exceptions that are not LinkageErrors.

      The following test confirms that a RuntimeException thrown by ClassLoader.loadClass is exposed directly by class resolution.

      ~~~

      public class Test {
         public static void main(String... args) throws Exception {
             PreemptingClassLoader l = new PreemptingClassLoader("A", "B");
             Class<?> c = l.loadClass("A");
             Runnable r = (Runnable) c.getDeclaredConstructor().newInstance();
             r.run();
         }
      }

      ~~~

      public class A implements Runnable {
         public void run() {
             System.out.println(B.foo);
         }
      }

      ~~~

      public class B {
         public static String foo = "foo";
      }

      ~~~

      import java.util.*;
      import java.io.*;

      public class PreemptingClassLoader extends ClassLoader {

       private final Set<String> names = new HashSet<>();

       public PreemptingClassLoader(String... names) {
         for (String n : names) this.names.add(n);
       }

       protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
         if (!names.contains(name)) return super.loadClass(name, resolve);
         Class<?> result = findLoadedClass(name);
         if (result == null) {
           if (name.equals("B")) throw new RuntimeException("Hi, mom"); // INSERTED TO TRIGGER EXCEPTION
           String filename = name.replace('.', '/') + ".class";
           try (InputStream data = getResourceAsStream(filename)) {
             if (data == null) throw new ClassNotFoundException();
             try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
               int b;
               do {
                 b = data.read();
                 if (b >= 0) buffer.write(b);
               } while (b >= 0);
               byte[] bytes = buffer.toByteArray();
               result = defineClass(name, bytes, 0, bytes.length);
             }
           }
           catch (IOException e) {
             throw new ClassNotFoundException("Error reading class file", e);
           }
         }
         if (resolve) resolveClass(result);
         return result;
       }

      }

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              dlsmith Dan Smith
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: