/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. * * This program and the accompanying materials are made available under * the terms of the Common Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/cpl-v10.html * * $Id: ClassLoaderResolver.java,v 1.1.1.1.2.2 2004/07/10 03:34:53 vlad_r Exp $ */ package com.vladium.util; // ---------------------------------------------------------------------------- /** * This non-instantiable non-subclassable class acts as the global point for * choosing a ClassLoader for dynamic class/resource loading at any point * in an application. * * @see ResourceLoader * @see IClassLoadStrategy * @see DefaultClassLoadStrategy * * @author Vlad Roubtsov, (C) 2003 */ public abstract class ClassLoaderResolver { // public: ................................................................ // NOTE: don't use Logger in this class to avoid infinite recursion /** * This method selects the "best" classloader instance to be used for * class/resource loading by whoever calls this method. The decision * typically involves choosing between the caller's current, thread context, * system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy} * instance established by the last call to {@link #setStrategy}.<P> * * This method does not throw. * * @param caller [null input eliminates the caller's current classloader * from consideration] * * @return classloader to be used by the caller ['null' indicates the * primordial loader] */ public static synchronized ClassLoader getClassLoader (final Class caller) { final ClassLoadContext ctx = new ClassLoadContext (caller); return s_strategy.getClassLoader (ctx); } /** * This method selects the "best" classloader instance to be used for * class/resource loading by whoever calls this method. The decision * typically involves choosing between the caller's current, thread context, * system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy} * instance established by the last call to {@link #setStrategy}.<P> * * This method uses its own caller to set the call context. To be able to * override this decision explicitly, use {@link #getClassLoader(Class)}.<P> * * This method does not throw. * * @return classloader to be used by the caller ['null' indicates the * primordial loader] */ public static synchronized ClassLoader getClassLoader () { final Class caller = getCallerClass (1); // 'caller' can be set to null final ClassLoadContext ctx = new ClassLoadContext (caller); return s_strategy.getClassLoader (ctx); } /* * Indexes into the current method call context with a given offset. Offset 0 * corresponds to the immediate caller, offset 1 corresponds to its caller, * etc.<P> * * Invariant: getCallerClass(0) == class containing code that performs this call */ public static Class getCallerClass (final int callerOffset) { if (CALLER_RESOLVER == null) return null; // only happens if <clinit> failed return CALLER_RESOLVER.getClassContext () [CALL_CONTEXT_OFFSET + callerOffset]; } /** * Returns 'true' if 'loader2' is a delegation child of 'loader1' [or if * 'loader1'=='loader2']. Of course, this works only for classloaders that * set their parent pointers correctly. 'null' is interpreted as the * primordial loader [i.e., everybody's parent]. */ public static boolean isChild (final ClassLoader loader1, ClassLoader loader2) { if (loader1 == loader2) return true; if (loader2 == null) return false; if (loader1 == null) return true; for ( ; loader2 != null; loader2 = loader2.getParent ()) { if (loader2 == loader1) return true; } return false; } /** * Gets the current classloader selection strategy setting. */ public static synchronized IClassLoadStrategy getStrategy () { return s_strategy; } /** * Sets the classloader selection strategy to be used by subsequent calls * to {@link #getClassLoader()}. An instance of {@link ClassLoaderResolver.DefaultClassLoadStrategy} * is in effect if this method is never called. * * @param strategy new strategy [may not be null] * @return previous setting */ public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy) { if (strategy == null) throw new IllegalArgumentException ("null input: strategy"); final IClassLoadStrategy old = s_strategy; s_strategy = strategy; return old; } // protected: ............................................................. // package: ............................................................... // private: ............................................................... private static final class DefaultClassLoadStrategy implements IClassLoadStrategy { public ClassLoader getClassLoader (final ClassLoadContext ctx) { if (ctx == null) throw new IllegalArgumentException ("null input: ctx"); final Class caller = ctx.getCallerClass (); final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader (); ClassLoader result; if (caller == null) result = contextLoader; else { final ClassLoader callerLoader = caller.getClassLoader (); // if 'callerLoader' and 'contextLoader' are in a parent-child // relationship, always choose the child: // SF BUG 975080: change the sibling case to use 'callerLoader' // to work around ANT 1.6.x incorrect classloading model: if (isChild (callerLoader, contextLoader)) result = contextLoader; else result = callerLoader; } final ClassLoader systemLoader = ClassLoader.getSystemClassLoader (); // precaution for when deployed as a bootstrap or extension class: if (isChild (result, systemLoader)) result = systemLoader; return result; } } // end of nested class /** * A helper class to get the call context. It subclasses SecurityManager * to make getClassContext() accessible. An instance of CallerResolver * only needs to be created, not installed as an actual security * manager. */ private static final class CallerResolver extends SecurityManager { protected Class [] getClassContext () { return super.getClassContext (); } } // end of nested class private ClassLoaderResolver () {} // prevent subclassing private static IClassLoadStrategy s_strategy; // initialized in <clinit> private static final int CALL_CONTEXT_OFFSET = 2; // may need to change if this class is redesigned private static final CallerResolver CALLER_RESOLVER; // set in <clinit> //private static Throwable CLINIT_FAILURE; static { CallerResolver temp = null; try { // this can fail if the current SecurityManager does not allow // RuntimePermission ("createSecurityManager"): temp = new CallerResolver (); } catch (Throwable t) { //CLINIT_FAILURE = t; } CALLER_RESOLVER = temp; s_strategy = new DefaultClassLoadStrategy (); } } // end of class // ----------------------------------------------------------------------------