1/* 2 * Copyright (c) 2002, 2017, 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 26package com.sun.jmx.mbeanserver; 27 28 29import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; 30import java.security.Permission; 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.Hashtable; 34import java.util.List; 35import java.util.Map; 36import java.lang.System.Logger.Level; 37import javax.management.MBeanPermission; 38 39import javax.management.ObjectName; 40import javax.management.loading.PrivateClassLoader; 41import 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 */ 50final 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 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 **/ 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 **/ 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 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 149 public final Class<?> loadClassWithout(ClassLoader without, String className) 150 throws ClassNotFoundException { 151 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 152 MBEANSERVER_LOGGER.log(Level.TRACE, 153 className + " without " + without); 154 } 155 156 // without is null => just behave as loadClass 157 // 158 if (without == null) 159 return loadClass(loaders, className, null, null); 160 161 // We must try to load the class without the given loader. 162 // 163 startValidSearch(without, className); 164 try { 165 return loadClass(loaders, className, without, null); 166 } finally { 167 stopValidSearch(without, className); 168 } 169 } 170 171 172 public final Class<?> loadClassBefore(ClassLoader stop, String className) 173 throws ClassNotFoundException { 174 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 175 MBEANSERVER_LOGGER.log(Level.TRACE, 176 className + " before " + stop); 177 } 178 179 if (stop == null) 180 return loadClass(loaders, className, null, null); 181 182 startValidSearch(stop, className); 183 try { 184 return loadClass(loaders, className, null, stop); 185 } finally { 186 stopValidSearch(stop, className); 187 } 188 } 189 190 191 private Class<?> loadClass(final LoaderEntry list[], 192 final String className, 193 final ClassLoader without, 194 final ClassLoader stop) 195 throws ClassNotFoundException { 196 ReflectUtil.checkPackageAccess(className); 197 final int size = list.length; 198 for(int i=0; i<size; i++) { 199 try { 200 final ClassLoader cl = list[i].loader; 201 if (cl == null) // bootstrap class loader 202 return Class.forName(className, false, null); 203 if (cl == without) 204 continue; 205 if (cl == stop) 206 break; 207 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 208 MBEANSERVER_LOGGER.log(Level.TRACE, "Trying loader = " + cl); 209 } 210 /* We used to have a special case for "instanceof 211 MLet" here, where we invoked the method 212 loadClass(className, null) to prevent infinite 213 recursion. But the rule whereby the MLet only 214 consults loaders that precede it in the CLR (via 215 loadClassBefore) means that the recursion can't 216 happen, and the test here caused some legitimate 217 classloading to fail. For example, if you have 218 dependencies C->D->E with loaders {E D C} in the 219 CLR in that order, you would expect to be able to 220 load C. The problem is that while resolving D, CLR 221 delegation is disabled, so it can't find E. */ 222 return Class.forName(className, false, cl); 223 } catch (ClassNotFoundException e) { 224 // OK: continue with next class 225 } 226 } 227 228 throw new ClassNotFoundException(className); 229 } 230 231 private synchronized void startValidSearch(ClassLoader aloader, 232 String className) 233 throws ClassNotFoundException { 234 // Check if we have such a current search 235 // 236 List<ClassLoader> excluded = search.get(className); 237 if ((excluded!= null) && (excluded.contains(aloader))) { 238 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 239 MBEANSERVER_LOGGER.log(Level.TRACE, 240 "Already requested loader = " + 241 aloader + " class = " + className); 242 } 243 throw new ClassNotFoundException(className); 244 } 245 246 // Add an entry 247 // 248 if (excluded == null) { 249 excluded = new ArrayList<ClassLoader>(1); 250 search.put(className, excluded); 251 } 252 excluded.add(aloader); 253 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 254 MBEANSERVER_LOGGER.log(Level.TRACE, 255 "loader = " + aloader + " class = " + className); 256 } 257 } 258 259 private synchronized void stopValidSearch(ClassLoader aloader, 260 String className) { 261 262 // Retrieve the search. 263 // 264 List<ClassLoader> excluded = search.get(className); 265 if (excluded != null) { 266 excluded.remove(aloader); 267 if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { 268 MBEANSERVER_LOGGER.log(Level.TRACE, 269 "loader = " + aloader + " class = " + className); 270 } 271 } 272 } 273 274 public final void addClassLoader(ClassLoader loader) { 275 add(null, loader); 276 } 277 278 public final void removeClassLoader(ClassLoader loader) { 279 remove(null, loader); 280 } 281 282 public final synchronized void addClassLoader(ObjectName name, 283 ClassLoader loader) { 284 loadersWithNames.put(name, loader); 285 if (!(loader instanceof PrivateClassLoader)) 286 add(name, loader); 287 } 288 289 public final synchronized void removeClassLoader(ObjectName name) { 290 ClassLoader loader = loadersWithNames.remove(name); 291 if (!(loader instanceof PrivateClassLoader)) 292 remove(name, loader); 293 } 294 295 public final ClassLoader getClassLoader(ObjectName name) { 296 ClassLoader instance = loadersWithNames.get(name); 297 if (instance != null) { 298 SecurityManager sm = System.getSecurityManager(); 299 if (sm != null) { 300 Permission perm = 301 new MBeanPermission(instance.getClass().getName(), 302 null, 303 name, 304 "getClassLoader"); 305 sm.checkPermission(perm); 306 } 307 } 308 return instance; 309 } 310 311} 312