aoqi@0: /* aoqi@0: * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.util; aoqi@0: aoqi@0: import java.io.BufferedReader; aoqi@0: import java.io.IOException; aoqi@0: import java.io.InputStream; aoqi@0: import java.io.InputStreamReader; aoqi@0: import java.net.URL; aoqi@0: import java.net.URLConnection; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Enumeration; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.LinkedHashMap; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.NoSuchElementException; aoqi@0: import java.util.Objects; aoqi@0: import java.util.ServiceConfigurationError; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * This is a temporary, modified copy of java.util.ServiceLoader, for use by aoqi@0: * javac, to work around bug JDK-8004082. aoqi@0: * aoqi@0: * The bug describes problems in the interaction between ServiceLoader and aoqi@0: * URLClassLoader, such that references to a jar file passed to URLClassLoader aoqi@0: * may be retained after calling URLClassLoader.close(), preventing the jar aoqi@0: * file from being deleted on Windows. aoqi@0: * aoqi@0: *
This is NOT part of any supported API.
aoqi@0: * If you write code that depends on this, you do so at your own risk.
aoqi@0: * This code and its internal interfaces are subject to change or
aoqi@0: * deletion without notice.
aoqi@0: */
aoqi@0:
aoqi@0: public final class ServiceLoader After invoking this method, subsequent invocations of the {@link
aoqi@0: * #iterator() iterator} method will lazily look up and instantiate
aoqi@0: * providers from scratch, just as is done by a newly-created loader.
aoqi@0: *
aoqi@0: * This method is intended for use in situations in which new providers
aoqi@0: * can be installed into a running Java virtual machine.
aoqi@0: */
aoqi@0: public void reload() {
aoqi@0: providers.clear();
aoqi@0: lookupIterator = new LazyIterator(service, loader);
aoqi@0: }
aoqi@0:
aoqi@0: private ServiceLoader(Class The iterator returned by this method first yields all of the
aoqi@0: * elements of the provider cache, in instantiation order. It then lazily
aoqi@0: * loads and instantiates any remaining providers, adding each one to the
aoqi@0: * cache in turn.
aoqi@0: *
aoqi@0: * To achieve laziness the actual work of parsing the available
aoqi@0: * provider-configuration files and instantiating providers must be done by
aoqi@0: * the iterator itself. Its {@link java.util.Iterator#hasNext hasNext} and
aoqi@0: * {@link java.util.Iterator#next next} methods can therefore throw a
aoqi@0: * {@link ServiceConfigurationError} if a provider-configuration file
aoqi@0: * violates the specified format, or if it names a provider class that
aoqi@0: * cannot be found and instantiated, or if the result of instantiating the
aoqi@0: * class is not assignable to the service type, or if any other kind of
aoqi@0: * exception or error is thrown as the next provider is located and
aoqi@0: * instantiated. To write robust code it is only necessary to catch {@link
aoqi@0: * ServiceConfigurationError} when using a service iterator.
aoqi@0: *
aoqi@0: * If such an error is thrown then subsequent invocations of the
aoqi@0: * iterator will make a best effort to locate and instantiate the next
aoqi@0: * available provider, but in general such recovery cannot be guaranteed.
aoqi@0: *
aoqi@0: * The iterator returned by this method does not support removal.
aoqi@0: * Invoking its {@link java.util.Iterator#remove() remove} method will
aoqi@0: * cause an {@link UnsupportedOperationException} to be thrown.
aoqi@0: *
aoqi@0: * @return An iterator that lazily loads providers for this loader's
aoqi@0: * service
aoqi@0: */
aoqi@0: public Iterator An invocation of this convenience method of the form
aoqi@0: *
aoqi@0: * This convenience method simply locates the extension class loader,
aoqi@0: * call it extClassLoader, and then returns
aoqi@0: *
aoqi@0: * If the extension class loader cannot be found then the system class
aoqi@0: * loader is used; if there is no system class loader then the bootstrap
aoqi@0: * class loader is used.
aoqi@0: *
aoqi@0: * This method is intended for use when only installed providers are
aoqi@0: * desired. The resulting service will only find and load providers that
aoqi@0: * have been installed into the current Java virtual machine; providers on
aoqi@0: * the application's class path will be ignored.
aoqi@0: *
aoqi@0: * @param service
aoqi@0: * The interface or abstract class representing the service
aoqi@0: *
aoqi@0: * @return A new service loader
aoqi@0: */
aoqi@0: public static
aoqi@0: implements Iterable
aoqi@0: {
aoqi@0:
aoqi@0: private static final String PREFIX = "META-INF/services/";
aoqi@0:
aoqi@0: // The class or interface representing the service being loaded
aoqi@0: private Class service;
aoqi@0:
aoqi@0: // The class loader used to locate, load, and instantiate providers
aoqi@0: private ClassLoader loader;
aoqi@0:
aoqi@0: // Cached providers, in instantiation order
aoqi@0: private LinkedHashMap svc, ClassLoader cl) {
aoqi@0: service = Objects.requireNonNull(svc, "Service interface cannot be null");
aoqi@0: loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
aoqi@0: reload();
aoqi@0: }
aoqi@0:
aoqi@0: private static void fail(Class> service, String msg, Throwable cause)
aoqi@0: throws ServiceConfigurationError
aoqi@0: {
aoqi@0: throw new ServiceConfigurationError(service.getName() + ": " + msg,
aoqi@0: cause);
aoqi@0: }
aoqi@0:
aoqi@0: private static void fail(Class> service, String msg)
aoqi@0: throws ServiceConfigurationError
aoqi@0: {
aoqi@0: throw new ServiceConfigurationError(service.getName() + ": " + msg);
aoqi@0: }
aoqi@0:
aoqi@0: private static void fail(Class> service, URL u, int line, String msg)
aoqi@0: throws ServiceConfigurationError
aoqi@0: {
aoqi@0: fail(service, u + ":" + line + ": " + msg);
aoqi@0: }
aoqi@0:
aoqi@0: // Parse a single line from the given configuration file, adding the name
aoqi@0: // on the line to the names list.
aoqi@0: //
aoqi@0: private int parseLine(Class> service, URL u, BufferedReader r, int lc,
aoqi@0: List
aoqi@0: {
aoqi@0:
aoqi@0: Class service;
aoqi@0: ClassLoader loader;
aoqi@0: Enumeration service, ClassLoader loader) {
aoqi@0: this.service = service;
aoqi@0: this.loader = loader;
aoqi@0: }
aoqi@0:
aoqi@0: public boolean hasNext() {
aoqi@0: if (nextName != null) {
aoqi@0: return true;
aoqi@0: }
aoqi@0: if (configs == null) {
aoqi@0: try {
aoqi@0: String fullName = PREFIX + service.getName();
aoqi@0: if (loader == null)
aoqi@0: configs = ClassLoader.getSystemResources(fullName);
aoqi@0: else
aoqi@0: configs = loader.getResources(fullName);
aoqi@0: } catch (IOException x) {
aoqi@0: fail(service, "Error locating configuration files", x);
aoqi@0: }
aoqi@0: }
aoqi@0: while ((pending == null) || !pending.hasNext()) {
aoqi@0: if (!configs.hasMoreElements()) {
aoqi@0: return false;
aoqi@0: }
aoqi@0: pending = parse(service, configs.nextElement());
aoqi@0: }
aoqi@0: nextName = pending.next();
aoqi@0: return true;
aoqi@0: }
aoqi@0:
aoqi@0: public S next() {
aoqi@0: if (!hasNext()) {
aoqi@0: throw new NoSuchElementException();
aoqi@0: }
aoqi@0: String cn = nextName;
aoqi@0: nextName = null;
aoqi@0: Class> c = null;
aoqi@0: try {
aoqi@0: c = Class.forName(cn, false, loader);
aoqi@0: } catch (ClassNotFoundException x) {
aoqi@0: fail(service,
aoqi@0: "Provider " + cn + " not found");
aoqi@0: }
aoqi@0: if (!service.isAssignableFrom(c)) {
aoqi@0: fail(service,
aoqi@0: "Provider " + cn + " not a subtype");
aoqi@0: }
aoqi@0: try {
aoqi@0: S p = service.cast(c.newInstance());
aoqi@0: providers.put(cn, p);
aoqi@0: return p;
aoqi@0: } catch (Throwable x) {
aoqi@0: fail(service,
aoqi@0: "Provider " + cn + " could not be instantiated: " + x,
aoqi@0: x);
aoqi@0: }
aoqi@0: throw new Error(); // This cannot happen
aoqi@0: }
aoqi@0:
aoqi@0: public void remove() {
aoqi@0: throw new UnsupportedOperationException();
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Lazily loads the available providers of this loader's service.
aoqi@0: *
aoqi@0: * Design Note
aoqi@0: * Throwing an error in these cases may seem extreme. The rationale for
aoqi@0: * this behavior is that a malformed provider-configuration file, like a
aoqi@0: * malformed class file, indicates a serious problem with the way the Java
aoqi@0: * virtual machine is configured or is being used. As such it is
aoqi@0: * preferable to throw an error rather than try to recover or, even worse,
aoqi@0: * fail silently.
aoqi@0: *
aoqi@0: * iterator() {
aoqi@0: return new Iterator() {
aoqi@0:
aoqi@0: Iterator ServiceLoader load(Class service,
aoqi@0: ClassLoader loader)
aoqi@0: {
aoqi@0: return new ServiceLoader<>(service, loader);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates a new service loader for the given service type, using the
aoqi@0: * current thread's {@linkplain java.lang.Thread#getContextClassLoader
aoqi@0: * context class loader}.
aoqi@0: *
aoqi@0: *
aoqi@0: *
aoqi@0: * is equivalent to
aoqi@0: *
aoqi@0: *
aoqi@0: * ServiceLoader.load(service)
aoqi@0: *
aoqi@0: * @param service
aoqi@0: * The interface or abstract class representing the service
aoqi@0: *
aoqi@0: * @return A new service loader
aoqi@0: */
aoqi@0: public static
aoqi@0: * ServiceLoader.load(service,
aoqi@0: * Thread.currentThread().getContextClassLoader())
ServiceLoader load(Class service) {
aoqi@0: ClassLoader cl = Thread.currentThread().getContextClassLoader();
aoqi@0: return ServiceLoader.load(service, cl);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Creates a new service loader for the given service type, using the
aoqi@0: * extension class loader.
aoqi@0: *
aoqi@0: *
aoqi@0: *
aoqi@0: *
aoqi@0: * ServiceLoader.load(service, extClassLoader)
ServiceLoader loadInstalled(Class service) {
aoqi@0: ClassLoader cl = ClassLoader.getSystemClassLoader();
aoqi@0: ClassLoader prev = null;
aoqi@0: while (cl != null) {
aoqi@0: prev = cl;
aoqi@0: cl = cl.getParent();
aoqi@0: }
aoqi@0: return ServiceLoader.load(service, prev);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Returns a string describing this service.
aoqi@0: *
aoqi@0: * @return A descriptive string
aoqi@0: */
aoqi@0: public String toString() {
aoqi@0: return "java.util.ServiceLoader[" + service.getName() + "]";
aoqi@0: }
aoqi@0:
aoqi@0: }