1 /* 2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.misc; 27 28 import java.io.BufferedReader; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.InputStreamReader; 32 import java.net.URL; 33 import java.util.ArrayList; 34 import java.util.Enumeration; 35 import java.util.Iterator; 36 import java.util.List; 37 import java.util.NoSuchElementException; 38 import java.util.Set; 39 import java.util.TreeSet; 40 41 42 /** 43 * A simple service-provider lookup mechanism. A <i>service</i> is a 44 * well-known set of interfaces and (usually abstract) classes. A <i>service 45 * provider</i> is a specific implementation of a service. The classes in a 46 * provider typically implement the interfaces and subclass the classes defined 47 * in the service itself. Service providers may be installed in an 48 * implementation of the Java platform in the form of extensions, that is, jar 49 * files placed into any of the usual extension directories. Providers may 50 * also be made available by adding them to the applet or application class 51 * path or by some other platform-specific means. 52 * 53 * <p> In this lookup mechanism a service is represented by an interface or an 54 * abstract class. (A concrete class may be used, but this is not 55 * recommended.) A provider of a given service contains one or more concrete 56 * classes that extend this <i>service class</i> with data and code specific to 57 * the provider. This <i>provider class</i> will typically not be the entire 58 * provider itself but rather a proxy that contains enough information to 59 * decide whether the provider is able to satisfy a particular request together 60 * with code that can create the actual provider on demand. The details of 61 * provider classes tend to be highly service-specific; no single class or 62 * interface could possibly unify them, so no such class has been defined. The 63 * only requirement enforced here is that provider classes must have a 64 * zero-argument constructor so that they may be instantiated during lookup. 65 * 66 * <p> A service provider identifies itself by placing a provider-configuration 67 * file in the resource directory <tt>META-INF/services</tt>. The file's name 68 * should consist of the fully-qualified name of the abstract service class. 69 * The file should contain a list of fully-qualified concrete provider-class 70 * names, one per line. Space and tab characters surrounding each name, as 71 * well as blank lines, are ignored. The comment character is <tt>'#'</tt> 72 * (<tt>0x23</tt>); on each line all characters following the first comment 73 * character are ignored. The file must be encoded in UTF-8. 74 * 75 * <p> If a particular concrete provider class is named in more than one 76 * configuration file, or is named in the same configuration file more than 77 * once, then the duplicates will be ignored. The configuration file naming a 78 * particular provider need not be in the same jar file or other distribution 79 * unit as the provider itself. The provider must be accessible from the same 80 * class loader that was initially queried to locate the configuration file; 81 * note that this is not necessarily the class loader that found the file. 82 * 83 * <p> <b>Example:</b> Suppose we have a service class named 84 * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods: 85 * 86 * <pre> 87 * public abstract CharEncoder getEncoder(String encodingName); 88 * public abstract CharDecoder getDecoder(String encodingName); 89 * </pre> 90 * 91 * Each method returns an appropriate object or <tt>null</tt> if it cannot 92 * translate the given encoding. Typical <tt>CharCodec</tt> providers will 93 * support more than one encoding. 94 * 95 * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt> 96 * service then its jar file would contain the file 97 * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain 98 * the single line: 99 * 100 * <pre> 101 * sun.io.StandardCodec # Standard codecs for the platform 102 * </pre> 103 * 104 * To locate an encoder for a given encoding name, the internal I/O code would 105 * do something like this: 106 * 107 * <pre> 108 * CharEncoder getEncoder(String encodingName) { 109 * Iterator ps = Service.providers(CharCodec.class); 110 * while (ps.hasNext()) { 111 * CharCodec cc = (CharCodec)ps.next(); 112 * CharEncoder ce = cc.getEncoder(encodingName); 113 * if (ce != null) 114 * return ce; 115 * } 116 * return null; 117 * } 118 * </pre> 119 * 120 * The provider-lookup mechanism always executes in the security context of the 121 * caller. Trusted system code should typically invoke the methods in this 122 * class from within a privileged security context. 123 * 124 * @author Mark Reinhold 125 * @since 1.3 126 */ 127 128 public final class Service { 129 130 private static final String prefix = "META-INF/services/"; 131 132 private Service() { } 133 134 private static void fail(Class service, String msg, Throwable cause) 135 throws ServiceConfigurationError 136 { 137 ServiceConfigurationError sce 138 = new ServiceConfigurationError(service.getName() + ": " + msg); 139 sce.initCause(cause); 140 throw sce; 141 } 142 143 private static void fail(Class service, String msg) 144 throws ServiceConfigurationError 145 { 146 throw new ServiceConfigurationError(service.getName() + ": " + msg); 147 } 148 149 private static void fail(Class service, URL u, int line, String msg) 150 throws ServiceConfigurationError 151 { 152 fail(service, u + ":" + line + ": " + msg); 153 } 154 155 /** 156 * Parse a single line from the given configuration file, adding the name 157 * on the line to both the names list and the returned set iff the name is 158 * not already a member of the returned set. 159 */ 160 private static int parseLine(Class service, URL u, BufferedReader r, int lc, 161 List names, Set returned) 162 throws IOException, ServiceConfigurationError 163 { 164 String ln = r.readLine(); 165 if (ln == null) { 166 return -1; 167 } 168 int ci = ln.indexOf('#'); 169 if (ci >= 0) ln = ln.substring(0, ci); 170 ln = ln.trim(); 171 int n = ln.length(); 172 if (n != 0) { 173 if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) 174 fail(service, u, lc, "Illegal configuration-file syntax"); 175 int cp = ln.codePointAt(0); 176 if (!Character.isJavaIdentifierStart(cp)) 177 fail(service, u, lc, "Illegal provider-class name: " + ln); 178 for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { 179 cp = ln.codePointAt(i); 180 if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) 181 fail(service, u, lc, "Illegal provider-class name: " + ln); 182 } 183 if (!returned.contains(ln)) { 184 names.add(ln); 185 returned.add(ln); 186 } 187 } 188 return lc + 1; 189 } 190 191 /** 192 * Parse the content of the given URL as a provider-configuration file. 193 * 194 * @param service 195 * The service class for which providers are being sought; 196 * used to construct error detail strings 197 * 198 * @param url 199 * The URL naming the configuration file to be parsed 200 * 201 * @param returned 202 * A Set containing the names of provider classes that have already 203 * been returned. This set will be updated to contain the names 204 * that will be yielded from the returned <tt>Iterator</tt>. 205 * 206 * @return A (possibly empty) <tt>Iterator</tt> that will yield the 207 * provider-class names in the given configuration file that are 208 * not yet members of the returned set 209 * 210 * @throws ServiceConfigurationError 211 * If an I/O error occurs while reading from the given URL, or 212 * if a configuration-file format error is detected 213 */ 214 private static Iterator parse(Class service, URL u, Set returned) 215 throws ServiceConfigurationError 216 { 217 InputStream in = null; 218 BufferedReader r = null; 219 ArrayList names = new ArrayList(); 220 try { 221 in = u.openStream(); 222 r = new BufferedReader(new InputStreamReader(in, "utf-8")); 223 int lc = 1; 224 while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0); 225 } catch (IOException x) { 226 fail(service, ": " + x); 227 } finally { 228 try { 229 if (r != null) r.close(); 230 if (in != null) in.close(); 231 } catch (IOException y) { 232 fail(service, ": " + y); 233 } 234 } 235 return names.iterator(); 236 } 237 238 239 /** 240 * Private inner class implementing fully-lazy provider lookup 241 */ 242 private static class LazyIterator implements Iterator { 243 244 Class service; 245 ClassLoader loader; 246 Enumeration configs = null; 247 Iterator pending = null; 248 Set returned = new TreeSet(); 249 String nextName = null; 250 251 private LazyIterator(Class service, ClassLoader loader) { 252 this.service = service; 253 this.loader = loader; 254 } 255 256 public boolean hasNext() throws ServiceConfigurationError { 257 if (nextName != null) { 258 return true; 259 } 260 if (configs == null) { 261 try { 262 String fullName = prefix + service.getName(); 263 if (loader == null) 264 configs = ClassLoader.getSystemResources(fullName); 265 else 266 configs = loader.getResources(fullName); 267 } catch (IOException x) { 268 fail(service, ": " + x); 269 } 270 } 271 while ((pending == null) || !pending.hasNext()) { 272 if (!configs.hasMoreElements()) { 273 return false; 274 } 275 pending = parse(service, (URL)configs.nextElement(), returned); 276 } 277 nextName = (String)pending.next(); 278 return true; 279 } 280 281 public Object next() throws ServiceConfigurationError { 282 if (!hasNext()) { 283 throw new NoSuchElementException(); 284 } 285 String cn = nextName; 286 nextName = null; 287 Class<?> c = null; 288 try { 289 c = Class.forName(cn, false, loader); 290 } catch (ClassNotFoundException x) { 291 fail(service, 292 "Provider " + cn + " not found"); 293 } 294 if (!service.isAssignableFrom(c)) { 295 fail(service, 296 "Provider " + cn + " not a subtype"); 297 } 298 try { 299 return service.cast(c.newInstance()); 300 } catch (Throwable x) { 301 fail(service, 302 "Provider " + cn + " could not be instantiated: " + x, 303 x); 304 } 305 return null; /* This cannot happen */ 306 } 307 308 public void remove() { 309 throw new UnsupportedOperationException(); 310 } 311 312 } 313 314 315 /** 316 * Locates and incrementally instantiates the available providers of a 317 * given service using the given class loader. 318 * 319 * <p> This method transforms the name of the given service class into a 320 * provider-configuration filename as described above and then uses the 321 * <tt>getResources</tt> method of the given class loader to find all 322 * available files with that name. These files are then read and parsed to 323 * produce a list of provider-class names. The iterator that is returned 324 * uses the given class loader to lookup and then instantiate each element 325 * of the list. 326 * 327 * <p> Because it is possible for extensions to be installed into a running 328 * Java virtual machine, this method may return different results each time 329 * it is invoked. <p> 330 * 331 * @param service 332 * The service's abstract service class 333 * 334 * @param loader 335 * The class loader to be used to load provider-configuration files 336 * and instantiate provider classes, or <tt>null</tt> if the system 337 * class loader (or, failing that the bootstrap class loader) is to 338 * be used 339 * 340 * @return An <tt>Iterator</tt> that yields provider objects for the given 341 * service, in some arbitrary order. The iterator will throw a 342 * <tt>ServiceConfigurationError</tt> if a provider-configuration 343 * file violates the specified format or if a provider class cannot 344 * be found and instantiated. 345 * 346 * @throws ServiceConfigurationError 347 * If a provider-configuration file violates the specified format 348 * or names a provider class that cannot be found and instantiated 349 * 350 * @see #providers(java.lang.Class) 351 * @see #installedProviders(java.lang.Class) 352 */ 353 public static Iterator providers(Class service, ClassLoader loader) 354 throws ServiceConfigurationError 355 { 356 return new LazyIterator(service, loader); 357 } 358 359 360 /** 361 * Locates and incrementally instantiates the available providers of a 362 * given service using the context class loader. This convenience method 363 * is equivalent to 364 * 365 * <pre> 366 * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 367 * return Service.providers(service, cl); 368 * </pre> 369 * 370 * @param service 371 * The service's abstract service class 372 * 373 * @return An <tt>Iterator</tt> that yields provider objects for the given 374 * service, in some arbitrary order. The iterator will throw a 375 * <tt>ServiceConfigurationError</tt> if a provider-configuration 376 * file violates the specified format or if a provider class cannot 377 * be found and instantiated. 378 * 379 * @throws ServiceConfigurationError 380 * If a provider-configuration file violates the specified format 381 * or names a provider class that cannot be found and instantiated 382 * 383 * @see #providers(java.lang.Class, java.lang.ClassLoader) 384 */ 385 public static Iterator providers(Class service) 386 throws ServiceConfigurationError 387 { 388 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 389 return Service.providers(service, cl); 390 } 391 392 393 /** 394 * Locates and incrementally instantiates the available providers of a 395 * given service using the extension class loader. This convenience method 396 * simply locates the extension class loader, call it 397 * <tt>extClassLoader</tt>, and then does 398 * 399 * <pre> 400 * return Service.providers(service, extClassLoader); 401 * </pre> 402 * 403 * If the extension class loader cannot be found then the system class 404 * loader is used; if there is no system class loader then the bootstrap 405 * class loader is used. 406 * 407 * @param service 408 * The service's abstract service class 409 * 410 * @return An <tt>Iterator</tt> that yields provider objects for the given 411 * service, in some arbitrary order. The iterator will throw a 412 * <tt>ServiceConfigurationError</tt> if a provider-configuration 413 * file violates the specified format or if a provider class cannot 414 * be found and instantiated. 415 * 416 * @throws ServiceConfigurationError 417 * If a provider-configuration file violates the specified format 418 * or names a provider class that cannot be found and instantiated 419 * 420 * @see #providers(java.lang.Class, java.lang.ClassLoader) 421 */ 422 public static Iterator installedProviders(Class service) 423 throws ServiceConfigurationError 424 { 425 ClassLoader cl = ClassLoader.getSystemClassLoader(); 426 ClassLoader prev = null; 427 while (cl != null) { 428 prev = cl; 429 cl = cl.getParent(); 430 } 431 return Service.providers(service, prev); 432 } 433 434 } 435