/* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.net; import java.io.Closeable; import java.io.File; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSigner; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.SecureClassLoader; import java.util.Enumeration; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.util.jar.JarFile; import java.util.jar.Manifest; 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. *
* The AccessControlContext of the thread that created the instance of * URLClassLoader will be used when subsequently loading classes and * resources. *
* 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. * *
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. * @exception NullPointerException if {@code urls} is {@code null}. * @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}. 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. * *
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 * * @exception SecurityException if a security manager exists and its * {@code checkCreateClassLoader} method doesn't allow * creation of a class loader. * @exception NullPointerException if {@code urls} is {@code null}. * @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. * *
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
* @param factory the URLStreamHandlerFactory to use when creating URLs
*
* @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,
URLStreamHandlerFactory factory) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
acc = AccessController.getContext();
ucp = new URLClassPath(urls, factory, acc);
}
/* 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 The search order is described in the documentation for {@link
* #getResource(String)}.
* 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.
*
* 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.
*
* @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}{@code ("closeClassLoader")}
*
* @since 1.7
*/
public void close() throws IOException {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(new RuntimePermission("closeClassLoader"));
}
List
* If the URL specified is {@code null} 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.
* @exception NullPointerException if {@code name} is {@code null}.
*/
protected Class> findClass(final String name)
throws ClassNotFoundException
{
final Class> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction
* 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.
*
* 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.
*
* 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
* @exception NullPointerException if {@code codesource} is {@code null}.
* @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