All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.discovery.tools.DiscoverSingleton Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.discovery.tools;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.discovery.DiscoveryException;
import org.apache.commons.discovery.jdk.JDKHooks;
import org.apache.commons.discovery.resource.ClassLoaders;

/**
 * 

Discover singleton service providers. * This *

* *

DiscoverSingleton instances are cached by the Discovery service, * keyed by a combination of *

    *
  • thread context class loader,
  • *
  • groupContext, and
  • *
  • SPI.
  • *
* This DOES allow multiple instances of a given singleton class * to exist for different class loaders and different group contexts. *

* *

In the context of this package, a service interface is defined by a * Service Provider Interface (SPI). The SPI is expressed as a Java interface, * abstract class, or (base) class that defines an expected programming * interface. *

* *

DiscoverSingleton provides the find methods for locating and * instantiating a singleton instance of an implementation of a service (SPI). * Each form of find varies slightly, but they all perform the * same basic function. * * The simplest find methods are intended for direct use by * components looking for a service. If you are not sure which finder(s) * to use, you can narrow your search to one of these: *

    *
  • static <T> T find(Class<T> spi);
  • *
  • static <T> T find(Class<T> spi, Properties properties);
  • *
  • static <T> T find(Class<T> spi, String defaultImpl);
  • *
  • static <T> T find(Class<T> spi, * Properties properties, String defaultImpl);
  • *
  • static <T> T find(Class<T> spi, * String propertiesFileName, String defaultImpl);
  • *
  • static <T> T find(ClassLoaders loaders, SPInterface<T> spi, * PropertiesHolder holder, DefaultClassHolder<T> holder);
  • *
* * The DiscoverSingleton.find methods proceed as follows: *

*
    *

  • * Examine an internal cache to determine if the desired service was * previously identified and instantiated. If found in cache, return it. *
  • *

  • * Get the name of an implementation class. The name is the first * non-null value obtained from the following resources: *
      *
    • * The value of the (scoped) system property whose name is the same as * the SPI's fully qualified class name (as given by SPI.class.getName()). * The ScopedProperties class provides a way to bind * properties by classloader, in a secure hierarchy similar in concept * to the way classloader find class and resource files. * See ScopedProperties for more details. *

      If the ScopedProperties are not set by users, then behaviour * is equivalent to System.getProperty(). *

      *
    • *

    • * The value of a Properties properties property, if provided * as a parameter, whose name is the same as the SPI's fully qualifed class * name (as given by SPI.class.getName()). *
    • *

    • * The value obtained using the JDK1.3+ 'Service Provider' specification * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a * service named SPI.class.getName(). This is implemented * internally, so there is not a dependency on JDK 1.3+. *
    • *
    *
  • *

  • * If the name of the implementation class is non-null, load that class. * The class loaded is the first class loaded by the following sequence * of class loaders: *
      *
    • Thread Context Class Loader
    • *
    • DiscoverSingleton's Caller's Class Loader
    • *
    • SPI's Class Loader
    • *
    • DiscoverSingleton's (this class or wrapper) Class Loader
    • *
    • System Class Loader
    • *
    * An exception is thrown if the class cannot be loaded. *
  • *

  • * If the name of the implementation class is null, AND the default * implementation class (defaultImpl) is null, * then an exception is thrown. *
  • *

  • * If the name of the implementation class is null, AND the default * implementation class (defaultImpl) is non-null, * then load the default implementation class. The class loaded is the * first class loaded by the following sequence of class loaders: *
      *
    • SPI's Class Loader
    • *
    • DiscoverSingleton's (this class or wrapper) Class Loader
    • *
    • System Class Loader
    • *
    *

    * This limits the scope in which the default class loader can be found * to the SPI, DiscoverSingleton, and System class loaders. The assumption * here is that the default implementation is closely associated with the SPI * or system, and is not defined in the user's application space. *

    *

    * An exception is thrown if the class cannot be loaded. *

    *
  • *

  • * Verify that the loaded class implements the SPI: an exception is thrown * if the loaded class does not implement the SPI. *
  • *

  • * Create an instance of the class. *
  • *
* *

* Variances for various forms of the find * methods are discussed with each such method. * Variances include the following concepts: *

    *
  • rootFinderClass - a wrapper encapsulating a finder method * (factory or other helper class). The root finder class is used to * determine the 'real' caller, and hence the caller's class loader - * thereby preserving knowledge that is relevant to finding the * correct/expected implementation class. *
  • *
  • propertiesFileName - Properties may be specified * directly, or by property file name. A property file is loaded using the * same sequence of class loaders used to load the SPI implementation: *
      *
    • Thread Context Class Loader
    • *
    • DiscoverSingleton's Caller's Class Loader
    • *
    • SPI's Class Loader
    • *
    • DiscoverSingleton's (this class) Class Loader
    • *
    • System Class Loader
    • *
    *
  • *
  • groupContext - differentiates service providers for different * logical groups of service users, that might otherwise be forced to share * a common service and, more importantly, a common configuration of that * service. *

    The groupContext is used to qualify the name of the property file * name: groupContext + '.' + propertiesFileName. If that * file is not found, then the unqualified propertyFileName is used. *

    *

    In addition, groupContext is used to qualify the name of the system * property used to find the service implementation by prepending the value * of groupContext to the property name: * groupContext> + '.' + SPI.class.getName(). * Again, if a system property cannot be found by that name, then the * unqualified property name is used. *

    *
  • *
*

* *

IMPLEMENTATION NOTE - This implementation is modelled * after the SAXParserFactory and DocumentBuilderFactory implementations * (corresponding to the JAXP pluggability APIs) found in Apache Xerces. *

* * @version $Revision: 1089242 $ $Date: 2011-04-05 23:33:21 +0200 (Tue, 05 Apr 2011) $ */ public class DiscoverSingleton { /********************** (RELATIVELY) SIMPLE FINDERS ********************** * * These finders are suitable for direct use in components looking for a * service. If you are not sure which finder(s) to use, you can narrow * your search to one of these. */ /** * Find implementation of SPI. * * @param Service Provider Interface type. * @param spiClass Service Provider Interface Class. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(Class spiClass) throws DiscoveryException { return find(null, new SPInterface(spiClass), DiscoverClass.nullProperties, (DefaultClassHolder) null); } /** * Find implementation of SPI. * * @param Service Provider Interface type * * @param spiClass Service Provider Interface Class. * * @param properties Used to determine name of SPI implementation, * and passed to implementation.init() method if * implementation implements Service interface. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(Class spiClass, Properties properties) throws DiscoveryException { return find(null, new SPInterface(spiClass), new PropertiesHolder(properties), (DefaultClassHolder) null); } /** * Find implementation of SPI. * * @param Service Provider Interface type * * @param spiClass Service Provider Interface Class. * * @param defaultImpl Default implementation. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(Class spiClass, String defaultImpl) throws DiscoveryException { return find(null, new SPInterface(spiClass), DiscoverClass.nullProperties, new DefaultClassHolder(defaultImpl)); } /** * Find implementation of SPI. * * @param Service Provider Interface type * * @param spiClass Service Provider Interface Class. * * @param properties Used to determine name of SPI implementation, * and passed to implementation.init() method if * implementation implements Service interface. * * @param defaultImpl Default implementation. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(Class spiClass, Properties properties, String defaultImpl) throws DiscoveryException { return find(null, new SPInterface(spiClass), new PropertiesHolder(properties), new DefaultClassHolder(defaultImpl)); } /** * Find implementation of SPI. * * @param Service Provider Interface type * * @param spiClass Service Provider Interface Class. * * @param propertiesFileName Used to determine name of SPI implementation, * and passed to implementation.init() method if * implementation implements Service interface. * * @param defaultImpl Default implementation. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(Class spiClass, String propertiesFileName, String defaultImpl) throws DiscoveryException { return find(null, new SPInterface(spiClass), new PropertiesHolder(propertiesFileName), new DefaultClassHolder(defaultImpl)); } /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS *************** */ /** * Find implementation of SPI. * * @param Service Provider Interface type * * @param loaders The {@code ClassLoader} holder * * @param spi Service Provider Interface Class. * * @param properties Used to determine name of SPI implementation, * and passed to implementation.init() method if * implementation implements Service interface. * * @param defaultImpl Default implementation. * * @return Instance of a class implementing the SPI. * * @exception DiscoveryException Thrown if the name of a class implementing * the SPI cannot be found, if the class cannot be loaded and * instantiated, or if the resulting class does not implement * (or extend) the SPI. */ public static T find(ClassLoaders loaders, SPInterface spi, PropertiesHolder properties, DefaultClassHolder defaultImpl) throws DiscoveryException { ClassLoader contextLoader = JDKHooks.getJDKHooks().getThreadContextClassLoader(); @SuppressWarnings("unchecked") // spiName is assignable from stored object class T obj = (T) get(contextLoader, spi.getSPName()); if (obj == null) { try { obj = DiscoverClass.newInstance(loaders, spi, properties, defaultImpl); if (obj != null) { put(contextLoader, spi.getSPName(), obj); } } catch (DiscoveryException de) { throw de; } catch (Exception e) { throw new DiscoveryException("Unable to instantiate implementation class for " + spi.getSPName(), e); } } return obj; } /********************** CACHE-MANAGEMENT SUPPORT **********************/ /** * Release all internal references to previously created service * instances associated with the current thread context class loader. * The release() method is called for service instances that * implement the Service interface. * * This is useful in environments like servlet containers, * which implement application reloading by throwing away a ClassLoader. * Dangling references to objects in that class loader would prevent * garbage collection. */ public static synchronized void release() { EnvironmentCache.release(); } /** * Release any internal references to a previously created service * instance associated with the current thread context class loader. * If the SPI instance implements Service, then call * release(). * * @param spiClass The previously created service */ public static synchronized void release(Class spiClass) { Map spis = EnvironmentCache.get(JDKHooks.getJDKHooks().getThreadContextClassLoader()); if (spis != null) { spis.remove(spiClass.getName()); } } /************************* SPI CACHE SUPPORT ************************* * * Cache services by a 'key' unique to the requesting class/environment: * * When we 'release', it is expected that the caller of the 'release' * have the same thread context class loader... as that will be used * to identify all cached entries to be released. * * We will manage synchronization directly, so all caches are implemented * as HashMap (unsynchronized). * * - ClassLoader::groupContext::SPI::Instance Cache * Cache : HashMap * Key : Thread Context Class Loader (ClassLoader). * Value : groupContext::SPI Cache (HashMap). * * - groupContext::SPI::Instance Cache * Cache : HashMap * Key : groupContext (String). * Value : SPI Cache (HashMap). * * - SPI::Instance Cache * Cache : HashMap * Key : SPI Class Name (String). * Value : SPI Instance/Implementation (Object. */ /** * Implements first two levels of the cache (loader & groupContext). * Allows null keys, important as default groupContext is null. */ /** * Get service keyed by spi & classLoader. * * @param classLoader The class loader as key to retrieve the related cache * @param spiName The SPI class name * @return The object instance associated to the given class loader/SPI name */ private static synchronized Object get(ClassLoader classLoader, String spiName) { Map spis = EnvironmentCache.get(classLoader); if (spis != null) { return spis.get(spiName); } return null; } /** * Put service keyed by spi & classLoader. * * @param classLoader The {@link EnvironmentCache} key * @param spiName The SPI class name * @param service The SPI object reference */ private static synchronized void put(ClassLoader classLoader, String spiName, Object service) { if (service != null) { Map spis = EnvironmentCache.get(classLoader); if (spis == null) { spis = new HashMap(EnvironmentCache.smallHashSize); EnvironmentCache.put(classLoader, spis); } spis.put(spiName, service); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy