summaryrefslogtreecommitdiff
path: root/core/java12/com/vladium/util/ClassLoaderResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java12/com/vladium/util/ClassLoaderResolver.java')
-rw-r--r--core/java12/com/vladium/util/ClassLoaderResolver.java228
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