src/share/classes/com/sun/tools/javac/util/ServiceLoader.java

Sat, 07 Nov 2020 10:30:02 +0800

author
aoqi
date
Sat, 07 Nov 2020 10:30:02 +0800
changeset 3938
93012e2a5d1d
parent 0
959103a6100f
permissions
-rw-r--r--

Added tag mips-jdk8u275-b01 for changeset eb6ee6a5f2fe

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package com.sun.tools.javac.util;
aoqi@0 27
aoqi@0 28 import java.io.BufferedReader;
aoqi@0 29 import java.io.IOException;
aoqi@0 30 import java.io.InputStream;
aoqi@0 31 import java.io.InputStreamReader;
aoqi@0 32 import java.net.URL;
aoqi@0 33 import java.net.URLConnection;
aoqi@0 34 import java.util.ArrayList;
aoqi@0 35 import java.util.Enumeration;
aoqi@0 36 import java.util.Iterator;
aoqi@0 37 import java.util.LinkedHashMap;
aoqi@0 38 import java.util.List;
aoqi@0 39 import java.util.Map;
aoqi@0 40 import java.util.NoSuchElementException;
aoqi@0 41 import java.util.Objects;
aoqi@0 42 import java.util.ServiceConfigurationError;
aoqi@0 43
aoqi@0 44
aoqi@0 45 /**
aoqi@0 46 * This is a temporary, modified copy of java.util.ServiceLoader, for use by
aoqi@0 47 * javac, to work around bug JDK-8004082.
aoqi@0 48 *
aoqi@0 49 * The bug describes problems in the interaction between ServiceLoader and
aoqi@0 50 * URLClassLoader, such that references to a jar file passed to URLClassLoader
aoqi@0 51 * may be retained after calling URLClassLoader.close(), preventing the jar
aoqi@0 52 * file from being deleted on Windows.
aoqi@0 53 *
aoqi@0 54 * <p><b>This is NOT part of any supported API.
aoqi@0 55 * If you write code that depends on this, you do so at your own risk.
aoqi@0 56 * This code and its internal interfaces are subject to change or
aoqi@0 57 * deletion without notice.</b>
aoqi@0 58 */
aoqi@0 59
aoqi@0 60 public final class ServiceLoader<S>
aoqi@0 61 implements Iterable<S>
aoqi@0 62 {
aoqi@0 63
aoqi@0 64 private static final String PREFIX = "META-INF/services/";
aoqi@0 65
aoqi@0 66 // The class or interface representing the service being loaded
aoqi@0 67 private Class<S> service;
aoqi@0 68
aoqi@0 69 // The class loader used to locate, load, and instantiate providers
aoqi@0 70 private ClassLoader loader;
aoqi@0 71
aoqi@0 72 // Cached providers, in instantiation order
aoqi@0 73 private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
aoqi@0 74
aoqi@0 75 // The current lazy-lookup iterator
aoqi@0 76 private LazyIterator lookupIterator;
aoqi@0 77
aoqi@0 78 /**
aoqi@0 79 * Clear this loader's provider cache so that all providers will be
aoqi@0 80 * reloaded.
aoqi@0 81 *
aoqi@0 82 * <p> After invoking this method, subsequent invocations of the {@link
aoqi@0 83 * #iterator() iterator} method will lazily look up and instantiate
aoqi@0 84 * providers from scratch, just as is done by a newly-created loader.
aoqi@0 85 *
aoqi@0 86 * <p> This method is intended for use in situations in which new providers
aoqi@0 87 * can be installed into a running Java virtual machine.
aoqi@0 88 */
aoqi@0 89 public void reload() {
aoqi@0 90 providers.clear();
aoqi@0 91 lookupIterator = new LazyIterator(service, loader);
aoqi@0 92 }
aoqi@0 93
aoqi@0 94 private ServiceLoader(Class<S> svc, ClassLoader cl) {
aoqi@0 95 service = Objects.requireNonNull(svc, "Service interface cannot be null");
aoqi@0 96 loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
aoqi@0 97 reload();
aoqi@0 98 }
aoqi@0 99
aoqi@0 100 private static void fail(Class<?> service, String msg, Throwable cause)
aoqi@0 101 throws ServiceConfigurationError
aoqi@0 102 {
aoqi@0 103 throw new ServiceConfigurationError(service.getName() + ": " + msg,
aoqi@0 104 cause);
aoqi@0 105 }
aoqi@0 106
aoqi@0 107 private static void fail(Class<?> service, String msg)
aoqi@0 108 throws ServiceConfigurationError
aoqi@0 109 {
aoqi@0 110 throw new ServiceConfigurationError(service.getName() + ": " + msg);
aoqi@0 111 }
aoqi@0 112
aoqi@0 113 private static void fail(Class<?> service, URL u, int line, String msg)
aoqi@0 114 throws ServiceConfigurationError
aoqi@0 115 {
aoqi@0 116 fail(service, u + ":" + line + ": " + msg);
aoqi@0 117 }
aoqi@0 118
aoqi@0 119 // Parse a single line from the given configuration file, adding the name
aoqi@0 120 // on the line to the names list.
aoqi@0 121 //
aoqi@0 122 private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
aoqi@0 123 List<String> names)
aoqi@0 124 throws IOException, ServiceConfigurationError
aoqi@0 125 {
aoqi@0 126 String ln = r.readLine();
aoqi@0 127 if (ln == null) {
aoqi@0 128 return -1;
aoqi@0 129 }
aoqi@0 130 int ci = ln.indexOf('#');
aoqi@0 131 if (ci >= 0) ln = ln.substring(0, ci);
aoqi@0 132 ln = ln.trim();
aoqi@0 133 int n = ln.length();
aoqi@0 134 if (n != 0) {
aoqi@0 135 if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
aoqi@0 136 fail(service, u, lc, "Illegal configuration-file syntax");
aoqi@0 137 int cp = ln.codePointAt(0);
aoqi@0 138 if (!Character.isJavaIdentifierStart(cp))
aoqi@0 139 fail(service, u, lc, "Illegal provider-class name: " + ln);
aoqi@0 140 for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
aoqi@0 141 cp = ln.codePointAt(i);
aoqi@0 142 if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
aoqi@0 143 fail(service, u, lc, "Illegal provider-class name: " + ln);
aoqi@0 144 }
aoqi@0 145 if (!providers.containsKey(ln) && !names.contains(ln))
aoqi@0 146 names.add(ln);
aoqi@0 147 }
aoqi@0 148 return lc + 1;
aoqi@0 149 }
aoqi@0 150
aoqi@0 151 // Parse the content of the given URL as a provider-configuration file.
aoqi@0 152 //
aoqi@0 153 // @param service
aoqi@0 154 // The service type for which providers are being sought;
aoqi@0 155 // used to construct error detail strings
aoqi@0 156 //
aoqi@0 157 // @param u
aoqi@0 158 // The URL naming the configuration file to be parsed
aoqi@0 159 //
aoqi@0 160 // @return A (possibly empty) iterator that will yield the provider-class
aoqi@0 161 // names in the given configuration file that are not yet members
aoqi@0 162 // of the returned set
aoqi@0 163 //
aoqi@0 164 // @throws ServiceConfigurationError
aoqi@0 165 // If an I/O error occurs while reading from the given URL, or
aoqi@0 166 // if a configuration-file format error is detected
aoqi@0 167 //
aoqi@0 168 private Iterator<String> parse(Class<?> service, URL u)
aoqi@0 169 throws ServiceConfigurationError
aoqi@0 170 {
aoqi@0 171 InputStream in = null;
aoqi@0 172 BufferedReader r = null;
aoqi@0 173 ArrayList<String> names = new ArrayList<>();
aoqi@0 174 try {
aoqi@0 175 // The problem is that by default, streams opened with
aoqi@0 176 // u.openInputStream use a cached reference to a JarFile, which
aoqi@0 177 // is separate from the reference used by URLClassLoader, and
aoqi@0 178 // which is not closed by URLClassLoader.close().
aoqi@0 179 // The workaround is to disable caching for this specific jar file,
aoqi@0 180 // so that the reference to the jar file can be closed when the
aoqi@0 181 // file has been read.
aoqi@0 182 // Original code:
aoqi@0 183 // in = u.openStream();
aoqi@0 184 // Workaround ...
aoqi@0 185 URLConnection uc = u.openConnection();
aoqi@0 186 uc.setUseCaches(false);
aoqi@0 187 in = uc.getInputStream();
aoqi@0 188 // ... end of workaround.
aoqi@0 189 r = new BufferedReader(new InputStreamReader(in, "utf-8"));
aoqi@0 190 int lc = 1;
aoqi@0 191 while ((lc = parseLine(service, u, r, lc, names)) >= 0);
aoqi@0 192 } catch (IOException x) {
aoqi@0 193 fail(service, "Error reading configuration file", x);
aoqi@0 194 } finally {
aoqi@0 195 try {
aoqi@0 196 if (r != null) r.close();
aoqi@0 197 if (in != null) in.close();
aoqi@0 198 } catch (IOException y) {
aoqi@0 199 fail(service, "Error closing configuration file", y);
aoqi@0 200 }
aoqi@0 201 }
aoqi@0 202 return names.iterator();
aoqi@0 203 }
aoqi@0 204
aoqi@0 205 // Private inner class implementing fully-lazy provider lookup
aoqi@0 206 //
aoqi@0 207 private class LazyIterator
aoqi@0 208 implements Iterator<S>
aoqi@0 209 {
aoqi@0 210
aoqi@0 211 Class<S> service;
aoqi@0 212 ClassLoader loader;
aoqi@0 213 Enumeration<URL> configs = null;
aoqi@0 214 Iterator<String> pending = null;
aoqi@0 215 String nextName = null;
aoqi@0 216
aoqi@0 217 private LazyIterator(Class<S> service, ClassLoader loader) {
aoqi@0 218 this.service = service;
aoqi@0 219 this.loader = loader;
aoqi@0 220 }
aoqi@0 221
aoqi@0 222 public boolean hasNext() {
aoqi@0 223 if (nextName != null) {
aoqi@0 224 return true;
aoqi@0 225 }
aoqi@0 226 if (configs == null) {
aoqi@0 227 try {
aoqi@0 228 String fullName = PREFIX + service.getName();
aoqi@0 229 if (loader == null)
aoqi@0 230 configs = ClassLoader.getSystemResources(fullName);
aoqi@0 231 else
aoqi@0 232 configs = loader.getResources(fullName);
aoqi@0 233 } catch (IOException x) {
aoqi@0 234 fail(service, "Error locating configuration files", x);
aoqi@0 235 }
aoqi@0 236 }
aoqi@0 237 while ((pending == null) || !pending.hasNext()) {
aoqi@0 238 if (!configs.hasMoreElements()) {
aoqi@0 239 return false;
aoqi@0 240 }
aoqi@0 241 pending = parse(service, configs.nextElement());
aoqi@0 242 }
aoqi@0 243 nextName = pending.next();
aoqi@0 244 return true;
aoqi@0 245 }
aoqi@0 246
aoqi@0 247 public S next() {
aoqi@0 248 if (!hasNext()) {
aoqi@0 249 throw new NoSuchElementException();
aoqi@0 250 }
aoqi@0 251 String cn = nextName;
aoqi@0 252 nextName = null;
aoqi@0 253 Class<?> c = null;
aoqi@0 254 try {
aoqi@0 255 c = Class.forName(cn, false, loader);
aoqi@0 256 } catch (ClassNotFoundException x) {
aoqi@0 257 fail(service,
aoqi@0 258 "Provider " + cn + " not found");
aoqi@0 259 }
aoqi@0 260 if (!service.isAssignableFrom(c)) {
aoqi@0 261 fail(service,
aoqi@0 262 "Provider " + cn + " not a subtype");
aoqi@0 263 }
aoqi@0 264 try {
aoqi@0 265 S p = service.cast(c.newInstance());
aoqi@0 266 providers.put(cn, p);
aoqi@0 267 return p;
aoqi@0 268 } catch (Throwable x) {
aoqi@0 269 fail(service,
aoqi@0 270 "Provider " + cn + " could not be instantiated: " + x,
aoqi@0 271 x);
aoqi@0 272 }
aoqi@0 273 throw new Error(); // This cannot happen
aoqi@0 274 }
aoqi@0 275
aoqi@0 276 public void remove() {
aoqi@0 277 throw new UnsupportedOperationException();
aoqi@0 278 }
aoqi@0 279
aoqi@0 280 }
aoqi@0 281
aoqi@0 282 /**
aoqi@0 283 * Lazily loads the available providers of this loader's service.
aoqi@0 284 *
aoqi@0 285 * <p> The iterator returned by this method first yields all of the
aoqi@0 286 * elements of the provider cache, in instantiation order. It then lazily
aoqi@0 287 * loads and instantiates any remaining providers, adding each one to the
aoqi@0 288 * cache in turn.
aoqi@0 289 *
aoqi@0 290 * <p> To achieve laziness the actual work of parsing the available
aoqi@0 291 * provider-configuration files and instantiating providers must be done by
aoqi@0 292 * the iterator itself. Its {@link java.util.Iterator#hasNext hasNext} and
aoqi@0 293 * {@link java.util.Iterator#next next} methods can therefore throw a
aoqi@0 294 * {@link ServiceConfigurationError} if a provider-configuration file
aoqi@0 295 * violates the specified format, or if it names a provider class that
aoqi@0 296 * cannot be found and instantiated, or if the result of instantiating the
aoqi@0 297 * class is not assignable to the service type, or if any other kind of
aoqi@0 298 * exception or error is thrown as the next provider is located and
aoqi@0 299 * instantiated. To write robust code it is only necessary to catch {@link
aoqi@0 300 * ServiceConfigurationError} when using a service iterator.
aoqi@0 301 *
aoqi@0 302 * <p> If such an error is thrown then subsequent invocations of the
aoqi@0 303 * iterator will make a best effort to locate and instantiate the next
aoqi@0 304 * available provider, but in general such recovery cannot be guaranteed.
aoqi@0 305 *
aoqi@0 306 * <blockquote style="font-size: smaller; line-height: 1.2"><span
aoqi@0 307 * style="padding-right: 1em; font-weight: bold">Design Note</span>
aoqi@0 308 * Throwing an error in these cases may seem extreme. The rationale for
aoqi@0 309 * this behavior is that a malformed provider-configuration file, like a
aoqi@0 310 * malformed class file, indicates a serious problem with the way the Java
aoqi@0 311 * virtual machine is configured or is being used. As such it is
aoqi@0 312 * preferable to throw an error rather than try to recover or, even worse,
aoqi@0 313 * fail silently.</blockquote>
aoqi@0 314 *
aoqi@0 315 * <p> The iterator returned by this method does not support removal.
aoqi@0 316 * Invoking its {@link java.util.Iterator#remove() remove} method will
aoqi@0 317 * cause an {@link UnsupportedOperationException} to be thrown.
aoqi@0 318 *
aoqi@0 319 * @return An iterator that lazily loads providers for this loader's
aoqi@0 320 * service
aoqi@0 321 */
aoqi@0 322 public Iterator<S> iterator() {
aoqi@0 323 return new Iterator<S>() {
aoqi@0 324
aoqi@0 325 Iterator<Map.Entry<String,S>> knownProviders
aoqi@0 326 = providers.entrySet().iterator();
aoqi@0 327
aoqi@0 328 public boolean hasNext() {
aoqi@0 329 if (knownProviders.hasNext())
aoqi@0 330 return true;
aoqi@0 331 return lookupIterator.hasNext();
aoqi@0 332 }
aoqi@0 333
aoqi@0 334 public S next() {
aoqi@0 335 if (knownProviders.hasNext())
aoqi@0 336 return knownProviders.next().getValue();
aoqi@0 337 return lookupIterator.next();
aoqi@0 338 }
aoqi@0 339
aoqi@0 340 public void remove() {
aoqi@0 341 throw new UnsupportedOperationException();
aoqi@0 342 }
aoqi@0 343
aoqi@0 344 };
aoqi@0 345 }
aoqi@0 346
aoqi@0 347 /**
aoqi@0 348 * Creates a new service loader for the given service type and class
aoqi@0 349 * loader.
aoqi@0 350 *
aoqi@0 351 * @param service
aoqi@0 352 * The interface or abstract class representing the service
aoqi@0 353 *
aoqi@0 354 * @param loader
aoqi@0 355 * The class loader to be used to load provider-configuration files
aoqi@0 356 * and provider classes, or <tt>null</tt> if the system class
aoqi@0 357 * loader (or, failing that, the bootstrap class loader) is to be
aoqi@0 358 * used
aoqi@0 359 *
aoqi@0 360 * @return A new service loader
aoqi@0 361 */
aoqi@0 362 public static <S> ServiceLoader<S> load(Class<S> service,
aoqi@0 363 ClassLoader loader)
aoqi@0 364 {
aoqi@0 365 return new ServiceLoader<>(service, loader);
aoqi@0 366 }
aoqi@0 367
aoqi@0 368 /**
aoqi@0 369 * Creates a new service loader for the given service type, using the
aoqi@0 370 * current thread's {@linkplain java.lang.Thread#getContextClassLoader
aoqi@0 371 * context class loader}.
aoqi@0 372 *
aoqi@0 373 * <p> An invocation of this convenience method of the form
aoqi@0 374 *
aoqi@0 375 * <blockquote><pre>
aoqi@0 376 * ServiceLoader.load(<i>service</i>)</pre></blockquote>
aoqi@0 377 *
aoqi@0 378 * is equivalent to
aoqi@0 379 *
aoqi@0 380 * <blockquote><pre>
aoqi@0 381 * ServiceLoader.load(<i>service</i>,
aoqi@0 382 * Thread.currentThread().getContextClassLoader())</pre></blockquote>
aoqi@0 383 *
aoqi@0 384 * @param service
aoqi@0 385 * The interface or abstract class representing the service
aoqi@0 386 *
aoqi@0 387 * @return A new service loader
aoqi@0 388 */
aoqi@0 389 public static <S> ServiceLoader<S> load(Class<S> service) {
aoqi@0 390 ClassLoader cl = Thread.currentThread().getContextClassLoader();
aoqi@0 391 return ServiceLoader.load(service, cl);
aoqi@0 392 }
aoqi@0 393
aoqi@0 394 /**
aoqi@0 395 * Creates a new service loader for the given service type, using the
aoqi@0 396 * extension class loader.
aoqi@0 397 *
aoqi@0 398 * <p> This convenience method simply locates the extension class loader,
aoqi@0 399 * call it <tt><i>extClassLoader</i></tt>, and then returns
aoqi@0 400 *
aoqi@0 401 * <blockquote><pre>
aoqi@0 402 * ServiceLoader.load(<i>service</i>, <i>extClassLoader</i>)</pre></blockquote>
aoqi@0 403 *
aoqi@0 404 * <p> If the extension class loader cannot be found then the system class
aoqi@0 405 * loader is used; if there is no system class loader then the bootstrap
aoqi@0 406 * class loader is used.
aoqi@0 407 *
aoqi@0 408 * <p> This method is intended for use when only installed providers are
aoqi@0 409 * desired. The resulting service will only find and load providers that
aoqi@0 410 * have been installed into the current Java virtual machine; providers on
aoqi@0 411 * the application's class path will be ignored.
aoqi@0 412 *
aoqi@0 413 * @param service
aoqi@0 414 * The interface or abstract class representing the service
aoqi@0 415 *
aoqi@0 416 * @return A new service loader
aoqi@0 417 */
aoqi@0 418 public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
aoqi@0 419 ClassLoader cl = ClassLoader.getSystemClassLoader();
aoqi@0 420 ClassLoader prev = null;
aoqi@0 421 while (cl != null) {
aoqi@0 422 prev = cl;
aoqi@0 423 cl = cl.getParent();
aoqi@0 424 }
aoqi@0 425 return ServiceLoader.load(service, prev);
aoqi@0 426 }
aoqi@0 427
aoqi@0 428 /**
aoqi@0 429 * Returns a string describing this service.
aoqi@0 430 *
aoqi@0 431 * @return A descriptive string
aoqi@0 432 */
aoqi@0 433 public String toString() {
aoqi@0 434 return "java.util.ServiceLoader[" + service.getName() + "]";
aoqi@0 435 }
aoqi@0 436
aoqi@0 437 }

mercurial