KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.HashMap JavaDoc;
65 import java.util.Properties JavaDoc;
66
67 import org.apache.commons.discovery.DiscoveryException;
68 import org.apache.commons.discovery.jdk.JDKHooks;
69 import org.apache.commons.discovery.resource.ClassLoaders;
70
71
72 /**
73  * <p>Discover singleton service providers.
74  * This
75  * </p>
76  *
77  * <p>DiscoverSingleton instances are cached by the Discovery service,
78  * keyed by a combination of
79  * <ul>
80  * <li>thread context class loader,</li>
81  * <li>groupContext, and</li>
82  * <li>SPI.</li>
83  * </ul>
84  * This DOES allow multiple instances of a given <i>singleton</i> class
85  * to exist for different class loaders and different group contexts.
86  * </p>
87  *
88  * <p>In the context of this package, a service interface is defined by a
89  * Service Provider Interface (SPI). The SPI is expressed as a Java interface,
90  * abstract class, or (base) class that defines an expected programming
91  * interface.
92  * </p>
93  *
94  * <p>DiscoverSingleton provides the <code>find</code> methods for locating and
95  * instantiating a singleton instance of an implementation of a service (SPI).
96  * Each form of <code>find</code> varies slightly, but they all perform the
97  * same basic function.
98  *
99  * The simplest <code>find</code> methods are intended for direct use by
100  * components looking for a service. If you are not sure which finder(s)
101  * to use, you can narrow your search to one of these:
102  * <ul>
103  * <li>static Object find(Class spi);</li>
104  * <li>static Object find(Class spi, Properties properties);</li>
105  * <li>static Object find(Class spi, String defaultImpl);</li>
106  * <li>static Object find(Class spi,
107  * Properties properties, String defaultImpl);</li>
108  * <li>static Object find(Class spi,
109  * String propertiesFileName, String defaultImpl);</li>
110  * <li>static Object find(String groupContext, Class spi,
111  * Properties properties, String defaultImpl);</li>
112  * <li>static Object find(String groupContext, Class spi,
113  * String propertiesFileName, String defaultImpl);</li>
114  * </ul>
115  *
116  * The <code>DiscoverSingleton.find</code> methods proceed as follows:
117  * </p>
118  * <ul>
119  * <p><li>
120  * Examine an internal cache to determine if the desired service was
121  * previously identified and instantiated. If found in cache, return it.
122  * </li></p>
123  * <p><li>
124  * Get the name of an implementation class. The name is the first
125  * non-null value obtained from the following resources:
126  * <ul>
127  * <li>
128  * The value of the (scoped) system property whose name is the same as
129  * the SPI's fully qualified class name (as given by SPI.class.getName()).
130  * The <code>ScopedProperties</code> class provides a way to bind
131  * properties by classloader, in a secure hierarchy similar in concept
132  * to the way classloader find class and resource files.
133  * See <code>ScopedProperties</code> for more details.
134  * <p>If the ScopedProperties are not set by users, then behaviour
135  * is equivalent to <code>System.getProperty()</code>.
136  * </p>
137  * </li>
138  * <p><li>
139  * The value of a <code>Properties properties</code> property, if provided
140  * as a parameter, whose name is the same as the SPI's fully qualifed class
141  * name (as given by SPI.class.getName()).
142  * </li></p>
143  * <p><li>
144  * The value obtained using the JDK1.3+ 'Service Provider' specification
145  * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
146  * service named <code>SPI.class.getName()</code>. This is implemented
147  * internally, so there is not a dependency on JDK 1.3+.
148  * </li></p>
149  * </ul>
150  * </li></p>
151  * <p><li>
152  * If the name of the implementation class is non-null, load that class.
153  * The class loaded is the first class loaded by the following sequence
154  * of class loaders:
155  * <ul>
156  * <li>Thread Context Class Loader</li>
157  * <li>DiscoverSingleton's Caller's Class Loader</li>
158  * <li>SPI's Class Loader</li>
159  * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
160  * <li>System Class Loader</li>
161  * </ul>
162  * An exception is thrown if the class cannot be loaded.
163  * </li></p>
164  * <p><li>
165  * If the name of the implementation class is null, AND the default
166  * implementation class (<code>defaultImpl</code>) is null,
167  * then an exception is thrown.
168  * </li></p>
169  * <p><li>
170  * If the name of the implementation class is null, AND the default
171  * implementation class (<code>defaultImpl</code>) is non-null,
172  * then load the default implementation class. The class loaded is the
173  * first class loaded by the following sequence of class loaders:
174  * <ul>
175  * <li>SPI's Class Loader</li>
176  * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
177  * <li>System Class Loader</li>
178  * </ul>
179  * <p>
180  * This limits the scope in which the default class loader can be found
181  * to the SPI, DiscoverSingleton, and System class loaders. The assumption
182  * here is that the default implementation is closely associated with the SPI
183  * or system, and is not defined in the user's application space.
184  * </p>
185  * <p>
186  * An exception is thrown if the class cannot be loaded.
187  * </p>
188  * </li></p>
189  * <p><li>
190  * Verify that the loaded class implements the SPI: an exception is thrown
191  * if the loaded class does not implement the SPI.
192  * </li></p>
193  * <p><li>
194  * Create an instance of the class.
195  * </li></p>
196  * </ul>
197  *
198  * <p>
199  * Variances for various forms of the <code>find</code>
200  * methods are discussed with each such method.
201  * Variances include the following concepts:
202  * <ul>
203  * <li><b>rootFinderClass</b> - a wrapper encapsulating a finder method
204  * (factory or other helper class). The root finder class is used to
205  * determine the 'real' caller, and hence the caller's class loader -
206  * thereby preserving knowledge that is relevant to finding the
207  * correct/expected implementation class.
208  * </li>
209  * <li><b>propertiesFileName</b> - <code>Properties</code> may be specified
210  * directly, or by property file name. A property file is loaded using the
211  * same sequence of class loaders used to load the SPI implementation:
212  * <ul>
213  * <li>Thread Context Class Loader</li>
214  * <li>DiscoverSingleton's Caller's Class Loader</li>
215  * <li>SPI's Class Loader</li>
216  * <li>DiscoverSingleton's (this class) Class Loader</li>
217  * <li>System Class Loader</li>
218  * </ul>
219  * </li>
220  * <li><b>groupContext</b> - differentiates service providers for different
221  * logical groups of service users, that might otherwise be forced to share
222  * a common service and, more importantly, a common configuration of that
223  * service.
224  * <p>The groupContext is used to qualify the name of the property file
225  * name: <code>groupContext + '.' + propertiesFileName</code>. If that
226  * file is not found, then the unqualified propertyFileName is used.
227  * </p>
228  * <p>In addition, groupContext is used to qualify the name of the system
229  * property used to find the service implementation by prepending the value
230  * of <code>groupContext</code> to the property name:
231  * <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
232  * Again, if a system property cannot be found by that name, then the
233  * unqualified property name is used.
234  * </p>
235  * </li>
236  * </ul>
237  * </p>
238  *
239  * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
240  * after the SAXParserFactory and DocumentBuilderFactory implementations
241  * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
242  * </p>
243  *
244  * @author Richard A. Sitze
245  * @author Craig R. McClanahan
246  * @author Costin Manolache
247  * @version $Revision$ $Date$
248  */

249 public class DiscoverSingleton {
250     /********************** (RELATIVELY) SIMPLE FINDERS **********************
251      *
252      * These finders are suitable for direct use in components looking for a
253      * service. If you are not sure which finder(s) to use, you can narrow
254      * your search to one of these.
255      */

256     
257     /**
258      * Find implementation of SPI.
259      *
260      * @param spiClass Service Provider Interface Class.
261      *
262      * @return Instance of a class implementing the SPI.
263      *
264      * @exception DiscoveryException Thrown if the name of a class implementing
265      * the SPI cannot be found, if the class cannot be loaded and
266      * instantiated, or if the resulting class does not implement
267      * (or extend) the SPI.
268      */

269     public static Object JavaDoc find(Class JavaDoc spiClass)
270         throws DiscoveryException
271     {
272         return find(null,
273                     new SPInterface(spiClass),
274                     DiscoverClass.nullProperties,
275                     DiscoverClass.nullDefaultImpl);
276     }
277
278     /**
279      * Find implementation of SPI.
280      *
281      * @param spiClass Service Provider Interface Class.
282      *
283      * @param properties Used to determine name of SPI implementation,
284      * and passed to implementation.init() method if
285      * implementation implements Service interface.
286      *
287      * @return Instance of a class implementing the SPI.
288      *
289      * @exception DiscoveryException Thrown if the name of a class implementing
290      * the SPI cannot be found, if the class cannot be loaded and
291      * instantiated, or if the resulting class does not implement
292      * (or extend) the SPI.
293      */

294     public static Object JavaDoc find(Class JavaDoc spiClass, Properties JavaDoc properties)
295         throws DiscoveryException
296     {
297         return find(null,
298                     new SPInterface(spiClass),
299                     new PropertiesHolder(properties),
300                     DiscoverClass.nullDefaultImpl);
301     }
302
303     /**
304      * Find implementation of SPI.
305      *
306      * @param spiClass Service Provider Interface Class.
307      *
308      * @param defaultImpl Default implementation.
309      *
310      * @return Instance of a class implementing the SPI.
311      *
312      * @exception DiscoveryException Thrown if the name of a class implementing
313      * the SPI cannot be found, if the class cannot be loaded and
314      * instantiated, or if the resulting class does not implement
315      * (or extend) the SPI.
316      */

317     public static Object JavaDoc find(Class JavaDoc spiClass, String JavaDoc defaultImpl)
318         throws DiscoveryException
319     {
320         return find(null,
321                     new SPInterface(spiClass),
322                     DiscoverClass.nullProperties,
323                     new DefaultClassHolder(defaultImpl));
324     }
325
326     /**
327      * Find implementation of SPI.
328      *
329      * @param spiClass Service Provider Interface Class.
330      *
331      * @param properties Used to determine name of SPI implementation,
332      * and passed to implementation.init() method if
333      * implementation implements Service interface.
334      *
335      * @param defaultImpl Default implementation.
336      *
337      * @return Instance of a class implementing the SPI.
338      *
339      * @exception DiscoveryException Thrown if the name of a class implementing
340      * the SPI cannot be found, if the class cannot be loaded and
341      * instantiated, or if the resulting class does not implement
342      * (or extend) the SPI.
343      */

344     public static Object JavaDoc find(Class JavaDoc spiClass,
345                               Properties JavaDoc properties,
346                               String JavaDoc defaultImpl)
347         throws DiscoveryException
348     {
349         return find(null,
350                     new SPInterface(spiClass),
351                     new PropertiesHolder(properties),
352                     new DefaultClassHolder(defaultImpl));
353     }
354
355     /**
356      * Find implementation of SPI.
357      *
358      * @param spiClass Service Provider Interface Class.
359      *
360      * @param properties Used to determine name of SPI implementation,
361      * and passed to implementation.init() method if
362      * implementation implements Service interface.
363      *
364      * @param defaultImpl Default implementation.
365      *
366      * @return Instance of a class implementing the SPI.
367      *
368      * @exception DiscoveryException Thrown if the name of a class implementing
369      * the SPI cannot be found, if the class cannot be loaded and
370      * instantiated, or if the resulting class does not implement
371      * (or extend) the SPI.
372      */

373     public static Object JavaDoc find(Class JavaDoc spiClass,
374                               String JavaDoc propertiesFileName,
375                               String JavaDoc defaultImpl)
376         throws DiscoveryException
377     {
378         return find(null,
379                     new SPInterface(spiClass),
380                     new PropertiesHolder(propertiesFileName),
381                     new DefaultClassHolder(defaultImpl));
382     }
383     
384     /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS ***************
385      */

386
387
388     /**
389      * Find implementation of SPI.
390      *
391      * @param spiClass Service Provider Interface Class.
392      *
393      * @param properties Used to determine name of SPI implementation,
394      * and passed to implementation.init() method if
395      * implementation implements Service interface.
396      *
397      * @param defaultImpl Default implementation.
398      *
399      * @return Instance of a class implementing the SPI.
400      *
401      * @exception DiscoveryException Thrown if the name of a class implementing
402      * the SPI cannot be found, if the class cannot be loaded and
403      * instantiated, or if the resulting class does not implement
404      * (or extend) the SPI.
405      */

406     public static Object JavaDoc find(ClassLoaders loaders,
407                               SPInterface spi,
408                               PropertiesHolder properties,
409                               DefaultClassHolder defaultImpl)
410         throws DiscoveryException
411     {
412         ClassLoader JavaDoc contextLoader = JDKHooks.getJDKHooks().getThreadContextClassLoader();
413
414         Object JavaDoc obj = get(contextLoader, spi.getSPName());
415
416         if (obj == null) {
417             try {
418                 obj = DiscoverClass.newInstance(loaders, spi, properties, defaultImpl);
419                 
420                 if (obj != null) {
421                     put(contextLoader, spi.getSPName(), obj);
422                 }
423             } catch (DiscoveryException de) {
424                 throw de;
425             } catch (Exception JavaDoc e) {
426                 throw new DiscoveryException("Unable to instantiate implementation class for " + spi.getSPName(), e);
427             }
428         }
429         
430         return obj;
431     }
432
433     /********************** CACHE-MANAGEMENT SUPPORT **********************/
434     
435     /**
436      * Release all internal references to previously created service
437      * instances associated with the current thread context class loader.
438      * The <code>release()</code> method is called for service instances that
439      * implement the <code>Service</code> interface.
440      *
441      * This is useful in environments like servlet containers,
442      * which implement application reloading by throwing away a ClassLoader.
443      * Dangling references to objects in that class loader would prevent
444      * garbage collection.
445      */

446     public static synchronized void release() {
447         root_cache.release();
448     }
449
450     /**
451      * Release any internal references to a previously created service
452      * instance associated with the current thread context class loader.
453      * If the SPI instance implements <code>Service</code>, then call
454      * <code>release()</code>.
455      */

456     public static synchronized void release(Class JavaDoc spiClass) {
457         HashMap JavaDoc spis = (HashMap JavaDoc)root_cache.get(JDKHooks.getJDKHooks().getThreadContextClassLoader());
458         
459         if (spis != null) {
460             spis.remove(spiClass.getName());
461         }
462     }
463     
464     
465     /************************* SPI CACHE SUPPORT *************************
466      *
467      * Cache services by a 'key' unique to the requesting class/environment:
468      *
469      * When we 'release', it is expected that the caller of the 'release'
470      * have the same thread context class loader... as that will be used
471      * to identify all cached entries to be released.
472      *
473      * We will manage synchronization directly, so all caches are implemented
474      * as HashMap (unsynchronized).
475      *
476      * - ClassLoader::groupContext::SPI::Instance Cache
477      * Cache : HashMap
478      * Key : Thread Context Class Loader (<code>ClassLoader</code>).
479      * Value : groupContext::SPI Cache (<code>HashMap</code>).
480      *
481      * - groupContext::SPI::Instance Cache
482      * Cache : HashMap
483      * Key : groupContext (<code>String</code>).
484      * Value : SPI Cache (<code>HashMap</code>).
485      *
486      * - SPI::Instance Cache
487      * Cache : HashMap
488      * Key : SPI Class Name (<code>String</code>).
489      * Value : SPI Instance/Implementation (<code>Object</code>.
490      */

491
492     /**
493      * Implements first two levels of the cache (loader & groupContext).
494      * Allows null keys, important as default groupContext is null.
495      */

496     private static final EnvironmentCache root_cache = new EnvironmentCache();
497
498     /**
499      * Get service keyed by spi & classLoader.
500      */

501     private static synchronized Object JavaDoc get(ClassLoader JavaDoc classLoader,
502                                            String JavaDoc spiName)
503     {
504         HashMap JavaDoc spis = (HashMap JavaDoc)root_cache.get(classLoader);
505         
506         return (spis != null)
507                ? spis.get(spiName)
508                : null;
509     }
510     
511     /**
512      * Put service keyed by spi & classLoader.
513      */

514     private static synchronized void put(ClassLoader JavaDoc classLoader,
515                                          String JavaDoc spiName,
516                                          Object JavaDoc service)
517     {
518         if (service != null)
519         {
520             HashMap JavaDoc spis = (HashMap JavaDoc)root_cache.get(classLoader);
521             
522             if (spis == null) {
523                 spis = new HashMap JavaDoc(root_cache.smallHashSize);
524                 root_cache.put(classLoader, spis);
525             }
526             
527             spis.put(spiName, service);
528         }
529     }
530 }
531
Popular Tags