Save This Page
Home » openjdk-7 » sun » misc » [javadoc | source]
    1   /*
    2    * Copyright (c) 1999, 2003, 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    */
   25   
   26   package sun.misc;
   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   import java.util.Set;
   39   import java.util.TreeSet;
   40   
   41   
   42   /**
   43    * A simple service-provider lookup mechanism.  A <i>service</i> is a
   44    * well-known set of interfaces and (usually abstract) classes.  A <i>service
   45    * provider</i> is a specific implementation of a service.  The classes in a
   46    * provider typically implement the interfaces and subclass the classes defined
   47    * in the service itself.  Service providers may be installed in an
   48    * implementation of the Java platform in the form of extensions, that is, jar
   49    * files placed into any of the usual extension directories.  Providers may
   50    * also be made available by adding them to the applet or application class
   51    * path or by some other platform-specific means.
   52    *
   53    * <p> In this lookup mechanism a service is represented by an interface or an
   54    * abstract class.  (A concrete class may be used, but this is not
   55    * recommended.)  A provider of a given service contains one or more concrete
   56    * classes that extend this <i>service class</i> with data and code specific to
   57    * the provider.  This <i>provider class</i> will typically not be the entire
   58    * provider itself but rather a proxy that contains enough information to
   59    * decide whether the provider is able to satisfy a particular request together
   60    * with code that can create the actual provider on demand.  The details of
   61    * provider classes tend to be highly service-specific; no single class or
   62    * interface could possibly unify them, so no such class has been defined.  The
   63    * only requirement enforced here is that provider classes must have a
   64    * zero-argument constructor so that they may be instantiated during lookup.
   65    *
   66    * <p> A service provider identifies itself by placing a provider-configuration
   67    * file in the resource directory <tt>META-INF/services</tt>.  The file's name
   68    * should consist of the fully-qualified name of the abstract service class.
   69    * The file should contain a list of fully-qualified concrete provider-class
   70    * names, one per line.  Space and tab characters surrounding each name, as
   71    * well as blank lines, are ignored.  The comment character is <tt>'#'</tt>
   72    * (<tt>0x23</tt>); on each line all characters following the first comment
   73    * character are ignored.  The file must be encoded in UTF-8.
   74    *
   75    * <p> If a particular concrete provider class is named in more than one
   76    * configuration file, or is named in the same configuration file more than
   77    * once, then the duplicates will be ignored.  The configuration file naming a
   78    * particular provider need not be in the same jar file or other distribution
   79    * unit as the provider itself.  The provider must be accessible from the same
   80    * class loader that was initially queried to locate the configuration file;
   81    * note that this is not necessarily the class loader that found the file.
   82    *
   83    * <p> <b>Example:</b> Suppose we have a service class named
   84    * <tt>java.io.spi.CharCodec</tt>.  It has two abstract methods:
   85    *
   86    * <pre>
   87    *   public abstract CharEncoder getEncoder(String encodingName);
   88    *   public abstract CharDecoder getDecoder(String encodingName);
   89    * </pre>
   90    *
   91    * Each method returns an appropriate object or <tt>null</tt> if it cannot
   92    * translate the given encoding.  Typical <tt>CharCodec</tt> providers will
   93    * support more than one encoding.
   94    *
   95    * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt>
   96    * service then its jar file would contain the file
   97    * <tt>META-INF/services/java.io.spi.CharCodec</tt>.  This file would contain
   98    * the single line:
   99    *
  100    * <pre>
  101    *   sun.io.StandardCodec    # Standard codecs for the platform
  102    * </pre>
  103    *
  104    * To locate an encoder for a given encoding name, the internal I/O code would
  105    * do something like this:
  106    *
  107    * <pre>
  108    *   CharEncoder getEncoder(String encodingName) {
  109    *       Iterator ps = Service.providers(CharCodec.class);
  110    *       while (ps.hasNext()) {
  111    *           CharCodec cc = (CharCodec)ps.next();
  112    *           CharEncoder ce = cc.getEncoder(encodingName);
  113    *           if (ce != null)
  114    *               return ce;
  115    *       }
  116    *       return null;
  117    *   }
  118    * </pre>
  119    *
  120    * The provider-lookup mechanism always executes in the security context of the
  121    * caller.  Trusted system code should typically invoke the methods in this
  122    * class from within a privileged security context.
  123    *
  124    * @author Mark Reinhold
  125    * @since 1.3
  126    */
  127   
  128   public final class Service {
  129   
  130       private static final String prefix = "META-INF/services/";
  131   
  132       private Service() { }
  133   
  134       private static void fail(Class service, String msg, Throwable cause)
  135           throws ServiceConfigurationError
  136       {
  137           ServiceConfigurationError sce
  138               = new ServiceConfigurationError(service.getName() + ": " + msg);
  139           sce.initCause(cause);
  140           throw sce;
  141       }
  142   
  143       private static void fail(Class service, String msg)
  144           throws ServiceConfigurationError
  145       {
  146           throw new ServiceConfigurationError(service.getName() + ": " + msg);
  147       }
  148   
  149       private static void fail(Class service, URL u, int line, String msg)
  150           throws ServiceConfigurationError
  151       {
  152           fail(service, u + ":" + line + ": " + msg);
  153       }
  154   
  155       /**
  156        * Parse a single line from the given configuration file, adding the name
  157        * on the line to both the names list and the returned set iff the name is
  158        * not already a member of the returned set.
  159        */
  160       private static int parseLine(Class service, URL u, BufferedReader r, int lc,
  161                                    List names, Set returned)
  162           throws IOException, ServiceConfigurationError
  163       {
  164           String ln = r.readLine();
  165           if (ln == null) {
  166               return -1;
  167           }
  168           int ci = ln.indexOf('#');
  169           if (ci >= 0) ln = ln.substring(0, ci);
  170           ln = ln.trim();
  171           int n = ln.length();
  172           if (n != 0) {
  173               if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
  174                   fail(service, u, lc, "Illegal configuration-file syntax");
  175               int cp = ln.codePointAt(0);
  176               if (!Character.isJavaIdentifierStart(cp))
  177                   fail(service, u, lc, "Illegal provider-class name: " + ln);
  178               for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
  179                   cp = ln.codePointAt(i);
  180                   if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
  181                       fail(service, u, lc, "Illegal provider-class name: " + ln);
  182               }
  183               if (!returned.contains(ln)) {
  184                   names.add(ln);
  185                   returned.add(ln);
  186               }
  187           }
  188           return lc + 1;
  189       }
  190   
  191       /**
  192        * Parse the content of the given URL as a provider-configuration file.
  193        *
  194        * @param  service
  195        *         The service class for which providers are being sought;
  196        *         used to construct error detail strings
  197        *
  198        * @param  url
  199        *         The URL naming the configuration file to be parsed
  200        *
  201        * @param  returned
  202        *         A Set containing the names of provider classes that have already
  203        *         been returned.  This set will be updated to contain the names
  204        *         that will be yielded from the returned <tt>Iterator</tt>.
  205        *
  206        * @return A (possibly empty) <tt>Iterator</tt> that will yield the
  207        *         provider-class names in the given configuration file that are
  208        *         not yet members of the returned set
  209        *
  210        * @throws ServiceConfigurationError
  211        *         If an I/O error occurs while reading from the given URL, or
  212        *         if a configuration-file format error is detected
  213        */
  214       private static Iterator parse(Class service, URL u, Set returned)
  215           throws ServiceConfigurationError
  216       {
  217           InputStream in = null;
  218           BufferedReader r = null;
  219           ArrayList names = new ArrayList();
  220           try {
  221               in = u.openStream();
  222               r = new BufferedReader(new InputStreamReader(in, "utf-8"));
  223               int lc = 1;
  224               while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0);
  225           } catch (IOException x) {
  226               fail(service, ": " + x);
  227           } finally {
  228               try {
  229                   if (r != null) r.close();
  230                   if (in != null) in.close();
  231               } catch (IOException y) {
  232                   fail(service, ": " + y);
  233               }
  234           }
  235           return names.iterator();
  236       }
  237   
  238   
  239       /**
  240        * Private inner class implementing fully-lazy provider lookup
  241        */
  242       private static class LazyIterator implements Iterator {
  243   
  244           Class service;
  245           ClassLoader loader;
  246           Enumeration configs = null;
  247           Iterator pending = null;
  248           Set returned = new TreeSet();
  249           String nextName = null;
  250   
  251           private LazyIterator(Class service, ClassLoader loader) {
  252               this.service = service;
  253               this.loader = loader;
  254           }
  255   
  256           public boolean hasNext() throws ServiceConfigurationError {
  257               if (nextName != null) {
  258                   return true;
  259               }
  260               if (configs == null) {
  261                   try {
  262                       String fullName = prefix + service.getName();
  263                       if (loader == null)
  264                           configs = ClassLoader.getSystemResources(fullName);
  265                       else
  266                           configs = loader.getResources(fullName);
  267                   } catch (IOException x) {
  268                       fail(service, ": " + x);
  269                   }
  270               }
  271               while ((pending == null) || !pending.hasNext()) {
  272                   if (!configs.hasMoreElements()) {
  273                       return false;
  274                   }
  275                   pending = parse(service, (URL)configs.nextElement(), returned);
  276               }
  277               nextName = (String)pending.next();
  278               return true;
  279           }
  280   
  281           public Object next() throws ServiceConfigurationError {
  282               if (!hasNext()) {
  283                   throw new NoSuchElementException();
  284               }
  285               String cn = nextName;
  286               nextName = null;
  287               try {
  288                   return Class.forName(cn, true, loader).newInstance();
  289               } catch (ClassNotFoundException x) {
  290                   fail(service,
  291                        "Provider " + cn + " not found");
  292               } catch (Exception x) {
  293                   fail(service,
  294                        "Provider " + cn + " could not be instantiated: " + x,
  295                        x);
  296               }
  297               return null;        /* This cannot happen */
  298           }
  299   
  300           public void remove() {
  301               throw new UnsupportedOperationException();
  302           }
  303   
  304       }
  305   
  306   
  307       /**
  308        * Locates and incrementally instantiates the available providers of a
  309        * given service using the given class loader.
  310        *
  311        * <p> This method transforms the name of the given service class into a
  312        * provider-configuration filename as described above and then uses the
  313        * <tt>getResources</tt> method of the given class loader to find all
  314        * available files with that name.  These files are then read and parsed to
  315        * produce a list of provider-class names.  The iterator that is returned
  316        * uses the given class loader to lookup and then instantiate each element
  317        * of the list.
  318        *
  319        * <p> Because it is possible for extensions to be installed into a running
  320        * Java virtual machine, this method may return different results each time
  321        * it is invoked. <p>
  322        *
  323        * @param  service
  324        *         The service's abstract service class
  325        *
  326        * @param  loader
  327        *         The class loader to be used to load provider-configuration files
  328        *         and instantiate provider classes, or <tt>null</tt> if the system
  329        *         class loader (or, failing that the bootstrap class loader) is to
  330        *         be used
  331        *
  332        * @return An <tt>Iterator</tt> that yields provider objects for the given
  333        *         service, in some arbitrary order.  The iterator will throw a
  334        *         <tt>ServiceConfigurationError</tt> if a provider-configuration
  335        *         file violates the specified format or if a provider class cannot
  336        *         be found and instantiated.
  337        *
  338        * @throws ServiceConfigurationError
  339        *         If a provider-configuration file violates the specified format
  340        *         or names a provider class that cannot be found and instantiated
  341        *
  342        * @see #providers(java.lang.Class)
  343        * @see #installedProviders(java.lang.Class)
  344        */
  345       public static Iterator providers(Class service, ClassLoader loader)
  346           throws ServiceConfigurationError
  347       {
  348           return new LazyIterator(service, loader);
  349       }
  350   
  351   
  352       /**
  353        * Locates and incrementally instantiates the available providers of a
  354        * given service using the context class loader.  This convenience method
  355        * is equivalent to
  356        *
  357        * <pre>
  358        *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
  359        *   return Service.providers(service, cl);
  360        * </pre>
  361        *
  362        * @param  service
  363        *         The service's abstract service class
  364        *
  365        * @return An <tt>Iterator</tt> that yields provider objects for the given
  366        *         service, in some arbitrary order.  The iterator will throw a
  367        *         <tt>ServiceConfigurationError</tt> if a provider-configuration
  368        *         file violates the specified format or if a provider class cannot
  369        *         be found and instantiated.
  370        *
  371        * @throws ServiceConfigurationError
  372        *         If a provider-configuration file violates the specified format
  373        *         or names a provider class that cannot be found and instantiated
  374        *
  375        * @see #providers(java.lang.Class, java.lang.ClassLoader)
  376        */
  377       public static Iterator providers(Class service)
  378           throws ServiceConfigurationError
  379       {
  380           ClassLoader cl = Thread.currentThread().getContextClassLoader();
  381           return Service.providers(service, cl);
  382       }
  383   
  384   
  385       /**
  386        * Locates and incrementally instantiates the available providers of a
  387        * given service using the extension class loader.  This convenience method
  388        * simply locates the extension class loader, call it
  389        * <tt>extClassLoader</tt>, and then does
  390        *
  391        * <pre>
  392        *   return Service.providers(service, extClassLoader);
  393        * </pre>
  394        *
  395        * If the extension class loader cannot be found then the system class
  396        * loader is used; if there is no system class loader then the bootstrap
  397        * class loader is used.
  398        *
  399        * @param  service
  400        *         The service's abstract service class
  401        *
  402        * @return An <tt>Iterator</tt> that yields provider objects for the given
  403        *         service, in some arbitrary order.  The iterator will throw a
  404        *         <tt>ServiceConfigurationError</tt> if a provider-configuration
  405        *         file violates the specified format or if a provider class cannot
  406        *         be found and instantiated.
  407        *
  408        * @throws ServiceConfigurationError
  409        *         If a provider-configuration file violates the specified format
  410        *         or names a provider class that cannot be found and instantiated
  411        *
  412        * @see #providers(java.lang.Class, java.lang.ClassLoader)
  413        */
  414       public static Iterator installedProviders(Class service)
  415           throws ServiceConfigurationError
  416       {
  417           ClassLoader cl = ClassLoader.getSystemClassLoader();
  418           ClassLoader prev = null;
  419           while (cl != null) {
  420               prev = cl;
  421               cl = cl.getParent();
  422           }
  423           return Service.providers(service, prev);
  424       }
  425   
  426   }

Save This Page
Home » openjdk-7 » sun » misc » [javadoc | source]