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}. 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} has been called. 318: */ 319: private final AccessControlContext securityContext; 320: 321: 322: /** 323: * A reader for the current file listing class names of service 324: * implementors, or <code>null</code> when the last reader has 325: * been fetched. 326: */ 327: private BufferedReader reader; 328: 329: 330: /** 331: * The URL currently being processed. This is only used for 332: * emitting error messages. 333: */ 334: private URL currentURL; 335: 336: 337: /** 338: * The service provider that will be returned by the next call to 339: * {@link #next()}, or <code>null</code> if the iterator has 340: * already returned all service providers. 341: */ 342: private Object nextProvider; 343: 344: 345: /** 346: * Constructs an Iterator that loads and initializes services on 347: * demand. 348: * 349: * @param spi the service provider interface which the services 350: * must implement. Usually, this is a Java interface type, but it 351: * might also be an abstract class or even a concrete superclass. 352: * 353: * @param urls an Enumeration<URL> over the URLs that contain a 354: * resource 355: * <code>META-INF/services/<org.foo.SomeService></code>, as 356: * determined by {@link ClassLoader#getResources(String)}. 357: * 358: * @param loader the ClassLoader that gets used for loading 359: * service providers. 360: * 361: * @param securityContext the security context to use when loading 362: * and initializing service providers. 363: */ 364: ServiceIterator(Class spi, Enumeration urls, ClassLoader loader, 365: AccessControlContext securityContext) 366: { 367: this.spi = spi; 368: this.urls = urls; 369: this.loader = loader; 370: this.securityContext = securityContext; 371: this.nextProvider = loadNextServiceProvider(); 372: } 373: 374: 375: /** 376: * @throws NoSuchElementException if {@link #hasNext} returns 377: * <code>false</code>. 378: */ 379: public Object next() 380: { 381: Object result; 382: 383: if (!hasNext()) 384: throw new NoSuchElementException(); 385: 386: result = nextProvider; 387: nextProvider = loadNextServiceProvider(); 388: return result; 389: } 390: 391: 392: public boolean hasNext() 393: { 394: return nextProvider != null; 395: } 396: 397: 398: public void remove() 399: { 400: throw new UnsupportedOperationException(); 401: } 402: 403: 404: private Object loadNextServiceProvider() 405: { 406: String line; 407: 408: if (reader == null) 409: advanceReader(); 410: 411: for (;;) 412: { 413: /* If we have reached the last provider list, we cannot 414: * retrieve any further lines. 415: */ 416: if (reader == null) 417: return null; 418: 419: try 420: { 421: line = reader.readLine(); 422: } 423: catch (IOException readProblem) 424: { 425: log(Level.WARNING, "IOException upon reading {0}", currentURL, 426: readProblem); 427: line = null; 428: } 429: 430: /* When we are at the end of one list of services, 431: * switch over to the next one. 432: */ 433: if (line == null) 434: { 435: advanceReader(); 436: continue; 437: } 438: 439: 440: // Skip whitespace at the beginning and end of each line. 441: line = line.trim(); 442: 443: // Skip empty lines. 444: if (line.length() == 0) 445: continue; 446: 447: // Skip comment lines. 448: if (line.charAt(0) == '#') 449: continue; 450: 451: try 452: { 453: log(Level.FINE, 454: "Loading service provider \"{0}\", specified" 455: + " by \"META-INF/services/{1}\" in {2}.", 456: new Object[] { line, spi.getName(), currentURL }, 457: null); 458: 459: /* Load the class in the security context that was 460: * active when calling lookupProviders. 461: */ 462: return AccessController.doPrivileged( 463: new ServiceProviderLoadingAction(spi, line, loader), 464: securityContext); 465: } 466: catch (Exception ex) 467: { 468: String msg = "Cannot load service provider class \"{0}\"," 469: + " specified by \"META-INF/services/{1}\" in {2}"; 470: if (ex instanceof PrivilegedActionException 471: && ex.getCause() instanceof ClassCastException) 472: msg = "Service provider class \"{0}\" is not an instance" 473: + " of \"{1}\". Specified" 474: + " by \"META-INF/services/{1}\" in {2}."; 475: 476: log(Level.WARNING, msg, 477: new Object[] { line, spi.getName(), currentURL }, 478: ex); 479: continue; 480: } 481: } 482: } 483: 484: 485: private void advanceReader() 486: { 487: do 488: { 489: if (reader != null) 490: { 491: try 492: { 493: reader.close(); 494: log(Level.FINE, "closed {0}", currentURL, null); 495: } 496: catch (Exception ex) 497: { 498: log(Level.WARNING, "cannot close {0}", currentURL, ex); 499: } 500: reader = null; 501: currentURL = null; 502: } 503: 504: if (!urls.hasMoreElements()) 505: return; 506: 507: currentURL = (URL) urls.nextElement(); 508: try 509: { 510: reader = new BufferedReader(new InputStreamReader( 511: currentURL.openStream(), "UTF-8")); 512: log(Level.FINE, "opened {0}", currentURL, null); 513: } 514: catch (Exception ex) 515: { 516: log(Level.WARNING, "cannot open {0}", currentURL, ex); 517: } 518: } 519: while (reader == null); 520: } 521: } 522: 523: 524: // Package-private to avoid a trampoline. 525: /** 526: * Passes a log message to the <code>java.util.logging</code> 527: * framework. This call returns very quickly if no log message will 528: * be produced, so there is not much overhead in the standard case. 529: * 530: * @param the severity of the message, for instance {@link 531: * Level#WARNING}. 532: * 533: * @param msg the log message, for instance <code>“Could not 534: * load {0}.”</code> 535: * 536: * @param param the parameter(s) for the log message, or 537: * <code>null</code> if <code>msg</code> does not specify any 538: * parameters. If <code>param</code> is not an array, an array with 539: * <code>param</code> as its single element gets passed to the 540: * logging framework. 541: * 542: * @param t a Throwable that is associated with the log record, or 543: * <code>null</code> if the log message is not associated with a 544: * Throwable. 545: */ 546: static void log(Level level, String msg, Object param, Throwable t) 547: { 548: LogRecord rec; 549: 550: // Return quickly if no log message will be produced. 551: if (!LOGGER.isLoggable(level)) 552: return; 553: 554: rec = new LogRecord(level, msg); 555: if (param != null && param.getClass().isArray()) 556: rec.setParameters((Object[]) param); 557: else 558: rec.setParameters(new Object[] { param }); 559: 560: rec.setThrown(t); 561: 562: // While java.util.logging can sometimes infer the class and 563: // method of the caller, this automatic inference is not reliable 564: // on highly optimizing VMs. Also, log messages make more sense to 565: // developers when they display a public method in a public class; 566: // otherwise, they might feel tempted to figure out the internals 567: // of ServiceFactory in order to understand the problem. 568: rec.setSourceClassName(ServiceFactory.class.getName()); 569: rec.setSourceMethodName("lookupProviders"); 570: 571: LOGGER.log(rec); 572: } 573: }