KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > discovery > tools > DiscoverClass


1 /*
2  * $Header$
3  * $Revision$
4  * $Date$
5  *
6  * ====================================================================
7  *
8  * The Apache Software License, Version 1.1
9  *
10  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
11  * reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  * notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in
22  * the documentation and/or other materials provided with the
23  * distribution.
24  *
25  * 3. The end-user documentation included with the redistribution, if
26  * any, must include the following acknowlegement:
27  * "This product includes software developed by the
28  * Apache Software Foundation (http://www.apache.org/)."
29  * Alternately, this acknowlegement may appear in the software itself,
30  * if and wherever such third-party acknowlegements normally appear.
31  *
32  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33  * Foundation" must not be used to endorse or promote products derived
34  * from this software without prior written permission. For written
35  * permission, please contact apache@apache.org.
36  *
37  * 5. Products derived from this software may not be called "Apache"
38  * nor may "Apache" appear in their names without prior written
39  * permission of the Apache Group.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  * ====================================================================
54  *
55  * This software consists of voluntary contributions made by many
56  * individuals on behalf of the Apache Software Foundation. For more
57  * information on the Apache Software Foundation, please see
58  * <http://www.apache.org/>.
59  *
60  */

61
62 package org.apache.commons.discovery.tools;
63
64 import java.lang.reflect.InvocationTargetException JavaDoc;
65 import java.util.Properties JavaDoc;
66 import java.util.Vector JavaDoc;
67
68 import org.apache.commons.discovery.DiscoveryException;
69 import org.apache.commons.discovery.ResourceClass;
70 import org.apache.commons.discovery.listeners.FirstResourceClassListener;
71 import org.apache.commons.discovery.resource.ClassLoaders;
72 import org.apache.commons.discovery.resource.classes.DiscoverClasses;
73 import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
74
75
76 /**
77  * <p>Discover class that implements a given service interface,
78  * with discovery and configuration features similar to that employed
79  * by standard Java APIs such as JAXP.
80  * </p>
81  *
82  * <p>In the context of this package, a service interface is defined by a
83  * Service Provider Interface (SPI). The SPI is expressed as a Java interface,
84  * abstract class, or (base) class that defines an expected programming
85  * interface.
86  * </p>
87  *
88  * <p>DiscoverClass provides the <code>find</code> methods for locating a
89  * class that implements a service interface (SPI). Each form of
90  * <code>find</code> varies slightly, but they all perform the same basic
91  * function.
92  *
93  * The <code>DiscoverClass.find</code> methods proceed as follows:
94  * </p>
95  * <ul>
96  * <p><li>
97  * Get the name of an implementation class. The name is the first
98  * non-null value obtained from the following resources:
99  * <ul>
100  * <li>
101  * The value of the (scoped) system property whose name is the same as
102  * the SPI's fully qualified class name (as given by SPI.class.getName()).
103  * The <code>ScopedProperties</code> class provides a way to bind
104  * properties by classloader, in a secure hierarchy similar in concept
105  * to the way classloader find class and resource files.
106  * See <code>ScopedProperties</code> for more details.
107  * <p>If the ScopedProperties are not set by users, then behaviour
108  * is equivalent to <code>System.getProperty()</code>.
109  * </p>
110  * </li>
111  * <p><li>
112  * The value of a <code>Properties properties</code> property, if provided
113  * as a parameter, whose name is the same as the SPI's fully qualifed class
114  * name (as given by SPI.class.getName()).
115  * </li></p>
116  * <p><li>
117  * The value obtained using the JDK1.3+ 'Service Provider' specification
118  * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
119  * service named <code>SPI.class.getName()</code>. This is implemented
120  * internally, so there is not a dependency on JDK 1.3+.
121  * </li></p>
122  * </ul>
123  * </li></p>
124  * <p><li>
125  * If the name of the implementation class is non-null, load that class.
126  * The class loaded is the first class loaded by the following sequence
127  * of class loaders:
128  * <ul>
129  * <li>Thread Context Class Loader</li>
130  * <li>DiscoverSingleton's Caller's Class Loader</li>
131  * <li>SPI's Class Loader</li>
132  * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
133  * <li>System Class Loader</li>
134  * </ul>
135  * An exception is thrown if the class cannot be loaded.
136  * </li></p>
137  * <p><li>
138  * If the name of the implementation class is null, AND the default
139  * implementation class name (<code>defaultImpl</code>) is null,
140  * then an exception is thrown.
141  * </li></p>
142  * <p><li>
143  * If the name of the implementation class is null, AND the default
144  * implementation class (<code>defaultImpl</code>) is non-null,
145  * then load the default implementation class. The class loaded is the
146  * first class loaded by the following sequence of class loaders:
147  * <ul>
148  * <li>SPI's Class Loader</li>
149  * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
150  * <li>System Class Loader</li>
151  * </ul>
152  * <p>
153  * This limits the scope in which the default class loader can be found
154  * to the SPI, DiscoverSingleton, and System class loaders. The assumption here
155  * is that the default implementation is closely associated with the SPI
156  * or system, and is not defined in the user's application space.
157  * </p>
158  * <p>
159  * An exception is thrown if the class cannot be loaded.
160  * </p>
161  * </li></p>
162  * <p><li>
163  * Verify that the loaded class implements the SPI: an exception is thrown
164  * if the loaded class does not implement the SPI.
165  * </li></p>
166  * </ul>
167  * </p>
168  *
169  * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
170  * after the SAXParserFactory and DocumentBuilderFactory implementations
171  * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
172  * </p>
173  *
174  * @author Richard A. Sitze
175  * @author Craig R. McClanahan
176  * @author Costin Manolache
177  * @version $Revision$ $Date$
178  */

179 public class DiscoverClass {
180     /**
181      * Readable placeholder for a null value.
182      */

183     public static final DefaultClassHolder nullDefaultImpl = null;
184
185     /**
186      * Readable placeholder for a null value.
187      */

188     public static final PropertiesHolder nullProperties = null;
189     
190     
191     private ClassLoaders classLoaders = null;
192
193
194     /**
195      * Create a class instance with dynamic environment
196      * (thread context class loader is determined on each call).
197      *
198      * Dynamically construct class loaders on each call.
199      */

200     public DiscoverClass() {
201         this(null);
202     }
203
204     /**
205      * Create a class instance with dynamic environment
206      * (thread context class loader is determined on each call).
207      *
208      * Cache static list of class loaders for each call.
209      */

210     public DiscoverClass(ClassLoaders classLoaders) {
211         this.classLoaders = classLoaders;
212     }
213     
214     
215     public ClassLoaders getClassLoaders(Class JavaDoc spiClass) {
216         return classLoaders;
217     }
218
219
220     /**
221      * Find class implementing SPI.
222      *
223      * @param spiClass Service Provider Interface Class.
224      *
225      * @return Class implementing the SPI.
226      *
227      * @exception DiscoveryException Thrown if the name of a class implementing
228      * the SPI cannot be found, if the class cannot be loaded, or if
229      * the resulting class does not implement (or extend) the SPI.
230      */

231     public Class JavaDoc find(Class JavaDoc spiClass)
232         throws DiscoveryException
233     {
234         return find(getClassLoaders(spiClass),
235                     new SPInterface(spiClass),
236                     nullProperties,
237                     nullDefaultImpl);
238     }
239
240     /**
241      * Find class implementing SPI.
242      *
243      * @param spiClass Service Provider Interface Class.
244      *
245      * @param properties Used to determine name of SPI implementation.
246      *
247      * @return Class implementing the SPI.
248      *
249      * @exception DiscoveryException Thrown if the name of a class implementing
250      * the SPI cannot be found, if the class cannot be loaded, or if
251      * the resulting class does not implement (or extend) the SPI.
252      */

253     public Class JavaDoc find(Class JavaDoc spiClass, Properties JavaDoc properties)
254         throws DiscoveryException
255     {
256         return find(getClassLoaders(spiClass),
257                     new SPInterface(spiClass),
258                     new PropertiesHolder(properties),
259                     nullDefaultImpl);
260     }
261
262     /**
263      * Find class implementing SPI.
264      *
265      * @param spiClass Service Provider Interface Class.
266      *
267      * @param defaultImpl Default implementation name.
268      *
269      * @return Class implementing the SPI.
270      *
271      * @exception DiscoveryException Thrown if the name of a class implementing
272      * the SPI cannot be found, if the class cannot be loaded, or if
273      * the resulting class does not implement (or extend) the SPI.
274      */

275     public Class JavaDoc find(Class JavaDoc spiClass, String JavaDoc defaultImpl)
276         throws DiscoveryException
277     {
278         return find(getClassLoaders(spiClass),
279                     new SPInterface(spiClass),
280                     nullProperties,
281                     new DefaultClassHolder(defaultImpl));
282     }
283
284     /**
285      * Find class implementing SPI.
286      *
287      * @param spiClass Service Provider Interface Class.
288      *
289      * @param properties Used to determine name of SPI implementation,.
290      *
291      * @param defaultImpl Default implementation class.
292      *
293      * @return Class implementing the SPI.
294      *
295      * @exception DiscoveryException Thrown if the name of a class implementing
296      * the SPI cannot be found, if the class cannot be loaded, or if
297      * the resulting class does not implement (or extend) the SPI.
298      */

299     public Class JavaDoc find(Class JavaDoc spiClass, Properties JavaDoc properties, String JavaDoc defaultImpl)
300         throws DiscoveryException
301     {
302         return find(getClassLoaders(spiClass),
303                     new SPInterface(spiClass),
304                     new PropertiesHolder(properties),
305                     new DefaultClassHolder(defaultImpl));
306     }
307
308     /**
309      * Find class implementing SPI.
310      *
311      * @param spiClass Service Provider Interface Class.
312      *
313      * @param properties Used to determine name of SPI implementation,.
314      *
315      * @param defaultImpl Default implementation class.
316      *
317      * @return Class implementing the SPI.
318      *
319      * @exception DiscoveryException Thrown if the name of a class implementing
320      * the SPI cannot be found, if the class cannot be loaded, or if
321      * the resulting class does not implement (or extend) the SPI.
322      */

323     public Class JavaDoc find(Class JavaDoc spiClass, String JavaDoc propertiesFileName, String JavaDoc defaultImpl)
324         throws DiscoveryException
325     {
326         return find(getClassLoaders(spiClass),
327                     new SPInterface(spiClass),
328                     new PropertiesHolder(propertiesFileName),
329                     new DefaultClassHolder(defaultImpl));
330     }
331
332     /**
333      * Find class implementing SPI.
334      *
335      * @param spiClass Service Provider Interface Class.
336      *
337      * @param properties Used to determine name of SPI implementation,.
338      *
339      * @param defaultImpl Default implementation class.
340      *
341      * @return Class implementing the SPI.
342      *
343      * @exception DiscoveryException Thrown if the name of a class implementing
344      * the SPI cannot be found, if the class cannot be loaded, or if
345      * the resulting class does not implement (or extend) the SPI.
346      */

347     public static Class JavaDoc find(ClassLoaders loaders,
348                              SPInterface spi,
349                              PropertiesHolder properties,
350                              DefaultClassHolder defaultImpl)
351         throws DiscoveryException
352     {
353         if (loaders == null) {
354             loaders = ClassLoaders.getLibLoaders(spi.getSPClass(),
355                                                  DiscoverClass.class,
356                                                  true);
357         }
358
359         FirstResourceClassListener listener =
360             new FirstResourceClassListener();
361         
362         DiscoverClasses classDiscovery = new DiscoverClasses(loaders);
363         classDiscovery.setListener(listener);
364
365         String JavaDoc[] classNames =
366             discoverClassNames(spi,
367                                (properties == null)
368                                ? null
369                                : properties.getProperties(spi, loaders));
370         
371         if (classNames.length > 0) {
372             classDiscovery.find(classNames[0]);
373
374             // If it's set as a property.. it had better be there!
375
ResourceClass resource = listener.getFirst();
376             return (resource == null) ? null : resource.loadClass();
377         } else {
378             DiscoverServiceNames discoverServices = new DiscoverServiceNames(loaders);
379     
380             /**
381              * Feed service (name) discovery to classDiscovery
382              */

383             discoverServices.setListener(classDiscovery);
384
385             discoverServices.find(spi.getSPName());
386             ResourceClass resource = listener.getFirst();
387
388             Class JavaDoc clazz = (resource == null) ? null : resource.loadClass();
389
390             if (clazz != null) {
391                 return clazz;
392             }
393             
394             if (defaultImpl != null) {
395                 return defaultImpl.getDefaultClass(spi, loaders);
396             }
397         }
398         
399         throw new DiscoveryException("No implementation defined for " + spi.getSPName());
400     }
401     
402     /**
403      * Create new instance of class implementing SPI.
404      *
405      * @param spiClass Service Provider Interface Class.
406      *
407      * @return Instance of a class implementing the SPI.
408      *
409      * @exception DiscoveryException Thrown if the name of a class implementing
410      * the SPI cannot be found, if the class cannot be loaded and
411      * instantiated, or if the resulting class does not implement
412      * (or extend) the SPI.
413      */

414     public Object JavaDoc newInstance(Class JavaDoc spiClass)
415         throws DiscoveryException,
416                InstantiationException JavaDoc,
417                IllegalAccessException JavaDoc,
418                NoSuchMethodException JavaDoc,
419                InvocationTargetException JavaDoc
420     {
421         return newInstance(getClassLoaders(spiClass),
422                            new SPInterface(spiClass),
423                            nullProperties,
424                            nullDefaultImpl);
425     }
426
427     /**
428      * Create new instance of class implementing SPI.
429      *
430      * @param spiClass Service Provider Interface Class.
431      *
432      * @param properties Used to determine name of SPI implementation,
433      * and passed to implementation.init() method if
434      * implementation implements Service interface.
435      *
436      * @return Instance of a class implementing the SPI.
437      *
438      * @exception DiscoveryException Thrown if the name of a class implementing
439      * the SPI cannot be found, if the class cannot be loaded and
440      * instantiated, or if the resulting class does not implement
441      * (or extend) the SPI.
442      */

443     public Object JavaDoc newInstance(Class JavaDoc spiClass, Properties JavaDoc properties)
444         throws DiscoveryException,
445                InstantiationException JavaDoc,
446                IllegalAccessException JavaDoc,
447                NoSuchMethodException JavaDoc,
448                InvocationTargetException JavaDoc
449     {
450         return newInstance(getClassLoaders(spiClass),
451                            new SPInterface(spiClass),
452                            new PropertiesHolder(properties),
453                            nullDefaultImpl);
454     }
455
456     /**
457      * Create new instance of class implementing SPI.
458      *
459      * @param spiClass Service Provider Interface Class.
460      *
461      * @param defaultImpl Default implementation.
462      *
463      * @return Instance of a class implementing the SPI.
464      *
465      * @exception DiscoveryException Thrown if the name of a class implementing
466      * the SPI cannot be found, if the class cannot be loaded and
467      * instantiated, or if the resulting class does not implement
468      * (or extend) the SPI.
469      */

470     public Object JavaDoc newInstance(Class JavaDoc spiClass, String JavaDoc defaultImpl)
471         throws DiscoveryException,
472                InstantiationException JavaDoc,
473                IllegalAccessException JavaDoc,
474                NoSuchMethodException JavaDoc,
475                InvocationTargetException JavaDoc
476     {
477         return newInstance(getClassLoaders(spiClass),
478                            new SPInterface(spiClass),
479                            nullProperties,
480                            new DefaultClassHolder(defaultImpl));
481     }
482
483     /**
484      * Create new instance of class implementing SPI.
485      *
486      * @param spiClass Service Provider Interface Class.
487      *
488      * @param properties Used to determine name of SPI implementation,
489      * and passed to implementation.init() method if
490      * implementation implements Service interface.
491      *
492      * @param defaultImpl Default implementation.
493      *
494      * @return Instance of a class implementing the SPI.
495      *
496      * @exception DiscoveryException Thrown if the name of a class implementing
497      * the SPI cannot be found, if the class cannot be loaded and
498      * instantiated, or if the resulting class does not implement
499      * (or extend) the SPI.
500      */

501     public Object JavaDoc newInstance(Class JavaDoc spiClass, Properties JavaDoc properties, String JavaDoc defaultImpl)
502         throws DiscoveryException,
503                InstantiationException JavaDoc,
504                IllegalAccessException JavaDoc,
505                NoSuchMethodException JavaDoc,
506                InvocationTargetException JavaDoc
507     {
508         return newInstance(getClassLoaders(spiClass),
509                            new SPInterface(spiClass),
510                            new PropertiesHolder(properties),
511                            new DefaultClassHolder(defaultImpl));
512     }
513
514     /**
515      * Create new instance of class implementing SPI.
516      *
517      * @param spiClass Service Provider Interface Class.
518      *
519      * @param properties Used to determine name of SPI implementation,
520      * and passed to implementation.init() method if
521      * implementation implements Service interface.
522      *
523      * @param defaultImpl Default implementation.
524      *
525      * @return Instance of a class implementing the SPI.
526      *
527      * @exception DiscoveryException Thrown if the name of a class implementing
528      * the SPI cannot be found, if the class cannot be loaded and
529      * instantiated, or if the resulting class does not implement
530      * (or extend) the SPI.
531      */

532     public Object JavaDoc newInstance(Class JavaDoc spiClass, String JavaDoc propertiesFileName, String JavaDoc defaultImpl)
533         throws DiscoveryException,
534                InstantiationException JavaDoc,
535                IllegalAccessException JavaDoc,
536                NoSuchMethodException JavaDoc,
537                InvocationTargetException JavaDoc
538     {
539         return newInstance(getClassLoaders(spiClass),
540                            new SPInterface(spiClass),
541                            new PropertiesHolder(propertiesFileName),
542                            new DefaultClassHolder(defaultImpl));
543     }
544
545     /**
546      * Create new instance of class implementing SPI.
547      *
548      * @param spiClass Service Provider Interface Class.
549      *
550      * @param properties Used to determine name of SPI implementation,
551      * and passed to implementation.init() method if
552      * implementation implements Service interface.
553      *
554      * @param defaultImpl Default implementation.
555      *
556      * @return Instance of a class implementing the SPI.
557      *
558      * @exception DiscoveryException Thrown if the name of a class implementing
559      * the SPI cannot be found, if the class cannot be loaded and
560      * instantiated, or if the resulting class does not implement
561      * (or extend) the SPI.
562      */

563     public static Object JavaDoc newInstance(ClassLoaders loaders,
564                                      SPInterface spi,
565                                      PropertiesHolder properties,
566                                      DefaultClassHolder defaultImpl)
567         throws DiscoveryException,
568                InstantiationException JavaDoc,
569                IllegalAccessException JavaDoc,
570                NoSuchMethodException JavaDoc,
571                InvocationTargetException JavaDoc
572     {
573         return spi.newInstance(find(loaders, spi, properties, defaultImpl));
574     }
575
576     /**
577      * <p>Discover names of SPI implementation Classes from properties.
578      * The names are the non-null values, in order, obtained from the following
579      * resources:
580      * <ul>
581      * <li>ManagedProperty.getProperty(SPI.class.getName());</li>
582      * <li>properties.getProperty(SPI.class.getName());</li>
583      * </ul>
584      *
585      * @param properties Properties that may define the implementation
586      * class name(s).
587      *
588      * @return String[] Name of classes implementing the SPI.
589      *
590      * @exception DiscoveryException Thrown if the name of a class implementing
591      * the SPI cannot be found.
592      */

593     public static String JavaDoc[] discoverClassNames(SPInterface spi,
594                                               Properties JavaDoc properties)
595     {
596         Vector JavaDoc names = new Vector JavaDoc();
597         
598         String JavaDoc spiName = spi.getSPName();
599         String JavaDoc propertyName = spi.getPropertyName();
600
601         boolean includeAltProperty = !spiName.equals(propertyName);
602         
603         // Try the (managed) system property spiName
604
String JavaDoc className = getManagedProperty(spiName);
605         if (className != null) names.addElement(className);
606         
607         if (includeAltProperty) {
608             // Try the (managed) system property propertyName
609
className = getManagedProperty(propertyName);
610             if (className != null) names.addElement(className);
611         }
612
613         if (properties != null) {
614             // Try the properties parameter spiName
615
className = properties.getProperty(spiName);
616             if (className != null) names.addElement(className);
617
618             if (includeAltProperty) {
619                 // Try the properties parameter propertyName
620
className = properties.getProperty(propertyName);
621                 if (className != null) names.addElement(className);
622             }
623         }
624
625         String JavaDoc[] results = new String JavaDoc[names.size()];
626         names.copyInto(results);
627
628         return results;
629     }
630
631
632     /**
633      * Load the class whose name is given by the value of a (Managed)
634      * System Property.
635      *
636      * @see ManagedProperties
637      *
638      * @param attribute the name of the system property whose value is
639      * the name of the class to load.
640      */

641     public static String JavaDoc getManagedProperty(String JavaDoc propertyName) {
642         String JavaDoc value;
643         try {
644             value = ManagedProperties.getProperty(propertyName);
645         } catch (SecurityException JavaDoc e) {
646             value = null;
647         }
648         return value;
649     }
650 }
651
Popular Tags