diff options
Diffstat (limited to 'core/java12/com/vladium/util/ClassLoaderResolver.java')
-rw-r--r-- | core/java12/com/vladium/util/ClassLoaderResolver.java | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/core/java12/com/vladium/util/ClassLoaderResolver.java b/core/java12/com/vladium/util/ClassLoaderResolver.java new file mode 100644 index 0000000..caa35a8 --- /dev/null +++ b/core/java12/com/vladium/util/ClassLoaderResolver.java @@ -0,0 +1,228 @@ +/* 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 +// ----------------------------------------------------------------------------
\ No newline at end of file |