JDKClassLoader.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * RMI-IIOP v1.0
28 * Copyright IBM Corp. 1998 1999  All Rights Reserved
29 *
30 */
31
32package com.sun.corba.se.impl.util;
33
34import sun.corba.Bridge ;
35
36import java.util.Map ;
37import java.util.WeakHashMap ;
38import java.util.Collections ;
39
40import java.security.AccessController ;
41import java.security.PrivilegedAction ;
42
43/**
44 *  Utility method for crawling call stack to load class
45 */
46class JDKClassLoader {
47
48    private static final JDKClassLoaderCache classCache
49        = new JDKClassLoaderCache();
50
51    private static final Bridge bridge =
52        (Bridge)AccessController.doPrivileged(
53            new PrivilegedAction() {
54                public Object run() {
55                    return Bridge.get() ;
56                }
57            }
58        ) ;
59
60    static Class loadClass(Class aClass, String className)
61        throws ClassNotFoundException {
62
63        // Maintain the same error semantics as Class.forName()
64        if (className == null) {
65            throw new NullPointerException();
66        }
67        if (className.length() == 0) {
68            throw new ClassNotFoundException();
69        }
70
71        // It would be nice to bypass JDKClassLoader's attempts completely
72        // if it's known that the latest user defined ClassLoader will
73        // fail.
74        //
75        // Otherwise, we end up calling Class.forName here as well as in
76        // the next step in JDKBridge.  That can take a long time depending
77        // on the length of the classpath.
78
79        // Note: Looking at the only place in JDKBridge where this code
80        // is invoked, it is clear that aClass will always be null.
81        ClassLoader loader;
82        if (aClass != null) {
83            loader = aClass.getClassLoader();
84        } else {
85            loader = bridge.getLatestUserDefinedLoader();
86        }
87        // See createKey for a description of what's involved
88        Object key = classCache.createKey(className, loader);
89
90        if (classCache.knownToFail(key)) {
91            throw new ClassNotFoundException(className);
92        } else {
93            try {
94                // Loading this class with the call stack
95                // loader isn't known to fail, so try
96                // to load it.
97                return Class.forName(className, false, loader);
98            } catch(ClassNotFoundException cnfe) {
99                // Record that we failed to find the class
100                // with this particular loader.  This way, we won't
101                // waste time looking with this loader, again.
102                classCache.recordFailure(key);
103                throw cnfe;
104            }
105        }
106    }
107
108    /**
109     * Private cache implementation specific to JDKClassLoader.
110     */
111    private static class JDKClassLoaderCache
112    {
113        // JDKClassLoader couldn't find the class with the located
114        // ClassLoader.  Note this in our cache so JDKClassLoader
115        // can abort early next time.
116        public final void recordFailure(Object key) {
117            cache.put(key, JDKClassLoaderCache.KNOWN_TO_FAIL);
118        }
119
120        // Factory for a key (CacheKey is an implementation detail
121        // of JDKClassLoaderCache).
122        //
123        // A key currently consists of the class name as well as
124        // the latest user defined class loader, so it's fairly
125        // expensive to create.
126        public final Object createKey(String className, ClassLoader latestLoader) {
127            return new CacheKey(className, latestLoader);
128        }
129
130        // Determine whether or not this combination of class name
131        // and ClassLoader is known to fail.
132        public final boolean knownToFail(Object key) {
133            return cache.get(key) == JDKClassLoaderCache.KNOWN_TO_FAIL;
134        }
135
136        // Synchronized WeakHashMap
137        private final Map cache
138            = Collections.synchronizedMap(new WeakHashMap());
139
140        // Cache result used to mark the caches when there is
141        // no way JDKClassLoader could succeed with the given
142        // key
143        private static final Object KNOWN_TO_FAIL = new Object();
144
145        // Key consisting of the class name and the latest
146        // user defined class loader
147        private static class CacheKey
148        {
149            String className;
150            ClassLoader loader;
151
152            public CacheKey(String className, ClassLoader loader) {
153                this.className = className;
154                this.loader = loader;
155            }
156
157            // Try to incorporate both class name and loader
158            // into the hashcode
159            public int hashCode() {
160                if (loader == null)
161                    return className.hashCode();
162                else
163                    return className.hashCode() ^ loader.hashCode();
164            }
165
166            public boolean equals(Object obj) {
167                try {
168
169                    // WeakHashMap may compare null keys
170                    if (obj == null)
171                        return false;
172
173                    CacheKey other = (CacheKey)obj;
174
175                    // I've made a decision to actually compare the
176                    // loader references.  I don't want a case when
177                    // two loader instances override their equals
178                    // methods and only compare code base.
179                    //
180                    // This way, at worst, our performance will
181                    // be slower, but we know we'll do the correct
182                    // loading.
183                    return (className.equals(other.className) &&
184                            loader == other.loader);
185
186                } catch (ClassCastException cce) {
187                    return false;
188                }
189            }
190        }
191    }
192}
193