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 +
+ "]";
+ }
+
+}
signature.asc
Description: Digital signature
