1. /*
  2. * @(#)JDKClassLoader.java 1.20 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * Licensed Materials - Property of IBM
  9. * RMI-IIOP v1.0
  10. * Copyright IBM Corp. 1998 1999 All Rights Reserved
  11. *
  12. * US Government Users Restricted Rights - Use, duplication or
  13. * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  14. */
  15. package com.sun.corba.se.internal.util;
  16. import java.io.IOException;
  17. import java.io.*;
  18. import java.util.*;
  19. import java.lang.ref.WeakReference;
  20. import java.lang.reflect.*;
  21. import java.security.AccessController;
  22. import java.security.PrivilegedAction;
  23. /**
  24. * Utility method for crawling call stack to load class
  25. */
  26. class JDKClassLoader {
  27. private static final JDKClassLoaderCache classCache
  28. = new JDKClassLoaderCache();
  29. private static final Class[] NO_ARGS = new Class[] {};
  30. // latestUserDefinedLoader() is a private static method
  31. // in java.io.ObjectInputStream in JDK 1.3 and 1.4.
  32. // We use reflection in a doPrivileged block to get a
  33. // Method reference and make it accessible.
  34. //
  35. // We should be very careful about what happens when this
  36. // method no longer exists! Right now, Errors will be
  37. // thrown.
  38. private static final Method latestUserDefinedLoaderMethod
  39. = JDKClassLoader.getLatestUserDefinedLoaderMethod();
  40. private static Method getLatestUserDefinedLoaderMethod() {
  41. return (Method) AccessController.doPrivileged(new PrivilegedAction() {
  42. public Object run() {
  43. Method result = null;
  44. try {
  45. // Try to dig up the method we want
  46. Class io = java.io.ObjectInputStream.class;
  47. result
  48. = io.getDeclaredMethod("latestUserDefinedLoader",
  49. NO_ARGS);
  50. result.setAccessible(true);
  51. } catch (NoSuchMethodException nsme) {
  52. // Throw Errors right now. This is a serious problem
  53. throw new Error("java.io.ObjectInputStream"
  54. + " latestUserDefinedLoader "
  55. + nsme);
  56. }
  57. return result;
  58. }
  59. });
  60. }
  61. // Uses reflection and the final static Method (obtained
  62. // by reflection at class load time) to call the
  63. // latestUserDefinedLoader native code.
  64. private static ClassLoader getLatestUserDefinedLoader()
  65. {
  66. try {
  67. // Invoke the ObjectInputStream.latestUserDefinedLoader method
  68. return (ClassLoader)latestUserDefinedLoaderMethod.invoke(null,
  69. NO_ARGS);
  70. } catch (InvocationTargetException ite) {
  71. throw new Error("java.io.ObjectInputStream"
  72. + " latestUserDefinedLoader "
  73. + ite);
  74. } catch (IllegalAccessException iae) {
  75. throw new Error("java.io.ObjectInputStream"
  76. + " latestUserDefinedLoader "
  77. + iae);
  78. }
  79. }
  80. static Class loadClass(Class aClass, String className)
  81. throws ClassNotFoundException {
  82. // Maintain the same error semantics as Class.forName()
  83. if (className == null) {
  84. throw new NullPointerException();
  85. }
  86. if (className.length() == 0) {
  87. throw new ClassNotFoundException();
  88. }
  89. // It would be nice to bypass JDKClassLoader's attempts completely
  90. // if it's known that the latest user defined ClassLoader will
  91. // fail.
  92. //
  93. // Otherwise, we end up calling Class.forName here as well as in
  94. // the next step in JDKBridge. That can take a long time depending
  95. // on the length of the classpath.
  96. // Note: Looking at the only place in JDKBridge where this code
  97. // is invoked, it is clear that aClass will always be null.
  98. ClassLoader loader;
  99. if (aClass != null) {
  100. loader = aClass.getClassLoader();
  101. } else {
  102. loader = getLatestUserDefinedLoader();
  103. }
  104. // See createKey for a description of what's involved
  105. Object key = classCache.createKey(className, loader);
  106. if (classCache.knownToFail(key)) {
  107. throw new ClassNotFoundException(className);
  108. } else {
  109. try {
  110. // Loading this class with the call stack
  111. // loader isn't known to fail, so try
  112. // to load it.
  113. return Class.forName(className, false, loader);
  114. } catch(ClassNotFoundException cnfe) {
  115. // Record that we failed to find the class
  116. // with this particular loader. This way, we won't
  117. // waste time looking with this loader, again.
  118. classCache.recordFailure(key);
  119. throw cnfe;
  120. }
  121. }
  122. }
  123. /**
  124. * Private cache implementation specific to JDKClassLoader.
  125. */
  126. private static class JDKClassLoaderCache
  127. {
  128. // JDKClassLoader couldn't find the class with the located
  129. // ClassLoader. Note this in our cache so JDKClassLoader
  130. // can abort early next time.
  131. public final void recordFailure(Object key) {
  132. cache.put(key, JDKClassLoaderCache.KNOWN_TO_FAIL);
  133. }
  134. // Factory for a key (CacheKey is an implementation detail
  135. // of JDKClassLoaderCache).
  136. //
  137. // A key currently consists of the class name as well as
  138. // the latest user defined class loader, so it's fairly
  139. // expensive to create.
  140. public final Object createKey(String className, ClassLoader latestLoader) {
  141. return new CacheKey(className, latestLoader);
  142. }
  143. // Determine whether or not this combination of class name
  144. // and ClassLoader is known to fail.
  145. public final boolean knownToFail(Object key) {
  146. return cache.get(key) == JDKClassLoaderCache.KNOWN_TO_FAIL;
  147. }
  148. // Synchronized WeakHashMap
  149. private final Map cache
  150. = Collections.synchronizedMap(new WeakHashMap());
  151. // Cache result used to mark the caches when there is
  152. // no way JDKClassLoader could succeed with the given
  153. // key
  154. private static final Object KNOWN_TO_FAIL = new Object();
  155. // Key consisting of the class name and the latest
  156. // user defined class loader
  157. private static class CacheKey
  158. {
  159. String className;
  160. ClassLoader loader;
  161. public CacheKey(String className, ClassLoader loader) {
  162. this.className = className;
  163. this.loader = loader;
  164. }
  165. // Try to incorporate both class name and loader
  166. // into the hashcode
  167. public int hashCode() {
  168. if (loader == null)
  169. return className.hashCode();
  170. else
  171. return className.hashCode() ^ loader.hashCode();
  172. }
  173. public boolean equals(Object obj) {
  174. try {
  175. // WeakHashMap may compare null keys
  176. if (obj == null)
  177. return false;
  178. CacheKey other = (CacheKey)obj;
  179. // I've made a decision to actually compare the
  180. // loader references. I don't want a case when
  181. // two loader instances override their equals
  182. // methods and only compare code base.
  183. //
  184. // This way, at worst, our performance will
  185. // be slower, but we know we'll do the correct
  186. // loading.
  187. return (className.equals(other.className) &&
  188. loader == other.loader);
  189. } catch (ClassCastException cce) {
  190. return false;
  191. }
  192. }
  193. }
  194. }
  195. }