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

org.granite.util.ServiceLoader Maven / Gradle / Ivy

The newest version!
/**
 *   GRANITE DATA SERVICES
 *   Copyright (C) 2006-2014 GRANITE DATA SERVICES S.A.S.
 *
 *   This file is part of the Granite Data Services Platform.
 *
 *   Granite Data Services is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) any later version.
 *
 *   Granite Data Services 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 Lesser
 *   General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 *   USA, or see .
 */
package org.granite.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Franck WOLFF
 */
public class ServiceLoader implements Iterable {
	
	// Can't use granite logger here, because service loader can be used to load a specific
	// logger implementation (stack overflow...)
	private static final Logger jdkLog = Logger.getLogger(ServiceLoader.class.getName());
	
	private final Class service;
	private final ClassLoader loader;
	
	private List serviceClassNames;
	
	private Class[] constructorParameterTypes = new Class[0];
	private Object[] constructorParameters = new Object[0];
	
	private ServiceLoader(Class service, ClassLoader loader, List servicesNames) {
		this.service = service;
		this.loader = loader;
		this.serviceClassNames = servicesNames;
	}
	
	public void setConstructorParameters(Class[] constructorParameterTypes, Object[] constructorParameters) {		
		
		if (constructorParameterTypes == null)
			constructorParameterTypes = new Class[0];
		if (constructorParameters == null)
			constructorParameters = new Object[0];
		
		if (constructorParameterTypes.length != constructorParameters.length)
			throw new IllegalArgumentException("constructor types and argurments must have the same size");

		this.constructorParameterTypes = constructorParameterTypes;
		this.constructorParameters = constructorParameters;
	}

	public ServicesIterator iterator() {
		return new ServicesIterator(loader, serviceClassNames.iterator(), constructorParameterTypes, constructorParameters);
	}
	
	public void reload() {
		ServiceLoader serviceLoaderTmp = load(service, loader);
		this.serviceClassNames = serviceLoaderTmp.serviceClassNames;
	}
	
	public static  ServiceLoader load(Class service) {
		return load(service, Thread.currentThread().getContextClassLoader());
	}

	public static  ServiceLoader load(final Class service, final ClassLoader loader) {
		List serviceClassNames = new ArrayList();
		
		try {
			// Standard Java platforms.
			Enumeration en = loader.getResources("META-INF/services/" + service.getName());
			while (en.hasMoreElements())
				parse(en.nextElement(), serviceClassNames);
			
			// Android support (META-INF/** files are not included in APK files).
			en = loader.getResources("meta_inf/services/" + service.getName());
			while (en.hasMoreElements())
				parse(en.nextElement(), serviceClassNames);
			
			return new ServiceLoader(service, loader, serviceClassNames);
		}
		catch (Exception e) {
			jdkLog.log(Level.SEVERE, "Could not load services of type " + service, e);
			throw new RuntimeException(e);
		}
	}
	
	private static void parse(URL serviceFile, List serviceClassNames) {
		try {
		    InputStream is = serviceFile.openStream();
		    try {
		    	BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
	
		    	String serviceClassName = null;
		    	while ((serviceClassName = reader.readLine()) != null) {
		    		int comment = serviceClassName.indexOf('#');
		    		if (comment >= 0)
		    			serviceClassName = serviceClassName.substring(0, comment);
			        serviceClassName = serviceClassName.trim();
			        if (serviceClassName.length() > 0) {
			        	jdkLog.log(Level.FINE, "Adding service " + serviceClassName + " from " + serviceFile);
			        	serviceClassNames.add(serviceClassName);
			        }
		    	}
		    }
		    finally {
		    	is.close();
		    }
		}
		catch (Exception e) {
			jdkLog.log(Level.SEVERE, "Could not parse service file " + serviceFile, e);
		}
	}
	
	public static class ServicesIterator implements Iterator {
		
		private final ClassLoader loader;
		private final Iterator serviceClassNames;
		private final Class[] constructorParameterTypes;
		private final Object[] constructorParameters;

		private ServicesIterator(ClassLoader loader, Iterator servicesNames, Class[] constructorParameterTypes, Object[] constructorParameters) {
			this.loader = loader;
			this.serviceClassNames = servicesNames;
			this.constructorParameterTypes = constructorParameterTypes;
			this.constructorParameters = constructorParameters;
		}

		public boolean hasNext() {
			return serviceClassNames.hasNext();
		}

		public S next() {
			String serviceClassName = serviceClassNames.next();
			jdkLog.log(Level.FINE, "Loading service " + serviceClassName);
			try {
				@SuppressWarnings("unchecked")
				Class serviceClass = (Class)loader.loadClass(serviceClassName);
				return serviceClass.getConstructor(constructorParameterTypes).newInstance(constructorParameters);
			}
			catch (Throwable t) {
				jdkLog.log(Level.SEVERE, "Could not load service " + serviceClassName, t);
				throw new RuntimeException(t);
			}
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}
	}	
}