summaryrefslogtreecommitdiff
path: root/core/java12/com/vladium/util/ClassLoaderResolver.java
blob: caa35a8ed3880d9b51557ca8dfa23a976341e350 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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
// ----------------------------------------------------------------------------