1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: ClassLoaderResolver.java,v 1.1.1.1.2.2 2004/07/10 03:34:53 vlad_r Exp $ 8 */ 9package com.vladium.util; 10 11// ---------------------------------------------------------------------------- 12/** 13 * This non-instantiable non-subclassable class acts as the global point for 14 * choosing a ClassLoader for dynamic class/resource loading at any point 15 * in an application. 16 * 17 * @see ResourceLoader 18 * @see IClassLoadStrategy 19 * @see DefaultClassLoadStrategy 20 * 21 * @author Vlad Roubtsov, (C) 2003 22 */ 23public 24abstract class ClassLoaderResolver 25{ 26 // public: ................................................................ 27 28 // NOTE: don't use Logger in this class to avoid infinite recursion 29 30 /** 31 * This method selects the "best" classloader instance to be used for 32 * class/resource loading by whoever calls this method. The decision 33 * typically involves choosing between the caller's current, thread context, 34 * system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy} 35 * instance established by the last call to {@link #setStrategy}.<P> 36 * 37 * This method does not throw. 38 * 39 * @param caller [null input eliminates the caller's current classloader 40 * from consideration] 41 * 42 * @return classloader to be used by the caller ['null' indicates the 43 * primordial loader] 44 */ 45 public static synchronized ClassLoader getClassLoader (final Class caller) 46 { 47 final ClassLoadContext ctx = new ClassLoadContext (caller); 48 49 return s_strategy.getClassLoader (ctx); 50 } 51 52 /** 53 * This method selects the "best" classloader instance to be used for 54 * class/resource loading by whoever calls this method. The decision 55 * typically involves choosing between the caller's current, thread context, 56 * system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy} 57 * instance established by the last call to {@link #setStrategy}.<P> 58 * 59 * This method uses its own caller to set the call context. To be able to 60 * override this decision explicitly, use {@link #getClassLoader(Class)}.<P> 61 * 62 * This method does not throw. 63 * 64 * @return classloader to be used by the caller ['null' indicates the 65 * primordial loader] 66 */ 67 public static synchronized ClassLoader getClassLoader () 68 { 69 final Class caller = getCallerClass (1); // 'caller' can be set to null 70 final ClassLoadContext ctx = new ClassLoadContext (caller); 71 72 return s_strategy.getClassLoader (ctx); 73 } 74 75 /* 76 * Indexes into the current method call context with a given offset. Offset 0 77 * corresponds to the immediate caller, offset 1 corresponds to its caller, 78 * etc.<P> 79 * 80 * Invariant: getCallerClass(0) == class containing code that performs this call 81 */ 82 public static Class getCallerClass (final int callerOffset) 83 { 84 if (CALLER_RESOLVER == null) return null; // only happens if <clinit> failed 85 86 return CALLER_RESOLVER.getClassContext () [CALL_CONTEXT_OFFSET + callerOffset]; 87 } 88 89 /** 90 * Returns 'true' if 'loader2' is a delegation child of 'loader1' [or if 91 * 'loader1'=='loader2']. Of course, this works only for classloaders that 92 * set their parent pointers correctly. 'null' is interpreted as the 93 * primordial loader [i.e., everybody's parent]. 94 */ 95 public static boolean isChild (final ClassLoader loader1, ClassLoader loader2) 96 { 97 if (loader1 == loader2) return true; 98 if (loader2 == null) return false; 99 if (loader1 == null) return true; 100 101 for ( ; loader2 != null; loader2 = loader2.getParent ()) 102 { 103 if (loader2 == loader1) return true; 104 } 105 106 return false; 107 } 108 109 /** 110 * Gets the current classloader selection strategy setting. 111 */ 112 public static synchronized IClassLoadStrategy getStrategy () 113 { 114 return s_strategy; 115 } 116 117 /** 118 * Sets the classloader selection strategy to be used by subsequent calls 119 * to {@link #getClassLoader()}. An instance of {@link ClassLoaderResolver.DefaultClassLoadStrategy} 120 * is in effect if this method is never called. 121 * 122 * @param strategy new strategy [may not be null] 123 * @return previous setting 124 */ 125 public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy) 126 { 127 if (strategy == null) throw new IllegalArgumentException ("null input: strategy"); 128 129 final IClassLoadStrategy old = s_strategy; 130 s_strategy = strategy; 131 132 return old; 133 } 134 135 // protected: ............................................................. 136 137 // package: ............................................................... 138 139 // private: ............................................................... 140 141 142 private static final class DefaultClassLoadStrategy implements IClassLoadStrategy 143 { 144 public ClassLoader getClassLoader (final ClassLoadContext ctx) 145 { 146 if (ctx == null) throw new IllegalArgumentException ("null input: ctx"); 147 148 final Class caller = ctx.getCallerClass (); 149 final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader (); 150 151 ClassLoader result; 152 153 if (caller == null) 154 result = contextLoader; 155 else 156 { 157 final ClassLoader callerLoader = caller.getClassLoader (); 158 159 // if 'callerLoader' and 'contextLoader' are in a parent-child 160 // relationship, always choose the child: 161 162 // SF BUG 975080: change the sibling case to use 'callerLoader' 163 // to work around ANT 1.6.x incorrect classloading model: 164 165 if (isChild (callerLoader, contextLoader)) 166 result = contextLoader; 167 else 168 result = callerLoader; 169 } 170 171 final ClassLoader systemLoader = ClassLoader.getSystemClassLoader (); 172 173 // precaution for when deployed as a bootstrap or extension class: 174 if (isChild (result, systemLoader)) 175 result = systemLoader; 176 177 return result; 178 } 179 180 } // end of nested class 181 182 183 /** 184 * A helper class to get the call context. It subclasses SecurityManager 185 * to make getClassContext() accessible. An instance of CallerResolver 186 * only needs to be created, not installed as an actual security 187 * manager. 188 */ 189 private static final class CallerResolver extends SecurityManager 190 { 191 protected Class [] getClassContext () 192 { 193 return super.getClassContext (); 194 } 195 196 } // end of nested class 197 198 199 private ClassLoaderResolver () {} // prevent subclassing 200 201 202 private static IClassLoadStrategy s_strategy; // initialized in <clinit> 203 204 private static final int CALL_CONTEXT_OFFSET = 2; // may need to change if this class is redesigned 205 private static final CallerResolver CALLER_RESOLVER; // set in <clinit> 206 //private static Throwable CLINIT_FAILURE; 207 208 static 209 { 210 CallerResolver temp = null; 211 try 212 { 213 // this can fail if the current SecurityManager does not allow 214 // RuntimePermission ("createSecurityManager"): 215 216 temp = new CallerResolver (); 217 } 218 catch (Throwable t) 219 { 220 //CLINIT_FAILURE = t; 221 } 222 CALLER_RESOLVER = temp; 223 224 s_strategy = new DefaultClassLoadStrategy (); 225 } 226 227} // end of class 228// ----------------------------------------------------------------------------