Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@apache.org Received: (qmail 49163 invoked from network); 26 Jul 2002 22:35:26 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 26 Jul 2002 22:35:26 -0000 Received: (qmail 13523 invoked by uid 97); 26 Jul 2002 22:35:36 -0000 Delivered-To: qmlist-jakarta-archive-commons-dev@jakarta.apache.org Received: (qmail 13468 invoked by uid 97); 26 Jul 2002 22:35:35 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 13456 invoked by uid 97); 26 Jul 2002 22:35:34 -0000 X-Antivirus: nagoya (v4198 created Apr 24 2002) Date: 26 Jul 2002 22:35:05 -0000 Message-ID: <20020726223505.64741.qmail@icarus.apache.org> From: rsitze@apache.org To: jakarta-commons-cvs@apache.org Subject: cvs commit: jakarta-commons/discovery/src/java/org/apache/commons/discovery SPIContext.java ClassFinder.java Discovery.java ClassLoaderUtils.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N rsitze 2002/07/26 15:35:05 Modified: discovery/src/java/org/apache/commons/discovery SPIContext.java ClassFinder.java Discovery.java ClassLoaderUtils.java Log: Cleaned up javadoc & code organization. Introduced 'groupContext', which is used to qualify property names, and property file names. It will also be used to qualify cached services, so each group (as defined by groupContext) can have it's own instance of a service. Revision Changes Path 1.3 +2 -20 jakarta-commons/discovery/src/java/org/apache/commons/discovery/SPIContext.java Index: SPIContext.java =================================================================== RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/SPIContext.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- SPIContext.java 17 Jul 2002 22:03:06 -0000 1.2 +++ SPIContext.java 26 Jul 2002 22:35:05 -0000 1.3 @@ -70,13 +70,6 @@ * to include an spi and the Thread Context Class Loader for * the thread that created an instance of this object. * - * These matters are, for the most part, trivial. Bundling - * these two together is a convenience, but more importantly - * it gives a home to a portable mechanism for determining - * the thread context class loader. JDK 1.1 does not support - * the thread context class loader, yet the code below must - * be able to compile & execute in such an environment. - * * @author Richard A. Sitze */ public class SPIContext { @@ -85,14 +78,7 @@ * Wrapped bootstrap classloader if classLoader == null. */ private final ClassLoader threadContextClassLoader = - ClassLoaderUtils.findThreadContextClassLoader(); - - /** - * System class loader or null if not available (JDK 1.1). - * Wrapped bootstrap classloader if classLoader == null. - */ - private final ClassLoader systemClassLoader = - ClassLoaderUtils.findSystemClassLoader(); + ClassLoaderUtils.getThreadContextClassLoader(); /** * List of class loaders @@ -111,15 +97,11 @@ this.loaders = ClassLoaderUtils.compactUniq( new ClassLoader[] { threadContextClassLoader, BootstrapLoader.wrap(spi.getClassLoader()), - systemClassLoader }); + ClassLoaderUtils.getSystemClassLoader() }); } public ClassLoader getThreadContextClassLoader() { return threadContextClassLoader; - } - - public ClassLoader getSystemClassLoader() { - return systemClassLoader; } public ClassLoader[] getClassLoaders() { 1.5 +60 -33 jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassFinder.java Index: ClassFinder.java =================================================================== RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassFinder.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- ClassFinder.java 18 Jul 2002 18:32:34 -0000 1.4 +++ ClassFinder.java 26 Jul 2002 22:35:05 -0000 1.5 @@ -90,53 +90,70 @@ * looking for an implementation of. */ private final SPIContext spiContext; + private final String groupContext; private final Class rootFinderClass; - private final ClassLoader[] localLoaders; + /** + * This is NOT the same as spiContext.getClassLoaders(), + * which includes the thread context class loader. + */ + private final ClassLoader[] systemLoaders; + private final ClassLoader[] allLoaders; ClassLoader[] getAllLoaders() { return allLoaders; } - public ClassFinder(SPIContext spiContext, Class rootFinderClass) { + public ClassFinder(SPIContext spiContext, + String groupContext, + Class rootFinderClass) + { this.spiContext = spiContext; + this.groupContext = groupContext; this.rootFinderClass = rootFinderClass; - this.localLoaders = getLocalLoaders(spiContext, rootFinderClass); + this.systemLoaders = getSystemLoaders(spiContext, rootFinderClass); this.allLoaders = getAllLoaders(spiContext, rootFinderClass); //System.out.println("Finding '" + spiContext.getSPI().getName() + "'"); } - public ClassFinder(Class spi, Class rootFinderClass) { + public ClassFinder(Class spi, + String groupContext, + Class rootFinderClass) + { this.spiContext = new SPIContext(spi); + this.groupContext = groupContext; this.rootFinderClass = rootFinderClass; - this.localLoaders = getLocalLoaders(spiContext, rootFinderClass); + this.systemLoaders = getSystemLoaders(spiContext, rootFinderClass); this.allLoaders = getAllLoaders(spiContext, rootFinderClass); //System.out.println("Finding '" + spi.getName() + "'"); } + + public SPIContext getSPIContext() { return spiContext; } + + /** * Return the specified serviceImplName implementation - * class. If localOnly is true, try the - * class loaders local to the caller: root finder class's (default - * ServiceFinder) and system. If localOnly is - * false, try each of the following class loaders: - * thread context, caller's, spi's, root finder class's (default - * ServiceFinder), and system. + * class. If systemOnly is true, try the + * 'system' class loaders: spi's, root finder class's (default is + * Discovery) and system. If systemOnly is false, + * try each of the following class loaders: thread context, caller's, spi's, + * root finder class's (default is Discovery), and system. * * @param serviceImplName Fully qualified name of the implementation class - * @param localOnly Use only local class loader + * @param systemOnly Use only 'system' class loaders * (do not try thread context class loader). * * @exception DiscoveryException if a suitable instance cannot be created, * or if the class created is not an instance * of spi */ - public Class findClass(String serviceImplName, boolean localOnly) + public Class findClass(String serviceImplName, boolean systemOnly) throws DiscoveryException { Class clazz = ClassLoaderUtils.loadClass(serviceImplName, - localOnly ? localLoaders : allLoaders); + systemOnly ? systemLoaders : allLoaders); if (clazz != null && !spiContext.getSPI().isAssignableFrom(clazz)) { throw new DiscoveryException("Class " + serviceImplName + @@ -148,25 +165,37 @@ /** * Return the specified resourceName as an - * InputStream. If localOnly is - * true, try the class loaders local to the caller: - * root finder class's (default ServiceFinder) and system. - * If localOnly is false, try each of + * InputStream. If systemOnly is + * true, try the 'system' class loaders: spi's, + * root finder class's (default is Discovery), and system. + * If systemOnly is false, try each of * the following class loaders: thread context, caller's, spi's, - * root finder class's (default ServiceFinder), and system. + * root finder class's (default is Discovery), and system. * * @param resourceName name of the resource - * @param localOnly Use only local class loader + * @param system Use only 'system' class loaders * (do not try thread context class loader). * * @exception DiscoveryException if a suitable resource cannot be created. */ - public InputStream findResourceAsStream(String resourceName, boolean localOnly) + public InputStream findResourceAsStream(String resourceName) throws DiscoveryException { - return ClassLoaderUtils.getResourceAsStream(spiContext.getSPI().getPackage().getName(), - resourceName, - localOnly ? localLoaders : allLoaders); + String name = spiContext.getSPI().getPackage().getName(); + + InputStream stream = + (groupContext == null) + ? null + : ClassLoaderUtils.getResourceAsStream(name, + groupContext + "." + resourceName, + allLoaders); + + if (stream == null) + stream = ClassLoaderUtils.getResourceAsStream(name, + resourceName, + allLoaders); + + return stream; } /** @@ -289,16 +318,14 @@ } /** - * List of 'local' classes and class loaders. - * By using ClassLoaderHolder to holder Class and ClassLoader objects, - * we preserve the difference in behaviour between the two for loading - * resources.. + * List of 'system' class loaders to the SPI */ - private static final ClassLoader[] getLocalLoaders(SPIContext spiContext, - Class rootFinderClass) { + private static final ClassLoader[] getSystemLoaders(SPIContext spiContext, + Class rootFinderClass) { return ClassLoaderUtils.compactUniq( - new ClassLoader[] {BootstrapLoader.wrap(rootFinderClass.getClassLoader()), - spiContext.getSystemClassLoader() + new ClassLoader[] {BootstrapLoader.wrap(spiContext.getSPI().getClassLoader()), + BootstrapLoader.wrap(rootFinderClass.getClassLoader()), + ClassLoaderUtils.getSystemClassLoader() }); } @@ -309,7 +336,7 @@ getCallerClassLoader(rootFinderClass), BootstrapLoader.wrap(spiContext.getSPI().getClassLoader()), BootstrapLoader.wrap(rootFinderClass.getClassLoader()), - spiContext.getSystemClassLoader() + ClassLoaderUtils.getSystemClassLoader() }); } } 1.2 +581 -334 jakarta-commons/discovery/src/java/org/apache/commons/discovery/Discovery.java Index: Discovery.java =================================================================== RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/Discovery.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Discovery.java 18 Jul 2002 18:32:34 -0000 1.1 +++ Discovery.java 26 Jul 2002 22:35:05 -0000 1.2 @@ -71,13 +71,146 @@ /** - *

Discover service instances, + *

Discover service providers, * with discovery and configuration features similar to that employed - * by standard Java APIs such as JAXP.

- * + * by standard Java APIs such as JAXP. + *

+ * + *

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. + *

+ * + *

Discovery provides the find methods for locating and + * instantiating an implementation of a service (SPI). Each form of + * find varies slightly, but they all perform the same basic + * function. The Discovery 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 system property whose name is the same as the SPI's + * fully qualified class name (as given by SPI.class.getName()). + *
    • + *

    • + * 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
    • + *
    • Discovery's Caller's Class Loader
    • + *
    • SPI's Class Loader
    • + *
    • Discovery's (this class) 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 name (defaultImplName) is null, + * then an exception is thrown. + *
  • + *

  • + * If the name of the implementation class is null, AND the default + * implementation class name (defaultImplName) 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
    • + *
    • Discovery's (this class) Class Loader
    • + *
    • System Class Loader
    • + *
    + *

    + * This limits the scope in which the default class loader can be found + * to the SPI, Discovery, 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. + *
  • + *

  • + * If the loaded class implements the Service interface, + * then invoke the init(Properties) method, passing in the + * Properties properties parameter, if provided. + *
  • + *
      + * + *

      + * 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
        • + *
        • Discovery's Caller's Class Loader
        • + *
        • SPI's Class Loader
        • + *
        • Discovery'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. + *

        + *
      • + *
      + *

      + * + *

      + * Other concepts + *

      + * *

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

      + * (corresponding to the JAXP pluggability APIs) found in Apache Xerces. + *

      * * @author Richard A. Sitze * @author Craig R. McClanahan @@ -86,239 +219,105 @@ */ public class Discovery { /** - * Sets of previously encountered service interfaces (spis), keyed by the - * interface (Class). Each element is a ServiceCache. + * Readable placeholder for a null value. */ - private static final Hashtable service_caches = new Hashtable(13); + private static final Properties nullProperties = null; /** - *

      Locate and instantiate a service. The service implementation - * class is located using the following ordered lookup:

      - *
        - *
      • Try to load a class with the name obtained from the system - * property, having the same name as the spi class: - * spiContext.getSPI().getName().
      • - * - *
      • Try to load a class with the name obtained from the - * properties parameter, having the same - * name as the spi class: spiContext.getSPI().getName().
      • - * - *
      • Use the JDK1.3+ 'Service Provider' specification - * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) - * to locate a service named spiContext.getSPI().getName(). - * Implemented internally, so there is not a hard - * dependency on JDK 1.3+.
      • - * - *
      • Fall back to a default implementation class, as specified by - * non-null defaultImplName.
      • - *
      - * - *

      In most cases, after class NAME is found, then a - * number of attempts are made to load the class using different - * class loaders, in the following sequence: - *

        - *
      • Thread Context Class Loader
      • - *
      • Caller's Class Loader
      • - *
      • SPI's Class Loader
      • - *
      • ServiceFinder's (this class) Class Loader
      • - *
      • System Class Loader
      • - *
      - * - *

      The default implementation is loaded using: - *

        - *
      • ServiceFinder's (this class) Class Loader
      • - *
      • System Class Loader
      • - *
      - * - * @param ClassFinder Represents the class loaders provided - * by a root finder class, and the spiContext. - * - * @param spiContext The SPI Context identifies the SPI and the - * thread context class loader. - * spiContext.getSPI().getName() id's the (property) - * name of the service implementation. Presumed to be an interface, - * but there is nothing in the code that prevents it from - * being an abstract base class, or even a class. - * - * @param properties used as one alternative to find name of service - * implementation class, with property name specified by - * spi.getName(). If the implementation class found - * for spi implements the Service - * interface, then spiInstance.init(properties) is - * called. - * - * @param defaultImplName Name of the default implementation class. - * - * @exception DiscoveryException if the implementation class - * is not available, - * cannot be instantiated, - * or is not an instance of spi. - */ - private static Object find(ClassFinder classFinder, - SPIContext spiContext, - Properties properties, - String defaultImplName) - throws DiscoveryException - { - /** - * Return previously registered service object (not class) - * for this spi. Try each class loader in succession. - */ - Object service = null; - ClassLoader[] allLoaders = classFinder.getAllLoaders(); - - for (int idx = 0; service == null && idx < allLoaders.length; idx++) { - service = get(spiContext.getSPI().getName(), allLoaders[idx]); - } - - if (service != null) { - // First, try the system property - Class clazz = classFinder.systemFindClass(); + * Readable placeholder for a null value. + */ + private static final String nullDefaultImplName = null; - if (clazz == null) { - // Second, try the properties parameter - if (properties != null) - clazz = classFinder.findClass(properties); - - if (clazz == null) { - // Third, try to find a service by using the JDK1.3 jar - // discovery mechanism. - clazz = classFinder.jdk13FindClass(); - - if (clazz == null) { - // Fourth, try the fallback implementation class - clazz = classFinder.findClass(defaultImplName, true); - - if (clazz == null) { - throw new DiscoveryException - ("No implementation defined for " + - spiContext.getSPI().getName()); - } - } - } - } - - if (clazz != null) { - try { - service = clazz.newInstance(); - put(spiContext.getSPI().getName(), clazz.getClassLoader(), service); - } catch (Exception e) { - throw new DiscoveryException("Unable to instantiate " + spiContext.getSPI().getName(), e); - } - - if (service instanceof Service) { - ((Service)service).init(properties); - } - } - } - - return service; - } + /** + * Readable placeholder for a null value. + */ + private static final String nullGroupContext = null; + + + /********************** (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. + */ /** - *

      Locate and instantiate a service. The service implementation - * class is located using the following ordered lookup:

      - *
        - *
      • Try to load a class with the name obtained from the system - * property, having the same name as the spi class: - * spiContext.getSPI().getName().
      • - * - *
      • Try to load a class with the name obtained from the - * properties parameter, having the same - * name as the spi class: spiContext.getSPI().getName().
      • - * - *
      • Use the JDK1.3+ 'Service Provider' specification - * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) - * to locate a service named spiContext.getSPI().getName(). - * Implemented internally, so there is not a hard - * dependency on JDK 1.3+.
      • - * - *
      • Fall back to a default implementation class, as specified by - * non-null defaultImplName.
      • - *
      - * - *

      In most cases, after class NAME is found, then a - * number of attempts are made to load the class using different - * class loaders, in the following sequence: - *

        - *
      • Thread Context Class Loader
      • - *
      • Caller's Class Loader
      • - *
      • SPI's Class Loader
      • - *
      • ServiceFinder's (this class) Class Loader
      • - *
      • System Class Loader
      • - *
      - * - *

      The default implementation is loaded using: - *

        - *
      • ServiceFinder's (this class) Class Loader
      • - *
      • System Class Loader
      • - *
      - * - * @param rootFinderClass The root finder class. - * If a wrapper/factory class is used around 'ServiceFinder', - * then this will be that wrapper/factory class. - * - * @param spiContext The SPI Context identifies the SPI and the - * thread context class loader. - * spiContext.getSPI().getName() id's the (property) - * name of the service implementation. Presumed to be an interface, - * but there is nothing in the code that prevents it from - * being an abstract base class, or even a class. - * - * @param properties used as one alternative to find name of service - * implementation class, with property name specified by - * spi.getName(). If the implementation class found - * for spi implements the Service - * interface, then spiInstance.init(properties) is - * called. + * Find implementation of SPI. * - * @param defaultImplName Name of the default implementation class. - * - * @exception DiscoveryException if the implementation class - * is not available, - * cannot be instantiated, - * or is not an instance of spi. + * @param spi 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 Object find(Class rootFinderClass, - SPIContext spiContext, - Properties properties, - String defaultImplName) + public static Object find(Class spi) throws DiscoveryException { - // thread context can change on each call, - // so establish context for this one call. - ClassFinder classFinder = new ClassFinder(spiContext, rootFinderClass); - return find(classFinder, spiContext, properties, defaultImplName); + return find(Discovery.class, spi); } - + /** - * Equivalent to - * find(ServiceFinder.class, spiContext, properties, defaultImplName). + * Find implementation of SPI. + * + * @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. + * + * @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 Object find(SPIContext spiContext, - Properties properties, - String defaultImplName) + public static Object find(Class spi, Properties properties) throws DiscoveryException { - return find(Discovery.class, spiContext, properties, defaultImplName); + return find(Discovery.class, spi, properties); } - + /** - * Equivalent to - * find(new SPIContext(spi), properties, defaultImplName). + * Find implementation of SPI. + * + * @param spi Service Provider Interface Class. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, - Class spi, - Properties properties, - String defaultImplName) + public static Object find(Class spi, String defaultImplName) throws DiscoveryException { - return find(rootFinderClass, new SPIContext(spi), properties, defaultImplName); + return find(Discovery.class, spi, defaultImplName); } /** - * Equivalent to - * find(ServiceFinder.class, spi, properties, defaultImplName). + * Find implementation of SPI. + * + * @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 defaultImplName Default implementation name. + * + * @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 Object find(Class spi, Properties properties, @@ -327,200 +326,439 @@ { return find(Discovery.class, spi, properties, defaultImplName); } - + /** - * Load properties file, and call - * find(rootFinderClass, spiContext, properties, defaultImplName). + * Find implementation of SPI. + * + * @param spi Service Provider Interface Class. + * + * @param propertiesFileName The property file name. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, - SPIContext spiContext, - String overloadPrefix, + public static Object find(Class spi, String propertiesFileName, String defaultImplName) throws DiscoveryException { - // thread context can change on each call, - // so establish context for this one call. - ClassFinder classFinder = new ClassFinder(spiContext, rootFinderClass); - - Properties properties = null; - - if (propertiesFileName != null) { - try { - InputStream stream = - (overloadPrefix == null) - ? null - : classFinder.findResourceAsStream(overloadPrefix + "." + propertiesFileName, false); - - if (stream == null) - stream = classFinder.findResourceAsStream(propertiesFileName, false); - - if (stream != null) { - properties = new Properties(); - try { - properties.load(stream); - } finally { - stream.close(); - } - } - } catch (IOException e) { - } catch (SecurityException e) { - } - } - - return find(classFinder, spiContext, properties, defaultImplName); + return find(Discovery.class, spi, propertiesFileName, defaultImplName); } /** - * Load properties file, and call - * find(ServiceFinder.class, spiContext, propertiesFileName, defaultImplName). - */ - public static Object find(SPIContext spiContext, - String overloadPrefix, - String propertiesFileName, + * Find implementation of SPI. + * + * @param spi Service Provider Interface Class. + * + * @param groupContext qualifier for the name of the system property + * used to find the service implementation. If a system property + * cannot be found by that name, then the unqualified property + * name is used. + * + * @param properties Used to determine name of SPI implementation, + * and passed to implementation.init() method if + * implementation implements Service interface. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class spi, + String groupContext, + Properties properties, String defaultImplName) throws DiscoveryException { - return find(Discovery.class, spiContext, overloadPrefix, propertiesFileName, defaultImplName); + return find(Discovery.class, spi, groupContext, properties, defaultImplName); } /** - * Equivalent to - * find(rootFinderClass, new SPIContext(spi), propertiesFileName, defaultImplName). + * Find implementation of SPI unique to a group context. + * + * @param spi Service Provider Interface Class. + * + * @param groupContext qualifier for the property file name and for + * the system property name used to find the service implementation. + * If not found, the unqualified names are used. + * + * @param propertiesFileName The (qualified and unqualified) property file + * name. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, - Class spi, - String overloadPrefix, + public static Object find(Class spi, + String groupContext, String propertiesFileName, String defaultImplName) throws DiscoveryException { - return find(rootFinderClass, new SPIContext(spi), overloadPrefix, propertiesFileName, defaultImplName); + return find(Discovery.class, spi, groupContext, propertiesFileName, defaultImplName); } + + + /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS *************** + * + * These finders provide a rootFinderClass. The root finder is the wrapper + * class (factories or helper classes) that invoke the Discovery find + * methods, presumably providing (default) values for propertiesFileName + * and defaultImplName. Having access to this wrapper class provides a + * way to determine the 'real' caller, and hence the caller's class loader. + * Thus preserving knowledge that is relevant to finding the + * correct/expected implementation class. + */ + /** - * Equivalent to - * find(new SPIContext(spi), propertiesFileName, defaultImplName). - */ - public static Object find(Class spi, - String overloadPrefix, - String propertiesFileName, - String defaultImplName) + * Find implementation of SPI. + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @param spi 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, or if + * the resulting class does not implement the SPI. + */ + public static Object find(Class rootFinderClass, Class spi) throws DiscoveryException { - return find(new SPIContext(spi), overloadPrefix, propertiesFileName, defaultImplName); + return find(rootFinderClass, spi, nullProperties, nullDefaultImplName); } /** - * Load properties file, and call - * find(rootFinderClass, spiContext, properties, defaultImplName). - */ - public static Object find(Class rootFinderClass, - SPIContext spiContext, - String propertiesFileName, - String defaultImplName) + * Find implementation of SPI. + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @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. + * + * @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 Object find(Class rootFinderClass, Class spi, Properties properties) throws DiscoveryException { - return find(rootFinderClass, spiContext, (String)null, propertiesFileName, defaultImplName); + return find(rootFinderClass, spi, properties, nullDefaultImplName); } - + /** - * Load properties file, and call - * find(ServiceFinder.class, spiContext, propertiesFileName, defaultImplName). - */ - public static Object find(SPIContext spiContext, - String propertiesFileName, - String defaultImplName) + * Find implementation of SPI. + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @param spi Service Provider Interface Class. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, Class spi, String defaultImplName) throws DiscoveryException { - return find(Discovery.class, spiContext, (String)null, propertiesFileName, defaultImplName); + return find(rootFinderClass, spi, nullProperties, defaultImplName); } - + /** - * Equivalent to - * find(rootFinderClass, new SPIContext(spi), propertiesFileName, defaultImplName). - */ + * Find implementation of SPI. + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @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 defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, Class spi, - String propertiesFileName, + Properties properties, String defaultImplName) throws DiscoveryException { - return find(rootFinderClass, new SPIContext(spi), propertiesFileName, defaultImplName); + return loadClass(new ClassFinder(spi, nullGroupContext, rootFinderClass), + properties, defaultImplName); } /** - * Equivalent to - * find(new SPIContext(spi), propertiesFileName, defaultImplName). + * Find implementation of SPI. + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @param spi Service Provider Interface Class. + * + * @param propertiesFileName The property file name. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class spi, + public static Object find(Class rootFinderClass, + Class spi, String propertiesFileName, String defaultImplName) throws DiscoveryException { - return find(new SPIContext(spi), propertiesFileName, defaultImplName); + ClassFinder classFinder = new ClassFinder(spi, nullGroupContext, rootFinderClass); + return loadClass(classFinder, + loadProperties(classFinder, propertiesFileName), + defaultImplName); } /** * Find implementation of SPI. - * Equivalent to find(rootFinderClass, spi, (Properties)null, defaultImplName); - */ - public static Object find(Class rootFinderClass, Class spi, String defaultImplName) + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @param spi Service Provider Interface Class. + * + * @param groupContext qualifier for the property file name and for + * the system property name used to find the service implementation. + * If not found, the unqualified names are used. + * + * @param properties Used to determine name of SPI implementation, + * and passed to implementation.init() method if + * implementation implements Service interface. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, + Class spi, + String groupContext, + Properties properties, + String defaultImplName) throws DiscoveryException { - return find(rootFinderClass, spi, (Properties)null, defaultImplName); + return loadClass(new ClassFinder(spi, groupContext, rootFinderClass), + properties, defaultImplName); } /** * Find implementation of SPI. - * Equivalent to find(spi, (Properties)null, defaultImplName); - */ - public static Object find(Class spi, String defaultImplName) + * + * @param rootFinderClass Wrapper class used by end-user, that ultimately + * calls this finder method. + * + * @param spi Service Provider Interface Class. + * + * @param groupContext qualifier for the property file name and for + * the system property name used to find the service implementation. + * If not found, the unqualified names are used. + * + * @param propertiesFileName The property file name. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class rootFinderClass, + Class spi, + String groupContext, + String propertiesFileName, + String defaultImplName) throws DiscoveryException { - return find(spi, (Properties)null, defaultImplName); + ClassFinder classFinder = new ClassFinder(spi, groupContext, rootFinderClass); + return loadClass(classFinder, + loadProperties(classFinder, propertiesFileName), + defaultImplName); } - /** - * Find implementation of SPI. - * Equivalent to find(rootFinderClass, spi, properties, null); + + /************************* CORE LOADERS ************************* */ - public static Object find(Class rootFinderClass, Class spi, Properties properties) - throws DiscoveryException - { - return find(rootFinderClass, spi, properties, null); - } - + /** - * Find implementation of SPI. - * Equivalent to find(spi, properties, null); + * Load implementation of SPI. + * + * @param ClassFinder Represents the spiContext, class loaders + * (including root finder class), and the groupContext. + * + * @param properties Used to determine name of SPI implementation, + * and passed to implementation.init() method if + * implementation implements Service interface. + * + * @param defaultImplName Default implementation name. + * + * @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 Object find(Class spi, Properties properties) + private static Object loadClass(ClassFinder classFinder, + Properties properties, + String defaultImplName) throws DiscoveryException { - return find(spi, properties, null); - } + /** + * Return previously registered service object (not class) + * for this spi. Try each class loader in succession. + */ + Object service = null; + ClassLoader[] allLoaders = classFinder.getAllLoaders(); - /** - * Find implementation of SPI. - * Equivalent to find(rootFinderClass, spi, (Properties)null, null); - */ - public static Object find(Class rootFinderClass, Class spi) - throws DiscoveryException - { - return find(rootFinderClass, spi, (Properties)null, null); - } + for (int idx = 0; service == null && idx < allLoaders.length; idx++) { + service = get(classFinder.getSPIContext().getSPI().getName(), allLoaders[idx]); + } + if (service != null) { + // First, try the system property + Class clazz = classFinder.systemFindClass(); + + if (clazz == null) { + // Second, try the properties parameter + if (properties != null) + clazz = classFinder.findClass(properties); + + if (clazz == null) { + // Third, try to find a service by using the JDK1.3 jar + // discovery mechanism. + clazz = classFinder.jdk13FindClass(); + + if (clazz == null) { + // Fourth, try the fallback implementation class, + // but limit loaders to 'system' loaders, in an + // attempt to ensure that the default picked up is + // the one that one intended. + clazz = classFinder.findClass(defaultImplName, true); + + if (clazz == null) { + throw new DiscoveryException + ("No implementation defined for " + + classFinder.getSPIContext().getSPI().getName()); + } + } + } + } + + if (clazz != null) { + try { + service = clazz.newInstance(); + put(classFinder.getSPIContext().getSPI().getName(), clazz.getClassLoader(), service); + } catch (Exception e) { + throw new DiscoveryException("Unable to instantiate " + classFinder.getSPIContext().getSPI().getName(), e); + } + + if (service instanceof Service) { + ((Service)service).init(properties); + } + } + } + + return service; + } + /** - * Find implementation of SPI. - * Equivalent to find(spi, (Properties)null, null); - */ - public static Object find(Class spi) + * Load property file (qualified by groupContext param to classFinder). + * + * @param ClassFinder Represents the spiContext, class loaders + * (including root finder class), and the groupContext. + * + * @param propertiesFileName The property file name. + * + * @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. + */ + private static Properties loadProperties(ClassFinder classFinder, + String propertiesFileName) throws DiscoveryException { - return find(spi, (Properties)null, null); + Properties properties = null; + + if (propertiesFileName != null) { + try { + InputStream stream = + classFinder.findResourceAsStream(propertiesFileName); + + if (stream != null) { + properties = new Properties(); + try { + properties.load(stream); + } finally { + stream.close(); + } + } + } catch (IOException e) { + } catch (SecurityException e) { + } + } + + return properties; } - + + + /************************* SPI LIFE-CYCLE SUPPORT *************************/ + /** * Release any internal references to previously created service instances, * after calling the instance method release() on each of them. @@ -559,6 +797,15 @@ } } } + + + /************************* SPI CACHE SUPPORT *************************/ + + /** + * Sets of previously encountered service interfaces (spis), keyed by the + * interface (Class). Each element is a ServiceCache. + */ + private static final Hashtable service_caches = new Hashtable(13); /** * Get service keyed by spi & classLoader. 1.4 +60 -36 jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassLoaderUtils.java Index: ClassLoaderUtils.java =================================================================== RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassLoaderUtils.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- ClassLoaderUtils.java 18 Jul 2002 18:32:34 -0000 1.3 +++ ClassLoaderUtils.java 26 Jul 2002 22:35:05 -0000 1.4 @@ -82,6 +82,12 @@ * @author Costin Manolache */ public class ClassLoaderUtils { + /** + * This doesn't change across threads... so cache the value. + */ + private static final ClassLoader systemClassLoader + = findSystemClassLoader(); + private static final boolean debug = false; /** @@ -363,42 +369,52 @@ } /** - * Return the system class loader if available. - * Otherwise return null. If the system class loader - * is the bootstrap classloader, then it is 'wrapped' + * Return the thread context class loader if available. + * Otherwise return null. If the thread context class + * loader is the bootstrap classloader, then it is 'wrapped' * (see BootstrapLoader). Therefore this method only - * returns 'null' if a system class loader could not + * returns 'null' if a thread context class loader could not * be identified. * - * The system class loader is available for JDK 1.2 + * The thread context class loader is available for JDK 1.2 * or later, if certain security conditions are met. * * @exception DiscoveryException if a suitable class loader - * cannot be identified. + * cannot be identified. */ - public static ClassLoader findSystemClassLoader() + public static ClassLoader getThreadContextClassLoader() throws DiscoveryException { ClassLoader classLoader = null; try { // Are we running on a JDK 1.2 or later system? - Method method = ClassLoader.class.getMethod("getSystemClassLoader", null); + Method method = Thread.class.getMethod("getContextClassLoader", null); - // Get the system class loader (if there is one) + // Get the thread context class loader (if there is one) try { classLoader = - BootstrapLoader.wrap((ClassLoader)method.invoke(null, null)); + BootstrapLoader.wrap((ClassLoader)method.invoke(Thread.currentThread(), null)); } catch (IllegalAccessException e) { throw new DiscoveryException("Unexpected IllegalAccessException", e); } catch (InvocationTargetException e) { /** * InvocationTargetException is thrown by 'invoke' when - * the method being invoked (ClassLoader.getSystemClassLoader) + * the method being invoked (Thread.getContextClassLoader) * throws an exception. * - * ClassLoader.getSystemClassLoader() throws SecurityException - * if security permissions are restricted. + * Thread.getContextClassLoader() throws SecurityException + * when the context class loader isn't an ancestor of the + * calling class's class loader, or if security permissions + * are restricted. + * + * In the first case (the context class loader isn't an + * ancestor of the calling class's class loader), we want + * to ignore and keep going. We cannot help but also ignore + * the second case (restricted security permissions) with + * the logic below, but other calls elsewhere (to obtain + * a class loader) will re-trigger this exception where + * we can make a distinction. */ if (e.getTargetException() instanceof SecurityException) { classLoader = null; // ignore @@ -418,54 +434,62 @@ // Return the selected class loader return classLoader; } + + /** + * Return the system class loader if available. + * Otherwise return null. If the system class loader + * is the bootstrap classloader, then it is 'wrapped' + * (see BootstrapLoader). Therefore this method only + * returns 'null' if a system class loader could not + * be identified. + * + * The system class loader is available for JDK 1.2 + * or later, if certain security conditions are met. + * + * @exception DiscoveryException if a suitable class loader + * cannot be identified. + */ + public static ClassLoader getSystemClassLoader() { + return systemClassLoader; + } /** - * Return the thread context class loader if available. - * Otherwise return null. If the thread context class - * loader is the bootstrap classloader, then it is 'wrapped' + * Return the system class loader if available. + * Otherwise return null. If the system class loader + * is the bootstrap classloader, then it is 'wrapped' * (see BootstrapLoader). Therefore this method only - * returns 'null' if a thread context class loader could not + * returns 'null' if a system class loader could not * be identified. * - * The thread context class loader is available for JDK 1.2 + * The system class loader is available for JDK 1.2 * or later, if certain security conditions are met. * * @exception DiscoveryException if a suitable class loader - * cannot be identified. + * cannot be identified. */ - public static ClassLoader findThreadContextClassLoader() + private static ClassLoader findSystemClassLoader() throws DiscoveryException { ClassLoader classLoader = null; try { // Are we running on a JDK 1.2 or later system? - Method method = Thread.class.getMethod("getContextClassLoader", null); + Method method = ClassLoader.class.getMethod("getSystemClassLoader", null); - // Get the thread context class loader (if there is one) + // Get the system class loader (if there is one) try { classLoader = - BootstrapLoader.wrap((ClassLoader)method.invoke(Thread.currentThread(), null)); + BootstrapLoader.wrap((ClassLoader)method.invoke(null, null)); } catch (IllegalAccessException e) { throw new DiscoveryException("Unexpected IllegalAccessException", e); } catch (InvocationTargetException e) { /** * InvocationTargetException is thrown by 'invoke' when - * the method being invoked (Thread.getContextClassLoader) + * the method being invoked (ClassLoader.getSystemClassLoader) * throws an exception. * - * Thread.getContextClassLoader() throws SecurityException - * when the context class loader isn't an ancestor of the - * calling class's class loader, or if security permissions - * are restricted. - * - * In the first case (the context class loader isn't an - * ancestor of the calling class's class loader), we want - * to ignore and keep going. We cannot help but also ignore - * the second case (restricted security permissions) with - * the logic below, but other calls elsewhere (to obtain - * a class loader) will re-trigger this exception where - * we can make a distinction. + * ClassLoader.getSystemClassLoader() throws SecurityException + * if security permissions are restricted. */ if (e.getTargetException() instanceof SecurityException) { classLoader = null; // ignore -- To unsubscribe, e-mail: For additional commands, e-mail: