This adds the new service loading API, wrapping
our existing gnu.classpath.ServiceFactory.

Changelog:

2007-01-01  Andrew John Hughes  <[EMAIL PROTECTED]>

        * gnu/classpath/ServiceFactory.java:
        Add option of throwing a ServiceConfigurationError.
        (lookupProviders(Class,ClassLoader,boolean)): Implemented.
        * java/util/ServiceConfigurationError.java: New file.
        * java/util/ServiceLoader.java: Likewise.

-- 
Andrew :-)

Escape the Java Trap with GNU Classpath!
http://www.gnu.org/philosophy/java-trap.html
public class gcj extends Freedom implements Java { ... }
Index: gnu/classpath/ServiceFactory.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/classpath/ServiceFactory.java,v
retrieving revision 1.5
diff -u -3 -p -u -r1.5 ServiceFactory.java
--- gnu/classpath/ServiceFactory.java   12 Feb 2006 19:38:46 -0000      1.5
+++ gnu/classpath/ServiceFactory.java   1 Jan 2007 20:19:13 -0000
@@ -49,6 +49,7 @@ import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.ServiceConfigurationError;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.util.logging.Logger;
@@ -176,7 +177,6 @@ public final class ServiceFactory
    */
   private static final Logger LOGGER = Logger.getLogger("gnu.classpath");
 
-
   /**
    * Declared private in order to prevent constructing instances of
    * this utility class.
@@ -225,6 +225,51 @@ public final class ServiceFactory
   public static Iterator lookupProviders(Class spi,
                                          ClassLoader loader)
   {
+    return lookupProviders(spi, loader, false);
+  }
+
+  /**
+   * 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED]
+   * #lookupProviders(Class)}.
+   * @param error true if a [EMAIL PROTECTED] ServiceConfigurationError}
+   *              should be thrown when an error occurs, rather
+   *              than it merely being logged.
+   * @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,
+                                        boolean error)
+  {
     String resourceName;
     Enumeration urls;
 
@@ -246,10 +291,14 @@ public final class ServiceFactory
          * does not return anything (no providers installed).
          */
         log(Level.WARNING, "cannot access {0}", resourceName, ioex);
-        return Collections.EMPTY_LIST.iterator();
+       if (error)
+         throw new ServiceConfigurationError("Failed to access + " +
+                                             resourceName, ioex);
+       else
+         return Collections.EMPTY_LIST.iterator();
       }
 
-    return new ServiceIterator(spi, urls, loader,
+    return new ServiceIterator(spi, urls, loader, error,
                                AccessController.getContext());
   }
 
@@ -342,6 +391,11 @@ public final class ServiceFactory
      */
     private Object nextProvider;
 
+    /**
+     * True if a [EMAIL PROTECTED] ServiceConfigurationError} should be thrown
+     * when an error occurs, instead of it merely being logged.
+     */
+    private boolean error;
 
     /**
      * Constructs an Iterator that loads and initializes services on
@@ -359,16 +413,21 @@ public final class ServiceFactory
      * @param loader the ClassLoader that gets used for loading
      * service providers.
      *
+     * @param error true if a [EMAIL PROTECTED] ServiceConfigurationError}
+     *              should be thrown when an error occurs, rather
+     *              than it merely being logged.
+     *
      * @param securityContext the security context to use when loading
      * and initializing service providers.
      */
     ServiceIterator(Class spi, Enumeration urls, ClassLoader loader,
-                    AccessControlContext securityContext)
+                   boolean error, AccessControlContext securityContext)
     {
       this.spi = spi;
       this.urls = urls;
       this.loader = loader;
       this.securityContext = securityContext;
+      this.error = error;
       this.nextProvider = loadNextServiceProvider();
     }
 
@@ -426,6 +485,9 @@ public final class ServiceFactory
               log(Level.WARNING, "IOException upon reading {0}", currentURL,
                   readProblem);
               line = null;
+             if (error)
+               throw new ServiceConfigurationError("Error reading " +
+                                                   currentURL, readProblem);
             }
 
           /* When we are at the end of one list of services,
@@ -477,6 +539,13 @@ public final class ServiceFactory
               log(Level.WARNING, msg,                  
                   new Object[] { line, spi.getName(), currentURL },
                   ex);
+             if (error)
+               throw new ServiceConfigurationError("Cannot load service "+
+                                                   "provider class " +
+                                                   line + " specified by "+
+                                                   "\"META-INF/services/"+
+                                                   spi.getName() + "\" in "+
+                                                   currentURL, ex);
               continue;
             }
         }
@@ -497,6 +566,9 @@ public final class ServiceFactory
               catch (Exception ex)
                 {
                   log(Level.WARNING, "cannot close {0}", currentURL, ex);
+                 if (error)
+                   throw new ServiceConfigurationError("Cannot close " +
+                                                       currentURL, ex);
                 }
               reader = null;
               currentURL = null;
@@ -515,6 +587,9 @@ public final class ServiceFactory
         catch (Exception ex)
           {
             log(Level.WARNING, "cannot open {0}", currentURL, ex);
+           if (error)
+             throw new ServiceConfigurationError("Cannot open " +
+                                                 currentURL, ex);
           }
         }
       while (reader == null);
Index: java/util/ServiceConfigurationError.java
===================================================================
RCS file: java/util/ServiceConfigurationError.java
diff -N java/util/ServiceConfigurationError.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ java/util/ServiceConfigurationError.java    1 Jan 2007 20:19:14 -0000
@@ -0,0 +1,89 @@
+/* ServiceConfigurationError.java -- An error on service loading.
+   Copyright (C) 2007  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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 java.util;
+
+/**
+ * <p>
+ * An error thrown when a problem occurs during the loading
+ * of a service provider by a [EMAIL PROTECTED] ServiceLoader}.  Such
+ * an error can occur for a number of reasons:
+ * </p>
+ * <ul>
+ * <li>An I/O error occurs</li>
+ * <li>The configuration file doesn't meet the specifications</li>
+ * <li>A listed class can not be found</li>
+ * <li>A listed class does not implement the service</li>
+ * <li>A listed class can not be instantiated</li>
+ * </ul>
+ * 
+ * @author Andrew John Hughes ([EMAIL PROTECTED])
+ * @since 1.6
+ */
+public class ServiceConfigurationError
+  extends Error
+{
+  
+  /**
+   * Constructs a new [EMAIL PROTECTED] ServiceConfigurationError}
+   * with the specified message.
+   *
+   * @param message a message describing the error, or
+   *                <code>null</code> if none is required.
+   */
+  public ServiceConfigurationError(String message)
+  {
+    super(message);
+  }
+
+  /**
+   * Constructs a new [EMAIL PROTECTED] ServiceConfigurationError}
+   * with the specified message and cause.
+   *
+   * @param message a message describing the error, or
+   *                <code>null</code> if none is required.
+   * @param cause the cause of the error, or
+   *              <code>null</code> if this is unknown
+   *              or inappropriate.
+   */
+  public ServiceConfigurationError(String message,
+                                  Throwable cause)
+  {
+    super(message,cause);
+  }
+
+}
Index: java/util/ServiceLoader.java
===================================================================
RCS file: java/util/ServiceLoader.java
diff -N java/util/ServiceLoader.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ java/util/ServiceLoader.java        1 Jan 2007 20:19:14 -0000
@@ -0,0 +1,274 @@
+/* ServiceLoader.java -- Allows loading of plug-in services.
+   Copyright (C) 2006, 2007  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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 java.util;
+
+import gnu.classpath.ServiceFactory;
+
+/**
+ * <p>
+ * Facilities for loading service providers.  A service is
+ * defined by a set of interfaces or abstract classes, and
+ * a service provider gives a concrete implementation of this.
+ * Service providers may be installed as part of the runtime
+ * environment using JAR files in the extension directories,
+ * or may be simply supplied on the classpath.
+ * </p>
+ * <p>
+ * In terms of loading a service, the service is defined by
+ * a single interface or abstract class which the provider
+ * implements.  This may not constitute the entire service,
+ * but is simply a mechanism by which a provider of the
+ * service can be loaded and its capabilities determined.
+ * The variety of possible services means that no more
+ * requirements are made of the service provider other than
+ * that it must have an accessible zero argument constructor
+ * in order to allow an instance to be created.
+ * </p>
+ * <p>
+ * Service providers are listed in a file named after the
+ * service type in the directory <code>META-INF/services</code>.
+ * The file contains a list of classes, and must be encoded
+ * using UTF-8.  Whitespace is ignored.  Comments can be
+ * included by using a <code>'#'</code> prefix; anything occurring
+ * on the same line after this symbol is ignored. Duplicate classes
+ * are ignored.
+ * </p>
+ * <p>
+ * The classes are loaded using the same classloader that was
+ * queried in order to locate the configuration file.  As a result,
+ * the providers do not need to reside in the same JAR file as the
+ * resource; they merely have to be accessible to this classloader,
+ * which may differ from the one that loaded the file itself.
+ * </p>
+ * <p>
+ * Providers are located and instantiated lazily, as calls to the
+ * [EMAIL PROTECTED] #iterator()} are made.  Providers are cached, and those in
+ * the cache are returned first.  The cache may be cleared by calling
+ * [EMAIL PROTECTED] #reload()}.  Service loaders always execute in the 
security
+ * context of the caller, so ideally calls should be made from a trusted
+ * source.
+ * </p>
+ * <p>
+ * Note that this class is not thread-safe, and that strange errors may
+ * occur as the result of the use of remote URLs occurring on the classpath,
+ * which lead to erroneous web pages.
+ * </p>
+ *
+ * @author Andrew John Hughes ([EMAIL PROTECTED])
+ * @since 1.6
+ */
+public final class ServiceLoader<S>
+  implements Iterable<S>
+{
+
+  /**
+   * The class of the service provider.
+   */
+  private Class<S> spi;
+
+  /**
+   * The class loader for the service provider.
+   */
+  private ClassLoader loader;
+
+  /**
+   * The cache of service providers.
+   */
+  private List<S> cache;
+
+  /**
+   * The [EMAIL PROTECTED] gnu.classpath.ServiceFactory} iterator
+   * from which providers are obtained.
+   */
+  private Iterator<S> serviceIt;
+
+  /**
+   * Constructs a new [EMAIL PROTECTED] ServiceLoader} with
+   * the specified provider and class loader.
+   *
+   * @param spi the service to load.
+   * @param loader the class loader to use.
+   */
+  private ServiceLoader(Class<S> spi, ClassLoader loader)
+  {
+    this.spi = spi;
+    this.loader = loader;
+    cache = new ArrayList<S>();
+  }
+
+  /**
+   * Lazily loads the available providers.  The iterator first returns
+   * providers from the cache, in instantiation order, followed by any
+   * remaining providers, which are added to the cache after loading.
+   * The actual loading and parsing of the configuration file takes
+   * place in the [EMAIL PROTECTED] Iterator#hasNext()} and [EMAIL PROTECTED] 
Iterator#next()}
+   * methods, which means that they may result in a
+   * [EMAIL PROTECTED] ServiceConfigurationError} being thrown.  If such an 
error
+   * does occur, subsequent invocations will attempt to recover.
+   * The [EMAIL PROTECTED] remove()} method is not supported and instead throws
+   * an [EMAIL PROTECTED] UnsupportedOperationException}.
+   *
+   * @return an iterator that lazily loads service providers.
+   */
+  public Iterator<S> iterator()
+  {
+    return new Iterator<S>()
+      {
+       /**
+        * The cache iterator.
+        */
+       private Iterator<S> cacheIt = cache.iterator();
+
+       public boolean hasNext()
+       {
+         if (cacheIt.hasNext())
+           return true;
+         if (serviceIt == null)
+           serviceIt =
+             ServiceFactory.lookupProviders(spi, loader, true);
+         return serviceIt.hasNext();
+       }
+
+       public S next()
+       {
+         if (cacheIt.hasNext())
+           return cacheIt.next();
+         if (serviceIt == null)
+           serviceIt =
+             ServiceFactory.lookupProviders(spi, loader, true);
+         S nextService = serviceIt.next();
+         cache.add(nextService);
+         return nextService;
+       }
+
+       public void remove()
+       {
+         throw new UnsupportedOperationException();
+       }
+      };
+  }
+
+  /**
+   * Creates a new service loader for the given service,
+   * using the context class loader of the current thread.
+   * This is equivalent to calling <code>ServiceLoader.load(service,
+   * Thread.currentThread().getContextClassLoader())</code>.
+   *
+   * @param service the interface or abstract class that represents
+   *                the service.
+   * @return a new [EMAIL PROTECTED] ServiceLoader} instance.
+   */
+  public static <S> ServiceLoader<S> load(Class<S> service)
+  {
+    return load(service,
+               Thread.currentThread().getContextClassLoader());
+  }
+
+  /**
+   * Creates a new service loader for the given service,
+   * using the specified class loader.  The class loader is
+   * used to access the configuration file and the service
+   * provider instances themselves.  If the loader is
+   * <code>null</code>, the system class loader (or, if
+   * this is also <code>null</code>, the bootstrap class
+   * loader).
+   *
+   * @param service the interface or abstract class that represents
+   *                the service.
+   * @param loader the class loader used to load the configuration
+   *               file and service providers.
+   * @return a new [EMAIL PROTECTED] ServiceLoader} instance.
+   */
+  public static <S> ServiceLoader<S> load(Class<S> service,
+                                         ClassLoader loader)
+  {
+    if (loader == null)
+      loader = ClassLoader.getSystemClassLoader();
+    return new ServiceLoader(service, loader);
+  }
+
+  /**
+   * Creates a new service loader for the given service,
+   * using the extension class loader.  If the extension
+   * class loader can not be found, the system class loader
+   * is used (or, if this is <code>null</code>, the
+   * bootstrap class loader).  The primary use of this method
+   * is to only obtain installed services, ignoring any which
+   * may appear on the classpath.  This is equivalent to calling
+   * <code>load(service, extClassLoader)</code> where
+   * <code>extClassLoader</code> is the extension class loader
+   * (or <code>null</code> if this is unavailable).
+   *
+   * @param service the interface or abstract class that represents
+   *                the service.
+   * @return a new [EMAIL PROTECTED] ServiceLoader} instance.
+   */
+  public static <S> ServiceLoader<S> loadInstalled(Class<S> service)
+  {
+    /* We expect the extension class loader to be the parent
+     * of the system class loader, as in
+     * ClassLoader.getDefaultSystemClassLoader() */
+    return load(service,
+               ClassLoader.getSystemClassLoader().getParent());
+  }
+
+  /**
+   * Clears the cache of the provider, so that all providers
+   * are again read from the configuration file and instantiated.
+   */
+  public void reload()
+  {
+    cache.clear();
+  }
+
+  /**
+   * Returns a textual representation of this
+   * [EMAIL PROTECTED] ServiceLoader}. 
+   * 
+   * @return a textual representation of the
+   *         service loader.
+   */
+  public String toString()
+  {
+    return getClass().getName() +
+      "[spi=" + spi +
+      ",loader=" + loader +
+      "]";
+  }
+
+}

Attachment: signature.asc
Description: Digital signature

Reply via email to