1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.discovery.resource; 18 19 import java.util.LinkedList; 20 import java.util.List; 21 22 import org.apache.commons.discovery.jdk.JDKHooks; 23 24 /** 25 * There are many different contexts in which 26 * loaders can be used. This provides a holder 27 * for a set of class loaders, so that they 28 * don't have to be build back up everytime... 29 */ 30 public class ClassLoaders { 31 32 protected List<ClassLoader> classLoaders = new LinkedList<ClassLoader>(); 33 34 /** 35 * Construct a new class loader set. 36 */ 37 public ClassLoaders() { 38 } 39 40 /** 41 * Returns the size of class loaders set. 42 * 43 * @return The size of class loaders set 44 */ 45 public int size() { 46 return classLoaders.size(); 47 } 48 49 /** 50 * Returns the class loader positioned at the given index. 51 * 52 * @param idx The index the class loader has to be retrieved from 53 * @return The class loader positioned at the given index 54 */ 55 public ClassLoader get(int idx) { 56 return classLoaders.get(idx); 57 } 58 59 /** 60 * Specify a new class loader to be used in searching. 61 * 62 * The order of loaders determines the order of the result. 63 * It is recommended to add the most specific loaders first; 64 * {@code null} class loaders are discarded. 65 * 66 * @param classLoader The class loader has to added in the set 67 */ 68 public void put(ClassLoader classLoader) { 69 if (classLoader != null) { 70 classLoaders.add(classLoader); 71 } 72 } 73 74 /** 75 * Specify a new class loader to be used in searching. 76 * The order of loaders determines the order of the result. 77 * It is recommended to add the most specific loaders first; 78 * {@code null} class loaders are discarded. 79 * 80 * @param classLoader The class loader has to added in the set 81 * @param prune if true, verify that the class loader is 82 * not an Ancestor (@see isAncestor) before 83 * adding it to our list. 84 */ 85 public void put(ClassLoader classLoader, boolean prune) { 86 if (classLoader != null && !(prune && isAncestor(classLoader))) { 87 classLoaders.add(classLoader); 88 } 89 } 90 91 /** 92 * Check to see if <code>classLoader</code> is an 93 * ancestor of any contained class loader. 94 * 95 * This can be used to eliminate redundant class loaders 96 * IF all class loaders defer to parent class loaders 97 * before resolving a class. 98 * 99 * It may be that this is not always true. Therefore, 100 * this check is not done internally to eliminate 101 * redundant class loaders, but left to the discretion 102 * of the user. 103 * 104 * @param classLoader The class loader under test 105 * @return true, if the class loader under test is an ancestor 106 * of any contained class loader, false otherwise 107 */ 108 public boolean isAncestor(final ClassLoader classLoader) { 109 /* bootstrap classloader, at root of all trees! */ 110 if (classLoader == null) { 111 return true; 112 } 113 114 for (int idx = 0; idx < size(); idx++) { 115 for (ClassLoader walker = get(idx); 116 walker != null; 117 walker = walker.getParent()) { 118 if (walker == classLoader) { 119 return true; 120 } 121 } 122 } 123 return false; 124 } 125 126 /** 127 * Utility method. Returns a preloaded ClassLoaders instance 128 * containing the following class loaders, in order: 129 * 130 * <ul> 131 * <li>spi.getClassLoader</li> 132 * <li>seeker.getClassLoader</li> 133 * <li>System Class Loader</li> 134 * </ul> 135 * 136 * Note that the thread context class loader is NOT present. 137 * This is a reasonable set of loaders to try if the resource to be found 138 * should be restricted to a libraries containing the SPI and Factory. 139 * 140 * @param spi WHAT is being looked for (an implementation of this class, 141 * a default property file related to this class). 142 * @param factory WHO is performing the lookup. 143 * @param prune Determines if ancestors are allowed to be loaded or not. 144 * @return The class loaders holder 145 */ 146 public static ClassLoaders getLibLoaders(Class<?> spi, Class<?> factory, boolean prune) { 147 ClassLoaders loaders = new ClassLoaders(); 148 149 if (spi != null) { 150 loaders.put(spi.getClassLoader()); 151 } 152 if (factory != null) { 153 loaders.put(factory.getClassLoader(), prune); 154 } 155 loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), prune); 156 157 return loaders; 158 } 159 160 /** 161 * Utility method. Returns a preloaded ClassLoaders instance 162 * containing the following class loaders, in order: 163 * 164 * <ul> 165 * <li>Thread Context Class Loader</li> 166 * <li>spi.getClassLoader</li> 167 * <li>seeker.getClassLoader</li> 168 * <li>System Class Loader</li> 169 * </ul> 170 * 171 * Note that the thread context class loader IS present. 172 * This is a reasonable set of loaders to try if the resource to be found 173 * may be provided by an application. 174 * 175 * @param spi WHAT is being looked for (an implementation of this class, 176 * a default property file related to this class). 177 * @param factory WHO is performing the lookup (factory). 178 * @param prune Determines if ancestors are allowed to be loaded or not. 179 * @return The class loaders holder 180 */ 181 public static ClassLoaders getAppLoaders(Class<?> spi, Class<?> factory, boolean prune) { 182 ClassLoaders loaders = new ClassLoaders(); 183 184 loaders.put(JDKHooks.getJDKHooks().getThreadContextClassLoader()); 185 if (spi != null) { 186 loaders.put(spi.getClassLoader(), prune); 187 } 188 if (factory != null) { 189 loaders.put(factory.getClassLoader(), prune); 190 } 191 loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), prune); 192 193 return loaders; 194 } 195 196 }