49 * in the service itself. Service providers may be installed in an 50 * implementation of the Java platform in the form of extensions, that is, jar 51 * files placed into any of the usual extension directories. Providers may 52 * also be made available by adding them to the applet or application class 53 * path or by some other platform-specific means. 54 * <p/> 55 * <p> In this lookup mechanism a service is represented by an interface or an 56 * abstract class. (A concrete class may be used, but this is not 57 * recommended.) A provider of a given service contains one or more concrete 58 * classes that extend this <i>service class</i> with data and code specific to 59 * the provider. This <i>provider class</i> will typically not be the entire 60 * provider itself but rather a proxy that contains enough information to 61 * decide whether the provider is able to satisfy a particular request together 62 * with code that can create the actual provider on demand. The details of 63 * provider classes tend to be highly service-specific; no single class or 64 * interface could possibly unify them, so no such class has been defined. The 65 * only requirement enforced here is that provider classes must have a 66 * zero-argument constructor so that they may be instantiated during lookup. 67 * <p/> 68 * <p> A service provider identifies itself by placing a provider-configuration 69 * file in the resource directory <tt>META-INF/services</tt>. The file's name 70 * should consist of the fully-qualified name of the abstract service class. 71 * The file should contain a list of fully-qualified concrete provider-class 72 * names, one per line. Space and tab characters surrounding each name, as 73 * well as blank lines, are ignored. The comment character is <tt>'#'</tt> 74 * (<tt>0x23</tt>); on each line all characters following the first comment 75 * character are ignored. The file must be encoded in UTF-8. 76 * <p/> 77 * <p> If a particular concrete provider class is named in more than one 78 * configuration file, or is named in the same configuration file more than 79 * once, then the duplicates will be ignored. The configuration file naming a 80 * particular provider need not be in the same jar file or other distribution 81 * unit as the provider itself. The provider must be accessible from the same 82 * class loader that was initially queried to locate the configuration file; 83 * note that this is not necessarily the class loader that found the file. 84 * <p/> 85 * <p> <b>Example:</b> Suppose we have a service class named 86 * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods: 87 * <p/> 88 * <pre> 89 * public abstract CharEncoder getEncoder(String encodingName); 90 * public abstract CharDecoder getDecoder(String encodingName); 91 * </pre> 92 * <p/> 93 * Each method returns an appropriate object or <tt>null</tt> if it cannot 94 * translate the given encoding. Typical <tt>CharCodec</tt> providers will 95 * support more than one encoding. 96 * <p/> 97 * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt> 98 * service then its jar file would contain the file 99 * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain 100 * the single line: 101 * <p/> 102 * <pre> 103 * sun.io.StandardCodec # Standard codecs for the platform 104 * </pre> 105 * <p/> 106 * To locate an encoder for a given encoding name, the internal I/O code would 107 * do something like this: 108 * <p/> 109 * <pre> 110 * CharEncoder getEncoder(String encodingName) { 111 * for( CharCodec cc : ServiceFinder.find(CharCodec.class) ) { 112 * CharEncoder ce = cc.getEncoder(encodingName); 113 * if (ce != null) 114 * return ce; 115 * } 116 * return null; 117 * } 118 * </pre> 119 * <p/> 122 * class from within a privileged security context. 123 * 124 * @author Mark Reinhold 125 * @version 1.11, 03/12/19 126 * @since 1.3 127 */ 128 final class ServiceFinder<T> implements Iterable<T> { 129 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ServiceFinder.class); 130 131 private static final String prefix = "META-INF/services/"; 132 133 private final Class<T> serviceClass; 134 private final ClassLoader classLoader; 135 136 /** 137 * Locates and incrementally instantiates the available providers of a 138 * given service using the given class loader. 139 * <p/> 140 * <p> This method transforms the name of the given service class into a 141 * provider-configuration filename as described above and then uses the 142 * <tt>getResources</tt> method of the given class loader to find all 143 * available files with that name. These files are then read and parsed to 144 * produce a list of provider-class names. The iterator that is returned 145 * uses the given class loader to lookup and then instantiate each element 146 * of the list. 147 * <p/> 148 * <p> Because it is possible for extensions to be installed into a running 149 * Java virtual machine, this method may return different results each time 150 * it is invoked. <p> 151 * 152 * @param service The service's abstract service class 153 * @param loader The class loader to be used to load provider-configuration files 154 * and instantiate provider classes, or <tt>null</tt> if the system 155 * class loader (or, failing that the bootstrap class loader) is to 156 * be used 157 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 158 * or names a provider class that cannot be found and instantiated 159 * @see #find(Class) 160 */ 161 static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoader loader) { 162 if (null==service) { 163 throw LOGGER.logSevereException(new NullPointerException(LocalizationMessages.WSP_0032_SERVICE_CAN_NOT_BE_NULL())); 164 } 165 return new ServiceFinder<T>(service,loader); 166 } 167 168 /** 169 * Locates and incrementally instantiates the available providers of a 170 * given service using the context class loader. This convenience method 171 * is equivalent to 172 * <p/> 173 * <pre> 174 * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 176 * </pre> 177 * 178 * @param service The service's abstract service class 179 * 180 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 181 * or names a provider class that cannot be found and instantiated 182 * @see #find(Class, ClassLoader) 183 */ 184 public static <T> ServiceFinder<T> find(final Class<T> service) { 185 return find(service,Thread.currentThread().getContextClassLoader()); 186 } 187 188 private ServiceFinder(Class<T> service, ClassLoader loader) { 189 this.serviceClass = service; 190 this.classLoader = loader; 191 } 192 193 /** 194 * Returns discovered objects incrementally. 195 * 196 * @return An <tt>Iterator</tt> that yields provider objects for the given 197 * service, in some arbitrary order. The iterator will throw a 198 * <tt>ServiceConfigurationError</tt> if a provider-configuration 199 * file violates the specified format or if a provider class cannot 200 * be found and instantiated. 201 */ 202 public Iterator<T> iterator() { 203 return new LazyIterator<T>(serviceClass,classLoader); 204 } 205 206 /** 207 * Returns discovered objects all at once. 208 * 209 * @return 210 * can be empty but never null. 211 * 212 * @throws ServiceConfigurationError 213 */ 214 @SuppressWarnings({"unchecked"}) 215 public T[] toArray() { 216 List<T> result = new ArrayList<T>(); 217 for (T t : this) { 218 result.add(t); 267 cp = ln.codePointAt(i); 268 if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) 269 fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null); 270 } 271 if (!returned.contains(ln)) { 272 names.add(ln); 273 returned.add(ln); 274 } 275 } 276 return lc + 1; 277 } 278 279 /** 280 * Parse the content of the given URL as a provider-configuration file. 281 * 282 * @param service The service class for which providers are being sought; 283 * used to construct error detail strings 284 * @param u The URL naming the configuration file to be parsed 285 * @param returned A Set containing the names of provider classes that have already 286 * been returned. This set will be updated to contain the names 287 * that will be yielded from the returned <tt>Iterator</tt>. 288 * @return A (possibly empty) <tt>Iterator</tt> that will yield the 289 * provider-class names in the given configuration file that are 290 * not yet members of the returned set 291 * @throws ServiceConfigurationError If an I/O error occurs while reading from the given URL, or 292 * if a configuration-file format error is detected 293 */ 294 @SuppressWarnings({"StatementWithEmptyBody"}) 295 private static Iterator<String> parse(Class service, URL u, Set<String> returned) 296 throws ServiceConfigurationError { 297 InputStream in = null; 298 BufferedReader r = null; 299 ArrayList<String> names = new ArrayList<String>(); 300 try { 301 in = u.openStream(); 302 r = new BufferedReader(new InputStreamReader(in, "utf-8")); 303 int lc = 1; 304 while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0) ; 305 } catch (IOException x) { 306 fail(service, ": " + x, x); 307 } finally { 308 try { | 49 * in the service itself. Service providers may be installed in an 50 * implementation of the Java platform in the form of extensions, that is, jar 51 * files placed into any of the usual extension directories. Providers may 52 * also be made available by adding them to the applet or application class 53 * path or by some other platform-specific means. 54 * <p/> 55 * <p> In this lookup mechanism a service is represented by an interface or an 56 * abstract class. (A concrete class may be used, but this is not 57 * recommended.) A provider of a given service contains one or more concrete 58 * classes that extend this <i>service class</i> with data and code specific to 59 * the provider. This <i>provider class</i> will typically not be the entire 60 * provider itself but rather a proxy that contains enough information to 61 * decide whether the provider is able to satisfy a particular request together 62 * with code that can create the actual provider on demand. The details of 63 * provider classes tend to be highly service-specific; no single class or 64 * interface could possibly unify them, so no such class has been defined. The 65 * only requirement enforced here is that provider classes must have a 66 * zero-argument constructor so that they may be instantiated during lookup. 67 * <p/> 68 * <p> A service provider identifies itself by placing a provider-configuration 69 * file in the resource directory {@code META-INF/services}. The file's name 70 * should consist of the fully-qualified name of the abstract service class. 71 * The file should contain a list of fully-qualified concrete provider-class 72 * names, one per line. Space and tab characters surrounding each name, as 73 * well as blank lines, are ignored. The comment character is {@code '#'} 74 * ({@code 0x23}); on each line all characters following the first comment 75 * character are ignored. The file must be encoded in UTF-8. 76 * <p/> 77 * <p> If a particular concrete provider class is named in more than one 78 * configuration file, or is named in the same configuration file more than 79 * once, then the duplicates will be ignored. The configuration file naming a 80 * particular provider need not be in the same jar file or other distribution 81 * unit as the provider itself. The provider must be accessible from the same 82 * class loader that was initially queried to locate the configuration file; 83 * note that this is not necessarily the class loader that found the file. 84 * <p/> 85 * <p> <b>Example:</b> Suppose we have a service class named 86 * {@code java.io.spi.CharCodec}. It has two abstract methods: 87 * <p/> 88 * <pre> 89 * public abstract CharEncoder getEncoder(String encodingName); 90 * public abstract CharDecoder getDecoder(String encodingName); 91 * </pre> 92 * <p/> 93 * Each method returns an appropriate object or {@code null} if it cannot 94 * translate the given encoding. Typical {@code CharCodec} providers will 95 * support more than one encoding. 96 * <p/> 97 * <p> If {@code sun.io.StandardCodec} is a provider of the {@code CharCodec} 98 * service then its jar file would contain the file 99 * {@code META-INF/services/java.io.spi.CharCodec}. This file would contain 100 * the single line: 101 * <p/> 102 * <pre> 103 * sun.io.StandardCodec # Standard codecs for the platform 104 * </pre> 105 * <p/> 106 * To locate an encoder for a given encoding name, the internal I/O code would 107 * do something like this: 108 * <p/> 109 * <pre> 110 * CharEncoder getEncoder(String encodingName) { 111 * for( CharCodec cc : ServiceFinder.find(CharCodec.class) ) { 112 * CharEncoder ce = cc.getEncoder(encodingName); 113 * if (ce != null) 114 * return ce; 115 * } 116 * return null; 117 * } 118 * </pre> 119 * <p/> 122 * class from within a privileged security context. 123 * 124 * @author Mark Reinhold 125 * @version 1.11, 03/12/19 126 * @since 1.3 127 */ 128 final class ServiceFinder<T> implements Iterable<T> { 129 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ServiceFinder.class); 130 131 private static final String prefix = "META-INF/services/"; 132 133 private final Class<T> serviceClass; 134 private final ClassLoader classLoader; 135 136 /** 137 * Locates and incrementally instantiates the available providers of a 138 * given service using the given class loader. 139 * <p/> 140 * <p> This method transforms the name of the given service class into a 141 * provider-configuration filename as described above and then uses the 142 * {@code getResources} method of the given class loader to find all 143 * available files with that name. These files are then read and parsed to 144 * produce a list of provider-class names. The iterator that is returned 145 * uses the given class loader to lookup and then instantiate each element 146 * of the list. 147 * <p/> 148 * <p> Because it is possible for extensions to be installed into a running 149 * Java virtual machine, this method may return different results each time 150 * it is invoked. <p> 151 * 152 * @param service The service's abstract service class 153 * @param loader The class loader to be used to load provider-configuration files 154 * and instantiate provider classes, or {@code null} if the system 155 * class loader (or, failing that the bootstrap class loader) is to 156 * be used 157 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 158 * or names a provider class that cannot be found and instantiated 159 * @see #find(Class) 160 */ 161 static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoader loader) { 162 if (null==service) { 163 throw LOGGER.logSevereException(new NullPointerException(LocalizationMessages.WSP_0032_SERVICE_CAN_NOT_BE_NULL())); 164 } 165 return new ServiceFinder<T>(service,loader); 166 } 167 168 /** 169 * Locates and incrementally instantiates the available providers of a 170 * given service using the context class loader. This convenience method 171 * is equivalent to 172 * <p/> 173 * <pre> 174 * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 176 * </pre> 177 * 178 * @param service The service's abstract service class 179 * 180 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 181 * or names a provider class that cannot be found and instantiated 182 * @see #find(Class, ClassLoader) 183 */ 184 public static <T> ServiceFinder<T> find(final Class<T> service) { 185 return find(service,Thread.currentThread().getContextClassLoader()); 186 } 187 188 private ServiceFinder(Class<T> service, ClassLoader loader) { 189 this.serviceClass = service; 190 this.classLoader = loader; 191 } 192 193 /** 194 * Returns discovered objects incrementally. 195 * 196 * @return An {@code Iterator} that yields provider objects for the given 197 * service, in some arbitrary order. The iterator will throw a 198 * {@code ServiceConfigurationError} if a provider-configuration 199 * file violates the specified format or if a provider class cannot 200 * be found and instantiated. 201 */ 202 public Iterator<T> iterator() { 203 return new LazyIterator<T>(serviceClass,classLoader); 204 } 205 206 /** 207 * Returns discovered objects all at once. 208 * 209 * @return 210 * can be empty but never null. 211 * 212 * @throws ServiceConfigurationError 213 */ 214 @SuppressWarnings({"unchecked"}) 215 public T[] toArray() { 216 List<T> result = new ArrayList<T>(); 217 for (T t : this) { 218 result.add(t); 267 cp = ln.codePointAt(i); 268 if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) 269 fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null); 270 } 271 if (!returned.contains(ln)) { 272 names.add(ln); 273 returned.add(ln); 274 } 275 } 276 return lc + 1; 277 } 278 279 /** 280 * Parse the content of the given URL as a provider-configuration file. 281 * 282 * @param service The service class for which providers are being sought; 283 * used to construct error detail strings 284 * @param u The URL naming the configuration file to be parsed 285 * @param returned A Set containing the names of provider classes that have already 286 * been returned. This set will be updated to contain the names 287 * that will be yielded from the returned {@code Iterator}. 288 * @return A (possibly empty) {@code Iterator} that will yield the 289 * provider-class names in the given configuration file that are 290 * not yet members of the returned set 291 * @throws ServiceConfigurationError If an I/O error occurs while reading from the given URL, or 292 * if a configuration-file format error is detected 293 */ 294 @SuppressWarnings({"StatementWithEmptyBody"}) 295 private static Iterator<String> parse(Class service, URL u, Set<String> returned) 296 throws ServiceConfigurationError { 297 InputStream in = null; 298 BufferedReader r = null; 299 ArrayList<String> names = new ArrayList<String>(); 300 try { 301 in = u.openStream(); 302 r = new BufferedReader(new InputStreamReader(in, "utf-8")); 303 int lc = 1; 304 while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0) ; 305 } catch (IOException x) { 306 fail(service, ": " + x, x); 307 } finally { 308 try { |