001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.xbean.classloader; 018 019import java.io.IOException; 020import java.net.URL; 021import java.net.URLStreamHandlerFactory; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.Enumeration; 027import java.util.List; 028 029/** 030 * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class 031 * loader model to support a list of parent class loaders. Each operation that accesses a parent, has been replaced 032 * with a operation that checks each parent in order. This getParent method of this class will always return null, 033 * which may be interperated by the calling code to mean that this class loader is a direct child of the system class 034 * loader. 035 * 036 * @author Dain Sundstrom 037 * @version $Id: MultiParentClassLoader.java 725706 2008-12-11 14:58:11Z gnodet $ 038 * @since 2.0 039 */ 040public class MultiParentClassLoader extends NamedClassLoader { 041 private final ClassLoader[] parents; 042 private final boolean inverseClassLoading; 043 private final String[] hiddenClasses; 044 private final String[] nonOverridableClasses; 045 private final String[] hiddenResources; 046 private final String[] nonOverridableResources; 047 048 /** 049 * Creates a named class loader with no parents. 050 * @param name the name of this class loader 051 * @param urls the urls from which this class loader will classes and resources 052 */ 053 public MultiParentClassLoader(String name, URL[] urls) { 054 this(name, urls, ClassLoader.getSystemClassLoader()); 055 } 056 057 /** 058 * Creates a named class loader as a child of the specified parent. 059 * @param name the name of this class loader 060 * @param urls the urls from which this class loader will classes and resources 061 * @param parent the parent of this class loader 062 */ 063 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent) { 064 this(name, urls, new ClassLoader[] {parent}); 065 } 066 067 /** 068 * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory 069 * for accessing the urls.. 070 * @param name the name of this class loader 071 * @param urls the urls from which this class loader will classes and resources 072 * @param parent the parent of this class loader 073 * @param factory the URLStreamHandlerFactory used to access the urls 074 */ 075 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { 076 this(name, urls, new ClassLoader[] {parent}, factory); 077 } 078 079 /** 080 * Creates a named class loader as a child of the specified parents. 081 * @param name the name of this class loader 082 * @param urls the urls from which this class loader will classes and resources 083 * @param parents the parents of this class loader 084 */ 085 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents) { 086 this(name, urls, parents, false, new String[0], new String[0]); 087 } 088 089 public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { 090 this(name, urls, new ClassLoader[]{parent}, inverseClassLoading, hiddenClasses, nonOverridableClasses); 091 } 092 093 /** 094 * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory 095 * for accessing the urls.. 096 * @param name the name of this class loader 097 * @param urls the urls from which this class loader will classes and resources 098 * @param parents the parents of this class loader 099 * @param factory the URLStreamHandlerFactory used to access the urls 100 */ 101 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) { 102 super(name, urls, null, factory); 103 this.parents = copyParents(parents); 104 this.inverseClassLoading = false; 105 this.hiddenClasses = new String[0]; 106 this.nonOverridableClasses = new String[0]; 107 this.hiddenResources = new String[0]; 108 this.nonOverridableResources = new String[0]; 109 } 110 111 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) { 112 this(name, urls, parents, inverseClassLoading, (String[]) hiddenClasses.toArray(new String[hiddenClasses.size()]), (String[]) nonOverridableClasses.toArray(new String[nonOverridableClasses.size()])); 113 } 114 115 public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) { 116 super(name, urls); 117 this.parents = copyParents(parents); 118 this.inverseClassLoading = inverseClassLoading; 119 this.hiddenClasses = hiddenClasses; 120 this.nonOverridableClasses = nonOverridableClasses; 121 hiddenResources = toResources(hiddenClasses); 122 nonOverridableResources = toResources(nonOverridableClasses); 123 } 124 125 private static String[] toResources(String[] classes) { 126 String[] resources = new String[classes.length]; 127 for (int i = 0; i < classes.length; i++) { 128 String className = classes[i]; 129 resources[i] = className.replace('.', '/'); 130 } 131 return resources; 132 } 133 134 private static ClassLoader[] copyParents(ClassLoader[] parents) { 135 ClassLoader[] newParentsArray = new ClassLoader[parents.length]; 136 for (int i = 0; i < parents.length; i++) { 137 ClassLoader parent = parents[i]; 138 if (parent == null) { 139 throw new NullPointerException("parent[" + i + "] is null"); 140 } 141 newParentsArray[i] = parent; 142 } 143 return newParentsArray; 144 } 145 146 /** 147 * Gets the parents of this class loader. 148 * @return the parents of this class loader 149 */ 150 public ClassLoader[] getParents() { 151 return parents; 152 } 153 154 /** 155 * {@inheritDoc} 156 */ 157 protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 158 // 159 // Check if class is in the loaded classes cache 160 // 161 Class cachedClass = findLoadedClass(name); 162 if (cachedClass != null) { 163 return resolveClass(cachedClass, resolve); 164 } 165 166 // 167 // if we are using inverse class loading, check local urls first 168 // 169 if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) { 170 try { 171 Class clazz = findClass(name); 172 return resolveClass(clazz, resolve); 173 } catch (ClassNotFoundException ignored) { 174 } 175 } 176 177 // 178 // Check parent class loaders 179 // 180 if (!isHiddenClass(name)) { 181 for (int i = 0; i < parents.length; i++) { 182 ClassLoader parent = parents[i]; 183 try { 184 Class clazz = parent.loadClass(name); 185 return resolveClass(clazz, resolve); 186 } catch (ClassNotFoundException ignored) { 187 // this parent didn't have the class; try the next one 188 } 189 } 190 } 191 192 // 193 // if we are not using inverse class loading, check local urls now 194 // 195 // don't worry about excluding non-overridable classes here... we 196 // have alredy checked he parent and the parent didn't have the 197 // class, so we can override now 198 if (!isDestroyed()) { 199 try { 200 Class clazz = findClass(name); 201 return resolveClass(clazz, resolve); 202 } catch (ClassNotFoundException ignored) { 203 } 204 } 205 206 throw new ClassNotFoundException(name + " in classloader " + getName()); 207 } 208 209 private boolean isNonOverridableClass(String name) { 210 for (int i = 0; i < nonOverridableClasses.length; i++) { 211 if (name.startsWith(nonOverridableClasses[i])) { 212 return true; 213 } 214 } 215 return false; 216 } 217 218 private boolean isHiddenClass(String name) { 219 for (int i = 0; i < hiddenClasses.length; i++) { 220 if (name.startsWith(hiddenClasses[i])) { 221 return true; 222 } 223 } 224 return false; 225 } 226 227 private Class resolveClass(Class clazz, boolean resolve) { 228 if (resolve) { 229 resolveClass(clazz); 230 } 231 return clazz; 232 } 233 234 /** 235 * {@inheritDoc} 236 */ 237 public URL getResource(String name) { 238 if (isDestroyed()) { 239 return null; 240 } 241 242 // 243 // if we are using inverse class loading, check local urls first 244 // 245 if (inverseClassLoading && !isDestroyed() && !isNonOverridableResource(name)) { 246 URL url = findResource(name); 247 if (url != null) { 248 return url; 249 } 250 } 251 252 // 253 // Check parent class loaders 254 // 255 if (!isHiddenResource(name)) { 256 for (int i = 0; i < parents.length; i++) { 257 ClassLoader parent = parents[i]; 258 URL url = parent.getResource(name); 259 if (url != null) { 260 return url; 261 } 262 } 263 } 264 265 // 266 // if we are not using inverse class loading, check local urls now 267 // 268 // don't worry about excluding non-overridable resources here... we 269 // have alredy checked he parent and the parent didn't have the 270 // resource, so we can override now 271 if (!isDestroyed()) { 272 // parents didn't have the resource; attempt to load it from my urls 273 return findResource(name); 274 } 275 276 return null; 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 public Enumeration findResources(String name) throws IOException { 283 if (isDestroyed()) { 284 return Collections.enumeration(Collections.EMPTY_SET); 285 } 286 287 List resources = new ArrayList(); 288 289 // 290 // if we are using inverse class loading, add the resources from local urls first 291 // 292 if (inverseClassLoading && !isDestroyed()) { 293 List myResources = Collections.list(super.findResources(name)); 294 resources.addAll(myResources); 295 } 296 297 // 298 // Add parent resources 299 // 300 for (int i = 0; i < parents.length; i++) { 301 ClassLoader parent = parents[i]; 302 List parentResources = Collections.list(parent.getResources(name)); 303 resources.addAll(parentResources); 304 } 305 306 // 307 // if we are not using inverse class loading, add the resources from local urls now 308 // 309 if (!inverseClassLoading && !isDestroyed()) { 310 List myResources = Collections.list(super.findResources(name)); 311 resources.addAll(myResources); 312 } 313 314 return Collections.enumeration(resources); 315 } 316 317 private boolean isNonOverridableResource(String name) { 318 for (int i = 0; i < nonOverridableResources.length; i++) { 319 if (name.startsWith(nonOverridableResources[i])) { 320 return true; 321 } 322 } 323 return false; 324 } 325 326 private boolean isHiddenResource(String name) { 327 for (int i = 0; i < hiddenResources.length; i++) { 328 if (name.startsWith(hiddenResources[i])) { 329 return true; 330 } 331 } 332 return false; 333 } 334 335 /** 336 * {@inheritDoc} 337 */ 338 public String toString() { 339 return "[" + getClass().getName() + ":" + 340 " name=" + getName() + 341 " urls=" + Arrays.asList(getURLs()) + 342 " parents=" + Arrays.asList(parents) + 343 "]"; 344 } 345}