KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > lookup > MetaInfServicesLookup


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util.lookup;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.InputStreamReader JavaDoc;
26 import java.net.URL JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.LinkedHashSet JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.WeakHashMap JavaDoc;
36 import java.util.logging.Level JavaDoc;
37 import java.util.logging.Logger JavaDoc;
38 import org.openide.util.Lookup;
39 import org.openide.util.SharedClassObject;
40 import org.openide.util.WeakSet;
41
42 /** A lookup that implements the JDK1.3 JAR services mechanism and delegates
43  * to META-INF/services/name.of.class files.
44  * <p>It is not dynamic - so if you need to change the classloader or JARs,
45  * wrap it in a ProxyLookup and change the delegate when necessary.
46  * Existing instances will be kept if the implementation classes are unchanged,
47  * so there is "stability" in doing this provided some parent loaders are the same
48  * as the previous ones.
49  * <p>If this is to be made public, please move it to the org.openide.util.lookup
50  * package; currently used by the core via reflection, until it is needed some
51  * other way.
52  * @author Jaroslav Tulach, Jesse Glick
53  * @see "#14722"
54  */

55 final class MetaInfServicesLookup extends AbstractLookup {
56
57     private static final Logger JavaDoc LOGGER = Logger.getLogger(MetaInfServicesLookup.class.getName());
58
59     private static final Map JavaDoc<Class JavaDoc,Object JavaDoc> knownInstances = new WeakHashMap JavaDoc<Class JavaDoc,Object JavaDoc>();
60
61     /** A set of all requested classes.
62      * Note that classes that we actually succeeded on can never be removed
63      * from here because we hold a strong reference to the loader.
64      * However we also hold classes which are definitely not loadable by
65      * our loader.
66      */

67     private final Set JavaDoc<Class JavaDoc> classes = new WeakSet<Class JavaDoc>(); // Set<Class>
68

69     /** class loader to use */
70     private final ClassLoader JavaDoc loader;
71
72     /** Create a lookup reading from the classpath.
73      * That is, the same classloader as this class itself.
74      */

75     public MetaInfServicesLookup() {
76         this(MetaInfServicesLookup.class.getClassLoader());
77     }
78
79     /** Create a lookup reading from a specified classloader.
80      */

81     public MetaInfServicesLookup(ClassLoader JavaDoc loader) {
82         this.loader = loader;
83
84         LOGGER.log(Level.FINE, "Created: {0}", this);
85     }
86
87     public String JavaDoc toString() {
88         return "MetaInfServicesLookup[" + loader + "]"; // NOI18N
89
}
90
91     /* Tries to load appropriate resources from manifest files.
92      */

93     protected final void beforeLookup(Lookup.Template t) {
94         Class JavaDoc c = t.getType();
95
96         HashSet JavaDoc<AbstractLookup.R> listeners;
97
98         synchronized (this) {
99             if (classes.add(c)) {
100                 // Added new class, search for it.
101
LinkedHashSet JavaDoc<AbstractLookup.Pair<?>> arr = getPairsAsLHS();
102                 search(c, arr);
103
104                 // listeners are notified under while holding lock on class c,
105
// let say it is acceptable now
106
listeners = setPairsAndCollectListeners(arr);
107             } else {
108                 // ok, nothing needs to be done
109
return;
110             }
111         }
112
113         notifyCollectedListeners(listeners);
114     }
115
116     /** Finds all pairs and adds them to the collection.
117      *
118      * @param clazz class to find
119      * @param result collection to add Pair to
120      */

121     private void search(Class JavaDoc<?> clazz, Collection JavaDoc<AbstractLookup.Pair<?>> result) {
122         if (LOGGER.isLoggable(Level.FINER)) {
123             LOGGER.log(Level.FINER, "Searching for " + clazz.getName() + " in " + clazz.getClassLoader() + " from " + this);
124         }
125
126         String JavaDoc res = "META-INF/services/" + clazz.getName(); // NOI18N
127
Enumeration JavaDoc<URL JavaDoc> en;
128
129         try {
130             en = loader.getResources(res);
131         } catch (IOException JavaDoc ioe) {
132             // do not use ErrorManager because we are in the startup code
133
// and ErrorManager might not be ready
134
ioe.printStackTrace();
135
136             return;
137         }
138
139         // Do not create multiple instances in case more than one JAR
140
// has the same entry in it (and they load to the same class).
141
// Probably would not happen, assuming JARs only list classes
142
// they own, but just in case...
143
List JavaDoc<Item> foundClasses = new ArrayList JavaDoc<Item>();
144         Collection JavaDoc<Class JavaDoc> removeClasses = new ArrayList JavaDoc<Class JavaDoc>();
145
146         boolean foundOne = false;
147
148         while (en.hasMoreElements()) {
149             if (!foundOne) {
150                 foundOne = true;
151
152                 // Double-check that in fact we can load the *interface* class.
153
// For example, say class I is defined in two JARs, J1 and J2.
154
// There is also an implementation M1 defined in J1, and another
155
// implementation M2 defined in J2.
156
// Classloaders C1 and C2 are made from J1 and J2.
157
// A MetaInfServicesLookup is made from C1. Then the user asks to
158
// lookup I as loaded from C2. J1 has the services line and lists
159
// M1, and we can in fact make it. However it is not of the desired
160
// type to be looked up. Don't do this check, which could be expensive,
161
// unless we expect to be getting some results, however.
162
Class JavaDoc realMcCoy = null;
163
164                 try {
165                     realMcCoy = loader.loadClass(clazz.getName());
166                 } catch (ClassNotFoundException JavaDoc cnfe) {
167                     // our loader does not know about it, OK
168
}
169
170                 if (realMcCoy != clazz) {
171                     // Either the interface class is not available at all in our loader,
172
// or it is not the same version as we expected. Don't provide results.
173
if (LOGGER.isLoggable(Level.FINER)) {
174                         if (realMcCoy != null) {
175                             LOGGER.log(Level.FINER,
176                                 clazz.getName() + " is not the real McCoy! Actually found it in " +
177                                 realMcCoy.getClassLoader()
178                             ); // NOI18N
179
} else {
180                             LOGGER.log(Level.FINER, clazz.getName() + " could not be found in " + loader); // NOI18N
181
}
182                     }
183
184                     return;
185                 }
186             }
187
188             URL JavaDoc url = en.nextElement();
189             Item currentItem = null;
190
191             try {
192                 InputStream JavaDoc is = url.openStream();
193
194                 try {
195                     BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is, "UTF-8")); // NOI18N
196

197                     while (true) {
198                         String JavaDoc line = reader.readLine();
199
200                         if (line == null) {
201                             break;
202                         }
203
204                         line = line.trim();
205
206                         // is it position attribute?
207
if (line.startsWith("#position=")) {
208                             if (currentItem == null) {
209                                 LOGGER.log(Level.WARNING, "Found line '{0}' in {1} but there is no item to associate it with", new Object JavaDoc[] {line, url});
210                                 continue;
211                             }
212
213                             try {
214                                 currentItem.position = Integer.parseInt(line.substring(10));
215                             } catch (NumberFormatException JavaDoc e) {
216                                 // do not use ErrorManager because we are in the startup code
217
// and ErrorManager might not be ready
218
e.printStackTrace();
219                             }
220                         }
221
222                         if (currentItem != null) {
223                             insertItem(currentItem, foundClasses);
224                             currentItem = null;
225                         }
226
227                         // Ignore blank lines and comments.
228
if (line.length() == 0) {
229                             continue;
230                         }
231
232                         boolean remove = false;
233
234                         if (line.charAt(0) == '#') {
235                             if ((line.length() == 1) || (line.charAt(1) != '-')) {
236                                 continue;
237                             }
238
239                             // line starting with #- is a sign to remove that class from lookup
240
remove = true;
241                             line = line.substring(2);
242                         }
243
244                         Class JavaDoc inst = null;
245
246                         try {
247                             // Most lines are fully-qualified class names.
248
inst = Class.forName(line, false, loader);
249                         } catch (ClassNotFoundException JavaDoc cnfe) {
250                             if (remove) {
251                                 // if we are removing somthing and the something
252
// cannot be found it is ok to do nothing
253
continue;
254                             } else {
255                                 // but if we are not removing just rethrow
256
throw cnfe;
257                             }
258                         }
259
260                         if (!clazz.isAssignableFrom(inst)) {
261                             throw new ClassNotFoundException JavaDoc(inst.getName() + " not a subclass of " + clazz.getName()); // NOI18N
262
}
263
264                         if (remove) {
265                             removeClasses.add(inst);
266                         } else {
267                             // create new item here, but do not put it into
268
// foundClasses array yet because following line
269
// might specify its position
270
currentItem = new Item();
271                             currentItem.clazz = inst;
272                         }
273                     }
274
275                     if (currentItem != null) {
276                         insertItem(currentItem, foundClasses);
277                         currentItem = null;
278                     }
279                 } finally {
280                     is.close();
281                 }
282             } catch (ClassNotFoundException JavaDoc ex) {
283                 LOGGER.log(Level.WARNING, null, ex);
284             } catch (IOException JavaDoc ex) {
285                 LOGGER.log(Level.WARNING, null, ex);
286             }
287         }
288
289         LOGGER.log(Level.FINER, "Found impls of {0}: {1} and removed: {2} from: {3}", new Object JavaDoc[] {clazz.getName(), foundClasses, removeClasses, this});
290
291         foundClasses.removeAll(removeClasses);
292
293         for (Item item : foundClasses) {
294             if (removeClasses.contains(item.clazz)) {
295                 continue;
296             }
297
298             result.add(new P(item.clazz));
299         }
300     }
301
302     /**
303      * Insert item to the list according to item.position value.
304      */

305     private void insertItem(Item item, List JavaDoc<Item> list) {
306         // no position? -> add it to the end
307
if (item.position == -1) {
308             list.add(item);
309
310             return;
311         }
312
313         int index = -1;
314         for (Item i : list) {
315             index++;
316
317             if (i.position == -1) {
318                 list.add(index, item);
319
320                 return;
321             } else {
322                 if (i.position > item.position) {
323                     list.add(index, item);
324
325                     return;
326                 }
327             }
328         }
329
330         list.add(item);
331     }
332
333     private static class Item {
334         private Class JavaDoc clazz;
335         private int position = -1;
336         @Override JavaDoc
337         public String JavaDoc toString() {
338             return "MetaInfServicesLookup.Item[" + clazz.getName() + "]"; // NOI18N
339
}
340     }
341
342     /** Pair that holds name of a class and maybe the instance.
343      */

344     private static final class P extends AbstractLookup.Pair<Object JavaDoc> {
345         /** May be one of three things:
346          * 1. The implementation class which was named in the services file.
347          * 2. An instance of it.
348          * 3. Null, if creation of the instance resulted in an error.
349          */

350         private Object JavaDoc object;
351
352         public P(Class JavaDoc<?> clazz) {
353             this.object = clazz;
354         }
355
356         /** Finds the class.
357          */

358         private Class JavaDoc<? extends Object JavaDoc> clazz() {
359             Object JavaDoc o = object;
360
361             if (o instanceof Class JavaDoc) {
362                 return (Class JavaDoc<? extends Object JavaDoc>) o;
363             } else if (o != null) {
364                 return o.getClass();
365             } else {
366                 // Broken.
367
return Object JavaDoc.class;
368             }
369         }
370
371         public boolean equals(Object JavaDoc o) {
372             if (o instanceof P) {
373                 return ((P) o).clazz().equals(clazz());
374             }
375
376             return false;
377         }
378
379         public int hashCode() {
380             return clazz().hashCode();
381         }
382
383         protected boolean instanceOf(Class JavaDoc<?> c) {
384             return c.isAssignableFrom(clazz());
385         }
386
387         public Class JavaDoc<?> getType() {
388             return clazz();
389         }
390
391         public Object JavaDoc getInstance() {
392             Object JavaDoc o = object; // keeping local copy to avoid another
393

394             // thread to modify it under my hands
395
if (o instanceof Class JavaDoc) {
396                 synchronized (o) { // o is Class and we will not create
397
// 2 instances of the same class
398

399                     try {
400                         Class JavaDoc<?> c = ((Class JavaDoc) o);
401
402                         synchronized (knownInstances) { // guards only the static cache
403
o = knownInstances.get(c);
404                         }
405
406                         if (o == null) {
407                             if (SharedClassObject.class.isAssignableFrom(c)) {
408                                 o = SharedClassObject.findObject(c.asSubclass(SharedClassObject.class), true);
409                             } else {
410                                 o = c.newInstance();
411                             }
412
413                             synchronized (knownInstances) { // guards only the static cache
414
knownInstances.put(c, o);
415                             }
416                         }
417
418                         // Do not assign to instance var unless there is a complete synch
419
// block between the newInstance and this line. Otherwise we could
420
// be assigning a half-constructed instance that another thread
421
// could see and return immediately.
422
object = o;
423                     } catch (Exception JavaDoc ex) {
424                         LOGGER.log(Level.WARNING, null, ex);
425                         object = null;
426                     }
427                 }
428             }
429
430             return object;
431         }
432
433         public String JavaDoc getDisplayName() {
434             return clazz().getName();
435         }
436
437         public String JavaDoc getId() {
438             return clazz().getName();
439         }
440
441         protected boolean creatorOf(Object JavaDoc obj) {
442             return obj == object;
443         }
444     }
445 }
446
Popular Tags