src/java.base/share/classes/java/net/URL.java
Print this page
*** 25,36 ****
package java.net;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
! import java.util.StringTokenizer;
import sun.security.util.SecurityConstants;
/**
* Class {@code URL} represents a Uniform Resource
* Locator, a pointer to a "resource" on the World
--- 25,46 ----
package java.net;
import java.io.IOException;
import java.io.InputStream;
+ import java.security.AccessController;
+ import java.security.PrivilegedAction;
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
import java.util.Hashtable;
! import java.util.List;
! import java.util.Objects;
! import java.util.ServiceLoader;
! import java.util.Set;
! import java.util.concurrent.CopyOnWriteArrayList;
!
import sun.security.util.SecurityConstants;
/**
* Class {@code URL} represents a Uniform Resource
* Locator, a pointer to a "resource" on the World
*** 240,272 ****
* If this is the first URL object being created with the specified
* protocol, a <i>stream protocol handler</i> object, an instance of
* class {@code URLStreamHandler}, is created for that protocol:
* <ol>
* <li>If the application has previously set up an instance of
! * {@code URLStreamHandlerFactory} as the stream handler factory,
* then the {@code createURLStreamHandler} method of that instance
* is called with the protocol string as an argument to create the
* stream protocol handler.
* <li>If no {@code URLStreamHandlerFactory} has yet been set up,
* or if the factory's {@code createURLStreamHandler} method
! * returns {@code null}, then the constructor finds the
! * value of the system property:
! * <blockquote><pre>
! * java.protocol.handler.pkgs
! * </pre></blockquote>
! * If the value of that system property is not {@code null},
! * it is interpreted as a list of packages separated by a vertical
! * slash character '{@code |}'. The constructor tries to load
! * the class named:
! * <blockquote><pre>
! * <<i>package</i>>.<<i>protocol</i>>.Handler
! * </pre></blockquote>
! * where <<i>package</i>> is replaced by the name of the package
! * and <<i>protocol</i>> is replaced by the name of the protocol.
! * If this class does not exist, or if the class exists but it is not
! * a subclass of {@code URLStreamHandler}, then the next package
! * in the list is tried.
* <li>If the previous step fails to find a protocol handler, then the
* constructor tries to load a built-in protocol handler.
* If this class does not exist, or if the class exists but it is not a
* subclass of {@code URLStreamHandler}, then a
* {@code MalformedURLException} is thrown.
--- 250,285 ----
* If this is the first URL object being created with the specified
* protocol, a <i>stream protocol handler</i> object, an instance of
* class {@code URLStreamHandler}, is created for that protocol:
* <ol>
* <li>If the application has previously set up an instance of
! * {@code URLStreamHandlerFactory}, through {@linkplain
! * #setURLStreamHandlerFactory(URLStreamHandlerFactory)
! * setURLStreamHandlerFactory}, as the stream handler factory,
* then the {@code createURLStreamHandler} method of that instance
* is called with the protocol string as an argument to create the
* stream protocol handler.
* <li>If no {@code URLStreamHandlerFactory} has yet been set up,
* or if the factory's {@code createURLStreamHandler} method
! * returns {@code null}, then the list of factories, set through
! * {@linkplain #addURLStreamHandlerFactory(URLStreamHandlerFactory)
! * addURLStreamHandlerFactory} is consulted. The {@code
! * createURLStreamHandler} method of each factory is invoked, in
! * registration order, with the protocol string, until a factory returns
! * non-null, or all the factories in the list have been exhausted.
! * <li>If the previous step fails to find a protocol handler, then the
! * {@linkplain java.util.ServiceLoader ServiceLoader} mechanism is used
! * to locate a {@code URLStreamHandlerFactory} provider using the system
! * class loader. The ordering that providers are located is
! * implementation specific, and an implementation is free to cache the
! * located providers. A {@linkplain java.util.ServiceConfigurationError
! * ServiceConfigurationError}, {@code Error} or {@code RuntimeException}
! * thrown from the {@code createURLStreamHandler}, if encountered, will
! * be propagated to the calling thread. The {@code
! * createURLStreamHandler} method of each provider, if instantiated, is
! * invoked, with the protocol string, until a provider returns non-null,
! * or all providers have been exhausted.
* <li>If the previous step fails to find a protocol handler, then the
* constructor tries to load a built-in protocol handler.
* If this class does not exist, or if the class exists but it is not a
* subclass of {@code URLStreamHandler}, then a
* {@code MalformedURLException} is thrown.
*** 275,286 ****
* <p>Protocol handlers for the following protocols are guaranteed
* to exist on the search path :-
* <blockquote><pre>
* http, https, file, and jar
* </pre></blockquote>
! * Protocol handlers for additional protocols may also be
! * available.
*
* <p>No validation of the inputs is performed by this constructor.
*
* @param protocol the name of the protocol to use.
* @param host the name of the host.
--- 288,300 ----
* <p>Protocol handlers for the following protocols are guaranteed
* to exist on the search path :-
* <blockquote><pre>
* http, https, file, and jar
* </pre></blockquote>
! * Protocol handlers for additional protocols may also be available. Some
! * protocols, that are fundamental to the platform, may have restrictions
! * around when, or if, their built-in handlers can be overridden.
*
* <p>No validation of the inputs is performed by this constructor.
*
* @param protocol the name of the protocol to use.
* @param host the name of the host.
*** 1067,1080 ****
throws java.io.IOException {
return openConnection().getContent(classes);
}
/**
! * The URLStreamHandler factory.
*/
private static volatile URLStreamHandlerFactory factory;
/**
* Sets an application's {@code URLStreamHandlerFactory}.
* This method can be called at most once in a given Java Virtual
* Machine.
*
--- 1081,1098 ----
throws java.io.IOException {
return openConnection().getContent(classes);
}
/**
! * The Application URLStreamHandler factory.
*/
private static volatile URLStreamHandlerFactory factory;
+ /** The list of factories. */
+ private static final CopyOnWriteArrayList<URLStreamHandlerFactory> factoryList =
+ new CopyOnWriteArrayList<>();
+
/**
* Sets an application's {@code URLStreamHandlerFactory}.
* This method can be called at most once in a given Java Virtual
* Machine.
*
*** 1105,1192 ****
if (security != null) {
security.checkSetFactory();
}
handlers.clear();
- // ensure the core protocol handlers are loaded before setting
- // a custom URLStreamHandlerFactory
- ensureHandlersLoaded("jrt", "jar", "file");
-
// safe publication of URLStreamHandlerFactory with volatile write
factory = fac;
}
}
/**
! * A table of protocol handlers.
*/
! static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
! private static Object streamHandlerLock = new Object();
! /**
! * Returns the Stream Handler.
! * @param protocol the protocol to use
! */
! static URLStreamHandler getURLStreamHandler(String protocol) {
! URLStreamHandler handler = handlers.get(protocol);
! if (handler == null) {
! boolean checkedWithFactory = false;
// Use the factory (if any). Volatile read makes
// URLStreamHandlerFactory appear fully initialized to current thread.
URLStreamHandlerFactory fac = factory;
! if (fac != null) {
handler = fac.createURLStreamHandler(protocol);
! checkedWithFactory = true;
}
! // Try java protocol handler
! if (handler == null) {
! String packagePrefixList = null;
! packagePrefixList
! = java.security.AccessController.doPrivileged(
! new sun.security.action.GetPropertyAction(
! protocolPathProp,""));
! if (packagePrefixList != "") {
! packagePrefixList += "|";
}
! // REMIND: decide whether to allow the "null" class prefix
! // or not.
! packagePrefixList += "sun.net.www.protocol";
! StringTokenizer packagePrefixIter =
! new StringTokenizer(packagePrefixList, "|");
! while (handler == null &&
! packagePrefixIter.hasMoreTokens()) {
! String packagePrefix =
! packagePrefixIter.nextToken().trim();
! try {
! String clsName = packagePrefix + "." + protocol +
! ".Handler";
! Class<?> cls = null;
! try {
! cls = Class.forName(clsName);
! } catch (ClassNotFoundException e) {
! ClassLoader cl = ClassLoader.getSystemClassLoader();
! if (cl != null) {
! cls = cl.loadClass(clsName);
}
}
! if (cls != null) {
! handler =
! (URLStreamHandler)cls.newInstance();
}
! } catch (Exception e) {
! // any number of exceptions can get thrown here
}
}
}
synchronized (streamHandlerLock) {
URLStreamHandler handler2 = null;
--- 1123,1320 ----
if (security != null) {
security.checkSetFactory();
}
handlers.clear();
// safe publication of URLStreamHandlerFactory with volatile write
factory = fac;
}
}
/**
! * Adds a {@linkplain URLStreamHandlerFactory}. This method can be called
! * multiple times in order to create an effective list of factories.
! *
! * <p> Factories, added through a call to this method, will have their
! * {@code createURLStreamHandler} method called, in the order in which they
! * were added, to construct a stream protocol handler from a protocol name.
! * A factory that cannot construct a stream protocol handler for a
! * particular protocol name should return {@code null}. The next factory in
! * the list, if there is one, will then be consulted.
! *
! * <p> If there is a security manager, this method first calls
! * the security manager's {@code checkSetFactory} method
! * to ensure the operation is allowed.
! * This could result in a SecurityException.
! *
! * @apiNote
! * This method is intended to be used by long running applications, to
! * support adding additional system-wide protocol handlers, beyond that of
! * the built-in handlers. It can be used in some situations to override
! * built-in handlers, that may not be possible to locate using services,
! * like {@code jar} for example.
! *
! * @param factory the factory
! * @throws SecurityException if a security manager exists and its {@link
! * SecurityManager#checkSetFactory} method doesn't allow the operation
! * @throws NullPointerException if {@code factory} is null
! * @since 1.9
*/
! public static void addURLStreamHandlerFactory(URLStreamHandlerFactory factory) {
! Objects.requireNonNull(factory);
! synchronized (streamHandlerLock) {
! SecurityManager security = System.getSecurityManager();
! if (security != null) {
! security.checkSetFactory();
! }
! handlers.clear();
! factoryList.add(factory);
! }
! }
! private static final URLStreamHandlerFactory defaultFactory = new DefaultFactory();
! private static class DefaultFactory implements URLStreamHandlerFactory {
! private static String PREFIX = "sun.net.www.protocol";
!
! public URLStreamHandler createURLStreamHandler(String protocol) {
! String name = PREFIX + "." + protocol + ".Handler";
! try {
! Class<?> c = Class.forName(name);
! return (URLStreamHandler)c.newInstance();
! } catch (ClassNotFoundException x) {
! // ignore
! } catch (Exception e) {
! // For compatibility, all Exceptions are ignored.
! // any number of exceptions can get thrown here
! }
! return null;
! }
! }
+ private static final URLStreamHandler NULL_HANDLER = new URLStreamHandler() {
+ public URLConnection openConnection(URL u) { return null; }
+ public URLConnection openConnection(URL u, Proxy p) { return null; }
+ };
+
+ /** Returns a handler instance for the given protocol, null if the factories
+ * could not create a handler, or NULL_HANDLER if there are no factories.
+ */
+ private static URLStreamHandler handlerFromSettableFactory(String protocol) {
+ URLStreamHandler handler = NULL_HANDLER;
// Use the factory (if any). Volatile read makes
// URLStreamHandlerFactory appear fully initialized to current thread.
URLStreamHandlerFactory fac = factory;
! if (fac != null)
handler = fac.createURLStreamHandler(protocol);
!
! if (handler == NULL_HANDLER || handler == null) {
! for (URLStreamHandlerFactory f : factoryList) {
! handler = f.createURLStreamHandler(protocol);
! if (handler != null)
! return handler;
! }
! }
! return handler;
}
! // installed service providers
! private static volatile List<URLStreamHandlerFactory> installedProviders;
! private static final Object providersLock = new Object();
! private static boolean loadingProviders = false;
! private static List<URLStreamHandlerFactory> loadInstalledProviders() {
! List<URLStreamHandlerFactory> list = new ArrayList<>();
! ServiceLoader<URLStreamHandlerFactory> sl = ServiceLoader
! .load(URLStreamHandlerFactory.class, ClassLoader.getSystemClassLoader());
! // ServiceConfigurationError may be throw here
! for (URLStreamHandlerFactory provider: sl)
! list.add(provider);
!
! return list;
! }
!
! private static List<URLStreamHandlerFactory> installedProviders() {
! if (installedProviders == null) {
! synchronized (providersLock) {
! if (installedProviders == null) {
! if (loadingProviders) {
! throw new Error(
! "Circular loading of URL stream handler providers detected");
}
+ loadingProviders = true;
! // do not use a lambda, or method ref here.
! List<URLStreamHandlerFactory> list = AccessController
! .doPrivileged(new PrivilegedAction<List<URLStreamHandlerFactory>>() {
! public List<URLStreamHandlerFactory> run() {
! return URL.loadInstalledProviders();
! }
! });
!
! installedProviders = Collections.unmodifiableList(list);
! }
! }
! }
! return installedProviders;
! }
! private static final String SL_SKIP_PROTOCOL = "jar";
! private static URLStreamHandler handlerFromInstalledProviders(String protocol) {
! if (protocol.equalsIgnoreCase(SL_SKIP_PROTOCOL))
! return null;
! URLStreamHandler handler = null;
! for (URLStreamHandlerFactory f : installedProviders()) {
! handler = f.createURLStreamHandler(protocol);
! if (handler != null)
! return handler;
}
+ return handler;
}
!
! private static final String[] UNOVERRIDEABLE_PROTOCOLS = {"file", "jrt"};
! private static boolean overrideable(String protocol) {
! for (String p : UNOVERRIDEABLE_PROTOCOLS)
! if (protocol.equalsIgnoreCase(p))
! return false;
! return true;
}
!
! /**
! * A table of protocol handlers.
! */
! static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
! private static final Object streamHandlerLock = new Object();
!
! /**
! * Returns the Stream Handler.
! * @param protocol the protocol to use
! */
! static URLStreamHandler getURLStreamHandler(String protocol) {
!
! URLStreamHandler handler = handlers.get(protocol);
!
! if (handler != null)
! return handler;
!
! boolean checkedWithFactory = false;
!
! if (overrideable(protocol)) {
! handler = handlerFromSettableFactory(protocol);
! if (handler != NULL_HANDLER)
! checkedWithFactory = true;
!
! if (handler == null || handler == NULL_HANDLER) {
! handler = handlerFromInstalledProviders(protocol);
}
}
+ if (handler == null) {
+ // Try the built-in protocol handler
+ handler = defaultFactory.createURLStreamHandler(protocol);
}
synchronized (streamHandlerLock) {
URLStreamHandler handler2 = null;
*** 1199,1213 ****
return handler2;
}
// Check with factory if another thread set a
// factory since our last check
! if (!checkedWithFactory && (fac = factory) != null) {
! handler2 = fac.createURLStreamHandler(protocol);
}
! if (handler2 != null) {
// The handler from the factory must be given more
// importance. Discard the default handler that
// this thread created.
handler = handler2;
}
--- 1327,1341 ----
return handler2;
}
// Check with factory if another thread set a
// factory since our last check
! if (!checkedWithFactory) {
! handler2 = handlerFromSettableFactory(protocol);
}
! if (!(handler2 == null || handler2 == NULL_HANDLER)) {
// The handler from the factory must be given more
// importance. Discard the default handler that
// this thread created.
handler = handler2;
}
*** 1216,1242 ****
if (handler != null) {
handlers.put(protocol, handler);
}
}
- }
return handler;
-
}
/**
- * Ensures that the given protocol handlers are loaded
- */
- private static void ensureHandlersLoaded(String... protocols) {
- for (String protocol: protocols) {
- getURLStreamHandler(protocol);
- }
- }
-
-
- /**
* WriteObject is called to save the state of the URL to an
* ObjectOutputStream. The handler is not saved since it is
* specific to this system.
*
* @serialData the default write object value. When read back in,
--- 1344,1358 ----