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 */ 017 package org.apache.commons.discovery.resource; 018 019 import java.util.LinkedList; 020 import java.util.List; 021 022 import org.apache.commons.discovery.jdk.JDKHooks; 023 024 /** 025 * There are many different contexts in which 026 * loaders can be used. This provides a holder 027 * for a set of class loaders, so that they 028 * don't have to be build back up everytime... 029 */ 030 public class ClassLoaders { 031 032 protected List<ClassLoader> classLoaders = new LinkedList<ClassLoader>(); 033 034 /** 035 * Construct a new class loader set. 036 */ 037 public ClassLoaders() { 038 } 039 040 /** 041 * Returns the size of class loaders set. 042 * 043 * @return The size of class loaders set 044 */ 045 public int size() { 046 return classLoaders.size(); 047 } 048 049 /** 050 * Returns the class loader positioned at the given index. 051 * 052 * @param idx The index the class loader has to be retrieved from 053 * @return The class loader positioned at the given index 054 */ 055 public ClassLoader get(int idx) { 056 return classLoaders.get(idx); 057 } 058 059 /** 060 * Specify a new class loader to be used in searching. 061 * 062 * The order of loaders determines the order of the result. 063 * It is recommended to add the most specific loaders first; 064 * {@code null} class loaders are discarded. 065 * 066 * @param classLoader The class loader has to added in the set 067 */ 068 public void put(ClassLoader classLoader) { 069 if (classLoader != null) { 070 classLoaders.add(classLoader); 071 } 072 } 073 074 /** 075 * Specify a new class loader to be used in searching. 076 * The order of loaders determines the order of the result. 077 * It is recommended to add the most specific loaders first; 078 * {@code null} class loaders are discarded. 079 * 080 * @param classLoader The class loader has to added in the set 081 * @param prune if true, verify that the class loader is 082 * not an Ancestor (@see isAncestor) before 083 * adding it to our list. 084 */ 085 public void put(ClassLoader classLoader, boolean prune) { 086 if (classLoader != null && !(prune && isAncestor(classLoader))) { 087 classLoaders.add(classLoader); 088 } 089 } 090 091 /** 092 * Check to see if <code>classLoader</code> is an 093 * ancestor of any contained class loader. 094 * 095 * This can be used to eliminate redundant class loaders 096 * IF all class loaders defer to parent class loaders 097 * before resolving a class. 098 * 099 * 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 }