1. /*
  2. * @(#)ClassLoaderRepositorySupport.java 1.23 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.jmx.mbeanserver;
  8. // Java import
  9. import java.util.ArrayList;
  10. import java.util.Arrays;
  11. import java.util.Hashtable;
  12. import java.util.Iterator;
  13. import java.util.List;
  14. import java.util.Vector;
  15. // JMX import
  16. import javax.management.ObjectName;
  17. import javax.management.loading.ClassLoaderRepository;
  18. import javax.management.loading.PrivateClassLoader;
  19. import com.sun.jmx.trace.Trace;
  20. /**
  21. * This class keeps the list of Class Loaders registered in the MBean Server.
  22. * It provides the necessary methods to load classes using the
  23. * registered Class Loaders.
  24. *
  25. * @since 1.5
  26. * @since.unbundled JMX RI 1.2
  27. */
  28. public final class ClassLoaderRepositorySupport
  29. implements ModifiableClassLoaderRepository {
  30. /* We associate an optional ObjectName with each entry so that
  31. we can remove the correct entry when unregistering an MBean
  32. that is a ClassLoader. The same object could be registered
  33. under two different names (even though this is not recommended)
  34. so if we did not do this we could disturb the defined
  35. semantics for the order of ClassLoaders in the repository. */
  36. private static class LoaderEntry {
  37. ObjectName name; // can be null
  38. ClassLoader loader;
  39. LoaderEntry(ObjectName name, ClassLoader loader) {
  40. this.name = name;
  41. this.loader = loader;
  42. }
  43. }
  44. private static final LoaderEntry[] EMPTY_LOADER_ARRAY = new LoaderEntry[0];
  45. /**
  46. * List of class loaders
  47. * Only read-only actions should be performed on this object.
  48. *
  49. * We do O(n) operations on this array, e.g. when removing
  50. * a ClassLoader. The assumption is that the number of elements
  51. * is small, probably less than ten, and that the vast majority
  52. * of operations are searches (loadClass) which are by definition
  53. * linear.
  54. */
  55. private LoaderEntry[] loaders = EMPTY_LOADER_ARRAY;
  56. /**
  57. * Same behaviour as add(Object o) in {@link java.util.List}.
  58. * Replace the loader list with a new one in which the new
  59. * loader has been added.
  60. **/
  61. private synchronized boolean add(ObjectName name, ClassLoader cl) {
  62. List l = new ArrayList(Arrays.asList(loaders));
  63. l.add(new LoaderEntry(name, cl));
  64. loaders = (LoaderEntry[]) l.toArray(EMPTY_LOADER_ARRAY);
  65. return true;
  66. }
  67. /**
  68. * Same behaviour as remove(Object o) in {@link java.util.List}.
  69. * Replace the loader list with a new one in which the old loader
  70. * has been removed.
  71. *
  72. * The ObjectName may be null, in which case the entry to
  73. * be removed must also have a null ObjectName and the ClassLoader
  74. * values must match. If the ObjectName is not null, then
  75. * the first entry with a matching ObjectName is removed,
  76. * regardless of whether ClassLoader values match. (In fact,
  77. * the ClassLoader parameter will usually be null in this case.)
  78. **/
  79. private synchronized boolean remove(ObjectName name, ClassLoader cl) {
  80. final int size = loaders.length;
  81. for (int i = 0; i < size; i++) {
  82. LoaderEntry entry = loaders[i];
  83. boolean match =
  84. (name == null) ?
  85. cl == entry.loader :
  86. name.equals(entry.name);
  87. if (match) {
  88. LoaderEntry[] newloaders = new LoaderEntry[size - 1];
  89. System.arraycopy(loaders, 0, newloaders, 0, i);
  90. System.arraycopy(loaders, i + 1, newloaders, i,
  91. size - 1 - i);
  92. loaders = newloaders;
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. /**
  99. * List of valid search
  100. */
  101. private final Hashtable search= new Hashtable(10);
  102. /**
  103. * List of named class loaders.
  104. */
  105. private final Hashtable loadersWithNames = new Hashtable(10);
  106. private final static String dbgTag = "ClassLoaderRepositorySupport";
  107. // from javax.management.loading.DefaultLoaderRepository
  108. public final Class loadClass(String className)
  109. throws ClassNotFoundException {
  110. return loadClass(loaders, className, null, null);
  111. }
  112. // from javax.management.loading.DefaultLoaderRepository
  113. public final Class loadClassWithout(ClassLoader without, String className)
  114. throws ClassNotFoundException {
  115. if (isTraceOn()) {
  116. trace("loadClassWithout", className + "\twithout " + without);
  117. }
  118. // without is null => just behave as loadClass
  119. //
  120. if (without == null)
  121. return loadClass(loaders, className, null, null);
  122. // We must try to load the class without the given loader.
  123. //
  124. startValidSearch(without, className);
  125. try {
  126. return loadClass(loaders, className, without, null);
  127. } finally {
  128. stopValidSearch(without, className);
  129. }
  130. }
  131. public final Class loadClassBefore(ClassLoader stop, String className)
  132. throws ClassNotFoundException {
  133. if (isTraceOn())
  134. trace("loadClassBefore", className + "\tbefore " + stop);
  135. if (stop == null)
  136. return loadClass(loaders, className, null, null);
  137. startValidSearch(stop, className);
  138. try {
  139. return loadClass(loaders, className, null, stop);
  140. } finally {
  141. stopValidSearch(stop, className);
  142. }
  143. }
  144. private Class loadClass(final LoaderEntry list[],
  145. final String className,
  146. final ClassLoader without,
  147. final ClassLoader stop)
  148. throws ClassNotFoundException {
  149. final int size = list.length;
  150. for(int i=0; i<size; i++) {
  151. try {
  152. final ClassLoader cl = list[i].loader;
  153. if (cl == null) // bootstrap class loader
  154. return Class.forName(className, false, null);
  155. if (cl == without)
  156. continue;
  157. if (cl == stop)
  158. break;
  159. if (isTraceOn()) {
  160. trace("loadClass", "trying loader = " + cl);
  161. }
  162. /* We used to have a special case for "instanceof
  163. MLet" here, where we invoked the method
  164. loadClass(className, null) to prevent infinite
  165. recursion. But the rule whereby the MLet only
  166. consults loaders that precede it in the CLR (via
  167. loadClassBefore) means that the recursion can't
  168. happen, and the test here caused some legitimate
  169. classloading to fail. For example, if you have
  170. dependencies C->D->E with loaders {E D C} in the
  171. CLR in that order, you would expect to be able to
  172. load C. The problem is that while resolving D, CLR
  173. delegation is disabled, so it can't find E. */
  174. return Class.forName(className, false, cl);
  175. } catch (ClassNotFoundException e) {
  176. // OK: continue with next class
  177. }
  178. }
  179. throw new ClassNotFoundException(className);
  180. }
  181. private synchronized void startValidSearch(ClassLoader aloader,
  182. String className)
  183. throws ClassNotFoundException {
  184. // Check if we have such a current search
  185. //
  186. Vector excluded= (Vector) search.get(className);
  187. if ((excluded!= null) && (excluded.contains(aloader))) {
  188. if (isTraceOn()) {
  189. trace("startValidSearch", "already requested loader=" +
  190. aloader + " class= " + className);
  191. }
  192. throw new ClassNotFoundException(className);
  193. }
  194. // Add an entry
  195. //
  196. if (excluded == null) {
  197. excluded= new Vector(1);
  198. search.put(className, excluded);
  199. }
  200. excluded.addElement(aloader);
  201. if (isTraceOn()) {
  202. trace("startValidSearch", "loader=" + aloader + " class= " +
  203. className);
  204. }
  205. }
  206. private synchronized void stopValidSearch(ClassLoader aloader,
  207. String className) {
  208. // Retrieve the search.
  209. //
  210. Vector excluded= (Vector) search.get(className);
  211. if (excluded!= null) {
  212. excluded.removeElement(aloader);
  213. if (isTraceOn()) {
  214. trace("stopValidSearch", "loader=" + aloader +
  215. " class= " + className);
  216. }
  217. }
  218. }
  219. public final void addClassLoader(ClassLoader loader) {
  220. add(null, loader);
  221. }
  222. public final void removeClassLoader(ClassLoader loader) {
  223. remove(null, loader);
  224. }
  225. public final synchronized void addClassLoader(ObjectName name,
  226. ClassLoader loader) {
  227. loadersWithNames.put(name, loader);
  228. if (!(loader instanceof PrivateClassLoader))
  229. add(name, loader);
  230. }
  231. public final synchronized void removeClassLoader(ObjectName name) {
  232. ClassLoader loader = (ClassLoader) loadersWithNames.remove(name);
  233. if (!(loader instanceof PrivateClassLoader))
  234. remove(name, loader);
  235. }
  236. public final ClassLoader getClassLoader(ObjectName name) {
  237. return (ClassLoader)loadersWithNames.get(name);
  238. }
  239. // TRACES & DEBUG
  240. //---------------
  241. private static boolean isTraceOn() {
  242. return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER);
  243. }
  244. private static void trace(String clz, String func, String info) {
  245. Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER,clz,func,info);
  246. }
  247. private static void trace(String func, String info) {
  248. trace(dbgTag, func, info);
  249. }
  250. private static boolean isDebugOn() {
  251. return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER);
  252. }
  253. private static void debug(String clz, String func, String info) {
  254. Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER,clz,func,info);
  255. }
  256. private static void debug(String func, String info) {
  257. debug(dbgTag, func, info);
  258. }
  259. }