Save This Page
Home » openjdk-7 » sun.rmi » server » [javadoc | source]
    1   /*
    2    * Copyright 1996-2005 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package sun.rmi.server;
   27   
   28   import java.io.File;
   29   import java.io.FilePermission;
   30   import java.io.IOException;
   31   import java.lang.ref.ReferenceQueue;
   32   import java.lang.ref.SoftReference;
   33   import java.lang.ref.WeakReference;
   34   import java.lang.reflect.Modifier;
   35   import java.lang.reflect.Proxy;
   36   import java.net.JarURLConnection;
   37   import java.net.MalformedURLException;
   38   import java.net.SocketPermission;
   39   import java.net.URL;
   40   import java.net.URLClassLoader;
   41   import java.net.URLConnection;
   42   import java.security.AccessControlContext;
   43   import java.security.CodeSource;
   44   import java.security.Permission;
   45   import java.security.Permissions;
   46   import java.security.PermissionCollection;
   47   import java.security.Policy;
   48   import java.security.ProtectionDomain;
   49   import java.rmi.server.LogStream;
   50   import java.util.Arrays;
   51   import java.util.Collections;
   52   import java.util.Enumeration;
   53   import java.util.HashMap;
   54   import java.util.IdentityHashMap;
   55   import java.util.Map;
   56   import java.util.StringTokenizer;
   57   import java.util.WeakHashMap;
   58   import sun.rmi.runtime.Log;
   59   import sun.security.action.GetPropertyAction;
   60   
   61   /**
   62    * <code>LoaderHandler</code> provides the implementation of the static
   63    * methods of the <code>java.rmi.server.RMIClassLoader</code> class.
   64    *
   65    * @author      Ann Wollrath
   66    * @author      Peter Jones
   67    * @author      Laird Dornin
   68    */
   69   public final class LoaderHandler {
   70   
   71       /** RMI class loader log level */
   72       static final int logLevel = LogStream.parseLevel(
   73           java.security.AccessController.doPrivileged(
   74               new GetPropertyAction("sun.rmi.loader.logLevel")));
   75   
   76       /* loader system log */
   77       static final Log loaderLog =
   78           Log.getLog("sun.rmi.loader", "loader", LoaderHandler.logLevel);
   79   
   80       /**
   81        * value of "java.rmi.server.codebase" property, as cached at class
   82        * initialization time.  It may contain malformed URLs.
   83        */
   84       private static String codebaseProperty = null;
   85       static {
   86           String prop = java.security.AccessController.doPrivileged(
   87               new GetPropertyAction("java.rmi.server.codebase"));
   88           if (prop != null && prop.trim().length() > 0) {
   89               codebaseProperty = prop;
   90           }
   91       }
   92   
   93       /** list of URLs represented by the codebase property, if valid */
   94       private static URL[] codebaseURLs = null;
   95   
   96       /** table of class loaders that use codebase property for annotation */
   97       private static final Map<ClassLoader, Void> codebaseLoaders =
   98           Collections.synchronizedMap(new IdentityHashMap<ClassLoader, Void>(5));
   99       static {
  100           for (ClassLoader codebaseLoader = ClassLoader.getSystemClassLoader();
  101                codebaseLoader != null;
  102                codebaseLoader = codebaseLoader.getParent())
  103           {
  104               codebaseLoaders.put(codebaseLoader, null);
  105           }
  106       }
  107   
  108       /**
  109        * table mapping codebase URL path and context class loader pairs
  110        * to class loader instances.  Entries hold class loaders with weak
  111        * references, so this table does not prevent loaders from being
  112        * garbage collected.
  113        */
  114       private static final HashMap<LoaderKey, LoaderEntry> loaderTable
  115           = new HashMap<LoaderKey, LoaderEntry>(5);
  116   
  117       /** reference queue for cleared class loader entries */
  118       private static final ReferenceQueue<Loader> refQueue
  119           = new ReferenceQueue<Loader>();
  120   
  121       /*
  122        * Disallow anyone from creating one of these.
  123        */
  124       private LoaderHandler() {}
  125   
  126       /**
  127        * Returns an array of URLs initialized with the value of the
  128        * java.rmi.server.codebase property as the URL path.
  129        */
  130       private static synchronized URL[] getDefaultCodebaseURLs()
  131           throws MalformedURLException
  132       {
  133           /*
  134            * If it hasn't already been done, convert the codebase property
  135            * into an array of URLs; this may throw a MalformedURLException.
  136            */
  137           if (codebaseURLs == null) {
  138               if (codebaseProperty != null) {
  139                   codebaseURLs = pathToURLs(codebaseProperty);
  140               } else {
  141                   codebaseURLs = new URL[0];
  142               }
  143           }
  144           return codebaseURLs;
  145       }
  146   
  147       /**
  148        * Load a class from a network location (one or more URLs),
  149        * but first try to resolve the named class through the given
  150        * "default loader".
  151        */
  152       public static Class loadClass(String codebase, String name,
  153                                     ClassLoader defaultLoader)
  154           throws MalformedURLException, ClassNotFoundException
  155       {
  156           if (loaderLog.isLoggable(Log.BRIEF)) {
  157               loaderLog.log(Log.BRIEF,
  158                   "name = \"" + name + "\", " +
  159                   "codebase = \"" + (codebase != null ? codebase : "") + "\"" +
  160                   (defaultLoader != null ?
  161                    ", defaultLoader = " + defaultLoader : ""));
  162           }
  163   
  164           URL[] urls;
  165           if (codebase != null) {
  166               urls = pathToURLs(codebase);
  167           } else {
  168               urls = getDefaultCodebaseURLs();
  169           }
  170   
  171           if (defaultLoader != null) {
  172               try {
  173                   Class c = Class.forName(name, false, defaultLoader);
  174                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  175                       loaderLog.log(Log.VERBOSE,
  176                           "class \"" + name + "\" found via defaultLoader, " +
  177                           "defined by " + c.getClassLoader());
  178                   }
  179                   return c;
  180               } catch (ClassNotFoundException e) {
  181               }
  182           }
  183   
  184           return loadClass(urls, name);
  185       }
  186   
  187       /**
  188        * Returns the class annotation (representing the location for
  189        * a class) that RMI will use to annotate the call stream when
  190        * marshalling objects of the given class.
  191        */
  192       public static String getClassAnnotation(Class cl) {
  193           String name = cl.getName();
  194   
  195           /*
  196            * Class objects for arrays of primitive types never need an
  197            * annotation, because they never need to be (or can be) downloaded.
  198            *
  199            * REMIND: should we (not) be annotating classes that are in
  200            * "java.*" packages?
  201            */
  202           int nameLength = name.length();
  203           if (nameLength > 0 && name.charAt(0) == '[') {
  204               // skip past all '[' characters (see bugid 4211906)
  205               int i = 1;
  206               while (nameLength > i && name.charAt(i) == '[') {
  207                   i++;
  208               }
  209               if (nameLength > i && name.charAt(i) != 'L') {
  210                   return null;
  211               }
  212           }
  213   
  214           /*
  215            * Get the class's class loader.  If it is null, the system class
  216            * loader, an ancestor of the base class loader (such as the loader
  217            * for installed extensions), return the value of the
  218            * "java.rmi.server.codebase" property.
  219            */
  220           ClassLoader loader = cl.getClassLoader();
  221           if (loader == null || codebaseLoaders.containsKey(loader)) {
  222               return codebaseProperty;
  223           }
  224   
  225           /*
  226            * Get the codebase URL path for the class loader, if it supports
  227            * such a notion (i.e., if it is a URLClassLoader or subclass).
  228            */
  229           String annotation = null;
  230           if (loader instanceof Loader) {
  231               /*
  232                * If the class loader is one of our RMI class loaders, we have
  233                * already computed the class annotation string, and no
  234                * permissions are required to know the URLs.
  235                */
  236               annotation = ((Loader) loader).getClassAnnotation();
  237   
  238           } else if (loader instanceof URLClassLoader) {
  239               try {
  240                   URL[] urls = ((URLClassLoader) loader).getURLs();
  241                   if (urls != null) {
  242                       /*
  243                        * If the class loader is not one of our RMI class loaders,
  244                        * we must verify that the current access control context
  245                        * has permission to know all of these URLs.
  246                        */
  247                       SecurityManager sm = System.getSecurityManager();
  248                       if (sm != null) {
  249                           Permissions perms = new Permissions();
  250                           for (int i = 0; i < urls.length; i++) {
  251                               Permission p =
  252                                   urls[i].openConnection().getPermission();
  253                               if (p != null) {
  254                                   if (!perms.implies(p)) {
  255                                       sm.checkPermission(p);
  256                                       perms.add(p);
  257                                   }
  258                               }
  259                           }
  260                       }
  261   
  262                       annotation = urlsToPath(urls);
  263                   }
  264               } catch (SecurityException e) {
  265                   /*
  266                    * If access was denied to the knowledge of the class
  267                    * loader's URLs, fall back to the default behavior.
  268                    */
  269               } catch (IOException e) {
  270                   /*
  271                    * This shouldn't happen, although it is declared to be
  272                    * thrown by openConnection() and getPermission().  If it
  273                    * does happen, forget about this class loader's URLs and
  274                    * fall back to the default behavior.
  275                    */
  276               }
  277           }
  278   
  279           if (annotation != null) {
  280               return annotation;
  281           } else {
  282               return codebaseProperty;    // REMIND: does this make sense??
  283           }
  284       }
  285   
  286       /**
  287        * Returns a classloader that loads classes from the given codebase URL
  288        * path.  The parent classloader of the returned classloader is the
  289        * context class loader.
  290        */
  291       public static ClassLoader getClassLoader(String codebase)
  292           throws MalformedURLException
  293       {
  294           ClassLoader parent = getRMIContextClassLoader();
  295   
  296           URL[] urls;
  297           if (codebase != null) {
  298               urls = pathToURLs(codebase);
  299           } else {
  300               urls = getDefaultCodebaseURLs();
  301           }
  302   
  303           /*
  304            * If there is a security manager, the current access control
  305            * context must have the "getClassLoader" RuntimePermission.
  306            */
  307           SecurityManager sm = System.getSecurityManager();
  308           if (sm != null) {
  309               sm.checkPermission(new RuntimePermission("getClassLoader"));
  310           } else {
  311               /*
  312                * But if no security manager is set, disable access to
  313                * RMI class loaders and simply return the parent loader.
  314                */
  315               return parent;
  316           }
  317   
  318           Loader loader = lookupLoader(urls, parent);
  319   
  320           /*
  321            * Verify that the caller has permission to access this loader.
  322            */
  323           if (loader != null) {
  324               loader.checkPermissions();
  325           }
  326   
  327           return loader;
  328       }
  329   
  330       /**
  331        * Return the security context of the given class loader.
  332        */
  333       public static Object getSecurityContext(ClassLoader loader) {
  334           /*
  335            * REMIND: This is a bogus JDK1.1-compatible implementation.
  336            * This method should never be called by application code anyway
  337            * (hence the deprecation), but should it do something different
  338            * and perhaps more useful, like return a String or a URL[]?
  339            */
  340           if (loader instanceof Loader) {
  341               URL[] urls = ((Loader) loader).getURLs();
  342               if (urls.length > 0) {
  343                   return urls[0];
  344               }
  345           }
  346           return null;
  347       }
  348   
  349       /**
  350        * Register a class loader as one whose classes should always be
  351        * annotated with the value of the "java.rmi.server.codebase" property.
  352        */
  353       public static void registerCodebaseLoader(ClassLoader loader) {
  354           codebaseLoaders.put(loader, null);
  355       }
  356   
  357       /**
  358        * Load a class from the RMI class loader corresponding to the given
  359        * codebase URL path in the current execution context.
  360        */
  361       private static Class loadClass(URL[] urls, String name)
  362           throws ClassNotFoundException
  363       {
  364           ClassLoader parent = getRMIContextClassLoader();
  365           if (loaderLog.isLoggable(Log.VERBOSE)) {
  366               loaderLog.log(Log.VERBOSE,
  367                   "(thread context class loader: " + parent + ")");
  368           }
  369   
  370           /*
  371            * If no security manager is set, disable access to RMI class
  372            * loaders and simply delegate request to the parent loader
  373            * (see bugid 4140511).
  374            */
  375           SecurityManager sm = System.getSecurityManager();
  376           if (sm == null) {
  377               try {
  378                   Class c = Class.forName(name, false, parent);
  379                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  380                       loaderLog.log(Log.VERBOSE,
  381                           "class \"" + name + "\" found via " +
  382                           "thread context class loader " +
  383                           "(no security manager: codebase disabled), " +
  384                           "defined by " + c.getClassLoader());
  385                   }
  386                   return c;
  387               } catch (ClassNotFoundException e) {
  388                   if (loaderLog.isLoggable(Log.BRIEF)) {
  389                       loaderLog.log(Log.BRIEF,
  390                           "class \"" + name + "\" not found via " +
  391                           "thread context class loader " +
  392                           "(no security manager: codebase disabled)", e);
  393                   }
  394                   throw new ClassNotFoundException(e.getMessage() +
  395                       " (no security manager: RMI class loader disabled)",
  396                       e.getException());
  397               }
  398           }
  399   
  400           /*
  401            * Get or create the RMI class loader for this codebase URL path
  402            * and parent class loader pair.
  403            */
  404           Loader loader = lookupLoader(urls, parent);
  405   
  406           try {
  407               if (loader != null) {
  408                   /*
  409                    * Verify that the caller has permission to access this loader.
  410                    */
  411                   loader.checkPermissions();
  412               }
  413           } catch (SecurityException e) {
  414               /*
  415                * If the current access control context does not have permission
  416                * to access all of the URLs in the codebase path, wrap the
  417                * resulting security exception in a ClassNotFoundException, so
  418                * the caller can handle this outcome just like any other class
  419                * loading failure (see bugid 4146529).
  420                */
  421               try {
  422                   /*
  423                    * But first, check to see if the named class could have been
  424                    * resolved without the security-offending codebase anyway;
  425                    * if so, return successfully (see bugids 4191926 & 4349670).
  426                    */
  427                   Class c = Class.forName(name, false, parent);
  428                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  429                       loaderLog.log(Log.VERBOSE,
  430                           "class \"" + name + "\" found via " +
  431                           "thread context class loader " +
  432                           "(access to codebase denied), " +
  433                           "defined by " + c.getClassLoader());
  434                   }
  435                   return c;
  436               } catch (ClassNotFoundException unimportant) {
  437                   /*
  438                    * Presumably the security exception is the more important
  439                    * exception to report in this case.
  440                    */
  441                   if (loaderLog.isLoggable(Log.BRIEF)) {
  442                       loaderLog.log(Log.BRIEF,
  443                           "class \"" + name + "\" not found via " +
  444                           "thread context class loader " +
  445                           "(access to codebase denied)", e);
  446                   }
  447                   throw new ClassNotFoundException(
  448                       "access to class loader denied", e);
  449               }
  450           }
  451   
  452           try {
  453               Class c = Class.forName(name, false, loader);
  454               if (loaderLog.isLoggable(Log.VERBOSE)) {
  455                   loaderLog.log(Log.VERBOSE,
  456                       "class \"" + name + "\" " + "found via codebase, " +
  457                       "defined by " + c.getClassLoader());
  458               }
  459               return c;
  460           } catch (ClassNotFoundException e) {
  461               if (loaderLog.isLoggable(Log.BRIEF)) {
  462                   loaderLog.log(Log.BRIEF,
  463                       "class \"" + name + "\" not found via codebase", e);
  464               }
  465               throw e;
  466           }
  467       }
  468   
  469       /**
  470        * Define and return a dynamic proxy class in a class loader with
  471        * URLs supplied in the given location.  The proxy class will
  472        * implement interface classes named by the given array of
  473        * interface names.
  474        */
  475       public static Class loadProxyClass(String codebase, String[] interfaces,
  476                                          ClassLoader defaultLoader)
  477           throws MalformedURLException, ClassNotFoundException
  478       {
  479           if (loaderLog.isLoggable(Log.BRIEF)) {
  480               loaderLog.log(Log.BRIEF,
  481                   "interfaces = " + Arrays.asList(interfaces) + ", " +
  482                   "codebase = \"" + (codebase != null ? codebase : "") + "\"" +
  483                   (defaultLoader != null ?
  484                    ", defaultLoader = " + defaultLoader : ""));
  485           }
  486   
  487           /*
  488            * This method uses a fairly complex algorithm to load the
  489            * proxy class and its interface classes in order to maximize
  490            * the likelihood that the proxy's codebase annotation will be
  491            * preserved.  The algorithm is (assuming that all of the
  492            * proxy interface classes are public):
  493            *
  494            * If the default loader is not null, try to load the proxy
  495            * interfaces through that loader. If the interfaces can be
  496            * loaded in that loader, try to define the proxy class in an
  497            * RMI class loader (child of the context class loader) before
  498            * trying to define the proxy in the default loader.  If the
  499            * attempt to define the proxy class succeeds, the codebase
  500            * annotation is preserved.  If the attempt fails, try to
  501            * define the proxy class in the default loader.
  502            *
  503            * If the interface classes can not be loaded from the default
  504            * loader or the default loader is null, try to load them from
  505            * the RMI class loader.  Then try to define the proxy class
  506            * in the RMI class loader.
  507            *
  508            * Additionally, if any of the proxy interface classes are not
  509            * public, all of the non-public interfaces must reside in the
  510            * same class loader or it will be impossible to define the
  511            * proxy class (an IllegalAccessError will be thrown).  An
  512            * attempt to load the interfaces from the default loader is
  513            * made.  If the attempt fails, a second attempt will be made
  514            * to load the interfaces from the RMI loader. If all of the
  515            * non-public interfaces classes do reside in the same class
  516            * loader, then we attempt to define the proxy class in the
  517            * class loader of the non-public interfaces.  No other
  518            * attempt to define the proxy class will be made.
  519            */
  520           ClassLoader parent = getRMIContextClassLoader();
  521           if (loaderLog.isLoggable(Log.VERBOSE)) {
  522               loaderLog.log(Log.VERBOSE,
  523                   "(thread context class loader: " + parent + ")");
  524           }
  525   
  526           URL[] urls;
  527           if (codebase != null) {
  528               urls = pathToURLs(codebase);
  529           } else {
  530               urls = getDefaultCodebaseURLs();
  531           }
  532   
  533           /*
  534            * If no security manager is set, disable access to RMI class
  535            * loaders and use the would-de parent instead.
  536            */
  537           SecurityManager sm = System.getSecurityManager();
  538           if (sm == null) {
  539               try {
  540                   Class c = loadProxyClass(interfaces, defaultLoader, parent,
  541                                            false);
  542                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  543                       loaderLog.log(Log.VERBOSE,
  544                           "(no security manager: codebase disabled) " +
  545                           "proxy class defined by " + c.getClassLoader());
  546                   }
  547                   return c;
  548               } catch (ClassNotFoundException e) {
  549                   if (loaderLog.isLoggable(Log.BRIEF)) {
  550                       loaderLog.log(Log.BRIEF,
  551                           "(no security manager: codebase disabled) " +
  552                           "proxy class resolution failed", e);
  553                   }
  554                   throw new ClassNotFoundException(e.getMessage() +
  555                       " (no security manager: RMI class loader disabled)",
  556                       e.getException());
  557               }
  558           }
  559   
  560           /*
  561            * Get or create the RMI class loader for this codebase URL path
  562            * and parent class loader pair.
  563            */
  564           Loader loader = lookupLoader(urls, parent);
  565   
  566           try {
  567               if (loader != null) {
  568                   /*
  569                    * Verify that the caller has permission to access this loader.
  570                    */
  571                   loader.checkPermissions();
  572               }
  573           } catch (SecurityException e) {
  574               /*
  575                * If the current access control context does not have permission
  576                * to access all of the URLs in the codebase path, wrap the
  577                * resulting security exception in a ClassNotFoundException, so
  578                * the caller can handle this outcome just like any other class
  579                * loading failure (see bugid 4146529).
  580                */
  581               try {
  582                   /*
  583                    * But first, check to see if the proxy class could have been
  584                    * resolved without the security-offending codebase anyway;
  585                    * if so, return successfully (see bugids 4191926 & 4349670).
  586                    */
  587                   Class c = loadProxyClass(interfaces, defaultLoader, parent,
  588                                            false);
  589                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  590                       loaderLog.log(Log.VERBOSE,
  591                           "(access to codebase denied) " +
  592                           "proxy class defined by " + c.getClassLoader());
  593                   }
  594                   return c;
  595               } catch (ClassNotFoundException unimportant) {
  596                   /*
  597                    * Presumably the security exception is the more important
  598                    * exception to report in this case.
  599                    */
  600                   if (loaderLog.isLoggable(Log.BRIEF)) {
  601                       loaderLog.log(Log.BRIEF,
  602                           "(access to codebase denied) " +
  603                           "proxy class resolution failed", e);
  604                   }
  605                   throw new ClassNotFoundException(
  606                       "access to class loader denied", e);
  607               }
  608           }
  609   
  610           try {
  611               Class c = loadProxyClass(interfaces, defaultLoader, loader, true);
  612               if (loaderLog.isLoggable(Log.VERBOSE)) {
  613                   loaderLog.log(Log.VERBOSE,
  614                                 "proxy class defined by " + c.getClassLoader());
  615               }
  616               return c;
  617           } catch (ClassNotFoundException e) {
  618               if (loaderLog.isLoggable(Log.BRIEF)) {
  619                   loaderLog.log(Log.BRIEF,
  620                                 "proxy class resolution failed", e);
  621               }
  622               throw e;
  623           }
  624       }
  625   
  626       /**
  627        * Define a proxy class in the default loader if appropriate.
  628        * Define the class in an RMI class loader otherwise.  The proxy
  629        * class will implement classes which are named in the supplied
  630        * interfaceNames.
  631        */
  632       private static Class loadProxyClass(String[] interfaceNames,
  633                                           ClassLoader defaultLoader,
  634                                           ClassLoader codebaseLoader,
  635                                           boolean preferCodebase)
  636           throws ClassNotFoundException
  637       {
  638           ClassLoader proxyLoader = null;
  639           Class[] classObjs = new Class[interfaceNames.length];
  640           boolean[] nonpublic = { false };
  641   
  642         defaultLoaderCase:
  643           if (defaultLoader != null) {
  644               try {
  645                   proxyLoader =
  646                       loadProxyInterfaces(interfaceNames, defaultLoader,
  647                                           classObjs, nonpublic);
  648                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  649                       ClassLoader[] definingLoaders =
  650                           new ClassLoader[classObjs.length];
  651                       for (int i = 0; i < definingLoaders.length; i++) {
  652                           definingLoaders[i] = classObjs[i].getClassLoader();
  653                       }
  654                       loaderLog.log(Log.VERBOSE,
  655                           "proxy interfaces found via defaultLoader, " +
  656                           "defined by " + Arrays.asList(definingLoaders));
  657                   }
  658               } catch (ClassNotFoundException e) {
  659                   break defaultLoaderCase;
  660               }
  661               if (!nonpublic[0]) {
  662                   if (preferCodebase) {
  663                       try {
  664                           return Proxy.getProxyClass(codebaseLoader, classObjs);
  665                       } catch (IllegalArgumentException e) {
  666                       }
  667                   }
  668                   proxyLoader = defaultLoader;
  669               }
  670               return loadProxyClass(proxyLoader, classObjs);
  671           }
  672   
  673           nonpublic[0] = false;
  674           proxyLoader = loadProxyInterfaces(interfaceNames, codebaseLoader,
  675                                             classObjs, nonpublic);
  676           if (loaderLog.isLoggable(Log.VERBOSE)) {
  677               ClassLoader[] definingLoaders = new ClassLoader[classObjs.length];
  678               for (int i = 0; i < definingLoaders.length; i++) {
  679                   definingLoaders[i] = classObjs[i].getClassLoader();
  680               }
  681               loaderLog.log(Log.VERBOSE,
  682                   "proxy interfaces found via codebase, " +
  683                   "defined by " + Arrays.asList(definingLoaders));
  684           }
  685           if (!nonpublic[0]) {
  686               proxyLoader = codebaseLoader;
  687           }
  688           return loadProxyClass(proxyLoader, classObjs);
  689       }
  690   
  691       /**
  692        * Define a proxy class in the given class loader.  The proxy
  693        * class will implement the given interfaces Classes.
  694        */
  695       private static Class loadProxyClass(ClassLoader loader, Class[] interfaces)
  696           throws ClassNotFoundException
  697       {
  698           try {
  699               return Proxy.getProxyClass(loader, interfaces);
  700           } catch (IllegalArgumentException e) {
  701               throw new ClassNotFoundException(
  702                   "error creating dynamic proxy class", e);
  703           }
  704       }
  705   
  706       /*
  707        * Load Class objects for the names in the interfaces array fron
  708        * the given class loader.
  709        *
  710        * We pass classObjs and nonpublic arrays to avoid needing a
  711        * multi-element return value.  nonpublic is an array to enable
  712        * the method to take a boolean argument by reference.
  713        *
  714        * nonpublic array is needed to signal when the return value of
  715        * this method should be used as the proxy class loader.  Because
  716        * null represents a valid class loader, that value is
  717        * insufficient to signal that the return value should not be used
  718        * as the proxy class loader.
  719        */
  720       private static ClassLoader loadProxyInterfaces(String[] interfaces,
  721                                                      ClassLoader loader,
  722                                                      Class[] classObjs,
  723                                                      boolean[] nonpublic)
  724           throws ClassNotFoundException
  725       {
  726           /* loader of a non-public interface class */
  727           ClassLoader nonpublicLoader = null;
  728   
  729           for (int i = 0; i < interfaces.length; i++) {
  730               Class cl =
  731                   (classObjs[i] = Class.forName(interfaces[i], false, loader));
  732   
  733               if (!Modifier.isPublic(cl.getModifiers())) {
  734                   ClassLoader current = cl.getClassLoader();
  735                   if (loaderLog.isLoggable(Log.VERBOSE)) {
  736                       loaderLog.log(Log.VERBOSE,
  737                           "non-public interface \"" + interfaces[i] +
  738                           "\" defined by " + current);
  739                   }
  740                   if (!nonpublic[0]) {
  741                       nonpublicLoader = current;
  742                       nonpublic[0] = true;
  743                   } else if (current != nonpublicLoader) {
  744                       throw new IllegalAccessError(
  745                           "non-public interfaces defined in different " +
  746                           "class loaders");
  747                   }
  748               }
  749           }
  750           return nonpublicLoader;
  751       }
  752   
  753       /**
  754        * Convert a string containing a space-separated list of URLs into a
  755        * corresponding array of URL objects, throwing a MalformedURLException
  756        * if any of the URLs are invalid.
  757        */
  758       private static URL[] pathToURLs(String path)
  759           throws MalformedURLException
  760       {
  761           synchronized (pathToURLsCache) {
  762               Object[] v = pathToURLsCache.get(path);
  763               if (v != null) {
  764                   return ((URL[])v[0]);
  765               }
  766           }
  767           StringTokenizer st = new StringTokenizer(path); // divide by spaces
  768           URL[] urls = new URL[st.countTokens()];
  769           for (int i = 0; st.hasMoreTokens(); i++) {
  770               urls[i] = new URL(st.nextToken());
  771           }
  772           synchronized (pathToURLsCache) {
  773               pathToURLsCache.put(path,
  774                                   new Object[] {urls, new SoftReference<String>(path)});
  775           }
  776           return urls;
  777       }
  778   
  779       /** map from weak(key=string) to [URL[], soft(key)] */
  780       private static final Map<String, Object[]> pathToURLsCache
  781           = new WeakHashMap<String, Object[]>(5);
  782   
  783       /**
  784        * Convert an array of URL objects into a corresponding string
  785        * containing a space-separated list of URLs.
  786        *
  787        * Note that if the array has zero elements, the return value is
  788        * null, not the empty string.
  789        */
  790       private static String urlsToPath(URL[] urls) {
  791           if (urls.length == 0) {
  792               return null;
  793           } else if (urls.length == 1) {
  794               return urls[0].toExternalForm();
  795           } else {
  796               StringBuffer path = new StringBuffer(urls[0].toExternalForm());
  797               for (int i = 1; i < urls.length; i++) {
  798                   path.append(' ');
  799                   path.append(urls[i].toExternalForm());
  800               }
  801               return path.toString();
  802           }
  803       }
  804   
  805       /**
  806        * Return the class loader to be used as the parent for an RMI class
  807        * loader used in the current execution context.
  808        */
  809       private static ClassLoader getRMIContextClassLoader() {
  810           /*
  811            * The current implementation simply uses the current thread's
  812            * context class loader.
  813            */
  814           return Thread.currentThread().getContextClassLoader();
  815       }
  816   
  817       /**
  818        * Look up the RMI class loader for the given codebase URL path
  819        * and the given parent class loader.  A new class loader instance
  820        * will be created and returned if no match is found.
  821        */
  822       private static Loader lookupLoader(final URL[] urls,
  823                                          final ClassLoader parent)
  824       {
  825           /*
  826            * If the requested codebase URL path is empty, the supplied
  827            * parent class loader will be sufficient.
  828            *
  829            * REMIND: To be conservative, this optimization is commented out
  830            * for now so that it does not open a security hole in the future
  831            * by providing untrusted code with direct access to the public
  832            * loadClass() method of a class loader instance that it cannot
  833            * get a reference to.  (It's an unlikely optimization anyway.)
  834            *
  835            * if (urls.length == 0) {
  836            *     return parent;
  837            * }
  838            */
  839   
  840           LoaderEntry entry;
  841           Loader loader;
  842   
  843           synchronized (LoaderHandler.class) {
  844               /*
  845                * Take this opportunity to remove from the table entries
  846                * whose weak references have been cleared.
  847                */
  848               while ((entry = (LoaderEntry) refQueue.poll()) != null) {
  849                   if (!entry.removed) {   // ignore entries removed below
  850                       loaderTable.remove(entry.key);
  851                   }
  852               }
  853   
  854               /*
  855                * Look up the codebase URL path and parent class loader pair
  856                * in the table of RMI class loaders.
  857                */
  858               LoaderKey key = new LoaderKey(urls, parent);
  859               entry = loaderTable.get(key);
  860   
  861               if (entry == null || (loader = entry.get()) == null) {
  862                   /*
  863                    * If entry was in table but it's weak reference was cleared,
  864                    * remove it from the table and mark it as explicitly cleared,
  865                    * so that new matching entry that we put in the table will
  866                    * not be erroneously removed when this entry is processed
  867                    * from the weak reference queue.
  868                    */
  869                   if (entry != null) {
  870                       loaderTable.remove(key);
  871                       entry.removed = true;
  872                   }
  873   
  874                   /*
  875                    * A matching loader was not found, so create a new class
  876                    * loader instance for the requested codebase URL path and
  877                    * parent class loader.  The instance is created within an
  878                    * access control context retricted to the permissions
  879                    * necessary to load classes from its codebase URL path.
  880                    */
  881                   AccessControlContext acc = getLoaderAccessControlContext(urls);
  882                   loader = java.security.AccessController.doPrivileged(
  883                       new java.security.PrivilegedAction<Loader>() {
  884                           public Loader run() {
  885                               return new Loader(urls, parent);
  886                           }
  887                       }, acc);
  888   
  889                   /*
  890                    * Finally, create an entry to hold the new loader with a
  891                    * weak reference and store it in the table with the key.
  892                    */
  893                   entry = new LoaderEntry(key, loader);
  894                   loaderTable.put(key, entry);
  895               }
  896           }
  897   
  898           return loader;
  899       }
  900   
  901       /**
  902        * LoaderKey holds a codebase URL path and parent class loader pair
  903        * used to look up RMI class loader instances in its class loader cache.
  904        */
  905       private static class LoaderKey {
  906   
  907           private URL[] urls;
  908   
  909           private ClassLoader parent;
  910   
  911           private int hashValue;
  912   
  913           public LoaderKey(URL[] urls, ClassLoader parent) {
  914               this.urls = urls;
  915               this.parent = parent;
  916   
  917               if (parent != null) {
  918                   hashValue = parent.hashCode();
  919               }
  920               for (int i = 0; i < urls.length; i++) {
  921                   hashValue ^= urls[i].hashCode();
  922               }
  923           }
  924   
  925           public int hashCode() {
  926               return hashValue;
  927           }
  928   
  929           public boolean equals(Object obj) {
  930               if (obj instanceof LoaderKey) {
  931                   LoaderKey other = (LoaderKey) obj;
  932                   if (parent != other.parent) {
  933                       return false;
  934                   }
  935                   if (urls == other.urls) {
  936                       return true;
  937                   }
  938                   if (urls.length != other.urls.length) {
  939                       return false;
  940                   }
  941                   for (int i = 0; i < urls.length; i++) {
  942                       if (!urls[i].equals(other.urls[i])) {
  943                           return false;
  944                       }
  945                   }
  946                   return true;
  947               } else {
  948                   return false;
  949               }
  950           }
  951       }
  952   
  953       /**
  954        * LoaderEntry contains a weak reference to an RMIClassLoader.  The
  955        * weak reference is registered with the private static "refQueue"
  956        * queue.  The entry contains the codebase URL path and parent class
  957        * loader key for the loader so that the mapping can be removed from
  958        * the table efficiently when the weak reference is cleared.
  959        */
  960       private static class LoaderEntry extends WeakReference<Loader> {
  961   
  962           public LoaderKey key;
  963   
  964           /**
  965            * set to true if the entry has been removed from the table
  966            * because it has been replaced, so it should not be attempted
  967            * to be removed again
  968            */
  969           public boolean removed = false;
  970   
  971           public LoaderEntry(LoaderKey key, Loader loader) {
  972               super(loader, refQueue);
  973               this.key = key;
  974           }
  975       }
  976   
  977       /**
  978        * Return the access control context that a loader for the given
  979        * codebase URL path should execute with.
  980        */
  981       private static AccessControlContext getLoaderAccessControlContext(
  982           URL[] urls)
  983       {
  984           /*
  985            * The approach used here is taken from the similar method
  986            * getAccessControlContext() in the sun.applet.AppletPanel class.
  987            */
  988           // begin with permissions granted to all code in current policy
  989           PermissionCollection perms =
  990               java.security.AccessController.doPrivileged(
  991                   new java.security.PrivilegedAction<PermissionCollection>() {
  992                   public PermissionCollection run() {
  993                       CodeSource codesource = new CodeSource(null,
  994                           (java.security.cert.Certificate[]) null);
  995                       Policy p = java.security.Policy.getPolicy();
  996                       if (p != null) {
  997                           return p.getPermissions(codesource);
  998                       } else {
  999                           return new Permissions();
 1000                       }
 1001                   }
 1002               });
 1003   
 1004           // createClassLoader permission needed to create loader in context
 1005           perms.add(new RuntimePermission("createClassLoader"));
 1006   
 1007           // add permissions to read any "java.*" property
 1008           perms.add(new java.util.PropertyPermission("java.*","read"));
 1009   
 1010           // add permissions reuiqred to load from codebase URL path
 1011           addPermissionsForURLs(urls, perms, true);
 1012   
 1013           /*
 1014            * Create an AccessControlContext that consists of a single
 1015            * protection domain with only the permissions calculated above.
 1016            */
 1017           ProtectionDomain pd = new ProtectionDomain(
 1018               new CodeSource((urls.length > 0 ? urls[0] : null),
 1019                   (java.security.cert.Certificate[]) null),
 1020               perms);
 1021           return new AccessControlContext(new ProtectionDomain[] { pd });
 1022       }
 1023   
 1024       /**
 1025        * Adds to the specified permission collection the permissions
 1026        * necessary to load classes from a loader with the specified URL
 1027        * path; if "forLoader" is true, also adds URL-specific
 1028        * permissions necessary for the security context that such a
 1029        * loader operates within, such as permissions necessary for
 1030        * granting automatic permissions to classes defined by the
 1031        * loader.  A given permission is only added to the collection if
 1032        * it is not already implied by the collection.
 1033        */
 1034       private static void addPermissionsForURLs(URL[] urls,
 1035                                                 PermissionCollection perms,
 1036                                                 boolean forLoader)
 1037       {
 1038           for (int i = 0; i < urls.length; i++) {
 1039               URL url = urls[i];
 1040               try {
 1041                   URLConnection urlConnection = url.openConnection();
 1042                   Permission p = urlConnection.getPermission();
 1043                   if (p != null) {
 1044                       if (p instanceof FilePermission) {
 1045                           /*
 1046                            * If the codebase is a file, the permission required
 1047                            * to actually read classes from the codebase URL is
 1048                            * the permission to read all files beneath the last
 1049                            * directory in the file path, either because JAR
 1050                            * files can refer to other JAR files in the same
 1051                            * directory, or because permission to read a
 1052                            * directory is not implied by permission to read the
 1053                            * contents of a directory, which all that might be
 1054                            * granted.
 1055                            */
 1056                           String path = p.getName();
 1057                           int endIndex = path.lastIndexOf(File.separatorChar);
 1058                           if (endIndex != -1) {
 1059                               path = path.substring(0, endIndex+1);
 1060                               if (path.endsWith(File.separator)) {
 1061                                   path += "-";
 1062                               }
 1063                               Permission p2 = new FilePermission(path, "read");
 1064                               if (!perms.implies(p2)) {
 1065                                   perms.add(p2);
 1066                               }
 1067                               perms.add(new FilePermission(path, "read"));
 1068                           } else {
 1069                               /*
 1070                                * No directory separator: use permission to
 1071                                * read the file.
 1072                                */
 1073                               if (!perms.implies(p)) {
 1074                                   perms.add(p);
 1075                               }
 1076                           }
 1077                       } else {
 1078                           if (!perms.implies(p)) {
 1079                               perms.add(p);
 1080                           }
 1081   
 1082                           /*
 1083                            * If the purpose of these permissions is to grant
 1084                            * them to an instance of a URLClassLoader subclass,
 1085                            * we must add permission to connect to and accept
 1086                            * from the host of non-"file:" URLs, otherwise the
 1087                            * getPermissions() method of URLClassLoader will
 1088                            * throw a security exception.
 1089                            */
 1090                           if (forLoader) {
 1091                               // get URL with meaningful host component
 1092                               URL hostURL = url;
 1093                               for (URLConnection conn = urlConnection;
 1094                                    conn instanceof JarURLConnection;)
 1095                               {
 1096                                   hostURL =
 1097                                       ((JarURLConnection) conn).getJarFileURL();
 1098                                   conn = hostURL.openConnection();
 1099                               }
 1100                               String host = hostURL.getHost();
 1101                               if (host != null &&
 1102                                   p.implies(new SocketPermission(host,
 1103                                                                  "resolve")))
 1104                               {
 1105                                   Permission p2 =
 1106                                       new SocketPermission(host,
 1107                                                            "connect,accept");
 1108                                   if (!perms.implies(p2)) {
 1109                                       perms.add(p2);
 1110                                   }
 1111                               }
 1112                           }
 1113                       }
 1114                   }
 1115               } catch (IOException e) {
 1116                   /*
 1117                    * This shouldn't happen, although it is declared to be
 1118                    * thrown by openConnection() and getPermission().  If it
 1119                    * does, don't bother granting or requiring any permissions
 1120                    * for this URL.
 1121                    */
 1122               }
 1123           }
 1124       }
 1125   
 1126       /**
 1127        * Loader is the actual class of the RMI class loaders created
 1128        * by the RMIClassLoader static methods.
 1129        */
 1130       private static class Loader extends URLClassLoader {
 1131   
 1132           /** parent class loader, kept here as an optimization */
 1133           private ClassLoader parent;
 1134   
 1135           /** string form of loader's codebase URL path, also an optimization */
 1136           private String annotation;
 1137   
 1138           /** permissions required to access loader through public API */
 1139           private Permissions permissions;
 1140   
 1141           private Loader(URL[] urls, ClassLoader parent) {
 1142               super(urls, parent);
 1143               this.parent = parent;
 1144   
 1145               /*
 1146                * Precompute the permissions required to access the loader.
 1147                */
 1148               permissions = new Permissions();
 1149               addPermissionsForURLs(urls, permissions, false);
 1150   
 1151               /*
 1152                * Caching the value of class annotation string here assumes
 1153                * that the protected method addURL() is never called on this
 1154                * class loader.
 1155                */
 1156               annotation = urlsToPath(urls);
 1157           }
 1158   
 1159           /**
 1160            * Return the string to be annotated with all classes loaded from
 1161            * this class loader.
 1162            */
 1163           public String getClassAnnotation() {
 1164               return annotation;
 1165           }
 1166   
 1167           /**
 1168            * Check that the current access control context has all of the
 1169            * permissions necessary to load classes from this loader.
 1170            */
 1171           private void checkPermissions() {
 1172               SecurityManager sm = System.getSecurityManager();
 1173               if (sm != null) {           // should never be null?
 1174                   Enumeration enum_ = permissions.elements();
 1175                   while (enum_.hasMoreElements()) {
 1176                       sm.checkPermission((Permission) enum_.nextElement());
 1177                   }
 1178               }
 1179           }
 1180   
 1181           /**
 1182            * Return the permissions to be granted to code loaded from the
 1183            * given code source.
 1184            */
 1185           protected PermissionCollection getPermissions(CodeSource codesource) {
 1186               PermissionCollection perms = super.getPermissions(codesource);
 1187               /*
 1188                * Grant the same permissions that URLClassLoader would grant.
 1189                */
 1190               return perms;
 1191           }
 1192   
 1193           /**
 1194            * Return a string representation of this loader (useful for
 1195            * debugging).
 1196            */
 1197           public String toString() {
 1198               return super.toString() + "[\"" + annotation + "\"]";
 1199           }
 1200       }
 1201   }

Save This Page
Home » openjdk-7 » sun.rmi » server » [javadoc | source]