- /*
- * @(#)JDKClassLoader.java 1.20 03/01/23
- *
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- /*
- * Licensed Materials - Property of IBM
- * RMI-IIOP v1.0
- * Copyright IBM Corp. 1998 1999 All Rights Reserved
- *
- * US Government Users Restricted Rights - Use, duplication or
- * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- */
-
- package com.sun.corba.se.internal.util;
-
- import java.io.IOException;
- import java.io.*;
- import java.util.*;
- import java.lang.ref.WeakReference;
- import java.lang.reflect.*;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
-
- /**
- * Utility method for crawling call stack to load class
- */
- class JDKClassLoader {
-
- private static final JDKClassLoaderCache classCache
- = new JDKClassLoaderCache();
-
- private static final Class[] NO_ARGS = new Class[] {};
-
- // latestUserDefinedLoader() is a private static method
- // in java.io.ObjectInputStream in JDK 1.3 and 1.4.
- // We use reflection in a doPrivileged block to get a
- // Method reference and make it accessible.
- //
- // We should be very careful about what happens when this
- // method no longer exists! Right now, Errors will be
- // thrown.
- private static final Method latestUserDefinedLoaderMethod
- = JDKClassLoader.getLatestUserDefinedLoaderMethod();
-
- private static Method getLatestUserDefinedLoaderMethod() {
- return (Method) AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
-
- Method result = null;
-
- try {
- // Try to dig up the method we want
- Class io = java.io.ObjectInputStream.class;
- result
- = io.getDeclaredMethod("latestUserDefinedLoader",
- NO_ARGS);
-
- result.setAccessible(true);
-
- } catch (NoSuchMethodException nsme) {
- // Throw Errors right now. This is a serious problem
- throw new Error("java.io.ObjectInputStream"
- + " latestUserDefinedLoader "
- + nsme);
- }
-
- return result;
- }
- });
- }
-
- // Uses reflection and the final static Method (obtained
- // by reflection at class load time) to call the
- // latestUserDefinedLoader native code.
- private static ClassLoader getLatestUserDefinedLoader()
- {
- try {
- // Invoke the ObjectInputStream.latestUserDefinedLoader method
- return (ClassLoader)latestUserDefinedLoaderMethod.invoke(null,
- NO_ARGS);
- } catch (InvocationTargetException ite) {
- throw new Error("java.io.ObjectInputStream"
- + " latestUserDefinedLoader "
- + ite);
- } catch (IllegalAccessException iae) {
- throw new Error("java.io.ObjectInputStream"
- + " latestUserDefinedLoader "
- + iae);
- }
- }
-
- static Class loadClass(Class aClass, String className)
- throws ClassNotFoundException {
-
- // Maintain the same error semantics as Class.forName()
- if (className == null) {
- throw new NullPointerException();
- }
- if (className.length() == 0) {
- throw new ClassNotFoundException();
- }
-
- // It would be nice to bypass JDKClassLoader's attempts completely
- // if it's known that the latest user defined ClassLoader will
- // fail.
- //
- // Otherwise, we end up calling Class.forName here as well as in
- // the next step in JDKBridge. That can take a long time depending
- // on the length of the classpath.
-
- // Note: Looking at the only place in JDKBridge where this code
- // is invoked, it is clear that aClass will always be null.
- ClassLoader loader;
- if (aClass != null) {
- loader = aClass.getClassLoader();
- } else {
- loader = getLatestUserDefinedLoader();
- }
- // See createKey for a description of what's involved
- Object key = classCache.createKey(className, loader);
-
- if (classCache.knownToFail(key)) {
- throw new ClassNotFoundException(className);
- } else {
- try {
- // Loading this class with the call stack
- // loader isn't known to fail, so try
- // to load it.
- return Class.forName(className, false, loader);
- } catch(ClassNotFoundException cnfe) {
- // Record that we failed to find the class
- // with this particular loader. This way, we won't
- // waste time looking with this loader, again.
- classCache.recordFailure(key);
- throw cnfe;
- }
- }
- }
-
- /**
- * Private cache implementation specific to JDKClassLoader.
- */
- private static class JDKClassLoaderCache
- {
- // JDKClassLoader couldn't find the class with the located
- // ClassLoader. Note this in our cache so JDKClassLoader
- // can abort early next time.
- public final void recordFailure(Object key) {
- cache.put(key, JDKClassLoaderCache.KNOWN_TO_FAIL);
- }
-
- // Factory for a key (CacheKey is an implementation detail
- // of JDKClassLoaderCache).
- //
- // A key currently consists of the class name as well as
- // the latest user defined class loader, so it's fairly
- // expensive to create.
- public final Object createKey(String className, ClassLoader latestLoader) {
- return new CacheKey(className, latestLoader);
- }
-
- // Determine whether or not this combination of class name
- // and ClassLoader is known to fail.
- public final boolean knownToFail(Object key) {
- return cache.get(key) == JDKClassLoaderCache.KNOWN_TO_FAIL;
- }
-
- // Synchronized WeakHashMap
- private final Map cache
- = Collections.synchronizedMap(new WeakHashMap());
-
- // Cache result used to mark the caches when there is
- // no way JDKClassLoader could succeed with the given
- // key
- private static final Object KNOWN_TO_FAIL = new Object();
-
- // Key consisting of the class name and the latest
- // user defined class loader
- private static class CacheKey
- {
- String className;
- ClassLoader loader;
-
- public CacheKey(String className, ClassLoader loader) {
- this.className = className;
- this.loader = loader;
- }
-
- // Try to incorporate both class name and loader
- // into the hashcode
- public int hashCode() {
- if (loader == null)
- return className.hashCode();
- else
- return className.hashCode() ^ loader.hashCode();
- }
-
- public boolean equals(Object obj) {
- try {
-
- // WeakHashMap may compare null keys
- if (obj == null)
- return false;
-
- CacheKey other = (CacheKey)obj;
-
- // I've made a decision to actually compare the
- // loader references. I don't want a case when
- // two loader instances override their equals
- // methods and only compare code base.
- //
- // This way, at worst, our performance will
- // be slower, but we know we'll do the correct
- // loading.
- return (className.equals(other.className) &&
- loader == other.loader);
-
- } catch (ClassCastException cce) {
- return false;
- }
- }
- }
- }
- }