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    */
ServiceFactory()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    */
lookupProviders(Class<P> spi, ClassLoader loader)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    */
lookupProviders(Class<P> spi, ClassLoader loader, boolean error)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    */
lookupProviders(Class<P> spi)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      */
ServiceIterator(Class<P> spi, Enumeration<URL> urls, ClassLoader loader, boolean error, AccessControlContext securityContext)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      */
next()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 
hasNext()456     public boolean hasNext()
457     {
458       return nextProvider != null;
459     }
460 
461 
remove()462     public void remove()
463     {
464       throw new UnsupportedOperationException();
465     }
466 
467 
loadNextServiceProvider()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 
advanceReader()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    */
log(Level level, String msg, Object param, Throwable t)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 }
654