1201 lines · 46.0 KB · Java
RawDownload
/* * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.rmi.server; import java.io.File;import java.io.FilePermission;import java.io.IOException;import java.lang.ref.ReferenceQueue;import java.lang.ref.SoftReference;import java.lang.ref.WeakReference;import java.lang.reflect.Modifier;import java.lang.reflect.Proxy;import java.net.JarURLConnection;import java.net.MalformedURLException;import java.net.SocketPermission;import java.net.URL;import java.net.URLClassLoader;import java.net.URLConnection;import java.security.AccessControlContext;import java.security.CodeSource;import java.security.Permission;import java.security.Permissions;import java.security.PermissionCollection;import java.security.Policy;import java.security.ProtectionDomain;import java.rmi.server.LogStream;import java.util.Arrays;import java.util.Collections;import java.util.Enumeration;import java.util.HashMap;import java.util.IdentityHashMap;import java.util.Map;import java.util.StringTokenizer;import java.util.WeakHashMap;import sun.rmi.runtime.Log;import sun.security.action.GetPropertyAction; /** * <code>LoaderHandler</code> provides the implementation of the static * methods of the <code>java.rmi.server.RMIClassLoader</code> class. * * @author      Ann Wollrath * @author      Peter Jones * @author      Laird Dornin */public final class LoaderHandler {     /** RMI class loader log level */    static final int logLevel = LogStream.parseLevel(        java.security.AccessController.doPrivileged(            new GetPropertyAction("sun.rmi.loader.logLevel")));     /* loader system log */    static final Log loaderLog =        Log.getLog("sun.rmi.loader", "loader", LoaderHandler.logLevel);     /**     * value of "java.rmi.server.codebase" property, as cached at class     * initialization time.  It may contain malformed URLs.     */    private static String codebaseProperty = null;    static {        String prop = java.security.AccessController.doPrivileged(            new GetPropertyAction("java.rmi.server.codebase"));        if (prop != null && prop.trim().length() > 0) {            codebaseProperty = prop;        }    }     /** list of URLs represented by the codebase property, if valid */    private static URL[] codebaseURLs = null;     /** table of class loaders that use codebase property for annotation */    private static final Map<ClassLoader, Void> codebaseLoaders =        Collections.synchronizedMap(new IdentityHashMap<ClassLoader, Void>(5));    static {        for (ClassLoader codebaseLoader = ClassLoader.getSystemClassLoader();             codebaseLoader != null;             codebaseLoader = codebaseLoader.getParent())        {            codebaseLoaders.put(codebaseLoader, null);        }    }     /**     * table mapping codebase URL path and context class loader pairs     * to class loader instances.  Entries hold class loaders with weak     * references, so this table does not prevent loaders from being     * garbage collected.     */    private static final HashMap<LoaderKey, LoaderEntry> loaderTable        = new HashMap<LoaderKey, LoaderEntry>(5);     /** reference queue for cleared class loader entries */    private static final ReferenceQueue<Loader> refQueue        = new ReferenceQueue<Loader>();     /*     * Disallow anyone from creating one of these.     */    private LoaderHandler() {}     /**     * Returns an array of URLs initialized with the value of the     * java.rmi.server.codebase property as the URL path.     */    private static synchronized URL[] getDefaultCodebaseURLs()        throws MalformedURLException    {        /*         * If it hasn't already been done, convert the codebase property         * into an array of URLs; this may throw a MalformedURLException.         */        if (codebaseURLs == null) {            if (codebaseProperty != null) {                codebaseURLs = pathToURLs(codebaseProperty);            } else {                codebaseURLs = new URL[0];            }        }        return codebaseURLs;    }     /**     * Load a class from a network location (one or more URLs),     * but first try to resolve the named class through the given     * "default loader".     */    public static Class loadClass(String codebase, String name,                                  ClassLoader defaultLoader)        throws MalformedURLException, ClassNotFoundException    {        if (loaderLog.isLoggable(Log.BRIEF)) {            loaderLog.log(Log.BRIEF,                "name = \"" + name + "\", " +                "codebase = \"" + (codebase != null ? codebase : "") + "\"" +                (defaultLoader != null ?                 ", defaultLoader = " + defaultLoader : ""));        }         URL[] urls;        if (codebase != null) {            urls = pathToURLs(codebase);        } else {            urls = getDefaultCodebaseURLs();        }         if (defaultLoader != null) {            try {                Class c = Class.forName(name, false, defaultLoader);                if (loaderLog.isLoggable(Log.VERBOSE)) {                    loaderLog.log(Log.VERBOSE,                        "class \"" + name + "\" found via defaultLoader, " +                        "defined by " + c.getClassLoader());                }                return c;            } catch (ClassNotFoundException e) {            }        }         return loadClass(urls, name);    }     /**     * Returns the class annotation (representing the location for     * a class) that RMI will use to annotate the call stream when     * marshalling objects of the given class.     */    public static String getClassAnnotation(Class cl) {        String name = cl.getName();         /*         * Class objects for arrays of primitive types never need an         * annotation, because they never need to be (or can be) downloaded.         *         * REMIND: should we (not) be annotating classes that are in         * "java.*" packages?         */        int nameLength = name.length();        if (nameLength > 0 && name.charAt(0) == '[') {            // skip past all '[' characters (see bugid 4211906)            int i = 1;            while (nameLength > i && name.charAt(i) == '[') {                i++;            }            if (nameLength > i && name.charAt(i) != 'L') {                return null;            }        }         /*         * Get the class's class loader.  If it is null, the system class         * loader, an ancestor of the base class loader (such as the loader         * for installed extensions), return the value of the         * "java.rmi.server.codebase" property.         */        ClassLoader loader = cl.getClassLoader();        if (loader == null || codebaseLoaders.containsKey(loader)) {            return codebaseProperty;        }         /*         * Get the codebase URL path for the class loader, if it supports         * such a notion (i.e., if it is a URLClassLoader or subclass).         */        String annotation = null;        if (loader instanceof Loader) {            /*             * If the class loader is one of our RMI class loaders, we have             * already computed the class annotation string, and no             * permissions are required to know the URLs.             */            annotation = ((Loader) loader).getClassAnnotation();         } else if (loader instanceof URLClassLoader) {            try {                URL[] urls = ((URLClassLoader) loader).getURLs();                if (urls != null) {                    /*                     * If the class loader is not one of our RMI class loaders,                     * we must verify that the current access control context                     * has permission to know all of these URLs.                     */                    SecurityManager sm = System.getSecurityManager();                    if (sm != null) {                        Permissions perms = new Permissions();                        for (int i = 0; i < urls.length; i++) {                            Permission p =                                urls[i].openConnection().getPermission();                            if (p != null) {                                if (!perms.implies(p)) {                                    sm.checkPermission(p);                                    perms.add(p);                                }                            }                        }                    }                     annotation = urlsToPath(urls);                }            } catch (SecurityException e) {                /*                 * If access was denied to the knowledge of the class                 * loader's URLs, fall back to the default behavior.                 */            } catch (IOException e) {                /*                 * This shouldn't happen, although it is declared to be                 * thrown by openConnection() and getPermission().  If it                 * does happen, forget about this class loader's URLs and                 * fall back to the default behavior.                 */            }        }         if (annotation != null) {            return annotation;        } else {            return codebaseProperty;    // REMIND: does this make sense??        }    }     /**     * Returns a classloader that loads classes from the given codebase URL     * path.  The parent classloader of the returned classloader is the     * context class loader.     */    public static ClassLoader getClassLoader(String codebase)        throws MalformedURLException    {        ClassLoader parent = getRMIContextClassLoader();         URL[] urls;        if (codebase != null) {            urls = pathToURLs(codebase);        } else {            urls = getDefaultCodebaseURLs();        }         /*         * If there is a security manager, the current access control         * context must have the "getClassLoader" RuntimePermission.         */        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            sm.checkPermission(new RuntimePermission("getClassLoader"));        } else {            /*             * But if no security manager is set, disable access to             * RMI class loaders and simply return the parent loader.             */            return parent;        }         Loader loader = lookupLoader(urls, parent);         /*         * Verify that the caller has permission to access this loader.         */        if (loader != null) {            loader.checkPermissions();        }         return loader;    }     /**     * Return the security context of the given class loader.     */    public static Object getSecurityContext(ClassLoader loader) {        /*         * REMIND: This is a bogus JDK1.1-compatible implementation.         * This method should never be called by application code anyway         * (hence the deprecation), but should it do something different         * and perhaps more useful, like return a String or a URL[]?         */        if (loader instanceof Loader) {            URL[] urls = ((Loader) loader).getURLs();            if (urls.length > 0) {                return urls[0];            }        }        return null;    }     /**     * Register a class loader as one whose classes should always be     * annotated with the value of the "java.rmi.server.codebase" property.     */    public static void registerCodebaseLoader(ClassLoader loader) {        codebaseLoaders.put(loader, null);    }     /**     * Load a class from the RMI class loader corresponding to the given     * codebase URL path in the current execution context.     */    private static Class loadClass(URL[] urls, String name)        throws ClassNotFoundException    {        ClassLoader parent = getRMIContextClassLoader();        if (loaderLog.isLoggable(Log.VERBOSE)) {            loaderLog.log(Log.VERBOSE,                "(thread context class loader: " + parent + ")");        }         /*         * If no security manager is set, disable access to RMI class         * loaders and simply delegate request to the parent loader         * (see bugid 4140511).         */        SecurityManager sm = System.getSecurityManager();        if (sm == null) {            try {                Class c = Class.forName(name, false, parent);                if (loaderLog.isLoggable(Log.VERBOSE)) {                    loaderLog.log(Log.VERBOSE,                        "class \"" + name + "\" found via " +                        "thread context class loader " +                        "(no security manager: codebase disabled), " +                        "defined by " + c.getClassLoader());                }                return c;            } catch (ClassNotFoundException e) {                if (loaderLog.isLoggable(Log.BRIEF)) {                    loaderLog.log(Log.BRIEF,                        "class \"" + name + "\" not found via " +                        "thread context class loader " +                        "(no security manager: codebase disabled)", e);                }                throw new ClassNotFoundException(e.getMessage() +                    " (no security manager: RMI class loader disabled)",                    e.getException());            }        }         /*         * Get or create the RMI class loader for this codebase URL path         * and parent class loader pair.         */        Loader loader = lookupLoader(urls, parent);         try {            if (loader != null) {                /*                 * Verify that the caller has permission to access this loader.                 */                loader.checkPermissions();            }        } catch (SecurityException e) {            /*             * If the current access control context does not have permission             * to access all of the URLs in the codebase path, wrap the             * resulting security exception in a ClassNotFoundException, so             * the caller can handle this outcome just like any other class             * loading failure (see bugid 4146529).             */            try {                /*                 * But first, check to see if the named class could have been                 * resolved without the security-offending codebase anyway;                 * if so, return successfully (see bugids 4191926 & 4349670).                 */                Class c = Class.forName(name, false, parent);                if (loaderLog.isLoggable(Log.VERBOSE)) {                    loaderLog.log(Log.VERBOSE,                        "class \"" + name + "\" found via " +                        "thread context class loader " +                        "(access to codebase denied), " +                        "defined by " + c.getClassLoader());                }                return c;            } catch (ClassNotFoundException unimportant) {                /*                 * Presumably the security exception is the more important                 * exception to report in this case.                 */                if (loaderLog.isLoggable(Log.BRIEF)) {                    loaderLog.log(Log.BRIEF,                        "class \"" + name + "\" not found via " +                        "thread context class loader " +                        "(access to codebase denied)", e);                }                throw new ClassNotFoundException(                    "access to class loader denied", e);            }        }         try {            Class c = Class.forName(name, false, loader);            if (loaderLog.isLoggable(Log.VERBOSE)) {                loaderLog.log(Log.VERBOSE,                    "class \"" + name + "\" " + "found via codebase, " +                    "defined by " + c.getClassLoader());            }            return c;        } catch (ClassNotFoundException e) {            if (loaderLog.isLoggable(Log.BRIEF)) {                loaderLog.log(Log.BRIEF,                    "class \"" + name + "\" not found via codebase", e);            }            throw e;        }    }     /**     * Define and return a dynamic proxy class in a class loader with     * URLs supplied in the given location.  The proxy class will     * implement interface classes named by the given array of     * interface names.     */    public static Class loadProxyClass(String codebase, String[] interfaces,                                       ClassLoader defaultLoader)        throws MalformedURLException, ClassNotFoundException    {        if (loaderLog.isLoggable(Log.BRIEF)) {            loaderLog.log(Log.BRIEF,                "interfaces = " + Arrays.asList(interfaces) + ", " +                "codebase = \"" + (codebase != null ? codebase : "") + "\"" +                (defaultLoader != null ?                 ", defaultLoader = " + defaultLoader : ""));        }         /*         * This method uses a fairly complex algorithm to load the         * proxy class and its interface classes in order to maximize         * the likelihood that the proxy's codebase annotation will be         * preserved.  The algorithm is (assuming that all of the         * proxy interface classes are public):         *         * If the default loader is not null, try to load the proxy         * interfaces through that loader. If the interfaces can be         * loaded in that loader, try to define the proxy class in an         * RMI class loader (child of the context class loader) before         * trying to define the proxy in the default loader.  If the         * attempt to define the proxy class succeeds, the codebase         * annotation is preserved.  If the attempt fails, try to

Lines 1–500 of 1201.