KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > remote > util > Service


1 /*
2  * @(#)Service.java 1.5 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package com.sun.jmx.remote.util;
8
9 import java.io.BufferedReader JavaDoc;
10 import java.io.IOException JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.InputStreamReader JavaDoc;
13 import java.net.URL JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Enumeration JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.NoSuchElementException JavaDoc;
19 import java.util.Set JavaDoc;
20 import java.util.TreeSet JavaDoc;
21
22
23 import java.security.AccessController JavaDoc;
24 import java.security.PrivilegedAction JavaDoc;
25
26 /**
27  * EXTRACTED FROM sun.misc.Service
28  * A simple service-provider lookup mechanism. A <i>service</i> is a
29  * well-known set of intjavax.management.remoteerfaces and (usually abstract) classes. A <i>service
30  * provider</i> is a specific implementation of a service. The classes in a
31  * provider typically implement the interfaces and subclass the classes defined
32  * in the service itself. Service providers may be installed in an
33  * implementation of the Java platform in the form of extensions, that is, jar
34  * files placed into any of the usual extension directories. Providers may
35  * also be made available by adding them to the applet or application class
36  * path or by some other platform-specific means.
37  *
38  * <p> In this lookup mechanism a service is represented by an interface or an
39  * abstract class. (A concrete class may be used, but this is not
40  * recommended.) A provider of a given service contains one or more concrete
41  * classes that extend this <i>service class</i> with data and code specific to
42  * the provider. This <i>provider class</i> will typically not be the entire
43  * provider itself but rather a proxy that contains enough information to
44  * decide whether the provider is able to satisfy a particular request together
45  * with code that can create the actual provider on demand. The details of
46  * provider classes tend to be highly service-specific; no single class or
47  * interface could possibly unify them, so no such class has been defined. The
48  * only requirement enforced here is that provider classes must have a
49  * zero-argument constructor so that they may be instantiated during lookup.
50  *
51  * <p> A service provider identifies itself by placing a provider-configuration
52  * file in the resource directory <tt>META-INF/services</tt>. The file's name
53  * should consist of the fully-qualified name of the abstract service class.
54  * The file should contain a list of fully-qualified concrete provider-class
55  * names, one per line. Space and tab characters surrounding each name, as
56  * well as blank lines, are ignored. The comment character is <tt>'#'</tt>
57  * (<tt>0x23</tt>); on each line all characters following the first comment
58  * character are ignored. The file must be encoded in UTF-8.
59  *
60  * <p> If a particular concrete provider class is named in more than one
61  * configuration file, or is named in the same configuration file more than
62  * once, then the duplicates will be ignored. The configuration file naming a
63  * particular provider need not be in the same jar file or other distribution
64  * unit as the provider itself. The provider must be accessible from the same
65  * class loader that was initially queried to locate the configuration file;
66  * note that this is not necessarily the class loader that found the file.
67  *
68  * <p> <b>Example:</b> Suppose we have a service class named
69  * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods:
70  *
71  * <pre>
72  * public abstract CharEncoder getEncoder(String encodingName);
73  * public abstract CharDecoder getDecoder(String encodingName);
74  * </pre>
75  *
76  * Each method returns an appropriate object or <tt>null</tt> if it cannot
77  * translate the given encoding. Typical <tt>CharCodec</tt> providers will
78  * support more than one encoding.
79  *
80  * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt>
81  * service then its jar file would contain the file
82  * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain
83  * the single line:
84  *
85  * <pre>
86  * sun.io.StandardCodec # Standard codecs for the platform
87  * </pre>
88  *
89  * To locate an encoder for a given encoding name, the internal I/O code would
90  * do something like this:
91  *
92  * <pre>
93  * CharEncoder getEncoder(String encodingName) {
94  * Iterator ps = Service.providers(CharCodec.class);
95  * while (ps.hasNext()) {
96  * CharCodec cc = (CharCodec)ps.next();
97  * CharEncoder ce = cc.getEncoder(encodingName);
98  * if (ce != null)
99  * return ce;
100  * }
101  * return null;
102  * }
103  * </pre>
104  *
105  * The provider-lookup mechanism always executes in the security context of the
106  * caller. Trusted system code should typically invoke the methods in this
107  * class from within a privileged security context.
108  *
109  */

110
111 public final class Service {
112
113     private static final String JavaDoc prefix = "META-INF/services/";
114
115     private Service() { }
116
117     private static void fail(Class JavaDoc service, String JavaDoc msg, Throwable JavaDoc cause)
118     throws IllegalArgumentException JavaDoc
119     {
120     IllegalArgumentException JavaDoc sce
121         = new IllegalArgumentException JavaDoc(service.getName() + ": " + msg);
122     
123     throw (IllegalArgumentException JavaDoc) EnvHelp.initCause(sce, cause);
124     }
125
126     private static void fail(Class JavaDoc service, String JavaDoc msg)
127     throws IllegalArgumentException JavaDoc
128     {
129     throw new IllegalArgumentException JavaDoc(service.getName() + ": " + msg);
130     }
131
132     private static void fail(Class JavaDoc service, URL JavaDoc u, int line, String JavaDoc msg)
133     throws IllegalArgumentException JavaDoc
134     {
135     fail(service, u + ":" + line + ": " + msg);
136     }
137
138     /**
139      * Parse a single line from the given configuration file, adding the name
140      * on the line to both the names list and the returned set iff the name is
141      * not already a member of the returned set.
142      */

143     private static int parseLine(Class JavaDoc service, URL JavaDoc u, BufferedReader JavaDoc r, int lc,
144                  List JavaDoc names, Set JavaDoc returned)
145     throws IOException JavaDoc, IllegalArgumentException JavaDoc
146     {
147     String JavaDoc ln = r.readLine();
148     if (ln == null) {
149         return -1;
150     }
151     int ci = ln.indexOf('#');
152     if (ci >= 0) ln = ln.substring(0, ci);
153     ln = ln.trim();
154     int n = ln.length();
155     if (n != 0) {
156         if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
157         fail(service, u, lc, "Illegal configuration-file syntax");
158         if (!Character.isJavaIdentifierStart(ln.charAt(0)))
159         fail(service, u, lc, "Illegal provider-class name: " + ln);
160         for (int i = 1; i < n; i++) {
161         char c = ln.charAt(i);
162         if (!Character.isJavaIdentifierPart(c) && (c != '.'))
163             fail(service, u, lc, "Illegal provider-class name: " + ln);
164         }
165         if (!returned.contains(ln)) {
166         names.add(ln);
167         returned.add(ln);
168         }
169     }
170     return lc + 1;
171     }
172
173     /**
174      * Parse the content of the given URL as a provider-configuration file.
175      *
176      * @param service
177      * The service class for which providers are being sought;
178      * used to construct error detail strings
179      *
180      * @param url
181      * The URL naming the configuration file to be parsed
182      *
183      * @param returned
184      * A Set containing the names of provider classes that have already
185      * been returned. This set will be updated to contain the names
186      * that will be yielded from the returned <tt>Iterator</tt>.
187      *
188      * @return A (possibly empty) <tt>Iterator</tt> that will yield the
189      * provider-class names in the given configuration file that are
190      * not yet members of the returned set
191      *
192      * @throws IllegalArgumentException
193      * If an I/O error occurs while reading from the given URL, or
194      * if a configuration-file format error is detected
195      */

196     private static Iterator JavaDoc parse(Class JavaDoc service, URL JavaDoc u, Set JavaDoc returned)
197     throws IllegalArgumentException JavaDoc
198     {
199     InputStream JavaDoc in = null;
200     BufferedReader JavaDoc r = null;
201     ArrayList JavaDoc names = new ArrayList JavaDoc();
202     try {
203         in = u.openStream();
204         r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(in, "utf-8"));
205         int lc = 1;
206         while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0);
207     } catch (IOException JavaDoc x) {
208         fail(service, ": " + x);
209     } finally {
210         try {
211         if (r != null) r.close();
212         if (in != null) in.close();
213         } catch (IOException JavaDoc y) {
214         fail(service, ": " + y);
215         }
216     }
217     return names.iterator();
218     }
219
220
221     /**
222      * Private inner class implementing fully-lazy provider lookup
223      */

224     private static class LazyIterator implements Iterator JavaDoc {
225
226     Class JavaDoc service;
227     ClassLoader JavaDoc loader;
228     Enumeration JavaDoc configs = null;
229     Iterator JavaDoc pending = null;
230     Set JavaDoc returned = new TreeSet JavaDoc();
231     String JavaDoc nextName = null;
232
233     private LazyIterator(Class JavaDoc service, ClassLoader JavaDoc loader) {
234         this.service = service;
235         this.loader = loader;
236     }
237
238     public boolean hasNext() throws IllegalArgumentException JavaDoc {
239         if (nextName != null) {
240         return true;
241         }
242         if (configs == null) {
243         try {
244             String JavaDoc fullName = prefix + service.getName();
245             if (loader == null)
246             configs = ClassLoader.getSystemResources(fullName);
247             else
248             configs = loader.getResources(fullName);
249         } catch (IOException JavaDoc x) {
250             fail(service, ": " + x);
251         }
252         }
253         while ((pending == null) || !pending.hasNext()) {
254         if (!configs.hasMoreElements()) {
255             return false;
256         }
257         pending = parse(service, (URL JavaDoc)configs.nextElement(), returned);
258         }
259         nextName = (String JavaDoc)pending.next();
260         return true;
261     }
262
263     public Object JavaDoc next() throws IllegalArgumentException JavaDoc {
264         if (!hasNext()) {
265         throw new NoSuchElementException JavaDoc();
266         }
267         String JavaDoc cn = nextName;
268         nextName = null;
269         try {
270         return Class.forName(cn, true, loader).newInstance();
271         } catch (ClassNotFoundException JavaDoc x) {
272         fail(service,
273              "Provider " + cn + " not found");
274         } catch (Exception JavaDoc x) {
275         fail(service,
276              "Provider " + cn + " could not be instantiated: " + x,
277              x);
278         }
279         return null; /* This cannot happen */
280     }
281
282     public void remove() {
283         throw new UnsupportedOperationException JavaDoc();
284     }
285
286     }
287
288
289     /**
290      * Locates and incrementally instantiates the available providers of a
291      * given service using the given class loader.
292      *
293      * <p> This method transforms the name of the given service class into a
294      * provider-configuration filename as described above and then uses the
295      * <tt>getResources</tt> method of the given class loader to find all
296      * available files with that name. These files are then read and parsed to
297      * produce a list of provider-class names. The iterator that is returned
298      * uses the given class loader to lookup and then instantiate each element
299      * of the list.
300      *
301      * <p> Because it is possible for extensions to be installed into a running
302      * Java virtual machine, this method may return different results each time
303      * it is invoked. <p>
304      *
305      * @param service
306      * The service's abstract service class
307      *
308      * @param loader
309      * The class loader to be used to load provider-configuration files
310      * and instantiate provider classes, or <tt>null</tt> if the system
311      * class loader (or, failing that the bootstrap class loader) is to
312      * be used
313      *
314      * @return An <tt>Iterator</tt> that yields provider objects for the given
315      * service, in some arbitrary order. The iterator will throw a
316      * <tt>IllegalArgumentException</tt> if a provider-configuration
317      * file violates the specified format or if a provider class cannot
318      * be found and instantiated.
319      *
320      * @throws IllegalArgumentException
321      * If a provider-configuration file violates the specified format
322      * or names a provider class that cannot be found and instantiated
323      *
324      */

325     public static Iterator JavaDoc providers(Class JavaDoc service, ClassLoader JavaDoc loader)
326     throws IllegalArgumentException JavaDoc
327     {
328     return new LazyIterator(service, loader);
329     }
330 }
331
Popular Tags