jdk/src/share/classes/java/util/ServiceLoader.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2005-2006 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package java.util;
       
    27 
       
    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.util.ArrayList;
       
    34 import java.util.Enumeration;
       
    35 import java.util.Iterator;
       
    36 import java.util.List;
       
    37 import java.util.NoSuchElementException;
       
    38 
       
    39 
       
    40 /**
       
    41  * A simple service-provider loading facility.
       
    42  *
       
    43  * <p> A <i>service</i> is a well-known set of interfaces and (usually
       
    44  * abstract) classes.  A <i>service provider</i> is a specific implementation
       
    45  * of a service.  The classes in a provider typically implement the interfaces
       
    46  * and subclass the classes defined in the service itself.  Service providers
       
    47  * can be installed in an implementation of the Java platform in the form of
       
    48  * extensions, that is, jar files placed into any of the usual extension
       
    49  * directories.  Providers can also be made available by adding them to the
       
    50  * application's class path or by some other platform-specific means.
       
    51  *
       
    52  * <p> For the purpose of loading, a service is represented by a single type,
       
    53  * that is, a single interface or abstract class.  (A concrete class can be
       
    54  * used, but this is not recommended.)  A provider of a given service contains
       
    55  * one or more concrete classes that extend this <i>service type</i> with data
       
    56  * and code specific to the provider.  The <i>provider class</i> is typically
       
    57  * not the entire provider itself but rather a proxy which contains enough
       
    58  * information to decide whether the provider is able to satisfy a particular
       
    59  * request together with code that can create the actual provider on demand.
       
    60  * The details of provider classes tend to be highly service-specific; no
       
    61  * single class or interface could possibly unify them, so no such type is
       
    62  * defined here.  The only requirement enforced by this facility is that
       
    63  * provider classes must have a zero-argument constructor so that they can be
       
    64  * instantiated during loading.
       
    65  *
       
    66  * <p><a name="format"> A service provider is identified by placing a
       
    67  * <i>provider-configuration file</i> in the resource directory
       
    68  * <tt>META-INF/services</tt>.  The file's name is the fully-qualified <a
       
    69  * href="../lang/ClassLoader.html#name">binary name</a> of the service's type.
       
    70  * The file contains a list of fully-qualified binary names of concrete
       
    71  * provider classes, one per line.  Space and tab characters surrounding each
       
    72  * name, as well as blank lines, are ignored.  The comment character is
       
    73  * <tt>'#'</tt> (<tt>'&#92;u0023'</tt>, <font size="-1">NUMBER SIGN</font>); on
       
    74  * each line all characters following the first comment character are ignored.
       
    75  * The file must be encoded in UTF-8.
       
    76  *
       
    77  * <p> If a particular concrete provider class is named in more than one
       
    78  * configuration file, or is named in the same configuration file more than
       
    79  * once, then the duplicates are ignored.  The configuration file naming a
       
    80  * particular provider need not be in the same jar file or other distribution
       
    81  * unit as the provider itself.  The provider must be accessible from the same
       
    82  * class loader that was initially queried to locate the configuration file;
       
    83  * note that this is not necessarily the class loader from which the file was
       
    84  * actually loaded.
       
    85  *
       
    86  * <p> Providers are located and instantiated lazily, that is, on demand.  A
       
    87  * service loader maintains a cache of the providers that have been loaded so
       
    88  * far.  Each invocation of the {@link #iterator iterator} method returns an
       
    89  * iterator that first yields all of the elements of the cache, in
       
    90  * instantiation order, and then lazily locates and instantiates any remaining
       
    91  * providers, adding each one to the cache in turn.  The cache can be cleared
       
    92  * via the {@link #reload reload} method.
       
    93  *
       
    94  * <p> Service loaders always execute in the security context of the caller.
       
    95  * Trusted system code should typically invoke the methods in this class, and
       
    96  * the methods of the iterators which they return, from within a privileged
       
    97  * security context.
       
    98  *
       
    99  * <p> Instances of this class are not safe for use by multiple concurrent
       
   100  * threads.
       
   101  *
       
   102  * <p> Unless otherwise specified, passing a <tt>null</tt> argument to any
       
   103  * method in this class will cause a {@link NullPointerException} to be thrown.
       
   104  *
       
   105  *
       
   106  * <p><span style="font-weight: bold; padding-right: 1em">Example</span>
       
   107  * Suppose we have a service type <tt>com.example.CodecSet</tt> which is
       
   108  * intended to represent sets of encoder/decoder pairs for some protocol.  In
       
   109  * this case it is an abstract class with two abstract methods:
       
   110  *
       
   111  * <blockquote><pre>
       
   112  * public abstract Encoder getEncoder(String encodingName);
       
   113  * public abstract Decoder getDecoder(String encodingName);</pre></blockquote>
       
   114  *
       
   115  * Each method returns an appropriate object or <tt>null</tt> if the provider
       
   116  * does not support the given encoding.  Typical providers support more than
       
   117  * one encoding.
       
   118  *
       
   119  * <p> If <tt>com.example.impl.StandardCodecs</tt> is an implementation of the
       
   120  * <tt>CodecSet</tt> service then its jar file also contains a file named
       
   121  *
       
   122  * <blockquote><pre>
       
   123  * META-INF/services/com.example.CodecSet</pre></blockquote>
       
   124  *
       
   125  * <p> This file contains the single line:
       
   126  *
       
   127  * <blockquote><pre>
       
   128  * com.example.impl.StandardCodecs    # Standard codecs</pre></blockquote>
       
   129  *
       
   130  * <p> The <tt>CodecSet</tt> class creates and saves a single service instance
       
   131  * at initialization:
       
   132  *
       
   133  * <blockquote><pre>
       
   134  * private static ServiceLoader&lt;CodecSet&gt; codecSetLoader
       
   135  *     = ServiceLoader.load(CodecSet.class);</pre></blockquote>
       
   136  *
       
   137  * <p> To locate an encoder for a given encoding name it defines a static
       
   138  * factory method which iterates through the known and available providers,
       
   139  * returning only when it has located a suitable encoder or has run out of
       
   140  * providers.
       
   141  *
       
   142  * <blockquote><pre>
       
   143  * public static Encoder getEncoder(String encodingName) {
       
   144  *     for (CodecSet cp : codecSetLoader) {
       
   145  *         Encoder enc = cp.getEncoder(encodingName);
       
   146  *         if (enc != null)
       
   147  *             return enc;
       
   148  *     }
       
   149  *     return null;
       
   150  * }</pre></blockquote>
       
   151  *
       
   152  * <p> A <tt>getDecoder</tt> method is defined similarly.
       
   153  *
       
   154  *
       
   155  * <p><span style="font-weight: bold; padding-right: 1em">Usage Note</span> If
       
   156  * the class path of a class loader that is used for provider loading includes
       
   157  * remote network URLs then those URLs will be dereferenced in the process of
       
   158  * searching for provider-configuration files.
       
   159  *
       
   160  * <p> This activity is normal, although it may cause puzzling entries to be
       
   161  * created in web-server logs.  If a web server is not configured correctly,
       
   162  * however, then this activity may cause the provider-loading algorithm to fail
       
   163  * spuriously.
       
   164  *
       
   165  * <p> A web server should return an HTTP 404 (Not Found) response when a
       
   166  * requested resource does not exist.  Sometimes, however, web servers are
       
   167  * erroneously configured to return an HTTP 200 (OK) response along with a
       
   168  * helpful HTML error page in such cases.  This will cause a {@link
       
   169  * ServiceConfigurationError} to be thrown when this class attempts to parse
       
   170  * the HTML page as a provider-configuration file.  The best solution to this
       
   171  * problem is to fix the misconfigured web server to return the correct
       
   172  * response code (HTTP 404) along with the HTML error page.
       
   173  *
       
   174  * @param  <S>
       
   175  *         The type of the service to be loaded by this loader
       
   176  *
       
   177  * @author Mark Reinhold
       
   178  * @since 1.6
       
   179  */
       
   180 
       
   181 public final class ServiceLoader<S>
       
   182     implements Iterable<S>
       
   183 {
       
   184 
       
   185     private static final String PREFIX = "META-INF/services/";
       
   186 
       
   187     // The class or interface representing the service being loaded
       
   188     private Class<S> service;
       
   189 
       
   190     // The class loader used to locate, load, and instantiate providers
       
   191     private ClassLoader loader;
       
   192 
       
   193     // Cached providers, in instantiation order
       
   194     private LinkedHashMap<String,S> providers = new LinkedHashMap<String,S>();
       
   195 
       
   196     // The current lazy-lookup iterator
       
   197     private LazyIterator lookupIterator;
       
   198 
       
   199     /**
       
   200      * Clear this loader's provider cache so that all providers will be
       
   201      * reloaded.
       
   202      *
       
   203      * <p> After invoking this method, subsequent invocations of the {@link
       
   204      * #iterator() iterator} method will lazily look up and instantiate
       
   205      * providers from scratch, just as is done by a newly-created loader.
       
   206      *
       
   207      * <p> This method is intended for use in situations in which new providers
       
   208      * can be installed into a running Java virtual machine.
       
   209      */
       
   210     public void reload() {
       
   211         providers.clear();
       
   212         lookupIterator = new LazyIterator(service, loader);
       
   213     }
       
   214 
       
   215     private ServiceLoader(Class<S> svc, ClassLoader cl) {
       
   216         service = svc;
       
   217         loader = cl;
       
   218         reload();
       
   219     }
       
   220 
       
   221     private static void fail(Class service, String msg, Throwable cause)
       
   222         throws ServiceConfigurationError
       
   223     {
       
   224         throw new ServiceConfigurationError(service.getName() + ": " + msg,
       
   225                                             cause);
       
   226     }
       
   227 
       
   228     private static void fail(Class service, String msg)
       
   229         throws ServiceConfigurationError
       
   230     {
       
   231         throw new ServiceConfigurationError(service.getName() + ": " + msg);
       
   232     }
       
   233 
       
   234     private static void fail(Class service, URL u, int line, String msg)
       
   235         throws ServiceConfigurationError
       
   236     {
       
   237         fail(service, u + ":" + line + ": " + msg);
       
   238     }
       
   239 
       
   240     // Parse a single line from the given configuration file, adding the name
       
   241     // on the line to the names list.
       
   242     //
       
   243     private int parseLine(Class service, URL u, BufferedReader r, int lc,
       
   244                           List<String> names)
       
   245         throws IOException, ServiceConfigurationError
       
   246     {
       
   247         String ln = r.readLine();
       
   248         if (ln == null) {
       
   249             return -1;
       
   250         }
       
   251         int ci = ln.indexOf('#');
       
   252         if (ci >= 0) ln = ln.substring(0, ci);
       
   253         ln = ln.trim();
       
   254         int n = ln.length();
       
   255         if (n != 0) {
       
   256             if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
       
   257                 fail(service, u, lc, "Illegal configuration-file syntax");
       
   258             int cp = ln.codePointAt(0);
       
   259             if (!Character.isJavaIdentifierStart(cp))
       
   260                 fail(service, u, lc, "Illegal provider-class name: " + ln);
       
   261             for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
       
   262                 cp = ln.codePointAt(i);
       
   263                 if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
       
   264                     fail(service, u, lc, "Illegal provider-class name: " + ln);
       
   265             }
       
   266             if (!providers.containsKey(ln) && !names.contains(ln))
       
   267                 names.add(ln);
       
   268         }
       
   269         return lc + 1;
       
   270     }
       
   271 
       
   272     // Parse the content of the given URL as a provider-configuration file.
       
   273     //
       
   274     // @param  service
       
   275     //         The service type for which providers are being sought;
       
   276     //         used to construct error detail strings
       
   277     //
       
   278     // @param  u
       
   279     //         The URL naming the configuration file to be parsed
       
   280     //
       
   281     // @return A (possibly empty) iterator that will yield the provider-class
       
   282     //         names in the given configuration file that are not yet members
       
   283     //         of the returned set
       
   284     //
       
   285     // @throws ServiceConfigurationError
       
   286     //         If an I/O error occurs while reading from the given URL, or
       
   287     //         if a configuration-file format error is detected
       
   288     //
       
   289     private Iterator<String> parse(Class service, URL u)
       
   290         throws ServiceConfigurationError
       
   291     {
       
   292         InputStream in = null;
       
   293         BufferedReader r = null;
       
   294         ArrayList<String> names = new ArrayList<String>();
       
   295         try {
       
   296             in = u.openStream();
       
   297             r = new BufferedReader(new InputStreamReader(in, "utf-8"));
       
   298             int lc = 1;
       
   299             while ((lc = parseLine(service, u, r, lc, names)) >= 0);
       
   300         } catch (IOException x) {
       
   301             fail(service, "Error reading configuration file", x);
       
   302         } finally {
       
   303             try {
       
   304                 if (r != null) r.close();
       
   305                 if (in != null) in.close();
       
   306             } catch (IOException y) {
       
   307                 fail(service, "Error closing configuration file", y);
       
   308             }
       
   309         }
       
   310         return names.iterator();
       
   311     }
       
   312 
       
   313     // Private inner class implementing fully-lazy provider lookup
       
   314     //
       
   315     private class LazyIterator
       
   316         implements Iterator<S>
       
   317     {
       
   318 
       
   319         Class<S> service;
       
   320         ClassLoader loader;
       
   321         Enumeration<URL> configs = null;
       
   322         Iterator<String> pending = null;
       
   323         String nextName = null;
       
   324 
       
   325         private LazyIterator(Class<S> service, ClassLoader loader) {
       
   326             this.service = service;
       
   327             this.loader = loader;
       
   328         }
       
   329 
       
   330         public boolean hasNext() {
       
   331             if (nextName != null) {
       
   332                 return true;
       
   333             }
       
   334             if (configs == null) {
       
   335                 try {
       
   336                     String fullName = PREFIX + service.getName();
       
   337                     if (loader == null)
       
   338                         configs = ClassLoader.getSystemResources(fullName);
       
   339                     else
       
   340                         configs = loader.getResources(fullName);
       
   341                 } catch (IOException x) {
       
   342                     fail(service, "Error locating configuration files", x);
       
   343                 }
       
   344             }
       
   345             while ((pending == null) || !pending.hasNext()) {
       
   346                 if (!configs.hasMoreElements()) {
       
   347                     return false;
       
   348                 }
       
   349                 pending = parse(service, configs.nextElement());
       
   350             }
       
   351             nextName = pending.next();
       
   352             return true;
       
   353         }
       
   354 
       
   355         public S next() {
       
   356             if (!hasNext()) {
       
   357                 throw new NoSuchElementException();
       
   358             }
       
   359             String cn = nextName;
       
   360             nextName = null;
       
   361             try {
       
   362                 S p = service.cast(Class.forName(cn, true, loader)
       
   363                                    .newInstance());
       
   364                 providers.put(cn, p);
       
   365                 return p;
       
   366             } catch (ClassNotFoundException x) {
       
   367                 fail(service,
       
   368                      "Provider " + cn + " not found");
       
   369             } catch (Throwable x) {
       
   370                 fail(service,
       
   371                      "Provider " + cn + " could not be instantiated: " + x,
       
   372                      x);
       
   373             }
       
   374             throw new Error();          // This cannot happen
       
   375         }
       
   376 
       
   377         public void remove() {
       
   378             throw new UnsupportedOperationException();
       
   379         }
       
   380 
       
   381     }
       
   382 
       
   383     /**
       
   384      * Lazily loads the available providers of this loader's service.
       
   385      *
       
   386      * <p> The iterator returned by this method first yields all of the
       
   387      * elements of the provider cache, in instantiation order.  It then lazily
       
   388      * loads and instantiates any remaining providers, adding each one to the
       
   389      * cache in turn.
       
   390      *
       
   391      * <p> To achieve laziness the actual work of parsing the available
       
   392      * provider-configuration files and instantiating providers must be done by
       
   393      * the iterator itself.  Its {@link java.util.Iterator#hasNext hasNext} and
       
   394      * {@link java.util.Iterator#next next} methods can therefore throw a
       
   395      * {@link ServiceConfigurationError} if a provider-configuration file
       
   396      * violates the specified format, or if it names a provider class that
       
   397      * cannot be found and instantiated, or if the result of instantiating the
       
   398      * class is not assignable to the service type, or if any other kind of
       
   399      * exception or error is thrown as the next provider is located and
       
   400      * instantiated.  To write robust code it is only necessary to catch {@link
       
   401      * ServiceConfigurationError} when using a service iterator.
       
   402      *
       
   403      * <p> If such an error is thrown then subsequent invocations of the
       
   404      * iterator will make a best effort to locate and instantiate the next
       
   405      * available provider, but in general such recovery cannot be guaranteed.
       
   406      *
       
   407      * <blockquote style="font-size: smaller; line-height: 1.2"><span
       
   408      * style="padding-right: 1em; font-weight: bold">Design Note</span>
       
   409      * Throwing an error in these cases may seem extreme.  The rationale for
       
   410      * this behavior is that a malformed provider-configuration file, like a
       
   411      * malformed class file, indicates a serious problem with the way the Java
       
   412      * virtual machine is configured or is being used.  As such it is
       
   413      * preferable to throw an error rather than try to recover or, even worse,
       
   414      * fail silently.</blockquote>
       
   415      *
       
   416      * <p> The iterator returned by this method does not support removal.
       
   417      * Invoking its {@link java.util.Iterator#remove() remove} method will
       
   418      * cause an {@link UnsupportedOperationException} to be thrown.
       
   419      *
       
   420      * @return  An iterator that lazily loads providers for this loader's
       
   421      *          service
       
   422      */
       
   423     public Iterator<S> iterator() {
       
   424         return new Iterator<S>() {
       
   425 
       
   426             Iterator<Map.Entry<String,S>> knownProviders
       
   427                 = providers.entrySet().iterator();
       
   428 
       
   429             public boolean hasNext() {
       
   430                 if (knownProviders.hasNext())
       
   431                     return true;
       
   432                 return lookupIterator.hasNext();
       
   433             }
       
   434 
       
   435             public S next() {
       
   436                 if (knownProviders.hasNext())
       
   437                     return knownProviders.next().getValue();
       
   438                 return lookupIterator.next();
       
   439             }
       
   440 
       
   441             public void remove() {
       
   442                 throw new UnsupportedOperationException();
       
   443             }
       
   444 
       
   445         };
       
   446     }
       
   447 
       
   448     /**
       
   449      * Creates a new service loader for the given service type and class
       
   450      * loader.
       
   451      *
       
   452      * @param  service
       
   453      *         The interface or abstract class representing the service
       
   454      *
       
   455      * @param  loader
       
   456      *         The class loader to be used to load provider-configuration files
       
   457      *         and provider classes, or <tt>null</tt> if the system class
       
   458      *         loader (or, failing that, the bootstrap class loader) is to be
       
   459      *         used
       
   460      *
       
   461      * @return A new service loader
       
   462      */
       
   463     public static <S> ServiceLoader<S> load(Class<S> service,
       
   464                                             ClassLoader loader)
       
   465     {
       
   466         return new ServiceLoader<S>(service, loader);
       
   467     }
       
   468 
       
   469     /**
       
   470      * Creates a new service loader for the given service type, using the
       
   471      * current thread's {@linkplain java.lang.Thread#getContextClassLoader
       
   472      * context class loader}.
       
   473      *
       
   474      * <p> An invocation of this convenience method of the form
       
   475      *
       
   476      * <blockquote><pre>
       
   477      * ServiceLoader.load(<i>service</i>)</pre></blockquote>
       
   478      *
       
   479      * is equivalent to
       
   480      *
       
   481      * <blockquote><pre>
       
   482      * ServiceLoader.load(<i>service</i>,
       
   483      *                    Thread.currentThread().getContextClassLoader())</pre></blockquote>
       
   484      *
       
   485      * @param  service
       
   486      *         The interface or abstract class representing the service
       
   487      *
       
   488      * @return A new service loader
       
   489      */
       
   490     public static <S> ServiceLoader<S> load(Class<S> service) {
       
   491         ClassLoader cl = Thread.currentThread().getContextClassLoader();
       
   492         return ServiceLoader.load(service, cl);
       
   493     }
       
   494 
       
   495     /**
       
   496      * Creates a new service loader for the given service type, using the
       
   497      * extension class loader.
       
   498      *
       
   499      * <p> This convenience method simply locates the extension class loader,
       
   500      * call it <tt><i>extClassLoader</i></tt>, and then returns
       
   501      *
       
   502      * <blockquote><pre>
       
   503      * ServiceLoader.load(<i>service</i>, <i>extClassLoader</i>)</pre></blockquote>
       
   504      *
       
   505      * <p> If the extension class loader cannot be found then the system class
       
   506      * loader is used; if there is no system class loader then the bootstrap
       
   507      * class loader is used.
       
   508      *
       
   509      * <p> This method is intended for use when only installed providers are
       
   510      * desired.  The resulting service will only find and load providers that
       
   511      * have been installed into the current Java virtual machine; providers on
       
   512      * the application's class path will be ignored.
       
   513      *
       
   514      * @param  service
       
   515      *         The interface or abstract class representing the service
       
   516      *
       
   517      * @return A new service loader
       
   518      */
       
   519     public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
       
   520         ClassLoader cl = ClassLoader.getSystemClassLoader();
       
   521         ClassLoader prev = null;
       
   522         while (cl != null) {
       
   523             prev = cl;
       
   524             cl = cl.getParent();
       
   525         }
       
   526         return ServiceLoader.load(service, prev);
       
   527     }
       
   528 
       
   529     /**
       
   530      * Returns a string describing this service.
       
   531      *
       
   532      * @return  A descriptive string
       
   533      */
       
   534     public String toString() {
       
   535         return "java.util.ServiceLoader[" + service.getName() + "]";
       
   536     }
       
   537 
       
   538 }