< prev index next >

src/java.base/share/classes/java/lang/ClassLoader.java

Print this page

        

@@ -58,10 +58,11 @@
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 import jdk.internal.loader.BuiltinClassLoader;
+import jdk.internal.loader.ClassLoaderValue;
 import jdk.internal.perf.PerfCounter;
 import jdk.internal.loader.BootLoader;
 import jdk.internal.loader.ClassLoaders;
 import jdk.internal.misc.Unsafe;
 import jdk.internal.misc.VM;

@@ -2968,10 +2969,97 @@
 
     // Retrieves the assertion directives from the VM.
     private static native AssertionStatusDirectives retrieveDirectives();
 
 
+    // A RuntimeException that acts as a substitute for the original exception
+    // (checked or unchecked) and mimics the original exception in every aspect
+    // except it's type.
+    private static class InitExceptionSubst extends RuntimeException {
+        static final ClassLoaderValue<InitExceptionSubst> INIT_EXCEPTIONS =
+            new ClassLoaderValue<>();
+
+        private static final long serialVersionUID = 1;
+
+        private final String threadName;
+        private final long time;
+        private final String originalExceptionClassName;
+        private final String localizedMessage;
+
+        InitExceptionSubst(Throwable originalException, boolean top) {
+            super(originalException.getMessage());
+
+            this.threadName = top ? Thread.currentThread().getName() : null;
+            this.time = top ? System.currentTimeMillis() : 0L;
+            this.originalExceptionClassName = originalException.getClass().getName();
+            this.localizedMessage = originalException.getLocalizedMessage();
+
+            // substitute originalException's cause
+            Throwable cause = originalException.getCause();
+            initCause(cause == null ? null : new InitExceptionSubst(cause, false));
+
+            // substitute originalException's suppressed exceptions if any
+            for (Throwable suppressed : originalException.getSuppressed()) {
+                addSuppressed(new InitExceptionSubst(suppressed, false));
+            }
+
+            // inherit stack trace elements from originalException
+            setStackTrace(originalException.getStackTrace());
+        }
+
+        @Override
+        public Throwable fillInStackTrace() {
+            // don't need our backtrace -
+            // will inherit stack trace elements from originalException
+            return this;
+        }
+
+        @Override
+        public String getLocalizedMessage() {
+            return localizedMessage;
+        }
+
+        @Override
+        public String toString() {
+            // Emulate toString() method as if called upon originalException
+            String message = getLocalizedMessage();
+            return time == 0L
+                   ? (message != null
+                      ? originalExceptionClassName + ": " + message
+                      : originalExceptionClassName
+                   )
+                   : (message != null
+                      ? originalExceptionClassName + ": "  +
+                        (System.currentTimeMillis() - time) + " ms ago in thread " +
+                        threadName + ": " + message
+                      : originalExceptionClassName + ": "  +
+                        (System.currentTimeMillis() - time) + " ms ago in thread " +
+                        threadName
+                   );
+        }
+    }
+
+    // Called by the VM as notification just before exception
+    // (typically ExceptionInInitializerError) is thrown after unsuccessful
+    // attempt to initialize the class for the 1st time
+    private static void recordInitException(Class<?> clazz, Throwable exc) {
+        InitExceptionSubst.INIT_EXCEPTIONS
+            .sub(clazz)
+            .putIfAbsent(clazz.getClassLoader0(), new InitExceptionSubst(exc, true));
+    }
+
+    // Called by the vm to throw NoClassDefFoundError as a consequence of
+    // class re-initialization attempt after the 1st class initialization
+    // attempt failed
+    private static void throwReinitException(Class<?> clazz) {
+        InitExceptionSubst cause = InitExceptionSubst.INIT_EXCEPTIONS
+            .sub(clazz)
+            .get(clazz.getClassLoader0());
+        throw (Error) new NoClassDefFoundError(
+            "Could not initialize " + clazz).initCause(cause);
+    }
+
     // -- Misc --
 
     /**
      * Returns the ConcurrentHashMap used as a storage for ClassLoaderValue(s)
      * associated with this ClassLoader, creating it if it doesn't already exist.
< prev index next >