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