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      */
getClassLoader(final Class caller)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      */
getClassLoader()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      */
getCallerClass(final int callerOffset)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      */
isChild(final ClassLoader loader1, ClassLoader loader2)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      */
getStrategy()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      */
setStrategy(final IClassLoadStrategy strategy)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     {
getClassLoader(final ClassLoadContext ctx)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     {
getClassContext()191         protected Class [] getClassContext ()
192         {
193             return super.getClassContext ();
194         }
195 
196     } // end of nested class
197 
198 
ClassLoaderResolver()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 // ----------------------------------------------------------------------------