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(Class, ClassLoader)}.
 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(Class, ClassLoader)} has
 318:      * been called.
 319:      */
 320:     private final AccessControlContext securityContext;
 321: 
 322: 
 323:     /**
 324:      * A reader for the current file listing class names of service
 325:      * implementors, or <code>null</code> when the last reader has
 326:      * been fetched.
 327:      */
 328:     private BufferedReader reader;
 329:     
 330: 
 331:     /**
 332:      * The URL currently being processed. This is only used for
 333:      * emitting error messages.
 334:      */
 335:     private URL currentURL;
 336: 
 337: 
 338:     /**
 339:      * The service provider that will be returned by the next call to
 340:      * {@link #next()}, or <code>null</code> if the iterator has
 341:      * already returned all service providers.
 342:      */
 343:     private Object nextProvider;
 344: 
 345: 
 346:     /**
 347:      * Constructs an Iterator that loads and initializes services on
 348:      * demand.
 349:      *
 350:      * @param spi the service provider interface which the services
 351:      * must implement. Usually, this is a Java interface type, but it
 352:      * might also be an abstract class or even a concrete superclass.
 353:      *
 354:      * @param urls an Enumeration<URL> over the URLs that contain a
 355:      * resource
 356:      * <code>META-INF/services/&lt;org.foo.SomeService&gt;</code>, as
 357:      * determined by {@link ClassLoader#getResources(String)}.
 358:      *
 359:      * @param loader the ClassLoader that gets used for loading
 360:      * service providers.
 361:      *
 362:      * @param securityContext the security context to use when loading
 363:      * and initializing service providers.
 364:      */
 365:     ServiceIterator(Class spi, Enumeration urls, ClassLoader loader,
 366:                     AccessControlContext securityContext)
 367:     {
 368:       this.spi = spi;
 369:       this.urls = urls;
 370:       this.loader = loader;
 371:       this.securityContext = securityContext;
 372:       this.nextProvider = loadNextServiceProvider();
 373:     }
 374: 
 375: 
 376:     /**
 377:      * @throws NoSuchElementException if {@link #hasNext} returns
 378:      * <code>false</code>.
 379:      */
 380:     public Object next()
 381:     {
 382:       Object result;
 383: 
 384:       if (!hasNext())
 385:         throw new NoSuchElementException();
 386: 
 387:       result = nextProvider;
 388:       nextProvider = loadNextServiceProvider();
 389:       return result;
 390:     }
 391: 
 392: 
 393:     public boolean hasNext()
 394:     {
 395:       return nextProvider != null;
 396:     }
 397: 
 398: 
 399:     public void remove()
 400:     {
 401:       throw new UnsupportedOperationException();
 402:     }
 403: 
 404: 
 405:     private Object loadNextServiceProvider()
 406:     {
 407:       String line;
 408:       
 409:       if (reader == null)
 410:         advanceReader();
 411: 
 412:       for (;;)
 413:         {
 414:           /* If we have reached the last provider list, we cannot
 415:            * retrieve any further lines.
 416:            */
 417:           if (reader == null)
 418:             return null;
 419: 
 420:           try
 421:             {
 422:               line = reader.readLine();
 423:             }
 424:           catch (IOException readProblem)
 425:             {
 426:               log(Level.WARNING, "IOException upon reading {0}", currentURL,
 427:                   readProblem);
 428:               line = null;
 429:             }
 430: 
 431:           /* When we are at the end of one list of services,
 432:            * switch over to the next one.
 433:            */
 434:           if (line == null)
 435:             {
 436:               advanceReader();
 437:               continue;
 438:             }
 439: 
 440: 
 441:           // Skip whitespace at the beginning and end of each line.
 442:           line = line.trim();
 443: 
 444:           // Skip empty lines.
 445:           if (line.length() == 0)
 446:             continue;
 447: 
 448:           // Skip comment lines.
 449:           if (line.charAt(0) == '#')
 450:             continue;
 451: 
 452:           try
 453:             {
 454:               log(Level.FINE,
 455:                   "Loading service provider \"{0}\", specified"
 456:                   + " by \"META-INF/services/{1}\" in {2}.",
 457:                   new Object[] { line, spi.getName(), currentURL },
 458:                   null);
 459: 
 460:               /* Load the class in the security context that was
 461:                * active when calling lookupProviders.
 462:                */
 463:               return AccessController.doPrivileged(
 464:                 new ServiceProviderLoadingAction(spi, line, loader),
 465:                 securityContext);
 466:             }
 467:           catch (Exception ex)
 468:             {
 469:               String msg = "Cannot load service provider class \"{0}\","
 470:                 + " specified by \"META-INF/services/{1}\" in {2}";
 471:               if (ex instanceof PrivilegedActionException
 472:                   && ex.getCause() instanceof ClassCastException)
 473:                 msg = "Service provider class \"{0}\" is not an instance"
 474:                   + " of \"{1}\". Specified"
 475:                   + " by \"META-INF/services/{1}\" in {2}.";
 476: 
 477:               log(Level.WARNING, msg,                  
 478:                   new Object[] { line, spi.getName(), currentURL },
 479:                   ex);
 480:               continue;
 481:             }
 482:         }
 483:     }
 484: 
 485: 
 486:     private void advanceReader()
 487:     {
 488:       do
 489:         {
 490:           if (reader != null)
 491:             {
 492:               try
 493:                 {
 494:                   reader.close();
 495:                   log(Level.FINE, "closed {0}", currentURL, null);
 496:                 }
 497:               catch (Exception ex)
 498:                 {
 499:                   log(Level.WARNING, "cannot close {0}", currentURL, ex);
 500:                 }
 501:               reader = null;
 502:               currentURL = null;
 503:             }
 504: 
 505:         if (!urls.hasMoreElements())
 506:           return;
 507: 
 508:         currentURL = (URL) urls.nextElement();
 509:         try
 510:           {
 511:             reader = new BufferedReader(new InputStreamReader(
 512:               currentURL.openStream(), "UTF-8"));
 513:             log(Level.FINE, "opened {0}", currentURL, null);
 514:           }
 515:         catch (Exception ex)
 516:           {
 517:             log(Level.WARNING, "cannot open {0}", currentURL, ex);
 518:           }
 519:         }
 520:       while (reader == null);
 521:     }
 522:   }
 523: 
 524: 
 525:   // Package-private to avoid a trampoline.
 526:   /**
 527:    * Passes a log message to the <code>java.util.logging</code>
 528:    * framework. This call returns very quickly if no log message will
 529:    * be produced, so there is not much overhead in the standard case.
 530:    *
 531:    * @param level the severity of the message, for instance {@link
 532:    * Level#WARNING}.
 533:    *
 534:    * @param msg the log message, for instance <code>&#x201c;Could not
 535:    * load {0}.&#x201d;</code>
 536:    *
 537:    * @param param the parameter(s) for the log message, or
 538:    * <code>null</code> if <code>msg</code> does not specify any
 539:    * parameters. If <code>param</code> is not an array, an array with
 540:    * <code>param</code> as its single element gets passed to the
 541:    * logging framework.
 542:    *
 543:    * @param t a Throwable that is associated with the log record, or
 544:    * <code>null</code> if the log message is not associated with a
 545:    * Throwable.
 546:    */
 547:   static void log(Level level, String msg, Object param, Throwable t)
 548:   {
 549:     LogRecord rec;
 550: 
 551:     // Return quickly if no log message will be produced.
 552:     if (!LOGGER.isLoggable(level))
 553:       return;
 554: 
 555:     rec = new LogRecord(level, msg);
 556:     if (param != null && param.getClass().isArray())
 557:       rec.setParameters((Object[]) param);
 558:     else
 559:       rec.setParameters(new Object[] { param });
 560: 
 561:     rec.setThrown(t);
 562: 
 563:     // While java.util.logging can sometimes infer the class and
 564:     // method of the caller, this automatic inference is not reliable
 565:     // on highly optimizing VMs. Also, log messages make more sense to
 566:     // developers when they display a public method in a public class;
 567:     // otherwise, they might feel tempted to figure out the internals
 568:     // of ServiceFactory in order to understand the problem.
 569:     rec.setSourceClassName(ServiceFactory.class.getName());
 570:     rec.setSourceMethodName("lookupProviders");
 571: 
 572:     LOGGER.log(rec);
 573:   }
 574: }