Home | History | Annotate | Download | only in util
      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  */
      9 package 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  */
     23 public
     24 abstract 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 // ----------------------------------------------------------------------------