Wed, 16 Oct 2013 16:33:04 -0400
8026286: Improper locking of annotation queues causes assertion failures.
8026063: Calls to annotate.flush() cause incorrect type annotations to be generated.
Summary: Fix locking in ClassReader.java
Reviewed-by: jfranck
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 | } |