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> ! * &lt;<i>package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler ! * </pre></blockquote> ! * where &lt;<i>package</i>&gt; is replaced by the name of the package ! * and &lt;<i>protocol</i>&gt; 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 ----