1 /* 2 * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jmx.mbeanserver; 27 28 29 import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; 30 import java.security.Permission; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.Hashtable; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.logging.Level; 37 import javax.management.MBeanPermission; 38 39 import javax.management.ObjectName; 40 import javax.management.loading.PrivateClassLoader; 41 import sun.reflect.misc.ReflectUtil; 42 43 /** 44 * This class keeps the list of Class Loaders registered in the MBean Server. 45 * It provides the necessary methods to load classes using the 46 * registered Class Loaders. 47 * 48 * @since 1.5 49 */ 50 final class ClassLoaderRepositorySupport 51 implements ModifiableClassLoaderRepository { 52 53 /* We associate an optional ObjectName with each entry so that 54 we can remove the correct entry when unregistering an MBean 55 that is a ClassLoader. The same object could be registered 56 under two different names (even though this is not recommended) 57 so if we did not do this we could disturb the defined 58 semantics for the order of ClassLoaders in the repository. */ 59 private static class LoaderEntry { 60 ObjectName name; // can be null 61 ClassLoader loader; 62 LoaderEntry(ObjectName name, ClassLoader loader)63 LoaderEntry(ObjectName name, ClassLoader loader) { 64 this.name = name; 65 this.loader = loader; 66 } 67 } 68 69 private static final LoaderEntry[] EMPTY_LOADER_ARRAY = new LoaderEntry[0]; 70 71 /** 72 * List of class loaders 73 * Only read-only actions should be performed on this object. 74 * 75 * We do O(n) operations on this array, e.g. when removing 76 * a ClassLoader. The assumption is that the number of elements 77 * is small, probably less than ten, and that the vast majority 78 * of operations are searches (loadClass) which are by definition 79 * linear. 80 */ 81 private LoaderEntry[] loaders = EMPTY_LOADER_ARRAY; 82 83 /** 84 * Same behavior as add(Object o) in {@link java.util.List}. 85 * Replace the loader list with a new one in which the new 86 * loader has been added. 87 **/ add(ObjectName name, ClassLoader cl)88 private synchronized boolean add(ObjectName name, ClassLoader cl) { 89 List<LoaderEntry> l = 90 new ArrayList<LoaderEntry>(Arrays.asList(loaders)); 91 l.add(new LoaderEntry(name, cl)); 92 loaders = l.toArray(EMPTY_LOADER_ARRAY); 93 return true; 94 } 95 96 /** 97 * Same behavior as remove(Object o) in {@link java.util.List}. 98 * Replace the loader list with a new one in which the old loader 99 * has been removed. 100 * 101 * The ObjectName may be null, in which case the entry to 102 * be removed must also have a null ObjectName and the ClassLoader 103 * values must match. If the ObjectName is not null, then 104 * the first entry with a matching ObjectName is removed, 105 * regardless of whether ClassLoader values match. (In fact, 106 * the ClassLoader parameter will usually be null in this case.) 107 **/ remove(ObjectName name, ClassLoader cl)108 private synchronized boolean remove(ObjectName name, ClassLoader cl) { 109 final int size = loaders.length; 110 for (int i = 0; i < size; i++) { 111 LoaderEntry entry = loaders[i]; 112 boolean match = 113 (name == null) ? 114 cl == entry.loader : 115 name.equals(entry.name); 116 if (match) { 117 LoaderEntry[] newloaders = new LoaderEntry[size - 1]; 118 System.arraycopy(loaders, 0, newloaders, 0, i); 119 System.arraycopy(loaders, i + 1, newloaders, i, 120 size - 1 - i); 121 loaders = newloaders; 122 return true; 123 } 124 } 125 return false; 126 } 127 128 129 /** 130 * List of valid search 131 */ 132 private final Map<String,List<ClassLoader>> search = 133 new Hashtable<String,List<ClassLoader>>(10); 134 135 /** 136 * List of named class loaders. 137 */ 138 private final Map<ObjectName,ClassLoader> loadersWithNames = 139 new Hashtable<ObjectName,ClassLoader>(10); 140 141 // from javax.management.loading.DefaultLoaderRepository loadClass(String className)142 public final Class<?> loadClass(String className) 143 throws ClassNotFoundException { 144 return loadClass(loaders, className, null, null); 145 } 146 147 148 // from javax.management.loading.DefaultLoaderRepository loadClassWithout(ClassLoader without, String className)149 public final Class<?> loadClassWithout(ClassLoader without, String className) 150 throws ClassNotFoundException { 151 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 152 MBEANSERVER_LOGGER.logp(Level.FINER, 153 ClassLoaderRepositorySupport.class.getName(), 154 "loadClassWithout", className + " without " + without); 155 } 156 157 // without is null => just behave as loadClass 158 // 159 if (without == null) 160 return loadClass(loaders, className, null, null); 161 162 // We must try to load the class without the given loader. 163 // 164 startValidSearch(without, className); 165 try { 166 return loadClass(loaders, className, without, null); 167 } finally { 168 stopValidSearch(without, className); 169 } 170 } 171 172 loadClassBefore(ClassLoader stop, String className)173 public final Class<?> loadClassBefore(ClassLoader stop, String className) 174 throws ClassNotFoundException { 175 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 176 MBEANSERVER_LOGGER.logp(Level.FINER, 177 ClassLoaderRepositorySupport.class.getName(), 178 "loadClassBefore", className + " before " + stop); 179 } 180 181 if (stop == null) 182 return loadClass(loaders, className, null, null); 183 184 startValidSearch(stop, className); 185 try { 186 return loadClass(loaders, className, null, stop); 187 } finally { 188 stopValidSearch(stop, className); 189 } 190 } 191 192 loadClass(final LoaderEntry list[], final String className, final ClassLoader without, final ClassLoader stop)193 private Class<?> loadClass(final LoaderEntry list[], 194 final String className, 195 final ClassLoader without, 196 final ClassLoader stop) 197 throws ClassNotFoundException { 198 ReflectUtil.checkPackageAccess(className); 199 final int size = list.length; 200 for(int i=0; i<size; i++) { 201 try { 202 final ClassLoader cl = list[i].loader; 203 if (cl == null) // bootstrap class loader 204 return Class.forName(className, false, null); 205 if (cl == without) 206 continue; 207 if (cl == stop) 208 break; 209 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 210 MBEANSERVER_LOGGER.logp(Level.FINER, 211 ClassLoaderRepositorySupport.class.getName(), 212 "loadClass", "Trying loader = " + cl); 213 } 214 /* We used to have a special case for "instanceof 215 MLet" here, where we invoked the method 216 loadClass(className, null) to prevent infinite 217 recursion. But the rule whereby the MLet only 218 consults loaders that precede it in the CLR (via 219 loadClassBefore) means that the recursion can't 220 happen, and the test here caused some legitimate 221 classloading to fail. For example, if you have 222 dependencies C->D->E with loaders {E D C} in the 223 CLR in that order, you would expect to be able to 224 load C. The problem is that while resolving D, CLR 225 delegation is disabled, so it can't find E. */ 226 return Class.forName(className, false, cl); 227 } catch (ClassNotFoundException e) { 228 // OK: continue with next class 229 } 230 } 231 232 throw new ClassNotFoundException(className); 233 } 234 startValidSearch(ClassLoader aloader, String className)235 private synchronized void startValidSearch(ClassLoader aloader, 236 String className) 237 throws ClassNotFoundException { 238 // Check if we have such a current search 239 // 240 List<ClassLoader> excluded = search.get(className); 241 if ((excluded!= null) && (excluded.contains(aloader))) { 242 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 243 MBEANSERVER_LOGGER.logp(Level.FINER, 244 ClassLoaderRepositorySupport.class.getName(), 245 "startValidSearch", "Already requested loader = " + 246 aloader + " class = " + className); 247 } 248 throw new ClassNotFoundException(className); 249 } 250 251 // Add an entry 252 // 253 if (excluded == null) { 254 excluded = new ArrayList<ClassLoader>(1); 255 search.put(className, excluded); 256 } 257 excluded.add(aloader); 258 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 259 MBEANSERVER_LOGGER.logp(Level.FINER, 260 ClassLoaderRepositorySupport.class.getName(), 261 "startValidSearch", 262 "loader = " + aloader + " class = " + className); 263 } 264 } 265 stopValidSearch(ClassLoader aloader, String className)266 private synchronized void stopValidSearch(ClassLoader aloader, 267 String className) { 268 269 // Retrieve the search. 270 // 271 List<ClassLoader> excluded = search.get(className); 272 if (excluded != null) { 273 excluded.remove(aloader); 274 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { 275 MBEANSERVER_LOGGER.logp(Level.FINER, 276 ClassLoaderRepositorySupport.class.getName(), 277 "stopValidSearch", 278 "loader = " + aloader + " class = " + className); 279 } 280 } 281 } 282 addClassLoader(ClassLoader loader)283 public final void addClassLoader(ClassLoader loader) { 284 add(null, loader); 285 } 286 removeClassLoader(ClassLoader loader)287 public final void removeClassLoader(ClassLoader loader) { 288 remove(null, loader); 289 } 290 addClassLoader(ObjectName name, ClassLoader loader)291 public final synchronized void addClassLoader(ObjectName name, 292 ClassLoader loader) { 293 loadersWithNames.put(name, loader); 294 if (!(loader instanceof PrivateClassLoader)) 295 add(name, loader); 296 } 297 removeClassLoader(ObjectName name)298 public final synchronized void removeClassLoader(ObjectName name) { 299 ClassLoader loader = loadersWithNames.remove(name); 300 if (!(loader instanceof PrivateClassLoader)) 301 remove(name, loader); 302 } 303 getClassLoader(ObjectName name)304 public final ClassLoader getClassLoader(ObjectName name) { 305 ClassLoader instance = loadersWithNames.get(name); 306 if (instance != null) { 307 SecurityManager sm = System.getSecurityManager(); 308 if (sm != null) { 309 Permission perm = 310 new MBeanPermission(instance.getClass().getName(), 311 null, 312 name, 313 "getClassLoader"); 314 sm.checkPermission(perm); 315 } 316 } 317 return instance; 318 } 319 320 } 321