001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.scan; 023 024import java.io.BufferedReader; 025import java.io.InputStream; 026import java.io.InputStreamReader; 027import java.net.URL; 028import java.util.ArrayList; 029import java.util.Enumeration; 030import java.util.Iterator; 031import java.util.List; 032import java.util.logging.Level; 033import java.util.logging.Logger; 034 035/** 036 * @author Franck WOLFF 037 */ 038public class ServiceLoader<S> implements Iterable<S> { 039 040 // Can't use granite logger here, because service loader can be used to load a specific 041 // logger implementation (stack overflow...) 042 private static final Logger jdkLog = Logger.getLogger(ServiceLoader.class.getName()); 043 044 private final Class<S> service; 045 private final ClassLoader loader; 046 047 private List<String> serviceClassNames; 048 049 private Class<?>[] constructorParameterTypes = new Class<?>[0]; 050 private Object[] constructorParameters = new Object[0]; 051 052 private ServiceLoader(Class<S> service, ClassLoader loader, List<String> servicesNames) { 053 this.service = service; 054 this.loader = loader; 055 this.serviceClassNames = servicesNames; 056 } 057 058 public void setConstructorParameters(Class<?>[] constructorParameterTypes, Object[] constructorParameters) { 059 060 if (constructorParameterTypes == null) 061 constructorParameterTypes = new Class<?>[0]; 062 if (constructorParameters == null) 063 constructorParameters = new Object[0]; 064 065 if (constructorParameterTypes.length != constructorParameters.length) 066 throw new IllegalArgumentException("constructor types and argurments must have the same size"); 067 068 this.constructorParameterTypes = constructorParameterTypes; 069 this.constructorParameters = constructorParameters; 070 } 071 072 public ServicesIterator<S> iterator() { 073 return new ServicesIterator<S>(loader, serviceClassNames.iterator(), constructorParameterTypes, constructorParameters); 074 } 075 076 public void reload() { 077 ServiceLoader<S> serviceLoaderTmp = load(service, loader); 078 this.serviceClassNames = serviceLoaderTmp.serviceClassNames; 079 } 080 081 public static <S> ServiceLoader<S> load(Class<S> service) { 082 return load(service, Thread.currentThread().getContextClassLoader()); 083 } 084 085 public static <S> ServiceLoader<S> load(final Class<S> service, final ClassLoader loader) { 086 List<String> serviceClassNames = new ArrayList<String>(); 087 088 try { 089 // Standard Java platforms. 090 Enumeration<URL> en = loader.getResources("META-INF/services/" + service.getName()); 091 while (en.hasMoreElements()) 092 parse(en.nextElement(), serviceClassNames); 093 094 // Android support (META-INF/** files are not included in APK files). 095 en = loader.getResources("meta_inf/services/" + service.getName()); 096 while (en.hasMoreElements()) 097 parse(en.nextElement(), serviceClassNames); 098 099 return new ServiceLoader<S>(service, loader, serviceClassNames); 100 } 101 catch (Exception e) { 102 jdkLog.log(Level.SEVERE, "Could not load services of type " + service, e); 103 throw new RuntimeException(e); 104 } 105 } 106 107 private static void parse(URL serviceFile, List<String> serviceClassNames) { 108 try { 109 InputStream is = serviceFile.openStream(); 110 try { 111 BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); 112 113 String serviceClassName = null; 114 while ((serviceClassName = reader.readLine()) != null) { 115 int comment = serviceClassName.indexOf('#'); 116 if (comment >= 0) 117 serviceClassName = serviceClassName.substring(0, comment); 118 serviceClassName = serviceClassName.trim(); 119 if (serviceClassName.length() > 0) { 120 jdkLog.log(Level.FINE, "Adding service " + serviceClassName + " from " + serviceFile); 121 serviceClassNames.add(serviceClassName); 122 } 123 } 124 } 125 finally { 126 is.close(); 127 } 128 } 129 catch (Exception e) { 130 jdkLog.log(Level.SEVERE, "Could not parse service file " + serviceFile, e); 131 } 132 } 133 134 public static class ServicesIterator<S> implements Iterator<S> { 135 136 private final ClassLoader loader; 137 private final Iterator<String> serviceClassNames; 138 private final Class<?>[] constructorParameterTypes; 139 private final Object[] constructorParameters; 140 141 private ServicesIterator(ClassLoader loader, Iterator<String> servicesNames, Class<?>[] constructorParameterTypes, Object[] constructorParameters) { 142 this.loader = loader; 143 this.serviceClassNames = servicesNames; 144 this.constructorParameterTypes = constructorParameterTypes; 145 this.constructorParameters = constructorParameters; 146 } 147 148 public boolean hasNext() { 149 return serviceClassNames.hasNext(); 150 } 151 152 public S next() { 153 String serviceClassName = serviceClassNames.next(); 154 jdkLog.log(Level.FINE, "Loading service " + serviceClassName); 155 try { 156 @SuppressWarnings("unchecked") 157 Class<? extends S> serviceClass = (Class<? extends S>)loader.loadClass(serviceClassName); 158 return serviceClass.getConstructor(constructorParameterTypes).newInstance(constructorParameters); 159 } 160 catch (Throwable t) { 161 jdkLog.log(Level.SEVERE, "Could not load service " + serviceClassName, t); 162 throw new RuntimeException(t); 163 } 164 } 165 166 public void remove() { 167 throw new UnsupportedOperationException(); 168 } 169 } 170}