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.Map;
021    import java.util.Properties;
022    
023    import org.apache.commons.discovery.DiscoveryException;
024    import org.apache.commons.discovery.jdk.JDKHooks;
025    import org.apache.commons.discovery.resource.ClassLoaders;
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 &lt;T&gt; T find(Class&lt;T&gt; spi);</li>
059     * <li>static &lt;T&gt; T find(Class&lt;T&gt; spi, Properties properties);</li>
060     * <li>static &lt;T&gt; T find(Class&lt;T&gt; spi, String defaultImpl);</li>
061     * <li>static &lt;T&gt; T find(Class&lt;T&gt; spi,
062     *                        Properties properties, String defaultImpl);</li>
063     * <li>static &lt;T&gt; T find(Class&lt;T&gt; spi,
064     *                        String propertiesFileName, String defaultImpl);</li>
065     * <li>static &lt;T&gt; T find(ClassLoaders loaders, SPInterface&lt;T&gt; spi,
066     *                        PropertiesHolder holder, DefaultClassHolder&lt;T&gt; holder);</li>
067     * </ul>
068     *
069     * The <code>DiscoverSingleton.find</code> methods proceed as follows:
070     * </p>
071     * <ul>
072     *   <p><li>
073     *   Examine an internal cache to determine if the desired service was
074     *   previously identified and instantiated.  If found in cache, return it.
075     *   </li></p>
076     *   <p><li>
077     *   Get the name of an implementation class.  The name is the first
078     *   non-null value obtained from the following resources:
079     *   <ul>
080     *     <li>
081     *     The value of the (scoped) system property whose name is the same as
082     *     the SPI's fully qualified class name (as given by SPI.class.getName()).
083     *     The <code>ScopedProperties</code> class provides a way to bind
084     *     properties by classloader, in a secure hierarchy similar in concept
085     *     to the way classloader find class and resource files.
086     *     See <code>ScopedProperties</code> for more details.
087     *     <p>If the ScopedProperties are not set by users, then behaviour
088     *     is equivalent to <code>System.getProperty()</code>.
089     *     </p>
090     *     </li>
091     *     <p><li>
092     *     The value of a <code>Properties properties</code> property, if provided
093     *     as a parameter, whose name is the same as the SPI's fully qualifed class
094     *     name (as given by SPI.class.getName()).
095     *     </li></p>
096     *     <p><li>
097     *     The value obtained using the JDK1.3+ 'Service Provider' specification
098     *     (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
099     *     service named <code>SPI.class.getName()</code>.  This is implemented
100     *     internally, so there is not a dependency on JDK 1.3+.
101     *     </li></p>
102     *   </ul>
103     *   </li></p>
104     *   <p><li>
105     *   If the name of the implementation class is non-null, load that class.
106     *   The class loaded is the first class loaded by the following sequence
107     *   of class loaders:
108     *   <ul>
109     *     <li>Thread Context Class Loader</li>
110     *     <li>DiscoverSingleton's Caller's Class Loader</li>
111     *     <li>SPI's Class Loader</li>
112     *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
113     *     <li>System Class Loader</li>
114     *   </ul>
115     *   An exception is thrown if the class cannot be loaded.
116     *   </li></p>
117     *   <p><li>
118     *   If the name of the implementation class is null, AND the default
119     *   implementation class (<code>defaultImpl</code>) is null,
120     *   then an exception is thrown.
121     *   </li></p>
122     *   <p><li>
123     *   If the name of the implementation class is null, AND the default
124     *   implementation class (<code>defaultImpl</code>) is non-null,
125     *   then load the default implementation class.  The class loaded is the
126     *   first class loaded by the following sequence of class loaders:
127     *   <ul>
128     *     <li>SPI's Class Loader</li>
129     *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
130     *     <li>System Class Loader</li>
131     *   </ul>
132     *   <p>
133     *   This limits the scope in which the default class loader can be found
134     *   to the SPI, DiscoverSingleton, and System class loaders.  The assumption
135     *   here is that the default implementation is closely associated with the SPI
136     *   or system, and is not defined in the user's application space.
137     *   </p>
138     *   <p>
139     *   An exception is thrown if the class cannot be loaded.
140     *   </p>
141     *   </li></p>
142     *   <p><li>
143     *   Verify that the loaded class implements the SPI: an exception is thrown
144     *   if the loaded class does not implement the SPI.
145     *   </li></p>
146     *   <p><li>
147     *   Create an instance of the class.
148     *   </li></p>
149     * </ul>
150     *
151     * <p>
152     * Variances for various forms of the <code>find</code>
153     * methods are discussed with each such method.
154     * Variances include the following concepts:
155     * <ul>
156     *   <li><b>rootFinderClass</b> - a wrapper encapsulating a finder method
157     *   (factory or other helper class).  The root finder class is used to
158     *   determine the 'real' caller, and hence the caller's class loader -
159     *   thereby preserving knowledge that is relevant to finding the
160     *   correct/expected implementation class.
161     *   </li>
162     *   <li><b>propertiesFileName</b> - <code>Properties</code> may be specified
163     *   directly, or by property file name.  A property file is loaded using the
164     *   same sequence of class loaders used to load the SPI implementation:
165     *   <ul>
166     *     <li>Thread Context Class Loader</li>
167     *     <li>DiscoverSingleton's Caller's Class Loader</li>
168     *     <li>SPI's Class Loader</li>
169     *     <li>DiscoverSingleton's (this class) Class Loader</li>
170     *     <li>System Class Loader</li>
171     *   </ul>
172     *   </li>
173     *   <li><b>groupContext</b> - differentiates service providers for different
174     *   logical groups of service users, that might otherwise be forced to share
175     *   a common service and, more importantly, a common configuration of that
176     *   service.
177     *   <p>The groupContext is used to qualify the name of the property file
178     *   name: <code>groupContext + '.' + propertiesFileName</code>.  If that
179     *   file is not found, then the unqualified propertyFileName is used.
180     *   </p>
181     *   <p>In addition, groupContext is used to qualify the name of the system
182     *   property used to find the service implementation by prepending the value
183     *   of <code>groupContext</code> to the property name:
184     *   <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
185     *   Again, if a system property cannot be found by that name, then the
186     *   unqualified property name is used.
187     *   </p>
188     *   </li>
189     * </ul>
190     * </p>
191     *
192     * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
193     * after the SAXParserFactory and DocumentBuilderFactory implementations
194     * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
195     * </p>
196     *
197     * @version $Revision: 1089242 $ $Date: 2011-04-05 23:33:21 +0200 (Tue, 05 Apr 2011) $
198     */
199    public class DiscoverSingleton {
200    
201        /********************** (RELATIVELY) SIMPLE FINDERS **********************
202         *
203         * These finders are suitable for direct use in components looking for a
204         * service.  If you are not sure which finder(s) to use, you can narrow
205         * your search to one of these.
206         */
207    
208        /**
209         * Find implementation of SPI.
210         *
211         * @param <T>  Service Provider Interface type.
212         * @param spiClass Service Provider Interface Class.
213         *
214         * @return Instance of a class implementing the SPI.
215         *
216         * @exception DiscoveryException Thrown if the name of a class implementing
217         *            the SPI cannot be found, if the class cannot be loaded and
218         *            instantiated, or if the resulting class does not implement
219         *            (or extend) the SPI.
220         */
221        public static <T> T find(Class<T> spiClass) throws DiscoveryException {
222            return find(null,
223                        new SPInterface<T>(spiClass),
224                        DiscoverClass.nullProperties,
225                        (DefaultClassHolder<T>) null);
226        }
227    
228        /**
229         * Find implementation of SPI.
230         *
231         * @param <T> Service Provider Interface type
232         *
233         * @param spiClass Service Provider Interface Class.
234         *
235         * @param properties Used to determine name of SPI implementation,
236         *                   and passed to implementation.init() method if
237         *                   implementation implements Service interface.
238         *
239         * @return Instance of a class implementing the SPI.
240         *
241         * @exception DiscoveryException Thrown if the name of a class implementing
242         *            the SPI cannot be found, if the class cannot be loaded and
243         *            instantiated, or if the resulting class does not implement
244         *            (or extend) the SPI.
245         */
246        public static <T> T find(Class<T> spiClass, Properties properties) throws DiscoveryException {
247            return find(null,
248                        new SPInterface<T>(spiClass),
249                        new PropertiesHolder(properties),
250                        (DefaultClassHolder<T>) null);
251        }
252    
253        /**
254         * Find implementation of SPI.
255         *
256         * @param <T> Service Provider Interface type
257         *
258         * @param spiClass Service Provider Interface Class.
259         *
260         * @param defaultImpl Default implementation.
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 <T> T find(Class<T> spiClass, String defaultImpl) throws DiscoveryException {
270            return find(null,
271                        new SPInterface<T>(spiClass),
272                        DiscoverClass.nullProperties,
273                        new DefaultClassHolder<T>(defaultImpl));
274        }
275    
276        /**
277         * Find implementation of SPI.
278         *
279         * @param <T> Service Provider Interface type
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         * @param defaultImpl Default implementation.
288         *
289         * @return Instance of a class implementing the SPI.
290         *
291         * @exception DiscoveryException Thrown if the name of a class implementing
292         *            the SPI cannot be found, if the class cannot be loaded and
293         *            instantiated, or if the resulting class does not implement
294         *            (or extend) the SPI.
295         */
296        public static <T> T find(Class<T> spiClass,
297                                  Properties properties,
298                                  String defaultImpl) throws DiscoveryException {
299            return find(null,
300                        new SPInterface<T>(spiClass),
301                        new PropertiesHolder(properties),
302                        new DefaultClassHolder<T>(defaultImpl));
303        }
304    
305        /**
306         * Find implementation of SPI.
307         *
308         * @param <T> Service Provider Interface type
309         *
310         * @param spiClass Service Provider Interface Class.
311         *
312         * @param propertiesFileName Used to determine name of SPI implementation,
313         *                   and passed to implementation.init() method if
314         *                   implementation implements Service interface.
315         *
316         * @param defaultImpl Default implementation.
317         *
318         * @return Instance of a class implementing the SPI.
319         *
320         * @exception DiscoveryException Thrown if the name of a class implementing
321         *            the SPI cannot be found, if the class cannot be loaded and
322         *            instantiated, or if the resulting class does not implement
323         *            (or extend) the SPI.
324         */
325        public static <T> T find(Class<T> spiClass,
326                                  String propertiesFileName,
327                                  String defaultImpl) throws DiscoveryException {
328            return find(null,
329                        new SPInterface<T>(spiClass),
330                        new PropertiesHolder(propertiesFileName),
331                        new DefaultClassHolder<T>(defaultImpl));
332        }
333    
334        /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS ***************
335         */
336    
337        /**
338         * Find implementation of SPI.
339         *
340         * @param <T> Service Provider Interface type
341         *
342         * @param loaders The {@code ClassLoader} holder
343         *
344         * @param spi Service Provider Interface Class.
345         *
346         * @param properties Used to determine name of SPI implementation,
347         *                   and passed to implementation.init() method if
348         *                   implementation implements Service interface.
349         *
350         * @param defaultImpl Default implementation.
351         *
352         * @return Instance of a class implementing the SPI.
353         *
354         * @exception DiscoveryException Thrown if the name of a class implementing
355         *            the SPI cannot be found, if the class cannot be loaded and
356         *            instantiated, or if the resulting class does not implement
357         *            (or extend) the SPI.
358         */
359        public static <T> T find(ClassLoaders loaders,
360                                  SPInterface<T> spi,
361                                  PropertiesHolder properties,
362                                  DefaultClassHolder<T> defaultImpl) throws DiscoveryException {
363            ClassLoader contextLoader = JDKHooks.getJDKHooks().getThreadContextClassLoader();
364    
365            @SuppressWarnings("unchecked") // spiName is assignable from stored object class
366            T obj = (T) get(contextLoader, spi.getSPName());
367    
368            if (obj == null) {
369                try {
370                    obj = DiscoverClass.newInstance(loaders, spi, properties, defaultImpl);
371    
372                    if (obj != null) {
373                        put(contextLoader, spi.getSPName(), obj);
374                    }
375                } catch (DiscoveryException de) {
376                    throw de;
377                } catch (Exception e) {
378                    throw new DiscoveryException("Unable to instantiate implementation class for " + spi.getSPName(), e);
379                }
380            }
381    
382            return obj;
383        }
384    
385        /********************** CACHE-MANAGEMENT SUPPORT **********************/
386    
387        /**
388         * Release all internal references to previously created service
389         * instances associated with the current thread context class loader.
390         * The <code>release()</code> method is called for service instances that
391         * implement the <code>Service</code> interface.
392         *
393         * This is useful in environments like servlet containers,
394         * which implement application reloading by throwing away a ClassLoader.
395         * Dangling references to objects in that class loader would prevent
396         * garbage collection.
397         */
398        public static synchronized void release() {
399            EnvironmentCache.release();
400        }
401    
402        /**
403         * Release any internal references to a previously created service
404         * instance associated with the current thread context class loader.
405         * If the SPI instance implements <code>Service</code>, then call
406         * <code>release()</code>.
407         *
408         * @param spiClass The previously created service
409         */
410        public static synchronized void release(Class<?> spiClass) {
411            Map<String, Object> spis = EnvironmentCache.get(JDKHooks.getJDKHooks().getThreadContextClassLoader());
412    
413            if (spis != null) {
414                spis.remove(spiClass.getName());
415            }
416        }
417    
418        /************************* SPI CACHE SUPPORT *************************
419         *
420         * Cache services by a 'key' unique to the requesting class/environment:
421         *
422         * When we 'release', it is expected that the caller of the 'release'
423         * have the same thread context class loader... as that will be used
424         * to identify all cached entries to be released.
425         *
426         * We will manage synchronization directly, so all caches are implemented
427         * as HashMap (unsynchronized).
428         *
429         * - ClassLoader::groupContext::SPI::Instance Cache
430         *         Cache : HashMap
431         *         Key   : Thread Context Class Loader (<code>ClassLoader</code>).
432         *         Value : groupContext::SPI Cache (<code>HashMap</code>).
433         * 
434         * - groupContext::SPI::Instance Cache
435         *         Cache : HashMap
436         *         Key   : groupContext (<code>String</code>).
437         *         Value : SPI Cache (<code>HashMap</code>).
438         * 
439         * - SPI::Instance Cache
440         *         Cache : HashMap
441         *         Key   : SPI Class Name (<code>String</code>).
442         *         Value : SPI Instance/Implementation (<code>Object</code>.
443         */
444    
445        /**
446         * Implements first two levels of the cache (loader & groupContext).
447         * Allows null keys, important as default groupContext is null.
448         */
449    
450        /**
451         * Get service keyed by spi & classLoader.
452         *
453         * @param classLoader The class loader as key to retrieve the related cache
454         * @param spiName The SPI class name
455         * @return The object instance associated to the given class loader/SPI name
456         */
457        private static synchronized Object get(ClassLoader classLoader,
458                                               String spiName) {
459            Map<String, Object> spis = EnvironmentCache.get(classLoader);
460    
461            if (spis != null) {
462                return spis.get(spiName);
463            }
464            return null;
465        }
466    
467        /**
468         * Put service keyed by spi & classLoader.
469         *
470         * @param classLoader The {@link EnvironmentCache} key
471         * @param spiName The SPI class name
472         * @param service The SPI object reference
473         */
474        private static synchronized void put(ClassLoader classLoader,
475                                             String spiName,
476                                             Object service) {
477            if (service != null) {
478                Map<String, Object> spis = EnvironmentCache.get(classLoader);
479    
480                if (spis == null) {
481                    spis = new HashMap<String, Object>(EnvironmentCache.smallHashSize);
482                    EnvironmentCache.put(classLoader, spis);
483                }
484    
485                spis.put(spiName, service);
486            }
487        }
488    
489    }