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

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

Go to download

Its an port to fix bugs in OSGi support. ... The Apache Commons Discovery 0.5 component is about discovering, or finding, implementations for pluggable interfaces.

There is a newer version: 7.4.0
Show newest version
/*
 * 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 - 2024 Weber Informatics LLC | Privacy Policy