/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 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 <http://www.gnu.org/licenses/>.
*/
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<S> implements Iterable<S> {
// 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<S> service;
private final ClassLoader loader;
private List<String> serviceClassNames;
private Class<?>[] constructorParameterTypes = new Class<?>[0];
private Object[] constructorParameters = new Object[0];
private ServiceLoader(Class<S> service, ClassLoader loader, List<String> 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<S> iterator() {
return new ServicesIterator<S>(loader, serviceClassNames.iterator(), constructorParameterTypes, constructorParameters);
}
public void reload() {
ServiceLoader<S> serviceLoaderTmp = load(service, loader);
this.serviceClassNames = serviceLoaderTmp.serviceClassNames;
}
public static <S> ServiceLoader<S> load(Class<S> service) {
return load(service, Thread.currentThread().getContextClassLoader());
}
public static <S> ServiceLoader<S> load(final Class<S> service, final ClassLoader loader) {
List<String> serviceClassNames = new ArrayList<String>();
try {
// Standard Java platforms.
Enumeration<URL> 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<S>(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<String> 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<S> implements Iterator<S> {
private final ClassLoader loader;
private final Iterator<String> serviceClassNames;
private final Class<?>[] constructorParameterTypes;
private final Object[] constructorParameters;
private ServicesIterator(ClassLoader loader, Iterator<String> 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<? extends S> serviceClass = (Class<? extends S>)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();
}
}
}