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

Tue, 07 May 2013 14:27:30 -0700

author
jjg
date
Tue, 07 May 2013 14:27:30 -0700
changeset 1728
43c2f7cb9c76
parent 0
959103a6100f
permissions
-rw-r--r--

8004082: test/tools/javac/plugin/showtype/Test.java fails on windows: jtreg can't delete plugin.jar
Reviewed-by: vromero, mcimadamore

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

mercurial