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

org.daisy.common.spi.ServiceLoader Maven / Gradle / Ivy

package org.daisy.common.spi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceConfigurationError;

import com.google.common.collect.AbstractIterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServiceLoader implements Iterable {
	
	private static final Logger logger = LoggerFactory.getLogger(ServiceLoader.class);
	
	private static final Map,ServiceLoader> cache = new HashMap,ServiceLoader>();
	
	private static ClassLoader lastContextClassLoader = null;
	
	public static  ServiceLoader load(Class serviceType) {
		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl != lastContextClassLoader) {
			cache.clear();
			synchronized (Memoize.singletons) {
				for (Object o : Memoize.singletons.values())
					if (o instanceof ServiceWithProperties)
						try {
							((ServiceWithProperties)o).spi_deactivate();
						} catch (AbstractMethodError e) {}
				Memoize.singletons.clear();
			}
		}
		lastContextClassLoader = ccl;
		ServiceLoader loader;
		if (cache.containsKey(serviceType)) {
			loader = (ServiceLoader)cache.get(serviceType);
		} else {
			loader = new ServiceLoader(serviceType);
			cache.put(serviceType, loader);
		}
		return loader;
	}
	
	private Class serviceType;
	private Iterable serviceLoader;
	
	private ServiceLoader(Class serviceType) {
		this.serviceType = serviceType;
	}
	
	public Iterator iterator() {
		return new AbstractIterator() {
			Iterator serviceIterator;
			public S computeNext() {
				if (serviceIterator == null) {
					try {
						if (serviceLoader == null) {
							serviceLoader = memoize(serviceType, java.util.ServiceLoader.load(serviceType));
						}
						serviceIterator = serviceLoader.iterator();
					} catch (Throwable e) {
						logger.error("Failed to load service providers", e);
						return endOfData();
					}
				}
				while (serviceIterator.hasNext()) {
					try {
						return serviceIterator.next();
					} catch (Throwable e) {
						if (e instanceof ServiceConfigurationError && e.getCause() instanceof ActivationException) {
							logger.info(e.getMessage() + ": " + e.getCause().getMessage());
							if (e.getCause().getCause() != null)
								logger.trace("Cause:", e.getCause().getCause());
						} else {
							logger.error("Failed to instantiate provider of service '" + serviceType.getCanonicalName() + "'", e);
						}
					}
				}
				return endOfData();
			}
		};
	}
	
	/*
	 * No two Iterables returned by this function contain multiple instances of the same class
	 */
	private static  Iterable memoize(Class serviceType, final Iterable iterable) {
		return new Memoize(serviceType) {
			protected Iterator _iterator() {
				return iterable.iterator();
			}
		};
	}
	
	private static abstract class Memoize implements Iterable {
		/* set of previously returned objects */
		private static final Map,Object> singletons = new HashMap,Object>();
		private final Class serviceType;
		private final List list = new ArrayList();
		protected abstract Iterator _iterator();
		private Iterator _iterator;
		protected Memoize(Class serviceType) {
			this.serviceType = serviceType;
		}
		public final Iterator iterator() {
			return new Iterator() {
				private int index = 0;
				public boolean hasNext() {
					synchronized(list) {
						if (index < list.size())
							return true;
						if (_iterator == null)
							_iterator = _iterator();
						return _iterator.hasNext(); // this does not try to instantiate any objects yet
					}
				}
				// Note that it could happen that this call recursively calls itself, in a different
				// Iterator object but with the same underlying Iterable
				// (java.util.ServiceLoader). java.util.ServiceLoader can cope with that just fine,
				// and we are taking the necessary precautions to handle it here as well.
				public S next() throws NoSuchElementException {
					synchronized(list) {
						if (index < list.size())
							return list.get(index++);
						if (_iterator == null)
							_iterator = _iterator();
						S next = _iterator.next();
						synchronized(singletons) {
							if (singletons.containsKey(next.getClass())) {
								logger.trace("Object of type " + next.getClass() + " already loaded. Returning same object.");
								next = (S)singletons.get(next.getClass());
							} else
								singletons.put(next.getClass(), next);
						}
						list.add(next);
						return list.get(index++); // not returning next because next items may have
						                          // been added to the list in the meantime
					}
				}
				public void remove() {
					throw new UnsupportedOperationException();
				}
			};
		}
	}
}