Source for gnu.classpath.ServiceFactory

   1: /* ServiceFactory.java -- Factory for plug-in services.
   2:    Copyright (C) 2004  Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.classpath;
  40: 
  41: import java.io.BufferedReader;
  42: import java.io.IOException;
  43: import java.io.InputStreamReader;
  44: import java.net.URL;
  45: import java.security.AccessControlContext;
  46: import java.security.AccessController;
  47: import java.security.PrivilegedActionException;
  48: import java.util.Collections;
  49: import java.util.Enumeration;
  50: import java.util.Iterator;
  51: import java.util.List;
  52: import java.util.NoSuchElementException;
  53: import java.util.ServiceConfigurationError;
  54: import java.util.logging.Level;
  55: import java.util.logging.LogRecord;
  56: import java.util.logging.Logger;
  57: 
  58: 
  59: /**
  60:  * A factory for plug-ins that conform to a service provider
  61:  * interface. This is a general mechanism that gets used by a number
  62:  * of packages in the Java API. For instance, {@link
  63:  * java.nio.charset.spi.CharsetProvider} allows to write custom
  64:  * encoders and decoders for character sets, {@link
  65:  * javax.imageio.spi.ImageReaderSpi} allows to support custom image
  66:  * formats, and {@link javax.print.PrintService} makes it possible to
  67:  * write custom printer drivers.
  68:  *
  69:  * <p>The plug-ins are concrete implementations of the service
  70:  * provider interface, which is defined as an interface or an abstract
  71:  * class. The implementation classes must be public and have a public
  72:  * constructor that takes no arguments.
  73:  *
  74:  * <p>Plug-ins are usually deployed in JAR files. A JAR that provides
  75:  * an implementation of a service must declare this in a resource file
  76:  * whose name is the fully qualified service name and whose location
  77:  * is the directory <code>META-INF/services</code>. This UTF-8 encoded
  78:  * text file lists, on separate lines, the fully qualified names of
  79:  * the concrete implementations. Thus, one JAR file can provide an
  80:  * arbitrary number of implementations for an arbitrary count of
  81:  * service provider interfaces.
  82:  *
  83:  * <p><b>Example</b>
  84:  *
  85:  * <p>For example, a JAR might provide two implementations of the
  86:  * service provider interface <code>org.foo.ThinkService</code>,
  87:  * namely <code>com.acme.QuickThinker</code> and
  88:  * <code>com.acme.DeepThinker</code>. The code for <code>QuickThinker</code>
  89:  * woud look as follows:
  90:  *
  91:  * <pre>
  92:  * package com.acme;
  93:  *
  94:  * &#x2f;**
  95:  * * Provices a super-quick, but not very deep implementation of ThinkService.
  96:  * *&#x2f;
  97:  * public class QuickThinker
  98:  *   implements org.foo.ThinkService
  99:  * {
 100:  *   &#x2f;**
 101:  *   * Constructs a new QuickThinker. The service factory (which is
 102:  *   * part of the Java environment) calls this no-argument constructor
 103:  *   * when it looks up the available implementations of ThinkService.
 104:  *   *
 105:  *   * &lt;p&gt;Note that an application might query all available
 106:  *   * ThinkService providers, but use just one of them. Therefore,
 107:  *   * constructing an instance should be very inexpensive. For example,
 108:  *   * large data structures should only be allocated when the service
 109:  *   * actually gets used.
 110:  *   *&#x2f;
 111:  *   public QuickThinker()
 112:  *   {
 113:  *   }
 114:  *
 115:  *   &#x2f;**
 116:  *   * Returns the speed of this ThinkService in thoughts per second.
 117:  *   * Applications can choose among the available service providers
 118:  *   * based on this value.
 119:  *   *&#x2f;
 120:  *   public double getSpeed()
 121:  *   {
 122:  *     return 314159.2654;
 123:  *   }
 124:  *
 125:  *   &#x2f;**
 126:  *   * Produces a thought. While the returned thoughts are not very
 127:  *   * deep, they are generated in very short time.
 128:  *   *&#x2f;
 129:  *   public Thought think()
 130:  *   {
 131:  *     return null;
 132:  *   }
 133:  * }
 134:  * </pre>
 135:  *
 136:  * <p>The code for <code>com.acme.DeepThinker</code> is left as an
 137:  * exercise to the reader.
 138:  *
 139:  * <p>Acme&#x2019;s <code>ThinkService</code> plug-in gets deployed as
 140:  * a JAR file. Besides the bytecode and resources for
 141:  * <code>QuickThinker</code> and <code>DeepThinker</code>, it also
 142:  * contains the text file
 143:  * <code>META-INF/services/org.foo.ThinkService</code>:
 144:  *
 145:  * <pre>
 146:  * # Available implementations of org.foo.ThinkService
 147:  * com.acme.QuickThinker
 148:  * com.acme.DeepThinker
 149:  * </pre>
 150:  *
 151:  * <p><b>Thread Safety</b>
 152:  *
 153:  * <p>It is safe to use <code>ServiceFactory</code> from multiple
 154:  * concurrent threads without external synchronization.
 155:  *
 156:  * <p><b>Note for User Applications</b>
 157:  *
 158:  * <p>User applications that want to load plug-ins should not directly
 159:  * use <code>gnu.classpath.ServiceFactory</code>, because this class
 160:  * is only available in Java environments that are based on GNU
 161:  * Classpath. Instead, it is recommended that user applications call
 162:  * {@link
 163:  * javax.imageio.spi.ServiceRegistry#lookupProviders(Class)}. This API
 164:  * is actually independent of image I/O, and it is available on every
 165:  * environment.
 166:  *
 167:  * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a>
 168:  */
 169: public final class ServiceFactory
 170: {
 171:   /**
 172:    * A logger that gets informed when a service gets loaded, or
 173:    * when there is a problem with loading a service.
 174:    *
 175:    * <p>Because {@link java.util.logging.Logger#getLogger(String)}
 176:    * is thread-safe, we do not need to worry about synchronization
 177:    * here.
 178:    */
 179:   private static final Logger LOGGER = Logger.getLogger("gnu.classpath");
 180: 
 181:   /**
 182:    * Declared private in order to prevent constructing instances of
 183:    * this utility class.
 184:    */
 185:   private ServiceFactory()
 186:   {
 187:   }
 188: 
 189: 
 190:   /**
 191:    * Finds service providers that are implementing the specified
 192:    * Service Provider Interface.
 193:    *
 194:    * <p><b>On-demand loading:</b> Loading and initializing service
 195:    * providers is delayed as much as possible. The rationale is that
 196:    * typical clients will iterate through the set of installed service
 197:    * providers until one is found that matches some criteria (like
 198:    * supported formats, or quality of service). In such scenarios, it
 199:    * might make sense to install only the frequently needed service
 200:    * providers on the local machine. More exotic providers can be put
 201:    * onto a server; the server will only be contacted when no suitable
 202:    * service could be found locally.
 203:    *
 204:    * <p><b>Security considerations:</b> Any loaded service providers
 205:    * are loaded through the specified ClassLoader, or the system
 206:    * ClassLoader if <code>classLoader</code> is
 207:    * <code>null</code>. When <code>lookupProviders</code> is called,
 208:    * the current {@link AccessControlContext} gets recorded. This
 209:    * captured security context will determine the permissions when
 210:    * services get loaded via the <code>next()</code> method of the
 211:    * returned <code>Iterator</code>.
 212:    *
 213:    * @param spi the service provider interface which must be
 214:    * implemented by any loaded service providers.
 215:    *
 216:    * @param loader the class loader that will be used to load the
 217:    * service providers, or <code>null</code> for the system class
 218:    * loader. For using the context class loader, see {@link
 219:    * #lookupProviders(Class)}.
 220:    *
 221:    * @return an iterator over instances of <code>spi</code>.
 222:    *
 223:    * @throws IllegalArgumentException if <code>spi</code> is
 224:    * <code>null</code>.
 225:    */
 226:   public static <P> Iterator<P> lookupProviders(Class<P> spi,
 227:                                                 ClassLoader loader)
 228:   {
 229:     return lookupProviders(spi, loader, false);
 230:   }
 231: 
 232:   /**
 233:    * Finds service providers that are implementing the specified
 234:    * Service Provider Interface.
 235:    *
 236:    * <p><b>On-demand loading:</b> Loading and initializing service
 237:    * providers is delayed as much as possible. The rationale is that
 238:    * typical clients will iterate through the set of installed service
 239:    * providers until one is found that matches some criteria (like
 240:    * supported formats, or quality of service). In such scenarios, it
 241:    * might make sense to install only the frequently needed service
 242:    * providers on the local machine. More exotic providers can be put
 243:    * onto a server; the server will only be contacted when no suitable
 244:    * service could be found locally.
 245:    *
 246:    * <p><b>Security considerations:</b> Any loaded service providers
 247:    * are loaded through the specified ClassLoader, or the system
 248:    * ClassLoader if <code>classLoader</code> is
 249:    * <code>null</code>. When <code>lookupProviders</code> is called,
 250:    * the current {@link AccessControlContext} gets recorded. This
 251:    * captured security context will determine the permissions when
 252:    * services get loaded via the <code>next()</code> method of the
 253:    * returned <code>Iterator</code>.
 254:    *
 255:    * @param spi the service provider interface which must be
 256:    * implemented by any loaded service providers.
 257:    *
 258:    * @param loader the class loader that will be used to load the
 259:    * service providers, or <code>null</code> for the system class
 260:    * loader. For using the context class loader, see {@link
 261:    * #lookupProviders(Class)}.
 262:    * @param error true if a {@link ServiceConfigurationError}
 263:    *              should be thrown when an error occurs, rather
 264:    *              than it merely being logged.
 265:    * @return an iterator over instances of <code>spi</code>.
 266:    *
 267:    * @throws IllegalArgumentException if <code>spi</code> is
 268:    * <code>null</code>.
 269:    */
 270:   public static <P> Iterator<P> lookupProviders(Class<P> spi,
 271:                                                 ClassLoader loader,
 272:                                                 boolean error)
 273:   {
 274:     String resourceName;
 275:     Enumeration<URL> urls;
 276: 
 277:     if (spi == null)
 278:       throw new IllegalArgumentException();
 279: 
 280:     if (loader == null)
 281:       loader = ClassLoader.getSystemClassLoader();
 282: 
 283:     resourceName = "META-INF/services/" + spi.getName();
 284:     try
 285:       {
 286:         urls = loader.getResources(resourceName);
 287:       }
 288:     catch (IOException ioex)
 289:       {
 290:         /* If an I/O error occurs here, we cannot provide any service
 291:          * providers. In this case, we simply return an iterator that
 292:          * does not return anything (no providers installed).
 293:          */
 294:         log(Level.WARNING, "cannot access {0}", resourceName, ioex);
 295:         if (error)
 296:           throw new ServiceConfigurationError("Failed to access + " +
 297:                                               resourceName, ioex);
 298:         else
 299:           {
 300:             List<P> empty = Collections.emptyList();
 301:             return empty.iterator();
 302:           }
 303:       }
 304: 
 305:     return new ServiceIterator<P>(spi, urls, loader, error,
 306:                                   AccessController.getContext());
 307:   }
 308: 
 309: 
 310:   /**
 311:    * Finds service providers that are implementing the specified
 312:    * Service Provider Interface, using the context class loader
 313:    * for loading providers.
 314:    *
 315:    * @param spi the service provider interface which must be
 316:    * implemented by any loaded service providers.
 317:    *
 318:    * @return an iterator over instances of <code>spi</code>.
 319:    *
 320:    * @throws IllegalArgumentException if <code>spi</code> is
 321:    * <code>null</code>.
 322:    *
 323:    * @see #lookupProviders(Class, ClassLoader)
 324:    */
 325:   public static <P> Iterator<P> lookupProviders(Class<P> spi)
 326:   {
 327:     ClassLoader ctxLoader;
 328: 
 329:     ctxLoader = Thread.currentThread().getContextClassLoader();
 330:     return lookupProviders(spi, ctxLoader);
 331:   }
 332: 
 333: 
 334:   /**
 335:    * An iterator over service providers that are listed in service
 336:    * provider configuration files, which get passed as an Enumeration
 337:    * of URLs. This is a helper class for {@link
 338:    * ServiceFactory#lookupProviders(Class, ClassLoader)}.
 339:    *
 340:    * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a>
 341:    */
 342:   private static final class ServiceIterator<P>
 343:     implements Iterator<P>
 344:   {
 345:     /**
 346:      * The service provider interface (usually an interface, sometimes
 347:      * an abstract class) which the services must implement.
 348:      */
 349:     private final Class<P> spi;
 350: 
 351: 
 352:     /**
 353:      * An Enumeration<URL> over the URLs that contain a resource
 354:      * <code>META-INF/services/&lt;org.foo.SomeService&gt;</code>,
 355:      * as returned by {@link ClassLoader#getResources(String)}.
 356:      */
 357:     private final Enumeration<URL> urls;
 358: 
 359: 
 360:     /**
 361:      * The class loader used for loading service providers.
 362:      */
 363:     private final ClassLoader loader;
 364: 
 365: 
 366:     /**
 367:      * The security context used when loading and initializing service
 368:      * providers. We want to load and initialize all plug-in service
 369:      * providers under the same security context, namely the one that
 370:      * was active when {@link #lookupProviders(Class, ClassLoader)} has
 371:      * been called.
 372:      */
 373:     private final AccessControlContext securityContext;
 374: 
 375: 
 376:     /**
 377:      * A reader for the current file listing class names of service
 378:      * implementors, or <code>null</code> when the last reader has
 379:      * been fetched.
 380:      */
 381:     private BufferedReader reader;
 382: 
 383: 
 384:     /**
 385:      * The URL currently being processed. This is only used for
 386:      * emitting error messages.
 387:      */
 388:     private URL currentURL;
 389: 
 390: 
 391:     /**
 392:      * The service provider that will be returned by the next call to
 393:      * {@link #next()}, or <code>null</code> if the iterator has
 394:      * already returned all service providers.
 395:      */
 396:     private P nextProvider;
 397: 
 398:     /**
 399:      * True if a {@link ServiceConfigurationError} should be thrown
 400:      * when an error occurs, instead of it merely being logged.
 401:      */
 402:     private boolean error;
 403: 
 404:     /**
 405:      * Constructs an Iterator that loads and initializes services on
 406:      * demand.
 407:      *
 408:      * @param spi the service provider interface which the services
 409:      * must implement. Usually, this is a Java interface type, but it
 410:      * might also be an abstract class or even a concrete superclass.
 411:      *
 412:      * @param urls an Enumeration<URL> over the URLs that contain a
 413:      * resource
 414:      * <code>META-INF/services/&lt;org.foo.SomeService&gt;</code>, as
 415:      * determined by {@link ClassLoader#getResources(String)}.
 416:      *
 417:      * @param loader the ClassLoader that gets used for loading
 418:      * service providers.
 419:      *
 420:      * @param error true if a {@link ServiceConfigurationError}
 421:      *              should be thrown when an error occurs, rather
 422:      *              than it merely being logged.
 423:      *
 424:      * @param securityContext the security context to use when loading
 425:      * and initializing service providers.
 426:      */
 427:     ServiceIterator(Class<P> spi, Enumeration<URL> urls, ClassLoader loader,
 428:                     boolean error, AccessControlContext securityContext)
 429:     {
 430:       this.spi = spi;
 431:       this.urls = urls;
 432:       this.loader = loader;
 433:       this.securityContext = securityContext;
 434:       this.error = error;
 435:       this.nextProvider = loadNextServiceProvider();
 436:     }
 437: 
 438: 
 439:     /**
 440:      * @throws NoSuchElementException if {@link #hasNext} returns
 441:      * <code>false</code>.
 442:      */
 443:     public P next()
 444:     {
 445:       P result;
 446: 
 447:       if (!hasNext())
 448:         throw new NoSuchElementException();
 449: 
 450:       result = nextProvider;
 451:       nextProvider = loadNextServiceProvider();
 452:       return result;
 453:     }
 454: 
 455: 
 456:     public boolean hasNext()
 457:     {
 458:       return nextProvider != null;
 459:     }
 460: 
 461: 
 462:     public void remove()
 463:     {
 464:       throw new UnsupportedOperationException();
 465:     }
 466: 
 467: 
 468:     private P loadNextServiceProvider()
 469:     {
 470:       String line;
 471: 
 472:       if (reader == null)
 473:         advanceReader();
 474: 
 475:       for (;;)
 476:         {
 477:           /* If we have reached the last provider list, we cannot
 478:            * retrieve any further lines.
 479:            */
 480:           if (reader == null)
 481:             return null;
 482: 
 483:           try
 484:             {
 485:               line = reader.readLine();
 486:             }
 487:           catch (IOException readProblem)
 488:             {
 489:               log(Level.WARNING, "IOException upon reading {0}", currentURL,
 490:                   readProblem);
 491:               line = null;
 492:               if (error)
 493:                 throw new ServiceConfigurationError("Error reading " +
 494:                                                     currentURL, readProblem);
 495:             }
 496: 
 497:           /* When we are at the end of one list of services,
 498:            * switch over to the next one.
 499:            */
 500:           if (line == null)
 501:             {
 502:               advanceReader();
 503:               continue;
 504:             }
 505: 
 506: 
 507:           // Skip whitespace at the beginning and end of each line.
 508:           line = line.trim();
 509: 
 510:           // Skip empty lines.
 511:           if (line.length() == 0)
 512:             continue;
 513: 
 514:           // Skip comment lines.
 515:           if (line.charAt(0) == '#')
 516:             continue;
 517: 
 518:           try
 519:             {
 520:               log(Level.FINE,
 521:                   "Loading service provider \"{0}\", specified"
 522:                   + " by \"META-INF/services/{1}\" in {2}.",
 523:                   new Object[] { line, spi.getName(), currentURL },
 524:                   null);
 525: 
 526:               /* Load the class in the security context that was
 527:                * active when calling lookupProviders.
 528:                */
 529:               return AccessController.doPrivileged(
 530:                 new ServiceProviderLoadingAction<P>(spi, line, loader),
 531:                 securityContext);
 532:             }
 533:           catch (Exception ex)
 534:             {
 535:               String msg = "Cannot load service provider class \"{0}\","
 536:                 + " specified by \"META-INF/services/{1}\" in {2}";
 537:               if (ex instanceof PrivilegedActionException
 538:                   && ex.getCause() instanceof ClassCastException)
 539:                 msg = "Service provider class \"{0}\" is not an instance"
 540:                   + " of \"{1}\". Specified"
 541:                   + " by \"META-INF/services/{1}\" in {2}.";
 542: 
 543:               log(Level.WARNING, msg,
 544:                   new Object[] { line, spi.getName(), currentURL },
 545:                   ex);
 546:               if (error)
 547:                 throw new ServiceConfigurationError("Cannot load service "+
 548:                                                     "provider class " +
 549:                                                     line + " specified by "+
 550:                                                     "\"META-INF/services/"+
 551:                                                     spi.getName() + "\" in "+
 552:                                                     currentURL, ex);
 553:               continue;
 554:             }
 555:         }
 556:     }
 557: 
 558: 
 559:     private void advanceReader()
 560:     {
 561:       do
 562:         {
 563:           if (reader != null)
 564:             {
 565:               try
 566:                 {
 567:                   reader.close();
 568:                   log(Level.FINE, "closed {0}", currentURL, null);
 569:                 }
 570:               catch (Exception ex)
 571:                 {
 572:                   log(Level.WARNING, "cannot close {0}", currentURL, ex);
 573:                   if (error)
 574:                     throw new ServiceConfigurationError("Cannot close " +
 575:                                                         currentURL, ex);
 576:                 }
 577:               reader = null;
 578:               currentURL = null;
 579:             }
 580: 
 581:         if (!urls.hasMoreElements())
 582:           return;
 583: 
 584:         currentURL = urls.nextElement();
 585:         try
 586:           {
 587:             reader = new BufferedReader(new InputStreamReader(
 588:               currentURL.openStream(), "UTF-8"));
 589:             log(Level.FINE, "opened {0}", currentURL, null);
 590:           }
 591:         catch (Exception ex)
 592:           {
 593:             log(Level.WARNING, "cannot open {0}", currentURL, ex);
 594:             if (error)
 595:               throw new ServiceConfigurationError("Cannot open " +
 596:                                                   currentURL, ex);
 597:           }
 598:         }
 599:       while (reader == null);
 600:     }
 601:   }
 602: 
 603: 
 604:   // Package-private to avoid a trampoline.
 605:   /**
 606:    * Passes a log message to the <code>java.util.logging</code>
 607:    * framework. This call returns very quickly if no log message will
 608:    * be produced, so there is not much overhead in the standard case.
 609:    *
 610:    * @param level the severity of the message, for instance {@link
 611:    * Level#WARNING}.
 612:    *
 613:    * @param msg the log message, for instance <code>&#x201c;Could not
 614:    * load {0}.&#x201d;</code>
 615:    *
 616:    * @param param the parameter(s) for the log message, or
 617:    * <code>null</code> if <code>msg</code> does not specify any
 618:    * parameters. If <code>param</code> is not an array, an array with
 619:    * <code>param</code> as its single element gets passed to the
 620:    * logging framework.
 621:    *
 622:    * @param t a Throwable that is associated with the log record, or
 623:    * <code>null</code> if the log message is not associated with a
 624:    * Throwable.
 625:    */
 626:   static void log(Level level, String msg, Object param, Throwable t)
 627:   {
 628:     LogRecord rec;
 629: 
 630:     // Return quickly if no log message will be produced.
 631:     if (!LOGGER.isLoggable(level))
 632:       return;
 633: 
 634:     rec = new LogRecord(level, msg);
 635:     if (param != null && param.getClass().isArray())
 636:       rec.setParameters((Object[]) param);
 637:     else
 638:       rec.setParameters(new Object[] { param });
 639: 
 640:     rec.setThrown(t);
 641: 
 642:     // While java.util.logging can sometimes infer the class and
 643:     // method of the caller, this automatic inference is not reliable
 644:     // on highly optimizing VMs. Also, log messages make more sense to
 645:     // developers when they display a public method in a public class;
 646:     // otherwise, they might feel tempted to figure out the internals
 647:     // of ServiceFactory in order to understand the problem.
 648:     rec.setSourceClassName(ServiceFactory.class.getName());
 649:     rec.setSourceMethodName("lookupProviders");
 650: 
 651:     LOGGER.log(rec);
 652:   }
 653: }