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