- /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
- * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- */
- package java.net;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.lang.ref.*;
- import java.io.*;
- import java.net.URL;
- import java.net.URLConnection;
- import java.net.URLStreamHandlerFactory;
- import java.util.Enumeration;
- import java.util.*;
- import java.util.jar.Manifest;
- import java.util.jar.JarFile;
- import java.util.jar.Attributes;
- import java.util.jar.Attributes.Name;
- import java.security.CodeSigner;
- import java.security.PrivilegedAction;
- import java.security.PrivilegedExceptionAction;
- import java.security.AccessController;
- import java.security.AccessControlContext;
- import java.security.SecureClassLoader;
- import java.security.CodeSource;
- import java.security.Permission;
- import java.security.PermissionCollection;
- import sun.misc.Resource;
- import sun.misc.URLClassPath;
- import sun.net.www.ParseUtil;
- import sun.security.util.SecurityConstants;
- /**
- * This class loader is used to load classes and resources from a search
- * path of URLs referring to both JAR files and directories. Any URL that
- * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
- * is assumed to refer to a JAR file which will be opened as needed.
- * <p>
- * The AccessControlContext of the thread that created the instance of
- * URLClassLoader will be used when subsequently loading classes and
- * resources.
- * <p>
- * The classes that are loaded are by default granted permission only to
- * access the URLs specified when the URLClassLoader was created.
- *
- * @author David Connelly
- * @since 1.2
- */
- public class URLClassLoader extends SecureClassLoader implements Closeable {
- /* The search path for classes and resources */
- private final URLClassPath ucp;
- /* The context to be used when loading classes and resources */
- private final AccessControlContext acc;
- /**
- * Constructs a new URLClassLoader for the given URLs. The URLs will be
- * searched in the order specified for classes and resources after first
- * searching in the specified parent class loader. Any URL that ends with
- * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
- * to refer to a JAR file which will be downloaded and opened as needed.
- *
- * <p>If there is a security manager, this method first
- * calls the security manager's {@code checkCreateClassLoader} method
- * to ensure creation of a class loader is allowed.
- *
- * @param urls the URLs from which to load classes and resources
- * @param parent the parent class loader for delegation
- * @exception SecurityException if a security manager exists and its
- * {@code checkCreateClassLoader} method doesn't allow
- * creation of a class loader.
- * @see SecurityManager#checkCreateClassLoader
- */
- public URLClassLoader(URL[] urls, ClassLoader parent) {
- super(parent);
- // this is to make the stack depth consistent with 1.1
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkCreateClassLoader();
- }
- ucp = new URLClassPath(urls);
- this.acc = AccessController.getContext();
- }
- URLClassLoader(URL[] urls, ClassLoader parent,
- AccessControlContext acc) {
- super(parent);
- // this is to make the stack depth consistent with 1.1
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkCreateClassLoader();
- }
- ucp = new URLClassPath(urls);
- this.acc = acc;
- }
- /**
- * Constructs a new URLClassLoader for the specified URLs using the
- * default delegation parent <code>ClassLoader</code>. The URLs will
- * be searched in the order specified for classes and resources after
- * first searching in the parent class loader. Any URL that ends with
- * a '/' is assumed to refer to a directory. Otherwise, the URL is
- * assumed to refer to a JAR file which will be downloaded and opened
- * as needed.
- *
- * <p>If there is a security manager, this method first
- * calls the security manager's <code>checkCreateClassLoader</code> method
- * to ensure creation of a class loader is allowed.
- *
- * @param urls the URLs from which to load classes and resources
- *
- * @exception SecurityException if a security manager exists and its
- * <code>checkCreateClassLoader</code> method doesn't allow
- * creation of a class loader.
- * @see SecurityManager#checkCreateClassLoader
- */
- public URLClassLoader(URL[] urls) {
- super();
- // this is to make the stack depth consistent with 1.1
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkCreateClassLoader();
- }
- ucp = new URLClassPath(urls);
- this.acc = AccessController.getContext();
- }
- URLClassLoader(URL[] urls, AccessControlContext acc) {
- super();
- // this is to make the stack depth consistent with 1.1
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkCreateClassLoader();
- }
- ucp = new URLClassPath(urls);
- this.acc = acc;
- }
- /**
- * Constructs a new URLClassLoader for the specified URLs, parent
- * class loader, and URLStreamHandlerFactory. The parent argument
- * will be used as the parent class loader for delegation. The
- * factory argument will be used as the stream handler factory to
- * obtain protocol handlers when creating new jar URLs.
- *
- * <p>If there is a security manager, this method first
- * calls the security manager's <code>checkCreateClassLoader</code> method
- * to ensure creation of a class loader is allowed.
- *
- * @param urls the URLs from which to load classes and resources
- * @param parent the parent class loader for delegation
- * @param factory the URLStreamHandlerFactory to use when creating URLs
- *
- * @exception SecurityException if a security manager exists and its
- * <code>checkCreateClassLoader</code> method doesn't allow
- * creation of a class loader.
- * @see SecurityManager#checkCreateClassLoader
- */
- public URLClassLoader(URL[] urls, ClassLoader parent,
- URLStreamHandlerFactory factory) {
- super(parent);
- // this is to make the stack depth consistent with 1.1
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkCreateClassLoader();
- }
- ucp = new URLClassPath(urls, factory);
- acc = AccessController.getContext();
- }
- /* A map (used as a set) to keep track of closeable local resources
- * (either JarFiles or FileInputStreams). We don't care about
- * Http resources since they don't need to be closed.
- *
- * If the resource is coming from a jar file
- * we keep a (weak) reference to the JarFile object which can
- * be closed if URLClassLoader.close() called. Due to jar file
- * caching there will typically be only one JarFile object
- * per underlying jar file.
- *
- * For file resources, which is probably a less common situation
- * we have to keep a weak reference to each stream.
- */
- private WeakHashMap<Closeable,Void>
- closeables = new WeakHashMap<>();
- /**
- * Returns an input stream for reading the specified resource.
- * If this loader is closed, then any resources opened by this method
- * will be closed.
- *
- * <p> The search order is described in the documentation for {@link
- * #getResource(String)}. </p>
- *
- * @param name
- * The resource name
- *
- * @return An input stream for reading the resource, or <tt>null</tt>
- * if the resource could not be found
- *
- * @since 1.7
- */
- public InputStream getResourceAsStream(String name) {
- URL url = getResource(name);
- try {
- if (url == null) {
- return null;
- }
- URLConnection urlc = url.openConnection();
- InputStream is = urlc.getInputStream();
- if (urlc instanceof JarURLConnection) {
- JarURLConnection juc = (JarURLConnection)urlc;
- JarFile jar = juc.getJarFile();
- synchronized (closeables) {
- if (!closeables.containsKey(jar)) {
- closeables.put(jar, null);
- }
- }
- } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) {
- synchronized (closeables) {
- closeables.put(is, null);
- }
- }
- return is;
- } catch (IOException e) {
- return null;
- }
- }
- /**
- * Closes this URLClassLoader, so that it can no longer be used to load
- * new classes or resources that are defined by this loader.
- * Classes and resources defined by any of this loader's parents in the
- * delegation hierarchy are still accessible. Also, any classes or resources
- * that are already loaded, are still accessible.
- * <p>
- * In the case of jar: and file: URLs, it also closes any files
- * that were opened by it. If another thread is loading a
- * class when the {@code close} method is invoked, then the result of
- * that load is undefined.
- * <p>
- * The method makes a best effort attempt to close all opened files,
- * by catching {@link IOException}s internally. Unchecked exceptions
- * and errors are not caught. Calling close on an already closed
- * loader has no effect.
- * <p>
- * @throws IOException if closing any file opened by this class loader
- * resulted in an IOException. Any such exceptions are caught internally.
- * If only one is caught, then it is re-thrown. If more than one exception
- * is caught, then the second and following exceptions are added
- * as suppressed exceptions of the first one caught, which is then re-thrown.
- *
- * @throws SecurityException if a security manager is set, and it denies
- * {@link RuntimePermission}<tt>("closeClassLoader")</tt>
- *
- * @since 1.7
- */
- public void close() throws IOException {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- security.checkPermission(new RuntimePermission("closeClassLoader"));
- }
- List<IOException> errors = ucp.closeLoaders();
- // now close any remaining streams.
- synchronized (closeables) {
- Set<Closeable> keys = closeables.keySet();
- for (Closeable c : keys) {
- try {
- c.close();
- } catch (IOException ioex) {
- errors.add(ioex);
- }
- }
- closeables.clear();
- }
- if (errors.isEmpty()) {
- return;
- }
- IOException firstex = errors.remove(0);
- // Suppress any remaining exceptions
- for (IOException error: errors) {
- firstex.addSuppressed(error);
- }
- throw firstex;
- }
- /**
- * Appends the specified URL to the list of URLs to search for
- * classes and resources.
- * <p>
- * If the URL specified is <code>null</code> or is already in the
- * list of URLs, or if this loader is closed, then invoking this
- * method has no effect.
- *
- * @param url the URL to be added to the search path of URLs
- */
- protected void addURL(URL url) {
- ucp.addURL(url);
- }
- /**
- * Returns the search path of URLs for loading classes and resources.
- * This includes the original list of URLs specified to the constructor,
- * along with any URLs subsequently appended by the addURL() method.
- * @return the search path of URLs for loading classes and resources.
- */
- public URL[] getURLs() {
- return ucp.getURLs();
- }
- /**
- * Finds and loads the class with the specified name from the URL search
- * path. Any URLs referring to JAR files are loaded and opened as needed
- * until the class is found.
- *
- * @param name the name of the class
- * @return the resulting class
- * @exception ClassNotFoundException if the class could not be found,
- * or if the loader is closed.
- */
- protected Class<?> findClass(final String name)
- throws ClassNotFoundException
- {
- try {
- return AccessController.doPrivileged(
- new PrivilegedExceptionAction<Class>() {
- public Class run() throws ClassNotFoundException {
- String path = name.replace('.', '/').concat(".class");
- Resource res = ucp.getResource(path, false);
- if (res != null) {
- try {
- return defineClass(name, res);
- } catch (IOException e) {
- throw new ClassNotFoundException(name, e);
- }
- } else {
- throw new ClassNotFoundException(name);
- }
- }
- }, acc);
- } catch (java.security.PrivilegedActionException pae) {
- throw (ClassNotFoundException) pae.getException();
- }
- }
- /*
- * Retrieve the package using the specified package name.
- * If non-null, verify the package using the specified code
- * source and manifest.
- */
- private Package getAndVerifyPackage(String pkgname,
- Manifest man, URL url) {
- Package pkg = getPackage(pkgname);
- if (pkg != null) {
- // Package found, so check package sealing.
- if (pkg.isSealed()) {
- // Verify that code source URL is the same.
- if (!pkg.isSealed(url)) {
- throw new SecurityException(
- "sealing violation: package " + pkgname + " is sealed");
- }
- } else {
- // Make sure we are not attempting to seal the package
- // at this code source URL.
- if ((man != null) && isSealed(pkgname, man)) {
- throw new SecurityException(
- "sealing violation: can't seal package " + pkgname +
- ": already loaded");
- }
- }
- }
- return pkg;
- }
- /*
- * Defines a Class using the class bytes obtained from the specified
- * Resource. The resulting Class must be resolved before it can be
- * used.
- */
- private Class defineClass(String name, Resource res) throws IOException {
- long t0 = System.nanoTime();
- int i = name.lastIndexOf('.');
- URL url = res.getCodeSourceURL();
- if (i != -1) {
- String pkgname = name.substring(0, i);
- // Check if package already loaded.
- Manifest man = res.getManifest();
- if (getAndVerifyPackage(pkgname, man, url) == null) {
- try {
- if (man != null) {
- definePackage(pkgname, man, url);
- } else {
- definePackage(pkgname, null, null, null, null, null, null, null);
- }
- } catch (IllegalArgumentException iae) {
- // parallel-capable class loaders: re-verify in case of a
- // race condition
- if (getAndVerifyPackage(pkgname, man, url) == null) {
- // Should never happen
- throw new AssertionError("Cannot find package " +
- pkgname);
- }
- }
- }
- }
- // Now read the class bytes and define the class
- java.nio.ByteBuffer bb = res.getByteBuffer();
- if (bb != null) {
- // Use (direct) ByteBuffer:
- CodeSigner[] signers = res.getCodeSigners();
- CodeSource cs = new CodeSource(url, signers);
- sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
- return defineClass(name, bb, cs);
- } else {
- byte[] b = res.getBytes();
- // must read certificates AFTER reading bytes.
- CodeSigner[] signers = res.getCodeSigners();
- CodeSource cs = new CodeSource(url, signers);
- sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
- return defineClass(name, b, 0, b.length, cs);
- }
- }
- /**
- * Defines a new package by name in this ClassLoader. The attributes
- * contained in the specified Manifest will be used to obtain package
- * version and sealing information. For sealed packages, the additional
- * URL specifies the code source URL from which the package was loaded.
- *
- * @param name the package name
- * @param man the Manifest containing package version and sealing
- * information
- * @param url the code source url for the package, or null if none
- * @exception IllegalArgumentException if the package name duplicates
- * an existing package either in this class loader or one
- * of its ancestors
- * @return the newly defined Package object
- */
- protected Package definePackage(String name, Manifest man, URL url)
- throws IllegalArgumentException
- {
- String path = name.replace('.', '/').concat("/");
- String specTitle = null, specVersion = null, specVendor = null;
- String implTitle = null, implVersion = null, implVendor = null;
- String sealed = null;
- URL sealBase = null;
- Attributes attr = man.getAttributes(path);
- if (attr != null) {
- specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
- specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
- specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
- implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
- implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
- implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
- sealed = attr.getValue(Name.SEALED);
- }
- attr = man.getMainAttributes();
- if (attr != null) {
- if (specTitle == null) {
- specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
- }
- if (specVersion == null) {
- specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
- }
- if (specVendor == null) {
- specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
- }
- if (implTitle == null) {
- implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
- }
- if (implVersion == null) {
- implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
- }
- if (implVendor == null) {
- implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
- }
- if (sealed == null) {
- sealed = attr.getValue(Name.SEALED);
- }
- }
- if ("true".equalsIgnoreCase(sealed)) {
- sealBase = url;
- }
- return definePackage(name, specTitle, specVersion, specVendor,
- implTitle, implVersion, implVendor, sealBase);
- }
- /*
- * Returns true if the specified package name is sealed according to the
- * given manifest.
- */
- private boolean isSealed(String name, Manifest man) {
- String path = name.replace('.', '/').concat("/");
- Attributes attr = man.getAttributes(path);
- String sealed = null;
- if (attr != null) {
- sealed = attr.getValue(Name.SEALED);
- }
- if (sealed == null) {
- if ((attr = man.getMainAttributes()) != null) {
- sealed = attr.getValue(Name.SEALED);
- }
- }
- return "true".equalsIgnoreCase(sealed);
- }
- /**
- * Finds the resource with the specified name on the URL search path.
- *
- * @param name the name of the resource
- * @return a <code>URL</code> for the resource, or <code>null</code>
- * if the resource could not be found, or if the loader is closed.
- */
- public URL findResource(final String name) {
- /*
- * The same restriction to finding classes applies to resources
- */
- URL url = AccessController.doPrivileged(
- new PrivilegedAction<URL>() {
- public URL run() {
- return ucp.findResource(name, true);
- }
- }, acc);
- return url != null ? ucp.checkURL(url) : null;
- }
- /**
- * Returns an Enumeration of URLs representing all of the resources
- * on the URL search path having the specified name.
- *
- * @param name the resource name
- * @exception IOException if an I/O exception occurs
- * @return an <code>Enumeration</code> of <code>URL</code>s
- * If the loader is closed, the Enumeration will be empty.
- */
- public Enumeration<URL> findResources(final String name)
- throws IOException
- {
- final Enumeration<URL> e = ucp.findResources(name, true);
- return new Enumeration<URL>() {
- private URL url = null;
- private boolean next() {
- if (url != null) {
- return true;
- }
- do {
- URL u = AccessController.doPrivileged(
- new PrivilegedAction<URL>() {
- public URL run() {
- if (!e.hasMoreElements())
- return null;
- return e.nextElement();
- }
- }, acc);
- if (u == null)
- break;
- url = ucp.checkURL(u);
- } while (url == null);
- return url != null;
- }
- public URL nextElement() {
- if (!next()) {
- throw new NoSuchElementException();
- }
- URL u = url;
- url = null;
- return u;
- }
- public boolean hasMoreElements() {
- return next();
- }
- };
- }
- /**
- * Returns the permissions for the given codesource object.
- * The implementation of this method first calls super.getPermissions
- * and then adds permissions based on the URL of the codesource.
- * <p>
- * If the protocol of this URL is "jar", then the permission granted
- * is based on the permission that is required by the URL of the Jar
- * file.
- * <p>
- * If the protocol is "file" and there is an authority component, then
- * permission to connect to and accept connections from that authority
- * may be granted. If the protocol is "file"
- * and the path specifies a file, then permission to read that
- * file is granted. If protocol is "file" and the path is
- * a directory, permission is granted to read all files
- * and (recursively) all files and subdirectories contained in
- * that directory.
- * <p>
- * If the protocol is not "file", then permission
- * to connect to and accept connections from the URL's host is granted.
- * @param codesource the codesource
- * @return the permissions granted to the codesource
- */
- protected PermissionCollection getPermissions(CodeSource codesource)
- {
- PermissionCollection perms = super.getPermissions(codesource);
- URL url = codesource.getLocation();
- Permission p;
- URLConnection urlConnection;
- try {
- urlConnection = url.openConnection();
- p = urlConnection.getPermission();
- } catch (java.io.IOException ioe) {
- p = null;
- urlConnection = null;
- }
- if (p instanceof FilePermission) {
- // if the permission has a separator char on the end,
- // it means the codebase is a directory, and we need
- // to add an additional permission to read recursively
- String path = p.getName();
- if (path.endsWith(File.separator)) {
- path += "-";
- p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
- }
- } else if ((p == null) && (url.getProtocol().equals("file"))) {
- String path = url.getFile().replace('/', File.separatorChar);
- path = ParseUtil.decode(path);
- if (path.endsWith(File.separator))
- path += "-";
- p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
- } else {
- /**
- * Not loading from a 'file:' URL so we want to give the class
- * permission to connect to and accept from the remote host
- * after we've made sure the host is the correct one and is valid.
- */
- URL locUrl = url;
- if (urlConnection instanceof JarURLConnection) {
- locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
- }
- String host = locUrl.getHost();
- if (host != null && (host.length() > 0))
- p = new SocketPermission(host,
- SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
- }
- // make sure the person that created this class loader
- // would have this permission
- if (p != null) {
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- final Permission fp = p;
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() throws SecurityException {
- sm.checkPermission(fp);
- return null;
- }
- }, acc);
- }
- perms.add(p);
- }
- return perms;
- }
- /**
- * Creates a new instance of URLClassLoader for the specified
- * URLs and parent class loader. If a security manager is
- * installed, the <code>loadClass</code> method of the URLClassLoader
- * returned by this method will invoke the
- * <code>SecurityManager.checkPackageAccess</code> method before
- * loading the class.
- *
- * @param urls the URLs to search for classes and resources
- * @param parent the parent class loader for delegation
- * @return the resulting class loader
- */
- public static URLClassLoader newInstance(final URL[] urls,
- final ClassLoader parent) {
- // Save the caller's context
- final AccessControlContext acc = AccessController.getContext();
- // Need a privileged block to create the class loader
- URLClassLoader ucl = AccessController.doPrivileged(
- new PrivilegedAction<URLClassLoader>() {
- public URLClassLoader run() {
- return new FactoryURLClassLoader(urls, parent, acc);
- }
- });
- return ucl;
- }
- /**
- * Creates a new instance of URLClassLoader for the specified
- * URLs and default parent class loader. If a security manager is
- * installed, the <code>loadClass</code> method of the URLClassLoader
- * returned by this method will invoke the
- * <code>SecurityManager.checkPackageAccess</code> before
- * loading the class.
- *
- * @param urls the URLs to search for classes and resources
- * @return the resulting class loader
- */
- public static URLClassLoader newInstance(final URL[] urls) {
- // Save the caller's context
- final AccessControlContext acc = AccessController.getContext();
- // Need a privileged block to create the class loader
- URLClassLoader ucl = AccessController.doPrivileged(
- new PrivilegedAction<URLClassLoader>() {
- public URLClassLoader run() {
- return new FactoryURLClassLoader(urls, acc);
- }
- });
- return ucl;
- }
- static {
- sun.misc.SharedSecrets.setJavaNetAccess (
- new sun.misc.JavaNetAccess() {
- public URLClassPath getURLClassPath (URLClassLoader u) {
- return u.ucp;
- }
- }
- );
- ClassLoader.registerAsParallelCapable();
- }
- }
- final class FactoryURLClassLoader extends URLClassLoader {
- static {
- ClassLoader.registerAsParallelCapable();
- }
- FactoryURLClassLoader(URL[] urls, ClassLoader parent,
- AccessControlContext acc) {
- super(urls, parent, acc);
- }
- FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
- super(urls, acc);
- }
- public final Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // First check if we have permission to access the package. This
- // should go away once we've added support for exported packages.
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- int i = name.lastIndexOf('.');
- if (i != -1) {
- sm.checkPackageAccess(name.substring(0, i));
- }
- }
- return super.loadClass(name, resolve);
- }
- }
2.6URLClassLoader源码解析
猜你喜欢
转载自blog.csdn.net/qq_18048847/article/details/80350132
今日推荐
周排行