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 
  32 package com.sun.corba.se.impl.util;
  33 
  34 import sun.corba.Bridge ;
  35 
  36 import java.util.Map ;
  37 import java.util.WeakHashMap ;
  38 import java.util.Collections ;
  39 
  40 import java.security.AccessController ;
  41 import java.security.PrivilegedAction ;
  42 
  43 /**
  44  *  Utility method for crawling call stack to load class
  45  */
  46 class 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 }