001: /*
002: * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.misc;
027:
028: import java.io.BufferedReader;
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.InputStreamReader;
032: import java.net.URL;
033: import java.util.ArrayList;
034: import java.util.Enumeration;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.NoSuchElementException;
038: import java.util.Set;
039: import java.util.TreeSet;
040:
041: /**
042: * A simple service-provider lookup mechanism. A <i>service</i> is a
043: * well-known set of interfaces and (usually abstract) classes. A <i>service
044: * provider</i> is a specific implementation of a service. The classes in a
045: * provider typically implement the interfaces and subclass the classes defined
046: * in the service itself. Service providers may be installed in an
047: * implementation of the Java platform in the form of extensions, that is, jar
048: * files placed into any of the usual extension directories. Providers may
049: * also be made available by adding them to the applet or application class
050: * path or by some other platform-specific means.
051: *
052: * <p> In this lookup mechanism a service is represented by an interface or an
053: * abstract class. (A concrete class may be used, but this is not
054: * recommended.) A provider of a given service contains one or more concrete
055: * classes that extend this <i>service class</i> with data and code specific to
056: * the provider. This <i>provider class</i> will typically not be the entire
057: * provider itself but rather a proxy that contains enough information to
058: * decide whether the provider is able to satisfy a particular request together
059: * with code that can create the actual provider on demand. The details of
060: * provider classes tend to be highly service-specific; no single class or
061: * interface could possibly unify them, so no such class has been defined. The
062: * only requirement enforced here is that provider classes must have a
063: * zero-argument constructor so that they may be instantiated during lookup.
064: *
065: * <p> A service provider identifies itself by placing a provider-configuration
066: * file in the resource directory <tt>META-INF/services</tt>. The file's name
067: * should consist of the fully-qualified name of the abstract service class.
068: * The file should contain a list of fully-qualified concrete provider-class
069: * names, one per line. Space and tab characters surrounding each name, as
070: * well as blank lines, are ignored. The comment character is <tt>'#'</tt>
071: * (<tt>0x23</tt>); on each line all characters following the first comment
072: * character are ignored. The file must be encoded in UTF-8.
073: *
074: * <p> If a particular concrete provider class is named in more than one
075: * configuration file, or is named in the same configuration file more than
076: * once, then the duplicates will be ignored. The configuration file naming a
077: * particular provider need not be in the same jar file or other distribution
078: * unit as the provider itself. The provider must be accessible from the same
079: * class loader that was initially queried to locate the configuration file;
080: * note that this is not necessarily the class loader that found the file.
081: *
082: * <p> <b>Example:</b> Suppose we have a service class named
083: * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods:
084: *
085: * <pre>
086: * public abstract CharEncoder getEncoder(String encodingName);
087: * public abstract CharDecoder getDecoder(String encodingName);
088: * </pre>
089: *
090: * Each method returns an appropriate object or <tt>null</tt> if it cannot
091: * translate the given encoding. Typical <tt>CharCodec</tt> providers will
092: * support more than one encoding.
093: *
094: * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt>
095: * service then its jar file would contain the file
096: * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain
097: * the single line:
098: *
099: * <pre>
100: * sun.io.StandardCodec # Standard codecs for the platform
101: * </pre>
102: *
103: * To locate an encoder for a given encoding name, the internal I/O code would
104: * do something like this:
105: *
106: * <pre>
107: * CharEncoder getEncoder(String encodingName) {
108: * Iterator ps = Service.providers(CharCodec.class);
109: * while (ps.hasNext()) {
110: * CharCodec cc = (CharCodec)ps.next();
111: * CharEncoder ce = cc.getEncoder(encodingName);
112: * if (ce != null)
113: * return ce;
114: * }
115: * return null;
116: * }
117: * </pre>
118: *
119: * The provider-lookup mechanism always executes in the security context of the
120: * caller. Trusted system code should typically invoke the methods in this
121: * class from within a privileged security context.
122: *
123: * @author Mark Reinhold
124: * @version 1.18, 07/05/05
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:
135: private static void fail(Class service, String msg, Throwable cause)
136: throws ServiceConfigurationError {
137: ServiceConfigurationError sce = new ServiceConfigurationError(
138: 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: throw new ServiceConfigurationError(service.getName() + ": "
146: + msg);
147: }
148:
149: private static void fail(Class service, URL u, int line, String msg)
150: throws ServiceConfigurationError {
151: fail(service, u + ":" + line + ": " + msg);
152: }
153:
154: /**
155: * Parse a single line from the given configuration file, adding the name
156: * on the line to both the names list and the returned set iff the name is
157: * not already a member of the returned set.
158: */
159: private static int parseLine(Class service, URL u,
160: BufferedReader r, int lc, List names, Set returned)
161: throws IOException, ServiceConfigurationError {
162: String ln = r.readLine();
163: if (ln == null) {
164: return -1;
165: }
166: int ci = ln.indexOf('#');
167: if (ci >= 0)
168: ln = ln.substring(0, ci);
169: ln = ln.trim();
170: int n = ln.length();
171: if (n != 0) {
172: if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
173: fail(service, u, lc,
174: "Illegal configuration-file syntax");
175: int cp = ln.codePointAt(0);
176: if (!Character.isJavaIdentifierStart(cp))
177: fail(service, u, lc, "Illegal provider-class name: "
178: + ln);
179: for (int i = Character.charCount(cp); i < n; i += Character
180: .charCount(cp)) {
181: cp = ln.codePointAt(i);
182: if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
183: fail(service, u, lc,
184: "Illegal provider-class name: " + ln);
185: }
186: if (!returned.contains(ln)) {
187: names.add(ln);
188: returned.add(ln);
189: }
190: }
191: return lc + 1;
192: }
193:
194: /**
195: * Parse the content of the given URL as a provider-configuration file.
196: *
197: * @param service
198: * The service class for which providers are being sought;
199: * used to construct error detail strings
200: *
201: * @param url
202: * The URL naming the configuration file to be parsed
203: *
204: * @param returned
205: * A Set containing the names of provider classes that have already
206: * been returned. This set will be updated to contain the names
207: * that will be yielded from the returned <tt>Iterator</tt>.
208: *
209: * @return A (possibly empty) <tt>Iterator</tt> that will yield the
210: * provider-class names in the given configuration file that are
211: * not yet members of the returned set
212: *
213: * @throws ServiceConfigurationError
214: * If an I/O error occurs while reading from the given URL, or
215: * if a configuration-file format error is detected
216: */
217: private static Iterator parse(Class service, URL u, Set returned)
218: throws ServiceConfigurationError {
219: InputStream in = null;
220: BufferedReader r = null;
221: ArrayList names = new ArrayList();
222: try {
223: in = u.openStream();
224: r = new BufferedReader(new InputStreamReader(in, "utf-8"));
225: int lc = 1;
226: while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0)
227: ;
228: } catch (IOException x) {
229: fail(service, ": " + x);
230: } finally {
231: try {
232: if (r != null)
233: r.close();
234: if (in != null)
235: in.close();
236: } catch (IOException y) {
237: fail(service, ": " + y);
238: }
239: }
240: return names.iterator();
241: }
242:
243: /**
244: * Private inner class implementing fully-lazy provider lookup
245: */
246: private static class LazyIterator implements Iterator {
247:
248: Class service;
249: ClassLoader loader;
250: Enumeration configs = null;
251: Iterator pending = null;
252: Set returned = new TreeSet();
253: String nextName = null;
254:
255: private LazyIterator(Class service, ClassLoader loader) {
256: this .service = service;
257: this .loader = loader;
258: }
259:
260: public boolean hasNext() throws ServiceConfigurationError {
261: if (nextName != null) {
262: return true;
263: }
264: if (configs == null) {
265: try {
266: String fullName = prefix + service.getName();
267: if (loader == null)
268: configs = ClassLoader
269: .getSystemResources(fullName);
270: else
271: configs = loader.getResources(fullName);
272: } catch (IOException x) {
273: fail(service, ": " + x);
274: }
275: }
276: while ((pending == null) || !pending.hasNext()) {
277: if (!configs.hasMoreElements()) {
278: return false;
279: }
280: pending = parse(service, (URL) configs.nextElement(),
281: returned);
282: }
283: nextName = (String) pending.next();
284: return true;
285: }
286:
287: public Object next() throws ServiceConfigurationError {
288: if (!hasNext()) {
289: throw new NoSuchElementException();
290: }
291: String cn = nextName;
292: nextName = null;
293: try {
294: return Class.forName(cn, true, loader).newInstance();
295: } catch (ClassNotFoundException x) {
296: fail(service, "Provider " + cn + " not found");
297: } catch (Exception x) {
298: fail(service, "Provider " + cn
299: + " could not be instantiated: " + x, x);
300: }
301: return null; /* This cannot happen */
302: }
303:
304: public void remove() {
305: throw new UnsupportedOperationException();
306: }
307:
308: }
309:
310: /**
311: * Locates and incrementally instantiates the available providers of a
312: * given service using the given class loader.
313: *
314: * <p> This method transforms the name of the given service class into a
315: * provider-configuration filename as described above and then uses the
316: * <tt>getResources</tt> method of the given class loader to find all
317: * available files with that name. These files are then read and parsed to
318: * produce a list of provider-class names. The iterator that is returned
319: * uses the given class loader to lookup and then instantiate each element
320: * of the list.
321: *
322: * <p> Because it is possible for extensions to be installed into a running
323: * Java virtual machine, this method may return different results each time
324: * it is invoked. <p>
325: *
326: * @param service
327: * The service's abstract service class
328: *
329: * @param loader
330: * The class loader to be used to load provider-configuration files
331: * and instantiate provider classes, or <tt>null</tt> if the system
332: * class loader (or, failing that the bootstrap class loader) is to
333: * be used
334: *
335: * @return An <tt>Iterator</tt> that yields provider objects for the given
336: * service, in some arbitrary order. The iterator will throw a
337: * <tt>ServiceConfigurationError</tt> if a provider-configuration
338: * file violates the specified format or if a provider class cannot
339: * be found and instantiated.
340: *
341: * @throws ServiceConfigurationError
342: * If a provider-configuration file violates the specified format
343: * or names a provider class that cannot be found and instantiated
344: *
345: * @see #providers(java.lang.Class)
346: * @see #installedProviders(java.lang.Class)
347: */
348: public static Iterator providers(Class service, ClassLoader loader)
349: throws ServiceConfigurationError {
350: return new LazyIterator(service, loader);
351: }
352:
353: /**
354: * Locates and incrementally instantiates the available providers of a
355: * given service using the context class loader. This convenience method
356: * is equivalent to
357: *
358: * <pre>
359: * ClassLoader cl = Thread.currentThread().getContextClassLoader();
360: * return Service.providers(service, cl);
361: * </pre>
362: *
363: * @param service
364: * The service's abstract service class
365: *
366: * @return An <tt>Iterator</tt> that yields provider objects for the given
367: * service, in some arbitrary order. The iterator will throw a
368: * <tt>ServiceConfigurationError</tt> if a provider-configuration
369: * file violates the specified format or if a provider class cannot
370: * be found and instantiated.
371: *
372: * @throws ServiceConfigurationError
373: * If a provider-configuration file violates the specified format
374: * or names a provider class that cannot be found and instantiated
375: *
376: * @see #providers(java.lang.Class, java.lang.ClassLoader)
377: */
378: public static Iterator providers(Class service)
379: throws ServiceConfigurationError {
380: ClassLoader cl = Thread.currentThread().getContextClassLoader();
381: return Service.providers(service, cl);
382: }
383:
384: /**
385: * Locates and incrementally instantiates the available providers of a
386: * given service using the extension class loader. This convenience method
387: * simply locates the extension class loader, call it
388: * <tt>extClassLoader</tt>, and then does
389: *
390: * <pre>
391: * return Service.providers(service, extClassLoader);
392: * </pre>
393: *
394: * If the extension class loader cannot be found then the system class
395: * loader is used; if there is no system class loader then the bootstrap
396: * class loader is used.
397: *
398: * @param service
399: * The service's abstract service class
400: *
401: * @return An <tt>Iterator</tt> that yields provider objects for the given
402: * service, in some arbitrary order. The iterator will throw a
403: * <tt>ServiceConfigurationError</tt> if a provider-configuration
404: * file violates the specified format or if a provider class cannot
405: * be found and instantiated.
406: *
407: * @throws ServiceConfigurationError
408: * If a provider-configuration file violates the specified format
409: * or names a provider class that cannot be found and instantiated
410: *
411: * @see #providers(java.lang.Class, java.lang.ClassLoader)
412: */
413: public static Iterator installedProviders(Class service)
414: throws ServiceConfigurationError {
415: ClassLoader cl = ClassLoader.getSystemClassLoader();
416: ClassLoader prev = null;
417: while (cl != null) {
418: prev = cl;
419: cl = cl.getParent();
420: }
421: return Service.providers(service, prev);
422: }
423:
424: }
|