Frames | No Frames |
1: /* ServiceFactory.java -- Factory for plug-in services. 2: Copyright (C) 2004 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.classpath; 40: 41: import java.io.BufferedReader; 42: import java.io.IOException; 43: import java.io.InputStreamReader; 44: import java.net.URL; 45: import java.security.AccessControlContext; 46: import java.security.AccessController; 47: import java.security.PrivilegedActionException; 48: import java.util.Collections; 49: import java.util.Enumeration; 50: import java.util.Iterator; 51: import java.util.NoSuchElementException; 52: import java.util.logging.Level; 53: import java.util.logging.LogRecord; 54: import java.util.logging.Logger; 55: 56: 57: /** 58: * A factory for plug-ins that conform to a service provider 59: * interface. This is a general mechanism that gets used by a number 60: * of packages in the Java API. For instance, {@link 61: * java.nio.charset.spi.CharsetProvider} allows to write custom 62: * encoders and decoders for character sets, {@link 63: * javax.imageio.spi.ImageReaderSpi} allows to support custom image 64: * formats, and {@link javax.print.PrintService} makes it possible to 65: * write custom printer drivers. 66: * 67: * <p>The plug-ins are concrete implementations of the service 68: * provider interface, which is defined as an interface or an abstract 69: * class. The implementation classes must be public and have a public 70: * constructor that takes no arguments. 71: * 72: * <p>Plug-ins are usually deployed in JAR files. A JAR that provides 73: * an implementation of a service must declare this in a resource file 74: * whose name is the fully qualified service name and whose location 75: * is the directory <code>META-INF/services</code>. This UTF-8 encoded 76: * text file lists, on separate lines, the fully qualified names of 77: * the concrete implementations. Thus, one JAR file can provide an 78: * arbitrary number of implementations for an arbitrary count of 79: * service provider interfaces. 80: * 81: * <p><b>Example</b> 82: * 83: * <p>For example, a JAR might provide two implementations of the 84: * service provider interface <code>org.foo.ThinkService</code>, 85: * namely <code>com.acme.QuickThinker</code> and 86: * <code>com.acme.DeepThinker</code>. The code for <code>QuickThinker</code> 87: * woud look as follows: 88: * 89: * <pre> 90: * package com.acme; 91: * 92: * /** 93: * * Provices a super-quick, but not very deep implementation of ThinkService. 94: * */ 95: * public class QuickThinker 96: * implements org.foo.ThinkService 97: * { 98: * /** 99: * * Constructs a new QuickThinker. The service factory (which is 100: * * part of the Java environment) calls this no-argument constructor 101: * * when it looks up the available implementations of ThinkService. 102: * * 103: * * <p>Note that an application might query all available 104: * * ThinkService providers, but use just one of them. Therefore, 105: * * constructing an instance should be very inexpensive. For example, 106: * * large data structures should only be allocated when the service 107: * * actually gets used. 108: * */ 109: * public QuickThinker() 110: * { 111: * } 112: * 113: * /** 114: * * Returns the speed of this ThinkService in thoughts per second. 115: * * Applications can choose among the available service providers 116: * * based on this value. 117: * */ 118: * public double getSpeed() 119: * { 120: * return 314159.2654; 121: * } 122: * 123: * /** 124: * * Produces a thought. While the returned thoughts are not very 125: * * deep, they are generated in very short time. 126: * */ 127: * public Thought think() 128: * { 129: * return null; 130: * } 131: * } 132: * </pre> 133: * 134: * <p>The code for <code>com.acme.DeepThinker</code> is left as an 135: * exercise to the reader. 136: * 137: * <p>Acme’s <code>ThinkService</code> plug-in gets deployed as 138: * a JAR file. Besides the bytecode and resources for 139: * <code>QuickThinker</code> and <code>DeepThinker</code>, it also 140: * contains the text file 141: * <code>META-INF/services/org.foo.ThinkService</code>: 142: * 143: * <pre> 144: * # Available implementations of org.foo.ThinkService 145: * com.acme.QuickThinker 146: * com.acme.DeepThinker 147: * </pre> 148: * 149: * <p><b>Thread Safety</b> 150: * 151: * <p>It is safe to use <code>ServiceFactory</code> from multiple 152: * concurrent threads without external synchronization. 153: * 154: * <p><b>Note for User Applications</b> 155: * 156: * <p>User applications that want to load plug-ins should not directly 157: * use <code>gnu.classpath.ServiceFactory</code>, because this class 158: * is only available in Java environments that are based on GNU 159: * Classpath. Instead, it is recommended that user applications call 160: * {@link 161: * javax.imageio.spi.ServiceRegistry#lookupProviders(Class)}. This API 162: * is actually independent of image I/O, and it is available on every 163: * environment. 164: * 165: * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a> 166: */ 167: public final class ServiceFactory 168: { 169: /** 170: * A logger that gets informed when a service gets loaded, or 171: * when there is a problem with loading a service. 172: * 173: * <p>Because {@link java.util.logging.Logger#getLogger(String)} 174: * is thread-safe, we do not need to worry about synchronization 175: * here. 176: */ 177: private static final Logger LOGGER = Logger.getLogger("gnu.classpath"); 178: 179: 180: /** 181: * Declared private in order to prevent constructing instances of 182: * this utility class. 183: */ 184: private ServiceFactory() 185: { 186: } 187: 188: 189: /** 190: * Finds service providers that are implementing the specified 191: * Service Provider Interface. 192: * 193: * <p><b>On-demand loading:</b> Loading and initializing service 194: * providers is delayed as much as possible. The rationale is that 195: * typical clients will iterate through the set of installed service 196: * providers until one is found that matches some criteria (like 197: * supported formats, or quality of service). In such scenarios, it 198: * might make sense to install only the frequently needed service 199: * providers on the local machine. More exotic providers can be put 200: * onto a server; the server will only be contacted when no suitable 201: * service could be found locally. 202: * 203: * <p><b>Security considerations:</b> Any loaded service providers 204: * are loaded through the specified ClassLoader, or the system 205: * ClassLoader if <code>classLoader</code> is 206: * <code>null</code>. When <code>lookupProviders</code> is called, 207: * the current {@link AccessControlContext} gets recorded. This 208: * captured security context will determine the permissions when 209: * services get loaded via the <code>next()</code> method of the 210: * returned <code>Iterator</code>. 211: * 212: * @param spi the service provider interface which must be 213: * implemented by any loaded service providers. 214: * 215: * @param loader the class loader that will be used to load the 216: * service providers, or <code>null</code> for the system class 217: * loader. For using the context class loader, see {@link 218: * #lookupProviders(Class)}. 219: * 220: * @return an iterator over instances of <code>spi</code>. 221: * 222: * @throws IllegalArgumentException if <code>spi</code> is 223: * <code>null</code>. 224: */ 225: public static Iterator lookupProviders(Class spi, 226: ClassLoader loader) 227: { 228: String resourceName; 229: Enumeration urls; 230: 231: if (spi == null) 232: throw new IllegalArgumentException(); 233: 234: if (loader == null) 235: loader = ClassLoader.getSystemClassLoader(); 236: 237: resourceName = "META-INF/services/" + spi.getName(); 238: try 239: { 240: urls = loader.getResources(resourceName); 241: } 242: catch (IOException ioex) 243: { 244: /* If an I/O error occurs here, we cannot provide any service 245: * providers. In this case, we simply return an iterator that 246: * does not return anything (no providers installed). 247: */ 248: log(Level.WARNING, "cannot access {0}", resourceName, ioex); 249: return Collections.EMPTY_LIST.iterator(); 250: } 251: 252: return new ServiceIterator(spi, urls, loader, 253: AccessController.getContext()); 254: } 255: 256: 257: /** 258: * Finds service providers that are implementing the specified 259: * Service Provider Interface, using the context class loader 260: * for loading providers. 261: * 262: * @param spi the service provider interface which must be 263: * implemented by any loaded service providers. 264: * 265: * @return an iterator over instances of <code>spi</code>. 266: * 267: * @throws IllegalArgumentException if <code>spi</code> is 268: * <code>null</code>. 269: * 270: * @see #lookupProviders(Class, ClassLoader) 271: */ 272: public static Iterator lookupProviders(Class spi) 273: { 274: ClassLoader ctxLoader; 275: 276: ctxLoader = Thread.currentThread().getContextClassLoader(); 277: return lookupProviders(spi, ctxLoader); 278: } 279: 280: 281: /** 282: * An iterator over service providers that are listed in service 283: * provider configuration files, which get passed as an Enumeration 284: * of URLs. This is a helper class for {@link 285: * ServiceFactory#lookupProviders(Class, ClassLoader)}. 286: * 287: * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a> 288: */ 289: private static final class ServiceIterator 290: implements Iterator 291: { 292: /** 293: * The service provider interface (usually an interface, sometimes 294: * an abstract class) which the services must implement. 295: */ 296: private final Class spi; 297: 298: 299: /** 300: * An Enumeration<URL> over the URLs that contain a resource 301: * <code>META-INF/services/<org.foo.SomeService></code>, 302: * as returned by {@link ClassLoader#getResources(String)}. 303: */ 304: private final Enumeration urls; 305: 306: 307: /** 308: * The class loader used for loading service providers. 309: */ 310: private final ClassLoader loader; 311: 312: 313: /** 314: * The security context used when loading and initializing service 315: * providers. We want to load and initialize all plug-in service 316: * providers under the same security context, namely the one that 317: * was active when {@link #lookupProviders(Class, ClassLoader)} has 318: * been called. 319: */ 320: private final AccessControlContext securityContext; 321: 322: 323: /** 324: * A reader for the current file listing class names of service 325: * implementors, or <code>null</code> when the last reader has 326: * been fetched. 327: */ 328: private BufferedReader reader; 329: 330: 331: /** 332: * The URL currently being processed. This is only used for 333: * emitting error messages. 334: */ 335: private URL currentURL; 336: 337: 338: /** 339: * The service provider that will be returned by the next call to 340: * {@link #next()}, or <code>null</code> if the iterator has 341: * already returned all service providers. 342: */ 343: private Object nextProvider; 344: 345: 346: /** 347: * Constructs an Iterator that loads and initializes services on 348: * demand. 349: * 350: * @param spi the service provider interface which the services 351: * must implement. Usually, this is a Java interface type, but it 352: * might also be an abstract class or even a concrete superclass. 353: * 354: * @param urls an Enumeration<URL> over the URLs that contain a 355: * resource 356: * <code>META-INF/services/<org.foo.SomeService></code>, as 357: * determined by {@link ClassLoader#getResources(String)}. 358: * 359: * @param loader the ClassLoader that gets used for loading 360: * service providers. 361: * 362: * @param securityContext the security context to use when loading 363: * and initializing service providers. 364: */ 365: ServiceIterator(Class spi, Enumeration urls, ClassLoader loader, 366: AccessControlContext securityContext) 367: { 368: this.spi = spi; 369: this.urls = urls; 370: this.loader = loader; 371: this.securityContext = securityContext; 372: this.nextProvider = loadNextServiceProvider(); 373: } 374: 375: 376: /** 377: * @throws NoSuchElementException if {@link #hasNext} returns 378: * <code>false</code>. 379: */ 380: public Object next() 381: { 382: Object result; 383: 384: if (!hasNext()) 385: throw new NoSuchElementException(); 386: 387: result = nextProvider; 388: nextProvider = loadNextServiceProvider(); 389: return result; 390: } 391: 392: 393: public boolean hasNext() 394: { 395: return nextProvider != null; 396: } 397: 398: 399: public void remove() 400: { 401: throw new UnsupportedOperationException(); 402: } 403: 404: 405: private Object loadNextServiceProvider() 406: { 407: String line; 408: 409: if (reader == null) 410: advanceReader(); 411: 412: for (;;) 413: { 414: /* If we have reached the last provider list, we cannot 415: * retrieve any further lines. 416: */ 417: if (reader == null) 418: return null; 419: 420: try 421: { 422: line = reader.readLine(); 423: } 424: catch (IOException readProblem) 425: { 426: log(Level.WARNING, "IOException upon reading {0}", currentURL, 427: readProblem); 428: line = null; 429: } 430: 431: /* When we are at the end of one list of services, 432: * switch over to the next one. 433: */ 434: if (line == null) 435: { 436: advanceReader(); 437: continue; 438: } 439: 440: 441: // Skip whitespace at the beginning and end of each line. 442: line = line.trim(); 443: 444: // Skip empty lines. 445: if (line.length() == 0) 446: continue; 447: 448: // Skip comment lines. 449: if (line.charAt(0) == '#') 450: continue; 451: 452: try 453: { 454: log(Level.FINE, 455: "Loading service provider \"{0}\", specified" 456: + " by \"META-INF/services/{1}\" in {2}.", 457: new Object[] { line, spi.getName(), currentURL }, 458: null); 459: 460: /* Load the class in the security context that was 461: * active when calling lookupProviders. 462: */ 463: return AccessController.doPrivileged( 464: new ServiceProviderLoadingAction(spi, line, loader), 465: securityContext); 466: } 467: catch (Exception ex) 468: { 469: String msg = "Cannot load service provider class \"{0}\"," 470: + " specified by \"META-INF/services/{1}\" in {2}"; 471: if (ex instanceof PrivilegedActionException 472: && ex.getCause() instanceof ClassCastException) 473: msg = "Service provider class \"{0}\" is not an instance" 474: + " of \"{1}\". Specified" 475: + " by \"META-INF/services/{1}\" in {2}."; 476: 477: log(Level.WARNING, msg, 478: new Object[] { line, spi.getName(), currentURL }, 479: ex); 480: continue; 481: } 482: } 483: } 484: 485: 486: private void advanceReader() 487: { 488: do 489: { 490: if (reader != null) 491: { 492: try 493: { 494: reader.close(); 495: log(Level.FINE, "closed {0}", currentURL, null); 496: } 497: catch (Exception ex) 498: { 499: log(Level.WARNING, "cannot close {0}", currentURL, ex); 500: } 501: reader = null; 502: currentURL = null; 503: } 504: 505: if (!urls.hasMoreElements()) 506: return; 507: 508: currentURL = (URL) urls.nextElement(); 509: try 510: { 511: reader = new BufferedReader(new InputStreamReader( 512: currentURL.openStream(), "UTF-8")); 513: log(Level.FINE, "opened {0}", currentURL, null); 514: } 515: catch (Exception ex) 516: { 517: log(Level.WARNING, "cannot open {0}", currentURL, ex); 518: } 519: } 520: while (reader == null); 521: } 522: } 523: 524: 525: // Package-private to avoid a trampoline. 526: /** 527: * Passes a log message to the <code>java.util.logging</code> 528: * framework. This call returns very quickly if no log message will 529: * be produced, so there is not much overhead in the standard case. 530: * 531: * @param level the severity of the message, for instance {@link 532: * Level#WARNING}. 533: * 534: * @param msg the log message, for instance <code>“Could not 535: * load {0}.”</code> 536: * 537: * @param param the parameter(s) for the log message, or 538: * <code>null</code> if <code>msg</code> does not specify any 539: * parameters. If <code>param</code> is not an array, an array with 540: * <code>param</code> as its single element gets passed to the 541: * logging framework. 542: * 543: * @param t a Throwable that is associated with the log record, or 544: * <code>null</code> if the log message is not associated with a 545: * Throwable. 546: */ 547: static void log(Level level, String msg, Object param, Throwable t) 548: { 549: LogRecord rec; 550: 551: // Return quickly if no log message will be produced. 552: if (!LOGGER.isLoggable(level)) 553: return; 554: 555: rec = new LogRecord(level, msg); 556: if (param != null && param.getClass().isArray()) 557: rec.setParameters((Object[]) param); 558: else 559: rec.setParameters(new Object[] { param }); 560: 561: rec.setThrown(t); 562: 563: // While java.util.logging can sometimes infer the class and 564: // method of the caller, this automatic inference is not reliable 565: // on highly optimizing VMs. Also, log messages make more sense to 566: // developers when they display a public method in a public class; 567: // otherwise, they might feel tempted to figure out the internals 568: // of ServiceFactory in order to understand the problem. 569: rec.setSourceClassName(ServiceFactory.class.getName()); 570: rec.setSourceMethodName("lookupProviders"); 571: 572: LOGGER.log(rec); 573: } 574: }