/* 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}.
*
* 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}.
*
* This method uses its own caller to set the call context. To be able to
* override this decision explicitly, use {@link #getClassLoader(Class)}.
*
* 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.
*
* 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 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
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
//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
// ----------------------------------------------------------------------------