Save This Page
Home » openjdk-7 » java » net » [javadoc | source]
    1   /*
    2    * Copyright 1997-2007 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 java.net;
   27   
   28   import java.lang.reflect.Method;
   29   import java.lang.reflect.Modifier;
   30   import java.io.File;
   31   import java.io.FilePermission;
   32   import java.io.InputStream;
   33   import java.io.IOException;
   34   import java.net.URL;
   35   import java.net.URLConnection;
   36   import java.net.URLStreamHandlerFactory;
   37   import java.util.Enumeration;
   38   import java.util.NoSuchElementException;
   39   import java.util.StringTokenizer;
   40   import java.util.jar.Manifest;
   41   import java.util.jar.Attributes;
   42   import java.util.jar.Attributes.Name;
   43   import java.security.CodeSigner;
   44   import java.security.PrivilegedAction;
   45   import java.security.PrivilegedExceptionAction;
   46   import java.security.AccessController;
   47   import java.security.AccessControlContext;
   48   import java.security.SecureClassLoader;
   49   import java.security.CodeSource;
   50   import java.security.Permission;
   51   import java.security.PermissionCollection;
   52   import sun.misc.Resource;
   53   import sun.misc.URLClassPath;
   54   import sun.net.www.ParseUtil;
   55   import sun.security.util.SecurityConstants;
   56   
   57   /**
   58    * This class loader is used to load classes and resources from a search
   59    * path of URLs referring to both JAR files and directories. Any URL that
   60    * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
   61    * is assumed to refer to a JAR file which will be opened as needed.
   62    * <p>
   63    * The AccessControlContext of the thread that created the instance of
   64    * URLClassLoader will be used when subsequently loading classes and
   65    * resources.
   66    * <p>
   67    * The classes that are loaded are by default granted permission only to
   68    * access the URLs specified when the URLClassLoader was created.
   69    *
   70    * @author  David Connelly
   71    * @since   1.2
   72    */
   73   public class URLClassLoader extends SecureClassLoader {
   74       /* The search path for classes and resources */
   75       URLClassPath ucp;
   76   
   77       /* The context to be used when loading classes and resources */
   78       private AccessControlContext acc;
   79   
   80       /**
   81        * Constructs a new URLClassLoader for the given URLs. The URLs will be
   82        * searched in the order specified for classes and resources after first
   83        * searching in the specified parent class loader. Any URL that ends with
   84        * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
   85        * to refer to a JAR file which will be downloaded and opened as needed.
   86        *
   87        * <p>If there is a security manager, this method first
   88        * calls the security manager's <code>checkCreateClassLoader</code> method
   89        * to ensure creation of a class loader is allowed.
   90        *
   91        * @param urls the URLs from which to load classes and resources
   92        * @param parent the parent class loader for delegation
   93        * @exception  SecurityException  if a security manager exists and its
   94        *             <code>checkCreateClassLoader</code> method doesn't allow
   95        *             creation of a class loader.
   96        * @see SecurityManager#checkCreateClassLoader
   97        */
   98       public URLClassLoader(URL[] urls, ClassLoader parent) {
   99           super(parent);
  100           // this is to make the stack depth consistent with 1.1
  101           SecurityManager security = System.getSecurityManager();
  102           if (security != null) {
  103               security.checkCreateClassLoader();
  104           }
  105           ucp = new URLClassPath(urls);
  106           acc = AccessController.getContext();
  107       }
  108   
  109       /**
  110        * Constructs a new URLClassLoader for the specified URLs using the
  111        * default delegation parent <code>ClassLoader</code>. The URLs will
  112        * be searched in the order specified for classes and resources after
  113        * first searching in the parent class loader. Any URL that ends with
  114        * a '/' is assumed to refer to a directory. Otherwise, the URL is
  115        * assumed to refer to a JAR file which will be downloaded and opened
  116        * as needed.
  117        *
  118        * <p>If there is a security manager, this method first
  119        * calls the security manager's <code>checkCreateClassLoader</code> method
  120        * to ensure creation of a class loader is allowed.
  121        *
  122        * @param urls the URLs from which to load classes and resources
  123        *
  124        * @exception  SecurityException  if a security manager exists and its
  125        *             <code>checkCreateClassLoader</code> method doesn't allow
  126        *             creation of a class loader.
  127        * @see SecurityManager#checkCreateClassLoader
  128        */
  129       public URLClassLoader(URL[] urls) {
  130           super();
  131           // this is to make the stack depth consistent with 1.1
  132           SecurityManager security = System.getSecurityManager();
  133           if (security != null) {
  134               security.checkCreateClassLoader();
  135           }
  136           ucp = new URLClassPath(urls);
  137           acc = AccessController.getContext();
  138       }
  139   
  140       /**
  141        * Constructs a new URLClassLoader for the specified URLs, parent
  142        * class loader, and URLStreamHandlerFactory. The parent argument
  143        * will be used as the parent class loader for delegation. The
  144        * factory argument will be used as the stream handler factory to
  145        * obtain protocol handlers when creating new jar URLs.
  146        *
  147        * <p>If there is a security manager, this method first
  148        * calls the security manager's <code>checkCreateClassLoader</code> method
  149        * to ensure creation of a class loader is allowed.
  150        *
  151        * @param urls the URLs from which to load classes and resources
  152        * @param parent the parent class loader for delegation
  153        * @param factory the URLStreamHandlerFactory to use when creating URLs
  154        *
  155        * @exception  SecurityException  if a security manager exists and its
  156        *             <code>checkCreateClassLoader</code> method doesn't allow
  157        *             creation of a class loader.
  158        * @see SecurityManager#checkCreateClassLoader
  159        */
  160       public URLClassLoader(URL[] urls, ClassLoader parent,
  161                             URLStreamHandlerFactory factory) {
  162           super(parent);
  163           // this is to make the stack depth consistent with 1.1
  164           SecurityManager security = System.getSecurityManager();
  165           if (security != null) {
  166               security.checkCreateClassLoader();
  167           }
  168           ucp = new URLClassPath(urls, factory);
  169           acc = AccessController.getContext();
  170       }
  171   
  172       /**
  173        * Appends the specified URL to the list of URLs to search for
  174        * classes and resources.
  175        * <p>
  176        * If the URL specified is <code>null</code> or is already in the
  177        * list of URLs, then invoking this method has no effect.
  178        *
  179        * @param url the URL to be added to the search path of URLs
  180        */
  181       protected void addURL(URL url) {
  182           ucp.addURL(url);
  183       }
  184   
  185       /**
  186        * Returns the search path of URLs for loading classes and resources.
  187        * This includes the original list of URLs specified to the constructor,
  188        * along with any URLs subsequently appended by the addURL() method.
  189        * @return the search path of URLs for loading classes and resources.
  190        */
  191       public URL[] getURLs() {
  192           return ucp.getURLs();
  193       }
  194   
  195       /**
  196        * Finds and loads the class with the specified name from the URL search
  197        * path. Any URLs referring to JAR files are loaded and opened as needed
  198        * until the class is found.
  199        *
  200        * @param name the name of the class
  201        * @return the resulting class
  202        * @exception ClassNotFoundException if the class could not be found
  203        */
  204       protected Class<?> findClass(final String name)
  205            throws ClassNotFoundException
  206       {
  207           try {
  208               return AccessController.doPrivileged(
  209                   new PrivilegedExceptionAction<Class>() {
  210                       public Class run() throws ClassNotFoundException {
  211                           String path = name.replace('.', '/').concat(".class");
  212                           Resource res = ucp.getResource(path, false);
  213                           if (res != null) {
  214                               try {
  215                                   return defineClass(name, res);
  216                               } catch (IOException e) {
  217                                   throw new ClassNotFoundException(name, e);
  218                               }
  219                           } else {
  220                               throw new ClassNotFoundException(name);
  221                           }
  222                       }
  223                   }, acc);
  224           } catch (java.security.PrivilegedActionException pae) {
  225               throw (ClassNotFoundException) pae.getException();
  226           }
  227       }
  228   
  229       /*
  230        * Defines a Class using the class bytes obtained from the specified
  231        * Resource. The resulting Class must be resolved before it can be
  232        * used.
  233        */
  234       private Class defineClass(String name, Resource res) throws IOException {
  235           int i = name.lastIndexOf('.');
  236           URL url = res.getCodeSourceURL();
  237           if (i != -1) {
  238               String pkgname = name.substring(0, i);
  239               // Check if package already loaded.
  240               Package pkg = getPackage(pkgname);
  241               Manifest man = res.getManifest();
  242               if (pkg != null) {
  243                   // Package found, so check package sealing.
  244                   if (pkg.isSealed()) {
  245                       // Verify that code source URL is the same.
  246                       if (!pkg.isSealed(url)) {
  247                           throw new SecurityException(
  248                               "sealing violation: package " + pkgname + " is sealed");
  249                       }
  250   
  251                   } else {
  252                       // Make sure we are not attempting to seal the package
  253                       // at this code source URL.
  254                       if ((man != null) && isSealed(pkgname, man)) {
  255                           throw new SecurityException(
  256                               "sealing violation: can't seal package " + pkgname +
  257                               ": already loaded");
  258                       }
  259                   }
  260               } else {
  261                   if (man != null) {
  262                       definePackage(pkgname, man, url);
  263                   } else {
  264                       definePackage(pkgname, null, null, null, null, null, null, null);
  265                   }
  266               }
  267           }
  268           // Now read the class bytes and define the class
  269           java.nio.ByteBuffer bb = res.getByteBuffer();
  270           if (bb != null) {
  271               // Use (direct) ByteBuffer:
  272               CodeSigner[] signers = res.getCodeSigners();
  273               CodeSource cs = new CodeSource(url, signers);
  274               return defineClass(name, bb, cs);
  275           } else {
  276               byte[] b = res.getBytes();
  277               // must read certificates AFTER reading bytes.
  278               CodeSigner[] signers = res.getCodeSigners();
  279               CodeSource cs = new CodeSource(url, signers);
  280               return defineClass(name, b, 0, b.length, cs);
  281           }
  282       }
  283   
  284       /**
  285        * Defines a new package by name in this ClassLoader. The attributes
  286        * contained in the specified Manifest will be used to obtain package
  287        * version and sealing information. For sealed packages, the additional
  288        * URL specifies the code source URL from which the package was loaded.
  289        *
  290        * @param name  the package name
  291        * @param man   the Manifest containing package version and sealing
  292        *              information
  293        * @param url   the code source url for the package, or null if none
  294        * @exception   IllegalArgumentException if the package name duplicates
  295        *              an existing package either in this class loader or one
  296        *              of its ancestors
  297        * @return the newly defined Package object
  298        */
  299       protected Package definePackage(String name, Manifest man, URL url)
  300           throws IllegalArgumentException
  301       {
  302           String path = name.replace('.', '/').concat("/");
  303           String specTitle = null, specVersion = null, specVendor = null;
  304           String implTitle = null, implVersion = null, implVendor = null;
  305           String sealed = null;
  306           URL sealBase = null;
  307   
  308           Attributes attr = man.getAttributes(path);
  309           if (attr != null) {
  310               specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
  311               specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  312               specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
  313               implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
  314               implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  315               implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  316               sealed      = attr.getValue(Name.SEALED);
  317           }
  318           attr = man.getMainAttributes();
  319           if (attr != null) {
  320               if (specTitle == null) {
  321                   specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  322               }
  323               if (specVersion == null) {
  324                   specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  325               }
  326               if (specVendor == null) {
  327                   specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  328               }
  329               if (implTitle == null) {
  330                   implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  331               }
  332               if (implVersion == null) {
  333                   implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  334               }
  335               if (implVendor == null) {
  336                   implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  337               }
  338               if (sealed == null) {
  339                   sealed = attr.getValue(Name.SEALED);
  340               }
  341           }
  342           if ("true".equalsIgnoreCase(sealed)) {
  343               sealBase = url;
  344           }
  345           return definePackage(name, specTitle, specVersion, specVendor,
  346                                implTitle, implVersion, implVendor, sealBase);
  347       }
  348   
  349       /*
  350        * Returns true if the specified package name is sealed according to the
  351        * given manifest.
  352        */
  353       private boolean isSealed(String name, Manifest man) {
  354           String path = name.replace('.', '/').concat("/");
  355           Attributes attr = man.getAttributes(path);
  356           String sealed = null;
  357           if (attr != null) {
  358               sealed = attr.getValue(Name.SEALED);
  359           }
  360           if (sealed == null) {
  361               if ((attr = man.getMainAttributes()) != null) {
  362                   sealed = attr.getValue(Name.SEALED);
  363               }
  364           }
  365           return "true".equalsIgnoreCase(sealed);
  366       }
  367   
  368       /**
  369        * Finds the resource with the specified name on the URL search path.
  370        *
  371        * @param name the name of the resource
  372        * @return a <code>URL</code> for the resource, or <code>null</code>
  373        * if the resource could not be found.
  374        */
  375       public URL findResource(final String name) {
  376           /*
  377            * The same restriction to finding classes applies to resources
  378            */
  379           URL url = AccessController.doPrivileged(
  380               new PrivilegedAction<URL>() {
  381                   public URL run() {
  382                       return ucp.findResource(name, true);
  383                   }
  384               }, acc);
  385   
  386           return url != null ? ucp.checkURL(url) : null;
  387       }
  388   
  389       /**
  390        * Returns an Enumeration of URLs representing all of the resources
  391        * on the URL search path having the specified name.
  392        *
  393        * @param name the resource name
  394        * @exception IOException if an I/O exception occurs
  395        * @return an <code>Enumeration</code> of <code>URL</code>s
  396        */
  397       public Enumeration<URL> findResources(final String name)
  398           throws IOException
  399       {
  400           final Enumeration<URL> e = ucp.findResources(name, true);
  401   
  402           return new Enumeration<URL>() {
  403               private URL url = null;
  404   
  405               private boolean next() {
  406                   if (url != null) {
  407                       return true;
  408                   }
  409                   do {
  410                       URL u = AccessController.doPrivileged(
  411                           new PrivilegedAction<URL>() {
  412                               public URL run() {
  413                                   if (!e.hasMoreElements())
  414                                       return null;
  415                                   return e.nextElement();
  416                               }
  417                           }, acc);
  418                       if (u == null)
  419                           break;
  420                       url = ucp.checkURL(u);
  421                   } while (url == null);
  422                   return url != null;
  423               }
  424   
  425               public URL nextElement() {
  426                   if (!next()) {
  427                       throw new NoSuchElementException();
  428                   }
  429                   URL u = url;
  430                   url = null;
  431                   return u;
  432               }
  433   
  434               public boolean hasMoreElements() {
  435                   return next();
  436               }
  437           };
  438       }
  439   
  440       /**
  441        * Returns the permissions for the given codesource object.
  442        * The implementation of this method first calls super.getPermissions
  443        * and then adds permissions based on the URL of the codesource.
  444        * <p>
  445        * If the protocol of this URL is "jar", then the permission granted
  446        * is based on the permission that is required by the URL of the Jar
  447        * file.
  448        * <p>
  449        * If the protocol is "file" and there is an authority component, then
  450        * permission to connect to and accept connections from that authority
  451        * may be granted. If the protocol is "file"
  452        * and the path specifies a file, then permission to read that
  453        * file is granted. If protocol is "file" and the path is
  454        * a directory, permission is granted to read all files
  455        * and (recursively) all files and subdirectories contained in
  456        * that directory.
  457        * <p>
  458        * If the protocol is not "file", then permission
  459        * to connect to and accept connections from the URL's host is granted.
  460        * @param codesource the codesource
  461        * @return the permissions granted to the codesource
  462        */
  463       protected PermissionCollection getPermissions(CodeSource codesource)
  464       {
  465           PermissionCollection perms = super.getPermissions(codesource);
  466   
  467           URL url = codesource.getLocation();
  468   
  469           Permission p;
  470           URLConnection urlConnection;
  471   
  472           try {
  473               urlConnection = url.openConnection();
  474               p = urlConnection.getPermission();
  475           } catch (java.io.IOException ioe) {
  476               p = null;
  477               urlConnection = null;
  478           }
  479   
  480           if (p instanceof FilePermission) {
  481               // if the permission has a separator char on the end,
  482               // it means the codebase is a directory, and we need
  483               // to add an additional permission to read recursively
  484               String path = p.getName();
  485               if (path.endsWith(File.separator)) {
  486                   path += "-";
  487                   p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
  488               }
  489           } else if ((p == null) && (url.getProtocol().equals("file"))) {
  490               String path = url.getFile().replace('/', File.separatorChar);
  491               path = ParseUtil.decode(path);
  492               if (path.endsWith(File.separator))
  493                   path += "-";
  494               p =  new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
  495           } else {
  496               /**
  497                * Not loading from a 'file:' URL so we want to give the class
  498                * permission to connect to and accept from the remote host
  499                * after we've made sure the host is the correct one and is valid.
  500                */
  501               URL locUrl = url;
  502               if (urlConnection instanceof JarURLConnection) {
  503                   locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
  504               }
  505               String host = locUrl.getHost();
  506               if (host != null && (host.length() > 0))
  507                   p = new SocketPermission(host,
  508                                            SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
  509           }
  510   
  511           // make sure the person that created this class loader
  512           // would have this permission
  513   
  514           if (p != null) {
  515               final SecurityManager sm = System.getSecurityManager();
  516               if (sm != null) {
  517                   final Permission fp = p;
  518                   AccessController.doPrivileged(new PrivilegedAction<Void>() {
  519                       public Void run() throws SecurityException {
  520                           sm.checkPermission(fp);
  521                           return null;
  522                       }
  523                   }, acc);
  524               }
  525               perms.add(p);
  526           }
  527           return perms;
  528       }
  529   
  530       /**
  531        * Creates a new instance of URLClassLoader for the specified
  532        * URLs and parent class loader. If a security manager is
  533        * installed, the <code>loadClass</code> method of the URLClassLoader
  534        * returned by this method will invoke the
  535        * <code>SecurityManager.checkPackageAccess</code> method before
  536        * loading the class.
  537        *
  538        * @param urls the URLs to search for classes and resources
  539        * @param parent the parent class loader for delegation
  540        * @return the resulting class loader
  541        */
  542       public static URLClassLoader newInstance(final URL[] urls,
  543                                                final ClassLoader parent) {
  544           // Save the caller's context
  545           AccessControlContext acc = AccessController.getContext();
  546           // Need a privileged block to create the class loader
  547           URLClassLoader ucl = AccessController.doPrivileged(
  548               new PrivilegedAction<URLClassLoader>() {
  549                   public URLClassLoader run() {
  550                       return new FactoryURLClassLoader(urls, parent);
  551                   }
  552               });
  553           // Now set the context on the loader using the one we saved,
  554           // not the one inside the privileged block...
  555           ucl.acc = acc;
  556           return ucl;
  557       }
  558   
  559       /**
  560        * Creates a new instance of URLClassLoader for the specified
  561        * URLs and default parent class loader. If a security manager is
  562        * installed, the <code>loadClass</code> method of the URLClassLoader
  563        * returned by this method will invoke the
  564        * <code>SecurityManager.checkPackageAccess</code> before
  565        * loading the class.
  566        *
  567        * @param urls the URLs to search for classes and resources
  568        * @return the resulting class loader
  569        */
  570       public static URLClassLoader newInstance(final URL[] urls) {
  571           // Save the caller's context
  572           AccessControlContext acc = AccessController.getContext();
  573           // Need a privileged block to create the class loader
  574           URLClassLoader ucl = AccessController.doPrivileged(
  575               new PrivilegedAction<URLClassLoader>() {
  576                   public URLClassLoader run() {
  577                       return new FactoryURLClassLoader(urls);
  578                   }
  579               });
  580   
  581           // Now set the context on the loader using the one we saved,
  582           // not the one inside the privileged block...
  583           ucl.acc = acc;
  584           return ucl;
  585       }
  586   
  587       static {
  588           sun.misc.SharedSecrets.setJavaNetAccess (
  589               new sun.misc.JavaNetAccess() {
  590                   public URLClassPath getURLClassPath (URLClassLoader u) {
  591                       return u.ucp;
  592                   }
  593               }
  594           );
  595       }
  596   }
  597   
  598   final class FactoryURLClassLoader extends URLClassLoader {
  599   
  600       FactoryURLClassLoader(URL[] urls, ClassLoader parent) {
  601           super(urls, parent);
  602       }
  603   
  604       FactoryURLClassLoader(URL[] urls) {
  605           super(urls);
  606       }
  607   
  608       public final synchronized Class loadClass(String name, boolean resolve)
  609           throws ClassNotFoundException
  610       {
  611           // First check if we have permission to access the package. This
  612           // should go away once we've added support for exported packages.
  613           SecurityManager sm = System.getSecurityManager();
  614           if (sm != null) {
  615               int i = name.lastIndexOf('.');
  616               if (i != -1) {
  617                   sm.checkPackageAccess(name.substring(0, i));
  618               }
  619           }
  620           return super.loadClass(name, resolve);
  621       }
  622   }

Save This Page
Home » openjdk-7 » java » net » [javadoc | source]