All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.internal.ServiceFinder Maven / Gradle / Ivy

There is a newer version: 4.0.0-M1
Show newest version
/*
 * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.internal;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.ReflectPermission;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.jersey.internal.util.ReflectionHelper;

/**
 * A simple service-provider lookup mechanism.  A service is a
 * well-known set of interfaces and (usually abstract) classes.  A service
 * provider is a specific implementation of a service.  The classes in a
 * provider typically implement the interfaces and subclass the classes defined
 * in the service itself.  Service providers may be installed in an
 * implementation of the Java platform in the form of extensions, that is, jar
 * files placed into any of the usual extension directories.  Providers may
 * also be made available by adding them to the applet or application class
 * path or by some other platform-specific means.
 * 

*

In this lookup mechanism a service is represented by an interface or an * abstract class. (A concrete class may be used, but this is not * recommended.) A provider of a given service contains one or more concrete * classes that extend this service class with data and code specific to * the provider. This provider class will typically not be the entire * provider itself but rather a proxy that contains enough information to * decide whether the provider is able to satisfy a particular request together * with code that can create the actual provider on demand. The details of * provider classes tend to be highly service-specific; no single class or * interface could possibly unify them, so no such class has been defined. The * only requirement enforced here is that provider classes must have a * zero-argument constructor so that they may be instantiated during lookup. *

*

The default service provider registration/lookup mechanism based * on META-INF/services files is described below. * For environments, where the basic mechanism is not suitable, clients * can enforce a different approach by setting their custom ServiceIteratorProvider * by calling setIteratorProvider. The call must be made prior to any lookup attempts. *

*

A service provider identifies itself by placing a provider-configuration * file in the resource directory META-INF/services. The file's name * should consist of the fully-qualified name of the abstract service class. * The file should contain a list of fully-qualified concrete provider-class * names, one per line. Space and tab characters surrounding each name, as * well as blank lines, are ignored. The comment character is '#' * (0x23); on each line all characters following the first comment * character are ignored. The file must be encoded in UTF-8. *

*

If a particular concrete provider class is named in more than one * configuration file, or is named in the same configuration file more than * once, then the duplicates will be ignored. The configuration file naming a * particular provider need not be in the same jar file or other distribution * unit as the provider itself. The provider must be accessible from the same * class loader that was initially queried to locate the configuration file; * note that this is not necessarily the class loader that found the file. *

*

Example: Suppose we have a service class named * java.io.spi.CharCodec. It has two abstract methods: *

*

 *   public abstract CharEncoder getEncoder(String encodingName);
 *   public abstract CharDecoder getDecoder(String encodingName);
 * 
*

* Each method returns an appropriate object or null if it cannot * translate the given encoding. Typical CharCodec providers will * support more than one encoding. *

*

If sun.io.StandardCodec is a provider of the CharCodec * service then its jar file would contain the file * META-INF/services/java.io.spi.CharCodec. This file would contain * the single line: *

*

 *   sun.io.StandardCodec    # Standard codecs for the platform
 * 
*

* To locate an codec for a given encoding name, the internal I/O code would * do something like this: *

*

 *   CharEncoder getEncoder(String encodingName) {
 *       for( CharCodec cc : ServiceFinder.find(CharCodec.class) ) {
 *           CharEncoder ce = cc.getEncoder(encodingName);
 *           if (ce != null)
 *               return ce;
 *       }
 *       return null;
 *   }
 * 
*

* The provider-lookup mechanism always executes in the security context of the * caller. Trusted system code should typically invoke the methods in this * class from within a privileged security context. * * @param the type of the service instance. * @author Mark Reinhold * @author Jakub Podlesak * @author Marek Potociar */ public final class ServiceFinder implements Iterable { private static final Logger LOGGER = Logger.getLogger(ServiceFinder.class.getName()); private static final String PREFIX = "META-INF/services/"; private final Class serviceClass; private final String serviceName; private final ClassLoader classLoader; private final boolean ignoreOnClassNotFound; static { final OsgiRegistry osgiRegistry = ReflectionHelper.getOsgiRegistryInstance(); if (osgiRegistry != null) { LOGGER.log(Level.CONFIG, "Running in an OSGi environment"); osgiRegistry.hookUp(); } else { LOGGER.log(Level.CONFIG, "Running in a non-OSGi environment"); } } private static Enumeration getResources(final ClassLoader loader, final String name) throws IOException { if (loader == null) { return getResources(name); } else { final Enumeration resources = loader.getResources(name); if ((resources != null) && resources.hasMoreElements()) { return resources; } else { return getResources(name); } } } private static Enumeration getResources(final String name) throws IOException { if (ServiceFinder.class.getClassLoader() != null) { return ServiceFinder.class.getClassLoader().getResources(name); } else { return ClassLoader.getSystemResources(name); } } private static ClassLoader _getContextClassLoader() { return AccessController.doPrivileged(ReflectionHelper.getContextClassLoaderPA()); } /** * Locates and incrementally instantiates the available providers of a * given service using the given class loader. *

*

This method transforms the name of the given service class into a * provider-configuration filename as described above and then uses the * getResources method of the given class loader to find all * available files with that name. These files are then read and parsed to * produce a list of provider-class names. The iterator that is returned * uses the given class loader to lookup and then instantiate each element * of the list. *

*

Because it is possible for extensions to be installed into a running * Java virtual machine, this method may return different results each time * it is invoked.

* @param service The service's abstract service class * @param loader The class loader to be used to load provider-configuration files * and instantiate provider classes, or null if the system * class loader (or, failing that the bootstrap class loader) is to * be used * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * @see #find(Class) * @param the type of the service instance. * @return the service finder */ public static ServiceFinder find(final Class service, final ClassLoader loader) throws ServiceConfigurationError { return find(service, loader, false); } /** * Locates and incrementally instantiates the available providers of a * given service using the given class loader. *

*

This method transforms the name of the given service class into a * provider-configuration filename as described above and then uses the * getResources method of the given class loader to find all * available files with that name. These files are then read and parsed to * produce a list of provider-class names. The iterator that is returned * uses the given class loader to lookup and then instantiate each element * of the list. *

*

Because it is possible for extensions to be installed into a running * Java virtual machine, this method may return different results each time * it is invoked.

* @param service The service's abstract service class * @param loader The class loader to be used to load provider-configuration files * and instantiate provider classes, or null if the system * class loader (or, failing that the bootstrap class loader) is to * be used * @param ignoreOnClassNotFound If a provider cannot be loaded by the class loader * then move on to the next available provider. * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * @see #find(Class) * @param the type of the service instance. * @return the service finder */ public static ServiceFinder find(final Class service, final ClassLoader loader, final boolean ignoreOnClassNotFound) throws ServiceConfigurationError { return new ServiceFinder(service, loader, ignoreOnClassNotFound); } /** * Locates and incrementally instantiates the available providers of a * given service using the context class loader. This convenience method * is equivalent to *

*

     *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
     *   return Service.providers(service, cl, false);
     * 
* @param service The service's abstract service class * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * @see #find(Class, ClassLoader) * @param the type of the service instance. * @return the service finder */ public static ServiceFinder find(final Class service) throws ServiceConfigurationError { return find(service, _getContextClassLoader(), false); } /** * Locates and incrementally instantiates the available providers of a * given service using the context class loader. This convenience method * is equivalent to *

*

     *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
     *   boolean ingore = ...
     *   return Service.providers(service, cl, ignore);
     * 
* @param service The service's abstract service class * @param ignoreOnClassNotFound If a provider cannot be loaded by the class loader * then move on to the next available provider. * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * @see #find(Class, ClassLoader) * @param the type of the service instance. * @return the service finder */ public static ServiceFinder find(final Class service, final boolean ignoreOnClassNotFound) throws ServiceConfigurationError { return find(service, _getContextClassLoader(), ignoreOnClassNotFound); } /** * Locates and incrementally instantiates the available classes of a given * service file using the context class loader. * * @param serviceName the service name correspond to a file in * META-INF/services that contains a list of fully qualified class * names * @throws ServiceConfigurationError If a service file violates the specified format * or names a provider class that cannot be found and instantiated * @return the service finder */ public static ServiceFinder find(final String serviceName) throws ServiceConfigurationError { return new ServiceFinder(Object.class, serviceName, _getContextClassLoader(), false); } /** * Register the service iterator provider to iterate on provider instances * or classes. *

* The default implementation registered, {@link DefaultServiceIteratorProvider}, * looks up provider classes in META-INF/service files. *

* This method must be called prior to any attempts to obtain provider * instances or classes. * * @param sip the service iterator provider. * @throws SecurityException if the provider cannot be registered. */ public static void setIteratorProvider(final ServiceIteratorProvider sip) throws SecurityException { ServiceIteratorProvider.setInstance(sip); } private ServiceFinder( final Class service, final ClassLoader loader, final boolean ignoreOnClassNotFound) { this(service, service.getName(), loader, ignoreOnClassNotFound); } private ServiceFinder( final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { this.serviceClass = service; this.serviceName = serviceName; this.classLoader = loader; this.ignoreOnClassNotFound = ignoreOnClassNotFound; } /** * Returns discovered objects incrementally. * * @return An Iterator that yields provider objects for the given * service, in some arbitrary order. The iterator will throw a * ServiceConfigurationError if a provider-configuration * file violates the specified format or if a provider class cannot * be found and instantiated. */ @Override public Iterator iterator() { return ServiceIteratorProvider.getInstance() .createIterator(serviceClass, serviceName, classLoader, ignoreOnClassNotFound); } /** * Returns discovered objects all at once. * * @return * can be empty but never null. * * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated */ @SuppressWarnings("unchecked") public T[] toArray() throws ServiceConfigurationError { final List result = new ArrayList(); for (final T t : this) { result.add(t); } return result.toArray((T[]) Array.newInstance(serviceClass, result.size())); } /** * Returns discovered classes all at once. * * @return * can be empty but never null. * * @throws ServiceConfigurationError If a provider-configuration file violates the specified format * or names a provider class that cannot be found */ @SuppressWarnings("unchecked") public Class[] toClassArray() throws ServiceConfigurationError { final List> result = new ArrayList>(); final ServiceIteratorProvider iteratorProvider = ServiceIteratorProvider.getInstance(); final Iterator> i = iteratorProvider .createClassIterator(serviceClass, serviceName, classLoader, ignoreOnClassNotFound); while (i.hasNext()) { result.add(i.next()); } return result.toArray((Class[]) Array.newInstance(Class.class, result.size())); } private static void fail(final String serviceName, final String msg, final Throwable cause) throws ServiceConfigurationError { final ServiceConfigurationError sce = new ServiceConfigurationError(serviceName + ": " + msg); sce.initCause(cause); throw sce; } private static void fail(final String serviceName, final String msg) throws ServiceConfigurationError { throw new ServiceConfigurationError(serviceName + ": " + msg); } private static void fail(final String serviceName, final URL u, final int line, final String msg) throws ServiceConfigurationError { fail(serviceName, u + ":" + line + ": " + msg); } /** * Parse a single line from the given configuration file, adding the name * on the line to both the names list and the returned set iff the name is * not already a member of the returned set. */ private static int parseLine(final String serviceName, final URL u, final BufferedReader r, final int lc, final List names, final Set returned) throws IOException, ServiceConfigurationError { String ln = r.readLine(); if (ln == null) { return -1; } final int ci = ln.indexOf('#'); if (ci >= 0) { ln = ln.substring(0, ci); } ln = ln.trim(); final int n = ln.length(); if (n != 0) { if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) { fail(serviceName, u, lc, LocalizationMessages.ILLEGAL_CONFIG_SYNTAX()); } int cp = ln.codePointAt(0); if (!Character.isJavaIdentifierStart(cp)) { fail(serviceName, u, lc, LocalizationMessages.ILLEGAL_PROVIDER_CLASS_NAME(ln)); } for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { cp = ln.codePointAt(i); if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) { fail(serviceName, u, lc, LocalizationMessages.ILLEGAL_PROVIDER_CLASS_NAME(ln)); } } if (!returned.contains(ln)) { names.add(ln); returned.add(ln); } } return lc + 1; } /** * Parse the content of the given URL as a provider-configuration file. * * @param serviceName The service class for which providers are being sought; * used to construct error detail strings * @param u The URL naming the configuration file to be parsed * @param returned A Set containing the names of provider classes that have already * been returned. This set will be updated to contain the names * that will be yielded from the returned Iterator. * @return A (possibly empty) Iterator that will yield the * provider-class names in the given configuration file that are * not yet members of the returned set * @throws ServiceConfigurationError If an I/O error occurs while reading from the given URL, or * if a configuration-file format error is detected */ @SuppressWarnings({"StatementWithEmptyBody"}) private static Iterator parse(final String serviceName, final URL u, final Set returned) throws ServiceConfigurationError { InputStream in = null; BufferedReader r = null; final ArrayList names = new ArrayList(); try { final URLConnection uConn = u.openConnection(); uConn.setUseCaches(false); in = uConn.getInputStream(); r = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(serviceName, u, r, lc, names, returned)) >= 0) { // continue } } catch (final IOException x) { fail(serviceName, ": " + x); } finally { try { if (r != null) { r.close(); } if (in != null) { in.close(); } } catch (final IOException y) { fail(serviceName, ": " + y); } } return names.iterator(); } private static class AbstractLazyIterator { final Class service; final String serviceName; final ClassLoader loader; final boolean ignoreOnClassNotFound; Enumeration configs = null; Iterator pending = null; Set returned = new TreeSet(); String nextName = null; private AbstractLazyIterator( final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { this.service = service; this.serviceName = serviceName; this.loader = loader; this.ignoreOnClassNotFound = ignoreOnClassNotFound; } protected final void setConfigs() { if (configs == null) { try { final String fullName = PREFIX + serviceName; configs = getResources(loader, fullName); } catch (final IOException x) { fail(serviceName, ": " + x); } } } public boolean hasNext() throws ServiceConfigurationError { if (nextName != null) { return true; } setConfigs(); while (nextName == null) { while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(serviceName, configs.nextElement(), returned); } nextName = pending.next(); if (ignoreOnClassNotFound) { try { AccessController.doPrivileged(ReflectionHelper.classForNameWithExceptionPEA(nextName, loader)); } catch (final ClassNotFoundException ex) { handleClassNotFoundException(); } catch (final PrivilegedActionException pae) { final Throwable thrown = pae.getException(); if (thrown instanceof ClassNotFoundException) { handleClassNotFoundException(); } else if (thrown instanceof NoClassDefFoundError) { // Dependent class of provider not found if (LOGGER.isLoggable(Level.CONFIG)) { // This assumes that ex.getLocalizedMessage() returns // the name of a dependent class that is not found LOGGER.log(Level.CONFIG, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_NOT_FOUND( thrown.getLocalizedMessage(), nextName, service)); } nextName = null; } else if (thrown instanceof ClassFormatError) { // Dependent class of provider not found if (LOGGER.isLoggable(Level.CONFIG)) { LOGGER.log(Level.CONFIG, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_FORMAT_ERROR( thrown.getLocalizedMessage(), nextName, service)); } nextName = null; } else if (thrown instanceof RuntimeException) { throw (RuntimeException) thrown; } else { throw new IllegalStateException(thrown); } } } } return true; } public void remove() { throw new UnsupportedOperationException(); } private void handleClassNotFoundException() { // Provider implementation not found if (LOGGER.isLoggable(Level.CONFIG)) { LOGGER.log(Level.CONFIG, LocalizationMessages.PROVIDER_NOT_FOUND(nextName, service)); } nextName = null; } } private static final class LazyClassIterator extends AbstractLazyIterator implements Iterator> { private LazyClassIterator( final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { super(service, serviceName, loader, ignoreOnClassNotFound); } @Override public Class next() { if (!hasNext()) { throw new NoSuchElementException(); } final String cn = nextName; nextName = null; try { final Class tClass = AccessController.doPrivileged( ReflectionHelper.classForNameWithExceptionPEA(cn, loader)); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "Loading next class: " + tClass.getName()); } return tClass; } catch (final ClassNotFoundException ex) { fail(serviceName, LocalizationMessages.PROVIDER_NOT_FOUND(cn, service)); } catch (final PrivilegedActionException pae) { final Throwable thrown = pae.getCause(); if (thrown instanceof ClassNotFoundException) { fail(serviceName, LocalizationMessages.PROVIDER_NOT_FOUND(cn, service)); } else if (thrown instanceof NoClassDefFoundError) { fail(serviceName, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_NOT_FOUND( thrown.getLocalizedMessage(), cn, service)); } else if (thrown instanceof ClassFormatError) { fail(serviceName, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_FORMAT_ERROR( thrown.getLocalizedMessage(), cn, service)); } else { fail(serviceName, LocalizationMessages.PROVIDER_CLASS_COULD_NOT_BE_LOADED(cn, service, thrown.getLocalizedMessage()), thrown); } } return null; /* This cannot happen */ } } private static final class LazyObjectIterator extends AbstractLazyIterator implements Iterator { private T t; private LazyObjectIterator( final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { super(service, serviceName, loader, ignoreOnClassNotFound); } @Override public boolean hasNext() throws ServiceConfigurationError { if (nextName != null) { return true; } setConfigs(); while (nextName == null) { while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(serviceName, configs.nextElement(), returned); } nextName = pending.next(); try { t = service.cast(AccessController.doPrivileged( ReflectionHelper.classForNameWithExceptionPEA(nextName, loader)).newInstance()); } catch (final InstantiationException ex) { if (ignoreOnClassNotFound) { if (LOGGER.isLoggable(Level.CONFIG)) { LOGGER.log(Level.CONFIG, LocalizationMessages.PROVIDER_COULD_NOT_BE_CREATED(nextName, service, ex.getLocalizedMessage())); } nextName = null; } else { fail(serviceName, LocalizationMessages.PROVIDER_COULD_NOT_BE_CREATED(nextName, service, ex.getLocalizedMessage()), ex); } } catch (final IllegalAccessException ex) { fail(serviceName, LocalizationMessages.PROVIDER_COULD_NOT_BE_CREATED(nextName, service, ex.getLocalizedMessage()), ex); } catch (final ClassNotFoundException ex) { handleClassNotFoundException(); } catch (final NoClassDefFoundError ex) { // Dependent class of provider not found if (ignoreOnClassNotFound) { if (LOGGER.isLoggable(Level.CONFIG)) { // This assumes that ex.getLocalizedMessage() returns // the name of a dependent class that is not found LOGGER.log(Level.CONFIG, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_NOT_FOUND( ex.getLocalizedMessage(), nextName, service)); } nextName = null; } else { fail(serviceName, LocalizationMessages .DEPENDENT_CLASS_OF_PROVIDER_NOT_FOUND(ex.getLocalizedMessage(), nextName, service), ex); } } catch (final PrivilegedActionException pae) { final Throwable cause = pae.getCause(); if (cause instanceof ClassNotFoundException) { handleClassNotFoundException(); } else if (cause instanceof ClassFormatError) { // Dependent class of provider not found if (ignoreOnClassNotFound) { if (LOGGER.isLoggable(Level.CONFIG)) { LOGGER.log(Level.CONFIG, LocalizationMessages.DEPENDENT_CLASS_OF_PROVIDER_FORMAT_ERROR( cause.getLocalizedMessage(), nextName, service)); } nextName = null; } else { fail(serviceName, LocalizationMessages .DEPENDENT_CLASS_OF_PROVIDER_FORMAT_ERROR(cause.getLocalizedMessage(), nextName, service), cause); } } else { fail(serviceName, LocalizationMessages .PROVIDER_COULD_NOT_BE_CREATED(nextName, service, cause.getLocalizedMessage()), cause); } } } return true; } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } nextName = null; if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "Loading next object: " + t.getClass().getName()); } return t; } private void handleClassNotFoundException() throws ServiceConfigurationError { if (ignoreOnClassNotFound) { // Provider implementation not found if (LOGGER.isLoggable(Level.CONFIG)) { LOGGER.log(Level.CONFIG, LocalizationMessages.PROVIDER_NOT_FOUND(nextName, service)); } nextName = null; } else { fail(serviceName, LocalizationMessages.PROVIDER_NOT_FOUND(nextName, service)); } } } /** * Supports iteration of provider instances or classes. *

* The default implementation looks up provider classes from META-INF/services * files, see {@link DefaultServiceIteratorProvider}. * This implementation may be overridden by invoking * {@link ServiceFinder#setIteratorProvider(org.glassfish.jersey.internal.ServiceFinder.ServiceIteratorProvider)}. */ public abstract static class ServiceIteratorProvider { private static volatile ServiceIteratorProvider sip; private static final Object sipLock = new Object(); private static ServiceIteratorProvider getInstance() { // TODO: check the following is a good practice: Double-check idiom for lazy initialization of fields. ServiceIteratorProvider result = sip; if (result == null) { // First check (no locking) synchronized (sipLock) { result = sip; if (result == null) { // Second check (with locking) sip = result = new DefaultServiceIteratorProvider(); } } } return result; } private static void setInstance(final ServiceIteratorProvider sip) throws SecurityException { final SecurityManager security = System.getSecurityManager(); if (security != null) { final ReflectPermission rp = new ReflectPermission("suppressAccessChecks"); security.checkPermission(rp); } synchronized (sipLock) { ServiceIteratorProvider.sip = sip; } } /** * Iterate over provider instances of a service. * * @param the type of the service. * @param service the service class. * @param serviceName the service name. * @param loader the class loader to utilize when loading provider * classes. * @param ignoreOnClassNotFound if true ignore an instance if the * corresponding provider class if cannot be found, * otherwise throw a {@link ClassNotFoundException}. * @return the provider instance iterator. */ public abstract Iterator createIterator(Class service, String serviceName, ClassLoader loader, boolean ignoreOnClassNotFound); /** * Iterate over provider classes of a service. * * @param the type of the service. * @param service the service class. * @param serviceName the service name. * @param loader the class loader to utilize when loading provider * classes. * @param ignoreOnClassNotFound if true ignore the provider class if * cannot be found, * otherwise throw a {@link ClassNotFoundException}. * @return the provider class iterator. */ public abstract Iterator> createClassIterator(Class service, String serviceName, ClassLoader loader, boolean ignoreOnClassNotFound); } /** * The default service iterator provider that looks up provider classes in * META-INF/services files. *

* This class may utilized if a {@link ServiceIteratorProvider} needs to * reuse the default implementation. */ public static final class DefaultServiceIteratorProvider extends ServiceIteratorProvider { @Override public Iterator createIterator(final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { return new LazyObjectIterator(service, serviceName, loader, ignoreOnClassNotFound); } @Override public Iterator> createClassIterator(final Class service, final String serviceName, final ClassLoader loader, final boolean ignoreOnClassNotFound) { return new LazyClassIterator(service, serviceName, loader, ignoreOnClassNotFound); } } }