001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.discovery.tools;
018    
019    import java.util.HashMap;
020    import java.util.Properties;
021    
022    import org.apache.commons.discovery.DiscoveryException;
023    import org.apache.commons.discovery.jdk.JDKHooks;
024    import org.apache.commons.discovery.resource.ClassLoaders;
025    
026    
027    /**
028     * <p>Discover singleton service providers.
029     * This 
030     * </p>
031     * 
032     * <p>DiscoverSingleton instances are cached by the Discovery service,
033     * keyed by a combination of
034     * <ul>
035     *   <li>thread context class loader,</li>
036     *   <li>groupContext, and</li>
037     *   <li>SPI.</li>
038     * </ul>
039     * This DOES allow multiple instances of a given <i>singleton</i> class
040     * to exist for different class loaders and different group contexts.
041     * </p>
042     * 
043     * <p>In the context of this package, a service interface is defined by a
044     * Service Provider Interface (SPI).  The SPI is expressed as a Java interface,
045     * abstract class, or (base) class that defines an expected programming
046     * interface.
047     * </p>
048     * 
049     * <p>DiscoverSingleton provides the <code>find</code> methods for locating and
050     * instantiating a singleton instance of an implementation of a service (SPI).
051     * Each form of <code>find</code> varies slightly, but they all perform the
052     * same basic function.
053     * 
054     * The simplest <code>find</code> methods are intended for direct use by
055     * components looking for a service.  If you are not sure which finder(s)
056     * to use, you can narrow your search to one of these:
057     * <ul>
058     * <li>static Object find(Class spi);</li>
059     * <li>static Object find(Class spi, Properties properties);</li>
060     * <li>static Object find(Class spi, String defaultImpl);</li>
061     * <li>static Object find(Class spi,
062     *                        Properties properties, String defaultImpl);</li>
063     * <li>static Object find(Class spi,
064     *                        String propertiesFileName, String defaultImpl);</li>
065     * <li>static Object find(String groupContext, Class spi,
066     *                        Properties properties, String defaultImpl);</li>
067     * <li>static Object find(String groupContext, Class spi,
068     *                        String propertiesFileName, String defaultImpl);</li>
069     * </ul>
070     * 
071     * The <code>DiscoverSingleton.find</code> methods proceed as follows:
072     * </p>
073     * <ul>
074     *   <p><li>
075     *   Examine an internal cache to determine if the desired service was
076     *   previously identified and instantiated.  If found in cache, return it.
077     *   </li></p>
078     *   <p><li>
079     *   Get the name of an implementation class.  The name is the first
080     *   non-null value obtained from the following resources:
081     *   <ul>
082     *     <li>
083     *     The value of the (scoped) system property whose name is the same as
084     *     the SPI's fully qualified class name (as given by SPI.class.getName()).
085     *     The <code>ScopedProperties</code> class provides a way to bind
086     *     properties by classloader, in a secure hierarchy similar in concept
087     *     to the way classloader find class and resource files.
088     *     See <code>ScopedProperties</code> for more details.
089     *     <p>If the ScopedProperties are not set by users, then behaviour
090     *     is equivalent to <code>System.getProperty()</code>.
091     *     </p>
092     *     </li>
093     *     <p><li>
094     *     The value of a <code>Properties properties</code> property, if provided
095     *     as a parameter, whose name is the same as the SPI's fully qualifed class
096     *     name (as given by SPI.class.getName()).
097     *     </li></p>
098     *     <p><li>
099     *     The value obtained using the JDK1.3+ 'Service Provider' specification
100     *     (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
101     *     service named <code>SPI.class.getName()</code>.  This is implemented
102     *     internally, so there is not a dependency on JDK 1.3+.
103     *     </li></p>
104     *   </ul>
105     *   </li></p>
106     *   <p><li>
107     *   If the name of the implementation class is non-null, load that class.
108     *   The class loaded is the first class loaded by the following sequence
109     *   of class loaders:
110     *   <ul>
111     *     <li>Thread Context Class Loader</li>
112     *     <li>DiscoverSingleton's Caller's Class Loader</li>
113     *     <li>SPI's Class Loader</li>
114     *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
115     *     <li>System Class Loader</li>
116     *   </ul>
117     *   An exception is thrown if the class cannot be loaded.
118     *   </li></p>
119     *   <p><li>
120     *   If the name of the implementation class is null, AND the default
121     *   implementation class (<code>defaultImpl</code>) is null,
122     *   then an exception is thrown.
123     *   </li></p>
124     *   <p><li>
125     *   If the name of the implementation class is null, AND the default
126     *   implementation class (<code>defaultImpl</code>) is non-null,
127     *   then load the default implementation class.  The class loaded is the
128     *   first class loaded by the following sequence of class loaders:
129     *   <ul>
130     *     <li>SPI's Class Loader</li>
131     *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
132     *     <li>System Class Loader</li>
133     *   </ul>
134     *   <p>
135     *   This limits the scope in which the default class loader can be found
136     *   to the SPI, DiscoverSingleton, and System class loaders.  The assumption
137     *   here is that the default implementation is closely associated with the SPI
138     *   or system, and is not defined in the user's application space.
139     *   </p>
140     *   <p>
141     *   An exception is thrown if the class cannot be loaded.
142     *   </p>
143     *   </li></p>
144     *   <p><li>
145     *   Verify that the loaded class implements the SPI: an exception is thrown
146     *   if the loaded class does not implement the SPI.
147     *   </li></p>
148     *   <p><li>
149     *   Create an instance of the class.
150     *   </li></p>
151     * </ul>
152     * 
153     * <p>
154     * Variances for various forms of the <code>find</code>
155     * methods are discussed with each such method.
156     * Variances include the following concepts:
157     * <ul>
158     *   <li><b>rootFinderClass</b> - a wrapper encapsulating a finder method
159     *   (factory or other helper class).  The root finder class is used to
160     *   determine the 'real' caller, and hence the caller's class loader -
161     *   thereby preserving knowledge that is relevant to finding the
162     *   correct/expected implementation class.
163     *   </li>
164     *   <li><b>propertiesFileName</b> - <code>Properties</code> may be specified
165     *   directly, or by property file name.  A property file is loaded using the
166     *   same sequence of class loaders used to load the SPI implementation:
167     *   <ul>
168     *     <li>Thread Context Class Loader</li>
169     *     <li>DiscoverSingleton's Caller's Class Loader</li>
170     *     <li>SPI's Class Loader</li>
171     *     <li>DiscoverSingleton's (this class) Class Loader</li>
172     *     <li>System Class Loader</li>
173     *   </ul>
174     *   </li>
175     *   <li><b>groupContext</b> - differentiates service providers for different
176     *   logical groups of service users, that might otherwise be forced to share
177     *   a common service and, more importantly, a common configuration of that
178     *   service.
179     *   <p>The groupContext is used to qualify the name of the property file
180     *   name: <code>groupContext + '.' + propertiesFileName</code>.  If that
181     *   file is not found, then the unqualified propertyFileName is used.
182     *   </p>
183     *   <p>In addition, groupContext is used to qualify the name of the system
184     *   property used to find the service implementation by prepending the value
185     *   of <code>groupContext</code> to the property name:
186     *   <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
187     *   Again, if a system property cannot be found by that name, then the
188     *   unqualified property name is used.
189     *   </p>
190     *   </li>
191     * </ul>
192     * </p>
193     * 
194     * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
195     * after the SAXParserFactory and DocumentBuilderFactory implementations
196     * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
197     * </p>
198     * 
199     * @author Richard A. Sitze
200     * @author Craig R. McClanahan
201     * @author Costin Manolache
202     * @version $Revision: 480374 $ $Date: 2006-11-29 04:33:25 +0100 (Mi, 29. Nov 2006) $
203     */
204    public class DiscoverSingleton {
205        /********************** (RELATIVELY) SIMPLE FINDERS **********************
206         * 
207         * These finders are suitable for direct use in components looking for a
208         * service.  If you are not sure which finder(s) to use, you can narrow
209         * your search to one of these.
210         */
211        
212        /**
213         * Find implementation of SPI.
214         * 
215         * @param spiClass Service Provider Interface Class.
216         * 
217         * @return Instance of a class implementing the SPI.
218         * 
219         * @exception DiscoveryException Thrown if the name of a class implementing
220         *            the SPI cannot be found, if the class cannot be loaded and
221         *            instantiated, or if the resulting class does not implement
222         *            (or extend) the SPI.
223         */
224        public static Object find(Class spiClass)
225            throws DiscoveryException
226        {
227            return find(null,
228                        new SPInterface(spiClass),
229                        DiscoverClass.nullProperties,
230                        DiscoverClass.nullDefaultImpl);
231        }
232    
233        /**
234         * Find implementation of SPI.
235         * 
236         * @param spiClass Service Provider Interface Class.
237         * 
238         * @param properties Used to determine name of SPI implementation,
239         *                   and passed to implementation.init() method if
240         *                   implementation implements Service interface.
241         * 
242         * @return Instance of a class implementing the SPI.
243         * 
244         * @exception DiscoveryException Thrown if the name of a class implementing
245         *            the SPI cannot be found, if the class cannot be loaded and
246         *            instantiated, or if the resulting class does not implement
247         *            (or extend) the SPI.
248         */
249        public static Object find(Class spiClass, Properties properties)
250            throws DiscoveryException
251        {
252            return find(null,
253                        new SPInterface(spiClass),
254                        new PropertiesHolder(properties),
255                        DiscoverClass.nullDefaultImpl);
256        }
257    
258        /**
259         * Find implementation of SPI.
260         * 
261         * @param spiClass Service Provider Interface Class.
262         * 
263         * @param defaultImpl Default implementation.
264         * 
265         * @return Instance of a class implementing the SPI.
266         * 
267         * @exception DiscoveryException Thrown if the name of a class implementing
268         *            the SPI cannot be found, if the class cannot be loaded and
269         *            instantiated, or if the resulting class does not implement
270         *            (or extend) the SPI.
271         */
272        public static Object find(Class spiClass, String defaultImpl)
273            throws DiscoveryException
274        {
275            return find(null,
276                        new SPInterface(spiClass),
277                        DiscoverClass.nullProperties,
278                        new DefaultClassHolder(defaultImpl));
279        }
280    
281        /**
282         * Find implementation of SPI.
283         * 
284         * @param spiClass Service Provider Interface Class.
285         * 
286         * @param properties Used to determine name of SPI implementation,
287         *                   and passed to implementation.init() method if
288         *                   implementation implements Service interface.
289         * 
290         * @param defaultImpl Default implementation.
291         * 
292         * @return Instance of a class implementing the SPI.
293         * 
294         * @exception DiscoveryException Thrown if the name of a class implementing
295         *            the SPI cannot be found, if the class cannot be loaded and
296         *            instantiated, or if the resulting class does not implement
297         *            (or extend) the SPI.
298         */
299        public static Object find(Class spiClass,
300                                  Properties properties,
301                                  String defaultImpl)
302            throws DiscoveryException
303        {
304            return find(null,
305                        new SPInterface(spiClass),
306                        new PropertiesHolder(properties),
307                        new DefaultClassHolder(defaultImpl));
308        }
309    
310        /**
311         * Find implementation of SPI.
312         * 
313         * @param spiClass Service Provider Interface Class.
314         * 
315         * @param propertiesFileName Used to determine name of SPI implementation,
316         *                   and passed to implementation.init() method if
317         *                   implementation implements Service interface.
318         * 
319         * @param defaultImpl Default implementation.
320         * 
321         * @return Instance of a class implementing the SPI.
322         * 
323         * @exception DiscoveryException Thrown if the name of a class implementing
324         *            the SPI cannot be found, if the class cannot be loaded and
325         *            instantiated, or if the resulting class does not implement
326         *            (or extend) the SPI.
327         */
328        public static Object find(Class spiClass,
329                                  String propertiesFileName,
330                                  String defaultImpl)
331            throws DiscoveryException
332        {
333            return find(null,
334                        new SPInterface(spiClass),
335                        new PropertiesHolder(propertiesFileName),
336                        new DefaultClassHolder(defaultImpl));
337        }
338        
339        /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS ***************
340         */
341    
342    
343        /**
344         * Find implementation of SPI.
345         * 
346         * @param spi Service Provider Interface Class.
347         * 
348         * @param properties Used to determine name of SPI implementation,
349         *                   and passed to implementation.init() method if
350         *                   implementation implements Service interface.
351         * 
352         * @param defaultImpl Default implementation.
353         * 
354         * @return Instance of a class implementing the SPI.
355         * 
356         * @exception DiscoveryException Thrown if the name of a class implementing
357         *            the SPI cannot be found, if the class cannot be loaded and
358         *            instantiated, or if the resulting class does not implement
359         *            (or extend) the SPI.
360         */
361        public static Object find(ClassLoaders loaders,
362                                  SPInterface spi,
363                                  PropertiesHolder properties,
364                                  DefaultClassHolder defaultImpl)
365            throws DiscoveryException
366        {
367            ClassLoader contextLoader = JDKHooks.getJDKHooks().getThreadContextClassLoader();
368    
369            Object obj = get(contextLoader, spi.getSPName());
370    
371            if (obj == null) {
372                try {
373                    obj = DiscoverClass.newInstance(loaders, spi, properties, defaultImpl);
374                    
375                    if (obj != null) {
376                        put(contextLoader, spi.getSPName(), obj);
377                    }
378                } catch (DiscoveryException de) {
379                    throw de;
380                } catch (Exception e) {
381                    throw new DiscoveryException("Unable to instantiate implementation class for " + spi.getSPName(), e);
382                }
383            }
384            
385            return obj;
386        }
387    
388        /********************** CACHE-MANAGEMENT SUPPORT **********************/
389        
390        /**
391         * Release all internal references to previously created service
392         * instances associated with the current thread context class loader.
393         * The <code>release()</code> method is called for service instances that
394         * implement the <code>Service</code> interface.
395         *
396         * This is useful in environments like servlet containers,
397         * which implement application reloading by throwing away a ClassLoader.
398         * Dangling references to objects in that class loader would prevent
399         * garbage collection.
400         */
401        public static synchronized void release() {
402            EnvironmentCache.release();
403        }
404    
405        /**
406         * Release any internal references to a previously created service
407         * instance associated with the current thread context class loader.
408         * If the SPI instance implements <code>Service</code>, then call
409         * <code>release()</code>.
410         */
411        public static synchronized void release(Class spiClass) {
412            HashMap spis = (HashMap)EnvironmentCache.get(JDKHooks.getJDKHooks().getThreadContextClassLoader());
413            
414            if (spis != null) {
415                spis.remove(spiClass.getName());
416            }
417        }
418        
419        
420        /************************* SPI CACHE SUPPORT *************************
421         * 
422         * Cache services by a 'key' unique to the requesting class/environment:
423         * 
424         * When we 'release', it is expected that the caller of the 'release'
425         * have the same thread context class loader... as that will be used
426         * to identify all cached entries to be released.
427         * 
428         * We will manage synchronization directly, so all caches are implemented
429         * as HashMap (unsynchronized).
430         * 
431         * - ClassLoader::groupContext::SPI::Instance Cache
432         *         Cache : HashMap
433         *         Key   : Thread Context Class Loader (<code>ClassLoader</code>).
434         *         Value : groupContext::SPI Cache (<code>HashMap</code>).
435         * 
436         * - groupContext::SPI::Instance Cache
437         *         Cache : HashMap
438         *         Key   : groupContext (<code>String</code>).
439         *         Value : SPI Cache (<code>HashMap</code>).
440         * 
441         * - SPI::Instance Cache
442         *         Cache : HashMap
443         *         Key   : SPI Class Name (<code>String</code>).
444         *         Value : SPI Instance/Implementation (<code>Object</code>.
445         */
446    
447        /**
448         * Implements first two levels of the cache (loader & groupContext).
449         * Allows null keys, important as default groupContext is null.
450         */
451        // FIXME: Why is this here? All the methods used are static.
452        //private static final EnvironmentCache root_cache = new EnvironmentCache();
453    
454        /**
455         * Get service keyed by spi & classLoader.
456         */
457        private static synchronized Object get(ClassLoader classLoader,
458                                               String spiName)
459        {
460            HashMap spis = (HashMap)EnvironmentCache.get(classLoader);
461            
462            return (spis != null)
463                   ? spis.get(spiName)
464                   : null;
465        }
466        
467        /**
468         * Put service keyed by spi & classLoader.
469         */
470        private static synchronized void put(ClassLoader classLoader,
471                                             String spiName,
472                                             Object service)
473        {
474            if (service != null)
475            {
476                HashMap spis = (HashMap)EnvironmentCache.get(classLoader);
477                
478                if (spis == null) {
479                    spis = new HashMap(EnvironmentCache.smallHashSize);
480                    EnvironmentCache.put(classLoader, spis);
481                }
482                
483                spis.put(spiName, service);
484            }
485        }
486    }