package org.springframework.cloud.util; import java.util.Iterator; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.cloud.service.ServiceConnectorCreator; /** * A variation of {@link ServiceLoader} that ignores exception encountered when loading services. * * <p> * The {@link ServiceLoader} class throws an error when it encounters a service that it cannot load. For * {@link ServiceConnectorCreator}, this can be a common occurrence due to missing dependencies. For example, if an app isn't using * MongoDB, thus not declaring dependencies on relevant classes, will cause {@link ServiceConnectorCreator} for MongoDB to fail. We * don't want to abandon other service connector creators in such cases. * </p> * * @author Ramnivas Laddad * * @param <T> the type of the service being loaded */ public class ServiceLoaderWithExceptionControl<T> implements Iterable<T> { private Iterable<T> underlying; private static Logger logger = Logger.getLogger(ServiceLoaderWithExceptionControl.class.getName()); public static <T> Iterable<T> load(Class<T> serviceType) { ServiceLoader<T> loader = ServiceLoader.load(serviceType); return new ServiceLoaderWithExceptionControl<T>(loader); } private ServiceLoaderWithExceptionControl(Iterable<T> underlying) { this.underlying = underlying; } @Override public Iterator<T> iterator() { return new ServiceLoaderIterator(underlying.iterator()); } private class ServiceLoaderIterator implements Iterator<T> { private Iterator<T> underlying; public ServiceLoaderIterator(Iterator<T> underlying) { this.underlying = underlying; } @Override public boolean hasNext() { return underlying.hasNext(); } @Override public T next() { try { return underlying.next(); } catch (ServiceConfigurationError e) { logger.log(Level.CONFIG, "Failed to load " + e); if (hasNext()) { return next(); } } return null; } @Override public void remove() { underlying.remove(); } } }