mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			574 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* ServiceFactory.java -- Factory for plug-in services.
 | |
|    Copyright (C) 2004  Free Software Foundation
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 | |
| 02111-1307 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package gnu.classpath;
 | |
| 
 | |
| import java.io.BufferedReader;
 | |
| import java.io.IOException;
 | |
| import java.io.InputStreamReader;
 | |
| import java.net.URL;
 | |
| import java.security.AccessControlContext;
 | |
| import java.security.AccessController;
 | |
| import java.security.PrivilegedActionException;
 | |
| import java.util.Collections;
 | |
| import java.util.Enumeration;
 | |
| import java.util.Iterator;
 | |
| import java.util.NoSuchElementException;
 | |
| import java.util.logging.Level;
 | |
| import java.util.logging.LogRecord;
 | |
| import java.util.logging.Logger;
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * A factory for plug-ins that conform to a service provider
 | |
|  * interface. This is a general mechanism that gets used by a number
 | |
|  * of packages in the Java API. For instance, {@link
 | |
|  * java.nio.charset.spi.CharsetProvider} allows to write custom
 | |
|  * encoders and decoders for character sets, {@link
 | |
|  * javax.imageio.spi.ImageReaderSpi} allows to support custom image
 | |
|  * formats, and {@link javax.print.PrintService} makes it possible to
 | |
|  * write custom printer drivers.
 | |
|  *
 | |
|  * <p>The plug-ins are concrete implementations of the service
 | |
|  * provider interface, which is defined as an interface or an abstract
 | |
|  * class. The implementation classes must be public and have a public
 | |
|  * constructor that takes no arguments.
 | |
|  *
 | |
|  * <p>Plug-ins are usually deployed in JAR files. A JAR that provides
 | |
|  * an implementation of a service must declare this in a resource file
 | |
|  * whose name is the fully qualified service name and whose location
 | |
|  * is the directory <code>META-INF/services</code>. This UTF-8 encoded
 | |
|  * text file lists, on separate lines, the fully qualified names of
 | |
|  * the concrete implementations. Thus, one JAR file can provide an
 | |
|  * arbitrary number of implementations for an arbitrary count of
 | |
|  * service provider interfaces.
 | |
|  *
 | |
|  * <p><b>Example</b>
 | |
|  *
 | |
|  * <p>For example, a JAR might provide two implementations of the
 | |
|  * service provider interface <code>org.foo.ThinkService</code>,
 | |
|  * namely <code>com.acme.QuickThinker</code> and
 | |
|  * <code>com.acme.DeepThinker</code>. The code for <code>QuickThinker</code>
 | |
|  * woud look as follows:
 | |
|  *
 | |
|  * <pre>
 | |
|  * package com.acme;
 | |
|  *
 | |
|  * /**
 | |
|  * * Provices a super-quick, but not very deep implementation of ThinkService.
 | |
|  * */
 | |
|  * public class QuickThinker
 | |
|  *   implements org.foo.ThinkService
 | |
|  * {
 | |
|  *   /**
 | |
|  *   * Constructs a new QuickThinker. The service factory (which is
 | |
|  *   * part of the Java environment) calls this no-argument constructor
 | |
|  *   * when it looks up the available implementations of ThinkService.
 | |
|  *   *
 | |
|  *   * <p>Note that an application might query all available
 | |
|  *   * ThinkService providers, but use just one of them. Therefore,
 | |
|  *   * constructing an instance should be very inexpensive. For example,
 | |
|  *   * large data structures should only be allocated when the service
 | |
|  *   * actually gets used.
 | |
|  *   */
 | |
|  *   public QuickThinker()
 | |
|  *   {
 | |
|  *   }
 | |
|  *
 | |
|  *   /**
 | |
|  *   * Returns the speed of this ThinkService in thoughts per second.
 | |
|  *   * Applications can choose among the available service providers
 | |
|  *   * based on this value.
 | |
|  *   */
 | |
|  *   public double getSpeed()
 | |
|  *   {
 | |
|  *     return 314159.2654;
 | |
|  *   }
 | |
|  *
 | |
|  *   /**
 | |
|  *   * Produces a thought. While the returned thoughts are not very
 | |
|  *   * deep, they are generated in very short time.
 | |
|  *   */
 | |
|  *   public Thought think()
 | |
|  *   {
 | |
|  *     return null;
 | |
|  *   }
 | |
|  * }
 | |
|  * </pre>
 | |
|  *
 | |
|  * <p>The code for <code>com.acme.DeepThinker</code> is left as an
 | |
|  * exercise to the reader.
 | |
|  *
 | |
|  * <p>Acme’s <code>ThinkService</code> plug-in gets deployed as
 | |
|  * a JAR file. Besides the bytecode and resources for
 | |
|  * <code>QuickThinker</code> and <code>DeepThinker</code>, it also
 | |
|  * contains the text file
 | |
|  * <code>META-INF/services/org.foo.ThinkService</code>:
 | |
|  *
 | |
|  * <pre>
 | |
|  * # Available implementations of org.foo.ThinkService
 | |
|  * com.acme.QuickThinker
 | |
|  * com.acme.DeepThinker
 | |
|  * </pre>
 | |
|  *
 | |
|  * <p><b>Thread Safety</b>
 | |
|  *
 | |
|  * <p>It is safe to use <code>ServiceFactory</code> from multiple
 | |
|  * concurrent threads without external synchronization.
 | |
|  *
 | |
|  * <p><b>Note for User Applications</b>
 | |
|  *
 | |
|  * <p>User applications that want to load plug-ins should not directly
 | |
|  * use <code>gnu.classpath.ServiceFactory</code>, because this class
 | |
|  * is only available in Java environments that are based on GNU
 | |
|  * Classpath. Instead, it is recommended that user applications call
 | |
|  * {@link
 | |
|  * javax.imageio.spi.ServiceRegistry#lookupProviders(Class)}. This API
 | |
|  * is actually independent of image I/O, and it is available on every
 | |
|  * environment.
 | |
|  *
 | |
|  * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a>
 | |
|  */
 | |
| public final class ServiceFactory
 | |
| {
 | |
|   /**
 | |
|    * A logger that gets informed when a service gets loaded, or
 | |
|    * when there is a problem with loading a service.
 | |
|    *
 | |
|    * <p>Because {@link java.util.logging.Logger#getLogger(String)}
 | |
|    * is thread-safe, we do not need to worry about synchronization
 | |
|    * here.
 | |
|    */
 | |
|   private static final Logger LOGGER = Logger.getLogger("gnu.classpath");
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Declared private in order to prevent constructing instances of
 | |
|    * this utility class.
 | |
|    */
 | |
|   private ServiceFactory()
 | |
|   {
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Finds service providers that are implementing the specified
 | |
|    * Service Provider Interface.
 | |
|    *
 | |
|    * <p><b>On-demand loading:</b> Loading and initializing service
 | |
|    * providers is delayed as much as possible. The rationale is that
 | |
|    * typical clients will iterate through the set of installed service
 | |
|    * providers until one is found that matches some criteria (like
 | |
|    * supported formats, or quality of service). In such scenarios, it
 | |
|    * might make sense to install only the frequently needed service
 | |
|    * providers on the local machine. More exotic providers can be put
 | |
|    * onto a server; the server will only be contacted when no suitable
 | |
|    * service could be found locally.
 | |
|    *
 | |
|    * <p><b>Security considerations:</b> Any loaded service providers
 | |
|    * are loaded through the specified ClassLoader, or the system
 | |
|    * ClassLoader if <code>classLoader</code> is
 | |
|    * <code>null</code>. When <code>lookupProviders</code> is called,
 | |
|    * the current {@link AccessControlContext} gets recorded. This
 | |
|    * captured security context will determine the permissions when
 | |
|    * services get loaded via the <code>next()</code> method of the
 | |
|    * returned <code>Iterator</code>.
 | |
|    *
 | |
|    * @param spi the service provider interface which must be
 | |
|    * implemented by any loaded service providers.
 | |
|    *
 | |
|    * @param loader the class loader that will be used to load the
 | |
|    * service providers, or <code>null</code> for the system class
 | |
|    * loader. For using the context class loader, see {@link
 | |
|    * #lookupProviders(Class)}.
 | |
|    *
 | |
|    * @return an iterator over instances of <code>spi</code>.
 | |
|    *
 | |
|    * @throws IllegalArgumentException if <code>spi</code> is
 | |
|    * <code>null</code>.
 | |
|    */
 | |
|   public static Iterator lookupProviders(Class spi,
 | |
|                                          ClassLoader loader)
 | |
|   {
 | |
|     String resourceName;
 | |
|     Enumeration urls;
 | |
| 
 | |
|     if (spi == null)
 | |
|       throw new IllegalArgumentException();
 | |
| 
 | |
|     if (loader == null)
 | |
|       loader = ClassLoader.getSystemClassLoader();
 | |
| 
 | |
|     resourceName = "META-INF/services/" + spi.getName();
 | |
|     try
 | |
|       {
 | |
|         urls = loader.getResources(resourceName);
 | |
|       }
 | |
|     catch (IOException ioex)
 | |
|       {
 | |
|         /* If an I/O error occurs here, we cannot provide any service
 | |
|          * providers. In this case, we simply return an iterator that
 | |
|          * does not return anything (no providers installed).
 | |
|          */
 | |
|         log(Level.WARNING, "cannot access {0}", resourceName, ioex);
 | |
|         return Collections.EMPTY_LIST.iterator();
 | |
|       }
 | |
| 
 | |
|     return new ServiceIterator(spi, urls, loader,
 | |
|                                AccessController.getContext());
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Finds service providers that are implementing the specified
 | |
|    * Service Provider Interface, using the context class loader
 | |
|    * for loading providers.
 | |
|    *
 | |
|    * @param spi the service provider interface which must be
 | |
|    * implemented by any loaded service providers.
 | |
|    *
 | |
|    * @return an iterator over instances of <code>spi</code>.
 | |
|    *
 | |
|    * @throws IllegalArgumentException if <code>spi</code> is
 | |
|    * <code>null</code>.
 | |
|    *
 | |
|    * @see #lookupProviders(Class, ClassLoader)
 | |
|    */
 | |
|   public static Iterator lookupProviders(Class spi)
 | |
|   {
 | |
|     ClassLoader ctxLoader;
 | |
| 
 | |
|     ctxLoader = Thread.currentThread().getContextClassLoader();
 | |
|     return lookupProviders(spi, ctxLoader);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * An iterator over service providers that are listed in service
 | |
|    * provider configuration files, which get passed as an Enumeration
 | |
|    * of URLs. This is a helper class for {@link
 | |
|    * ServiceFactory#lookupProviders}.
 | |
|    *
 | |
|    * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a>
 | |
|    */
 | |
|   private static final class ServiceIterator
 | |
|     implements Iterator
 | |
|   {
 | |
|     /**
 | |
|      * The service provider interface (usually an interface, sometimes
 | |
|      * an abstract class) which the services must implement.
 | |
|      */
 | |
|     private final Class spi;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * An Enumeration<URL> over the URLs that contain a resource
 | |
|      * <code>META-INF/services/<org.foo.SomeService></code>,
 | |
|      * as returned by {@link ClassLoader#getResources(String)}.
 | |
|      */
 | |
|     private final Enumeration urls;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * The class loader used for loading service providers.
 | |
|      */
 | |
|     private final ClassLoader loader;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * The security context used when loading and initializing service
 | |
|      * providers. We want to load and initialize all plug-in service
 | |
|      * providers under the same security context, namely the one that
 | |
|      * was active when {@link #lookupProviders} has been called.
 | |
|      */
 | |
|     private final AccessControlContext securityContext;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * A reader for the current file listing class names of service
 | |
|      * implementors, or <code>null</code> when the last reader has
 | |
|      * been fetched.
 | |
|      */
 | |
|     private BufferedReader reader;
 | |
|     
 | |
| 
 | |
|     /**
 | |
|      * The URL currently being processed. This is only used for
 | |
|      * emitting error messages.
 | |
|      */
 | |
|     private URL currentURL;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * The service provider that will be returned by the next call to
 | |
|      * {@link #next()}, or <code>null</code> if the iterator has
 | |
|      * already returned all service providers.
 | |
|      */
 | |
|     private Object nextProvider;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Constructs an Iterator that loads and initializes services on
 | |
|      * demand.
 | |
|      *
 | |
|      * @param spi the service provider interface which the services
 | |
|      * must implement. Usually, this is a Java interface type, but it
 | |
|      * might also be an abstract class or even a concrete superclass.
 | |
|      *
 | |
|      * @param urls an Enumeration<URL> over the URLs that contain a
 | |
|      * resource
 | |
|      * <code>META-INF/services/<org.foo.SomeService></code>, as
 | |
|      * determined by {@link ClassLoader#getResources(String)}.
 | |
|      *
 | |
|      * @param loader the ClassLoader that gets used for loading
 | |
|      * service providers.
 | |
|      *
 | |
|      * @param securityContext the security context to use when loading
 | |
|      * and initializing service providers.
 | |
|      */
 | |
|     ServiceIterator(Class spi, Enumeration urls, ClassLoader loader,
 | |
|                     AccessControlContext securityContext)
 | |
|     {
 | |
|       this.spi = spi;
 | |
|       this.urls = urls;
 | |
|       this.loader = loader;
 | |
|       this.securityContext = securityContext;
 | |
|       this.nextProvider = loadNextServiceProvider();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * @throws NoSuchElementException if {@link #hasNext} returns
 | |
|      * <code>false</code>.
 | |
|      */
 | |
|     public Object next()
 | |
|     {
 | |
|       Object result;
 | |
| 
 | |
|       if (!hasNext())
 | |
|         throw new NoSuchElementException();
 | |
| 
 | |
|       result = nextProvider;
 | |
|       nextProvider = loadNextServiceProvider();
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public boolean hasNext()
 | |
|     {
 | |
|       return nextProvider != null;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public void remove()
 | |
|     {
 | |
|       throw new UnsupportedOperationException();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     private Object loadNextServiceProvider()
 | |
|     {
 | |
|       String line;
 | |
|       
 | |
|       if (reader == null)
 | |
|         advanceReader();
 | |
| 
 | |
|       for (;;)
 | |
|         {
 | |
|           /* If we have reached the last provider list, we cannot
 | |
|            * retrieve any further lines.
 | |
|            */
 | |
|           if (reader == null)
 | |
|             return null;
 | |
| 
 | |
|           try
 | |
|             {
 | |
|               line = reader.readLine();
 | |
|             }
 | |
|           catch (IOException readProblem)
 | |
|             {
 | |
|               log(Level.WARNING, "IOException upon reading {0}", currentURL,
 | |
|                   readProblem);
 | |
|               line = null;
 | |
|             }
 | |
| 
 | |
|           /* When we are at the end of one list of services,
 | |
|            * switch over to the next one.
 | |
|            */
 | |
|           if (line == null)
 | |
|             {
 | |
|               advanceReader();
 | |
|               continue;
 | |
|             }
 | |
| 
 | |
| 
 | |
|           // Skip whitespace at the beginning and end of each line.
 | |
|           line = line.trim();
 | |
| 
 | |
|           // Skip empty lines.
 | |
|           if (line.length() == 0)
 | |
|             continue;
 | |
| 
 | |
|           // Skip comment lines.
 | |
|           if (line.charAt(0) == '#')
 | |
|             continue;
 | |
| 
 | |
|           try
 | |
|             {
 | |
|               log(Level.FINE,
 | |
|                   "Loading service provider \"{0}\", specified"
 | |
|                   + " by \"META-INF/services/{1}\" in {2}.",
 | |
|                   new Object[] { line, spi.getName(), currentURL },
 | |
|                   null);
 | |
| 
 | |
|               /* Load the class in the security context that was
 | |
|                * active when calling lookupProviders.
 | |
|                */
 | |
|               return AccessController.doPrivileged(
 | |
|                 new ServiceProviderLoadingAction(spi, line, loader),
 | |
|                 securityContext);
 | |
|             }
 | |
|           catch (Exception ex)
 | |
|             {
 | |
|               String msg = "Cannot load service provider class \"{0}\","
 | |
|                 + " specified by \"META-INF/services/{1}\" in {2}";
 | |
|               if (ex instanceof PrivilegedActionException
 | |
|                   && ex.getCause() instanceof ClassCastException)
 | |
|                 msg = "Service provider class \"{0}\" is not an instance"
 | |
|                   + " of \"{1}\". Specified"
 | |
|                   + " by \"META-INF/services/{1}\" in {2}.";
 | |
| 
 | |
|               log(Level.WARNING, msg,                  
 | |
|                   new Object[] { line, spi.getName(), currentURL },
 | |
|                   ex);
 | |
|               continue;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     private void advanceReader()
 | |
|     {
 | |
|       do
 | |
|         {
 | |
|           if (reader != null)
 | |
|             {
 | |
|               try
 | |
|                 {
 | |
|                   reader.close();
 | |
|                   log(Level.FINE, "closed {0}", currentURL, null);
 | |
|                 }
 | |
|               catch (Exception ex)
 | |
|                 {
 | |
|                   log(Level.WARNING, "cannot close {0}", currentURL, ex);
 | |
|                 }
 | |
|               reader = null;
 | |
|               currentURL = null;
 | |
|             }
 | |
| 
 | |
|         if (!urls.hasMoreElements())
 | |
|           return;
 | |
| 
 | |
|         currentURL = (URL) urls.nextElement();
 | |
|         try
 | |
|           {
 | |
|             reader = new BufferedReader(new InputStreamReader(
 | |
|               currentURL.openStream(), "UTF-8"));
 | |
|             log(Level.FINE, "opened {0}", currentURL, null);
 | |
|           }
 | |
|         catch (Exception ex)
 | |
|           {
 | |
|             log(Level.WARNING, "cannot open {0}", currentURL, ex);
 | |
|           }
 | |
|         }
 | |
|       while (reader == null);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Package-private to avoid a trampoline.
 | |
|   /**
 | |
|    * Passes a log message to the <code>java.util.logging</code>
 | |
|    * framework. This call returns very quickly if no log message will
 | |
|    * be produced, so there is not much overhead in the standard case.
 | |
|    *
 | |
|    * @param the severity of the message, for instance {@link
 | |
|    * Level#WARNING}.
 | |
|    *
 | |
|    * @param msg the log message, for instance <code>“Could not
 | |
|    * load {0}.”</code>
 | |
|    *
 | |
|    * @param param the parameter(s) for the log message, or
 | |
|    * <code>null</code> if <code>msg</code> does not specify any
 | |
|    * parameters. If <code>param</code> is not an array, an array with
 | |
|    * <code>param</code> as its single element gets passed to the
 | |
|    * logging framework.
 | |
|    *
 | |
|    * @param t a Throwable that is associated with the log record, or
 | |
|    * <code>null</code> if the log message is not associated with a
 | |
|    * Throwable.
 | |
|    */
 | |
|   static void log(Level level, String msg, Object param, Throwable t)
 | |
|   {
 | |
|     LogRecord rec;
 | |
| 
 | |
|     // Return quickly if no log message will be produced.
 | |
|     if (!LOGGER.isLoggable(level))
 | |
|       return;
 | |
| 
 | |
|     rec = new LogRecord(level, msg);
 | |
|     if (param != null && param.getClass().isArray())
 | |
|       rec.setParameters((Object[]) param);
 | |
|     else
 | |
|       rec.setParameters(new Object[] { param });
 | |
| 
 | |
|     rec.setThrown(t);
 | |
| 
 | |
|     // While java.util.logging can sometimes infer the class and
 | |
|     // method of the caller, this automatic inference is not reliable
 | |
|     // on highly optimizing VMs. Also, log messages make more sense to
 | |
|     // developers when they display a public method in a public class;
 | |
|     // otherwise, they might feel tempted to figure out the internals
 | |
|     // of ServiceFactory in order to understand the problem.
 | |
|     rec.setSourceClassName(ServiceFactory.class.getName());
 | |
|     rec.setSourceMethodName("lookupProviders");
 | |
| 
 | |
|     LOGGER.log(rec);
 | |
|   }
 | |
| }
 |