commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rsi...@apache.org
Subject cvs commit: jakarta-commons/discovery best-practices.html
Date Fri, 09 Aug 2002 14:58:00 GMT
rsitze      2002/08/09 07:58:00

  Modified:    discovery/src/java/org/apache/commons/discovery Service.java
                        ManagedProperties.java
               discovery/src/test/org/apache/commons/discovery TestAll.java
               discovery best-practices.html
  Added:       discovery/src/java/org/apache/commons/discovery
                        DiscoverSingleton.java SingletonService.java
                        DiscoverClass.java
               discovery/src/java/org/apache/commons/discovery/load
                        ClassLoaderUtils.java Loaders.java
                        BootstrapLoader.java SPIContext.java
               discovery/src/java/org/apache/commons/discovery/strategy
                        LoadStrategy.java Utils.java
                        DefaultLoadStrategy.java
  Removed:     discovery/src/java/org/apache/commons/discovery
                        Discovery.java ClassLoaderUtils.java
                        SPIContext.java BootstrapLoader.java
                        ClassFinder.java
  Log:
  Refactoring is my life...
  rename Discovery to DiscoverSingleton,
  new DiscoverClass,
  reorganized internals, leaving public interfaces in root package.
  Introduced a 'LoadStrategy' interface & default impl, with thoughts
  toward one day making that pluggable (via discovery, of course).
  
  Revision  Changes    Path
  1.4       +4 -6      jakarta-commons/discovery/src/java/org/apache/commons/discovery/Service.java
  
  Index: Service.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/Service.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Service.java	29 Jul 2002 20:38:22 -0000	1.3
  +++ Service.java	9 Aug 2002 14:57:59 -0000	1.4
  @@ -65,17 +65,15 @@
   
   
   /**
  - * <p>Optional Service interface to facilitate life-cycle management.<p>
  + * <p>Optional Service interface to facilitate Service instantiation.<p>
    * 
  - * <p>A service is not required to implement this interface, but if
  - * it does then the service is notified when it is being initialized
  - * and released by Discovery.</p>
  + * <p>A class is not required to implement this interface, but if
  + * it does then startup properties are passed to the class via <code>init</code>.
  + * </p>
    * 
    * @author Richard A. Sitze
    * @version $Revision$ $Date$
    */
   public interface Service {
       public void init(String groupContext, Properties properties);
  -
  -    public void release();
   }
  
  
  
  1.2       +2 -3      jakarta-commons/discovery/src/java/org/apache/commons/discovery/ManagedProperties.java
  
  Index: ManagedProperties.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/ManagedProperties.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ManagedProperties.java	5 Aug 2002 22:06:03 -0000	1.1
  +++ ManagedProperties.java	9 Aug 2002 14:57:59 -0000	1.2
  @@ -55,12 +55,11 @@
   
   package org.apache.commons.discovery;
   
  -import java.util.Map;
  -import java.util.Set;
  -import java.util.Properties;
   import java.util.Enumeration;
   import java.util.HashMap;
   import java.util.Hashtable;
  +import java.util.Map;
  +import java.util.Properties;
   
   
   /**
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/DiscoverSingleton.java
  
  Index: DiscoverSingleton.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery;
  
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Properties;
  
  import org.apache.commons.discovery.load.ClassLoaderUtils;
  import org.apache.commons.discovery.load.SPIContext;
  
  
  /**
   * <p>Discover singleton service providers,  with discovery and configuration features
   * similar to that employed by standard Java APIs such as JAXP.
   * </p>
   * 
   * <p>DiscoverSingleton instances are managed (life-cycle) by the Discovery
   * service, which maintains a cache keyed by a combination of
   * <ul>
   *   <li>thread context class loader,</li>
   *   <li>groupContext, and</li>
   *   <li>SPI.</li>
   * </ul>
   * This DOES allow multiple instances of a given <i>singleton</i> class
   * to exist for different class loaders and different group contexts.
   * </p>
   * 
   * <p>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.
   * </p>
   * 
   * <p>DiscoverSingleton provides the <code>find</code> methods for locating and
   * instantiating a singleton instance of an implementation of a service (SPI).
   * Each form of <code>find</code> varies slightly, but they all perform the
   * same basic function.
   * 
   * The simplest <code>find</code> 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:
   * <ul>
   * <li>static Object find(Class spi);</li>
   * <li>static Object find(Class spi, Properties properties);</li>
   * <li>static Object find(Class spi, String defaultImplName);</li>
   * <li>static Object find(Class spi, Properties properties, String defaultImplName);</li>
   * <li>static Object find(Class spi, String propertiesFileName, String defaultImplName);</li>
   * <li>static Object find(String groupContext, Class spi,
   *                        Properties properties, String defaultImplName);</li>
   * <li>static Object find(String groupContext, Class spi,
   *                        String propertiesFileName, String defaultImplName);</li>
   * </ul>
   * 
   * The <code>DiscoverSingleton.find</code> methods proceed as follows:
   * </p>
   * <ul>
   *   <p><li>
   *   Examine an internal cache to determine if the desired service was
   *   previously identified and instantiated.  If found in cache, return it.
   *   </li></p>
   *   <p><li>
   *   Get the name of an implementation class.  The name is the first
   *   non-null value obtained from the following resources:
   *   <ul>
   *     <li>
   *     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 <code>ScopedProperties</code> 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 <code>ScopedProperties</code> for more details.
   *     <p>If the ScopedProperties are not set by users, then behaviour
   *     is equivalent to <code>System.getProperty()</code>.
   *     </p>
   *     </li>
   *     <p><li>
   *     The value of a <code>Properties properties</code> 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()).
   *     </li></p>
   *     <p><li>
   *     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 <code>SPI.class.getName()</code>.  This is implemented
   *     internally, so there is not a dependency on JDK 1.3+.
   *     </li></p>
   *   </ul>
   *   </li></p>
   *   <p><li>
   *   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:
   *   <ul>
   *     <li>Thread Context Class Loader</li>
   *     <li>DiscoverSingleton's Caller's Class Loader</li>
   *     <li>SPI's Class Loader</li>
   *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
   *     <li>System Class Loader</li>
   *   </ul>
   *   An exception is thrown if the class cannot be loaded.
   *   </li></p>
   *   <p><li>
   *   If the name of the implementation class is null, AND the default
   *   implementation class name (<code>defaultImplName</code>) is null,
   *   then an exception is thrown.
   *   </li></p>
   *   <p><li>
   *   If the name of the implementation class is null, AND the default
   *   implementation class name (<code>defaultImplName</code>) is non-null,
   *   then load the default implementation class.  The class loaded is the
   *   first class loaded by the following sequence of class loaders:
   *   <ul>
   *     <li>SPI's Class Loader</li>
   *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
   *     <li>System Class Loader</li>
   *   </ul>
   *   <p>
   *   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.
   *   </p>
   *   <p>
   *   An exception is thrown if the class cannot be loaded.
   *   </p>
   *   </li></p>
   *   <p><li>
   *   Verify that the loaded class implements the SPI: an exception is thrown
   *   if the loaded class does not implement the SPI.
   *   </li></p>
   *   <p><li>
   *   Create an instance of the class.
   *   </li></p>
   *   <p><li>
   *   If the loaded class implements the <code>Service</code> interface,
   *   then invoke the <code>init(Properties)</code> method, passing in the
   *   <code>Properties properties</code> parameter, if provided.
   *   </li></p>
   * </ul>
   * 
   * <p>
   * Variances for various forms of the <code>find</code>
   * methods are discussed with each such method.
   * Variances include the following concepts:
   * <ul>
   *   <li><b>rootFinderClass</b> - 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.
   *   </li>
   *   <li><b>propertiesFileName</b> - <code>Properties</code> 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:
   *   <ul>
   *     <li>Thread Context Class Loader</li>
   *     <li>DiscoverSingleton's Caller's Class Loader</li>
   *     <li>SPI's Class Loader</li>
   *     <li>DiscoverSingleton's (this class) Class Loader</li>
   *     <li>System Class Loader</li>
   *   </ul>
   *   </li>
   *   <li><b>groupContext</b> - 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.
   *   <p>The groupContext is used to qualify the name of the property file
   *   name: <code>groupContext + '.' + propertiesFileName</code>.  If that
   *   file is not found, then the unqualified propertyFileName is used.
   *   </p>
   *   <p>In addition, groupContext is used to qualify the name of the system
   *   property used to find the service implementation by prepending the value
   *   of <code>groupContext</code> to the property name:
   *   <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
   *   Again, if a system property cannot be found by that name, then the
   *   unqualified property name is used.
   *   </p>
   *   </li>
   * </ul>
   * </p>
   * 
   * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
   * after the SAXParserFactory and DocumentBuilderFactory implementations
   * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
   * </p>
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   * @version $Revision$ $Date$
   */
  public class DiscoverSingleton {
      /**
       * Readable placeholder for a null value.
       */
      static final String     nullGroupContext = null;
      
      /**
       * Readable placeholder for a null value.
       */
      static final Properties nullProperties = null;
      
      /**
       * Readable placeholder for a null value.
       */
      static final String     nullDefaultImplName = 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.
       */
      
      /**
       * Find implementation 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 spi)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, spi);
      }
  
      /**
       * 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(Class spi, Properties properties)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, spi, properties);
      }
  
      /**
       * 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 spi, String defaultImplName)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, spi, 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,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, spi, 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 spi,
                                String propertiesFileName,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, spi, propertiesFileName, defaultImplName);
      }
  
      /**
       * Find implementation of SPI.
       * 
       * @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 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(String groupContext,
                                Class spi,
                                Properties properties,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, groupContext, spi, properties, defaultImplName);
      }
      
      /**
       * Find implementation of SPI unique to a group context.
       * 
       * @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 spi Service Provider Interface Class.
       * 
       * @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(String groupContext,
                                Class spi,
                                String propertiesFileName,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(DiscoverSingleton.class, groupContext, spi, 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 DiscoverSingleton 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.
       */
      
  
      /**
       * 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(rootFinderClass, spi, nullProperties, nullDefaultImplName);
      }
  
      /**
       * 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, spi, properties, nullDefaultImplName);
      }
  
      /**
       * 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(rootFinderClass, spi, nullProperties, 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,
                                Properties properties,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(rootFinderClass, nullGroupContext, spi, properties, 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 rootFinderClass,
                                Class spi,
                                String propertiesFileName,
                                String defaultImplName)
          throws DiscoveryException
      {
          return find(rootFinderClass, nullGroupContext,
                      spi, propertiesFileName, defaultImplName);
      }
  
      /**
       * Find implementation of SPI.
       * 
       * @param rootFinderClass Wrapper class used by end-user, that ultimately
       *        calls this finder method.
       * 
       * @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 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,
                                String groupContext,
                                Class spi,
                                Properties properties,
                                String defaultImplName)
          throws DiscoveryException
      {
          SPIContext spiContext = new SPIContext(groupContext, spi);
  
          /**
           * Return previously registered service object (not class)
           * for this spi, bound only to current thread context class loader.
           */
          Object service = get(spiContext);
  
          if (service == null) {
              DiscoverClass discoverClass = new DiscoverClass(rootFinderClass, groupContext);
              service = discoverClass.newInstance(spi, properties, defaultImplName);
  
              if (service != null) {
                  put(spiContext, service);
              }
          }
  
          return service;
      }
  
      /**
       * Find implementation of SPI.
       * 
       * @param rootFinderClass Wrapper class used by end-user, that ultimately
       *        calls this finder method.
       * 
       * @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 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,
                                String groupContext,
                                Class  spi,
                                String propertiesFileName,
                                String defaultImplName)
          throws DiscoveryException
      {
          SPIContext spiContext = new SPIContext(groupContext, spi);
  
          /**
           * Return previously registered service object (not class)
           * for this spi, bound only to current thread context class loader.
           */
          Object service = get(spiContext);
  
          if (service == null) {
              DiscoverClass discoverClass = new DiscoverClass(rootFinderClass, groupContext);
              service = discoverClass.newInstance(spi, propertiesFileName, defaultImplName);
  
              if (service != null) {
                  put(spiContext, service);
              }
          }
  
          return service;
      }
  
      
      /************************* SPI LIFE-CYCLE SUPPORT *************************/
      
      /**
       * Release all internal references to previously created service
       * instances associated with the current thread context class loader.
       * The <code>release()</code> method is called for service instances that
       * implement the <code>Service</code> 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 void release() {
          ClassLoader threadContextClassLoader =
              ClassLoaderUtils.getThreadContextClassLoader();
  
          /**
           * 'null' (bootstrap/system class loader) thread context class loader
           * is ok...  Until we learn otherwise.
           */
          synchronized (root_cache) {
              HashMap groups = (HashMap)root_cache.get(threadContextClassLoader);
  
              if (groups != null) {
                  Iterator groupIter = groups.values().iterator();
  
                  while (groupIter.hasNext()) {
                      HashMap spis = (HashMap)groupIter.next();
                      
                      if (spis != null) {
                          Iterator spiIter = spis.values().iterator();
          
                          while (spiIter.hasNext()) {
                              Object service = (Object)spiIter.next();
                              
                              if (service instanceof SingletonService)
                                  ((SingletonService)service).release();
                          }
                          spis.clear();
                      }
                  }
                  groups.clear();
              }
              root_cache.remove(threadContextClassLoader);
          }
      }
      
      
      /**
       * Release any internal references to a previously created service
       * instance associated with the current thread context class loader.
       * If the SPI instance implements <code>Service</code>, then call
       * <code>release()</code>.
       */
      public static void release(String groupContext) {
          ClassLoader threadContextClassLoader =
              ClassLoaderUtils.getThreadContextClassLoader();
  
          /**
           * 'null' (bootstrap/system class loader) thread context class loader
           * is ok...  Until we learn otherwise.
           */
          synchronized (root_cache) {
              HashMap groups = (HashMap)root_cache.get(threadContextClassLoader);
  
              if (groups != null) {
                  HashMap spis = (HashMap)groups.get(groupContext);
                  
                  if (spis != null) {
                      Iterator spiIter = spis.values().iterator();
      
                      while (spiIter.hasNext()) {
                          Object service = (Object)spiIter.next();
                          
                          if (service instanceof SingletonService)
                              ((SingletonService)service).release();
                      }
                      spis.clear();
                  }
                  groups.remove(groupContext);
              }
              root_cache.remove(threadContextClassLoader);
          }
      }
  
      /**
       * Release any internal references to a previously created service
       * instance associated with the current thread context class loader.
       * If the SPI instance implements <code>Service</code>, then call
       * <code>release()</code>.
       */
      public static void release(String groupContext, Class spi) {
          ClassLoader threadContextClassLoader =
              ClassLoaderUtils.getThreadContextClassLoader();
  
          /**
           * 'null' (bootstrap/system class loader) thread context class loader
           * is ok...  Until we learn otherwise.
           */
          if (spi != null) {
              synchronized (root_cache) {
                  HashMap groups = (HashMap)root_cache.get(threadContextClassLoader);
  
                  if (groups != null) {
                      HashMap spis = (HashMap)groups.get(groupContext);
  
                      if (spis != null) {
                          Object service = (Object)spis.get(spi.getName());
                          
                          if (service instanceof SingletonService)
                              ((SingletonService)service).release();
                          
                          spis.remove(spi.getName());
                      }
                  }
              }
          }
      }
  
      /**
       * Release any internal references to a previously created service
       * instance associated with the current thread context class loader.
       * If the SPI instance implements <code>Service</code>, then call
       * <code>release()</code>.
       */
      public static void release(Class spi) {
          release(nullGroupContext, spi);
      }
      
      
      /************************* 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 (<code>ClassLoader</code>).
       *         Value : groupContext::SPI Cache (<code>HashMap</code>).
       * 
       * - groupContext::SPI::Instance Cache
       *         Cache : HashMap
       *         Key   : groupContext (<code>String</code>).
       *         Value : SPI Cache (<code>HashMap</code>).
       * 
       * - SPI::Instance Cache
       *         Cache : HashMap
       *         Key   : SPI Class Name (<code>String</code>).
       *         Value : SPI Instance/Implementation (<code>Object</code>.
       */
  
      /**
       * Allows null key, important as default groupContext is null.
       */
      private static final HashMap root_cache = new HashMap();
  
      /**
       * Initial hash size for SPI's, default just seem TO big today..
       */
      private static final int smallHashSize = 13;
      
      /**
       * Get service keyed by spi & classLoader.
       */
      static Object get(SPIContext spiContext)
      {
          Object service = null;
  
          /**
           * 'null' (bootstrap/system class loader) thread context class loader
           * is ok...  Until we learn otherwise.
           */
          if (spiContext.getSPI() != null)
          {
              synchronized (root_cache) {
                  HashMap groups =
                      (HashMap)root_cache.get(spiContext.getThreadContextClassLoader());
  
                  if (groups != null) {
                      HashMap spis =
                          (HashMap)groups.get(spiContext.getGroupContext());
      
                      if (spis != null) {
                          
                          service = (Object)spis.get(spiContext.getSPI().getName());
                      }
                  }
              }
          }
  
          return service;
      }
      
      /**
       * Put service keyed by spi & classLoader.
       */
      static void put(SPIContext spiContext, Object service)
      {
          /**
           * 'null' (bootstrap/system class loader) thread context class loader
           * is ok...  Until we learn otherwise.
           */
          if (spiContext.getSPI() != null  &&
              service != null)
          {
              synchronized (root_cache) {
                  HashMap groups =
                      (HashMap)root_cache.get(spiContext.getThreadContextClassLoader());
                      
                  if (groups == null) {
                      groups = new HashMap(smallHashSize);
                      root_cache.put(spiContext.getThreadContextClassLoader(), groups);
                  }
  
                  HashMap spis =
                      (HashMap)groups.get(spiContext.getGroupContext());
  
                  if (spis == null) {
                      spis = new HashMap(smallHashSize);
                      groups.put(spiContext.getGroupContext(), spis);
                  }
  
                  spis.put(spiContext.getSPI().getName(), service);
              }
          }
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/SingletonService.java
  
  Index: SingletonService.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery;
  
  
  /**
   * <p>Optional Service interface to facilitate life-cycle management.<p>
   * 
   * <p>A service is not required to implement this interface, but if
   * it does then the service is notified when it is being initialized
   * and released by DiscoverSingleton.</p>
   * 
   * @author Richard A. Sitze
   * @version $Revision$ $Date$
   */
  public interface SingletonService extends Service {
      public void release();
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/DiscoverClass.java
  
  Index: DiscoverClass.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery;
  
  import java.util.Properties;
  
  import org.apache.commons.discovery.load.SPIContext;
  import org.apache.commons.discovery.strategy.LoadStrategy;
  import org.apache.commons.discovery.strategy.DefaultLoadStrategy;
  
  
  /**
   * <p>Discover class that implements a given service interface,
   * with discovery and configuration features similar to that employed
   * by standard Java APIs such as JAXP.
   * </p>
   * 
   * <p>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.
   * </p>
   * 
   * <p>DiscoverClass provides the <code>find</code> methods for locating a
   * class that implements a service interface (SPI).  Each form of
   * <code>find</code> varies slightly, but they all perform the same basic
   * function.
   * 
   * The <code>DiscoverClass.find</code> methods proceed as follows:
   * </p>
   * <ul>
   *   <p><li>
   *   Get the name of an implementation class.  The name is the first
   *   non-null value obtained from the following resources:
   *   <ul>
   *     <li>
   *     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 <code>ScopedProperties</code> 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 <code>ScopedProperties</code> for more details.
   *     <p>If the ScopedProperties are not set by users, then behaviour
   *     is equivalent to <code>System.getProperty()</code>.
   *     </p>
   *     </li>
   *     <p><li>
   *     The value of a <code>Properties properties</code> 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()).
   *     </li></p>
   *     <p><li>
   *     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 <code>SPI.class.getName()</code>.  This is implemented
   *     internally, so there is not a dependency on JDK 1.3+.
   *     </li></p>
   *   </ul>
   *   </li></p>
   *   <p><li>
   *   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:
   *   <ul>
   *     <li>Thread Context Class Loader</li>
   *     <li>DiscoverSingleton's Caller's Class Loader</li>
   *     <li>SPI's Class Loader</li>
   *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
   *     <li>System Class Loader</li>
   *   </ul>
   *   An exception is thrown if the class cannot be loaded.
   *   </li></p>
   *   <p><li>
   *   If the name of the implementation class is null, AND the default
   *   implementation class name (<code>defaultImplName</code>) is null,
   *   then an exception is thrown.
   *   </li></p>
   *   <p><li>
   *   If the name of the implementation class is null, AND the default
   *   implementation class name (<code>defaultImplName</code>) is non-null,
   *   then load the default implementation class.  The class loaded is the
   *   first class loaded by the following sequence of class loaders:
   *   <ul>
   *     <li>SPI's Class Loader</li>
   *     <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
   *     <li>System Class Loader</li>
   *   </ul>
   *   <p>
   *   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.
   *   </p>
   *   <p>
   *   An exception is thrown if the class cannot be loaded.
   *   </p>
   *   </li></p>
   *   <p><li>
   *   Verify that the loaded class implements the SPI: an exception is thrown
   *   if the loaded class does not implement the SPI.
   *   </li></p>
   * </ul>
   * </p>
   *
   * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
   * after the SAXParserFactory and DocumentBuilderFactory implementations
   * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
   * </p>
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   * @version $Revision$ $Date$
   */
  public class DiscoverClass {
      private final Class  rootFinderClass;
      private final String groupContext;
      
      DiscoverClass() {
          this(DiscoverClass.class, DiscoverSingleton.nullGroupContext);
      }
      
      /**
       * @param rootFinderClass Wrapper class used by end-user, that ultimately
       *        calls this finder method.  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.
       */
      DiscoverClass(Class rootFinderClass) {
          this(rootFinderClass, DiscoverSingleton.nullGroupContext);
      }
      
      /**
       * @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.
       *     <p>Differentiates services for different logical groups of service
       *        users, that might otherwise be forced to share a common service
       *        implementation, and/or a common configuration of that service.
       *        </p>
       *     <p>The groupContext is used to qualify the name of the property file
       *        name: <code>groupContext + '.' + propertiesFileName</code>.  If
       *        that file is not found, then the unqualified propertyFileName is
       *        used.
       *        </p>
       *     <p>In addition, groupContext is used to qualify the name of the
       *        system property used to find the service implementation by
       *        prepending the value of <code>groupContext</code> to the property
       *        name: <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
       *        Again, if a system property cannot be found by that name, then the
       *        unqualified property name is used.
       *        </p>
       */
      DiscoverClass(String groupContext) {
          this(DiscoverClass.class, groupContext);
      }
      
      /**
       * @param rootFinderClass Wrapper class used by end-user, that ultimately
       *        calls this finder method.  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.
       * 
       * @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.
       *     <p>Differentiates services for different logical groups of service
       *        users, that might otherwise be forced to share a common service
       *        implementation, and/or a common configuration of that service.
       *        </p>
       *     <p>The groupContext is used to qualify the name of the property file
       *        name: <code>groupContext + '.' + propertiesFileName</code>.  If
       *        that file is not found, then the unqualified propertyFileName is
       *        used.
       *        </p>
       *     <p>In addition, groupContext is used to qualify the name of the
       *        system property used to find the service implementation by
       *        prepending the value of <code>groupContext</code> to the property
       *        name: <code>groupContext&gt; + '.' + SPI.class.getName()</code>.
       *        Again, if a system property cannot be found by that name, then the
       *        unqualified property name is used.
       *        </p>
       */
      DiscoverClass(Class rootFinderClass, String groupContext) {
          this.rootFinderClass = rootFinderClass;
          this.groupContext = groupContext;
      }
      
      /**
       * Find class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @return 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 (or extend) the SPI.
       */
      public Class find(Class spi)
          throws DiscoveryException
      {
          return find(spi, DiscoverSingleton.nullProperties, DiscoverSingleton.nullDefaultImplName);
      }
  
      /**
       * Find class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param properties Used to determine name of SPI implementation.
       * 
       * @return 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 (or extend) the SPI.
       */
      public Class find(Class spi, Properties properties)
          throws DiscoveryException
      {
          return find(spi, properties, DiscoverSingleton.nullDefaultImplName);
      }
  
      /**
       * Find class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param defaultImplName Default implementation name.
       * 
       * @return 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 (or extend) the SPI.
       */
      public Class find(Class spi, String defaultImplName)
          throws DiscoveryException
      {
          return find(spi, DiscoverSingleton.nullProperties, defaultImplName);
      }
  
      /**
       * Find class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param properties Used to determine name of SPI implementation,.
       * 
       * @param defaultImplName Default implementation name.
       * 
       * @return 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 (or extend) the SPI.
       */
      public Class find(Class spi, Properties properties, String defaultImplName)
          throws DiscoveryException
      {
          SPIContext spiContext = new SPIContext(groupContext, spi);
          LoadStrategy strategy = new DefaultLoadStrategy(spiContext, rootFinderClass);
          return strategy.loadClass(properties, defaultImplName);
      }
      
      /**
       * Find class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param propertiesFileName The property file name.
       *        A property file is loaded using the following sequence of class
       *        loaders:
       *        <ul>
       *          <li>Thread Context Class Loader</li>
       *          <li>DiscoverSingleton's Caller's Class Loader</li>
       *          <li>SPI's Class Loader</li>
       *          <li>DiscoverSingleton's (this class) Class Loader</li>
       *          <li>System Class Loader</li>
       *        </ul>
       * 
       * @param defaultImplName Default implementation name.
       * 
       * @return 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 (or extend) the SPI.
       */    
      public Class find(Class spi, String propertiesFileName, String defaultImplName)
          throws DiscoveryException
      {
          SPIContext spiContext = new SPIContext(groupContext, spi);
          LoadStrategy strategy = new DefaultLoadStrategy(spiContext, rootFinderClass);
          return strategy.loadClass(strategy.loadProperties(propertiesFileName), defaultImplName);
      }
  
      
      /**
       * Create new instance of class implementing 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 Object newInstance(Class spi)
          throws DiscoveryException
      {
          return newInstance(spi, DiscoverSingleton.nullProperties, DiscoverSingleton.nullDefaultImplName);
      }
  
      /**
       * Create new instance of class implementing 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 Object newInstance(Class spi, Properties properties)
          throws DiscoveryException
      {
          return newInstance(spi, properties, DiscoverSingleton.nullDefaultImplName);
      }
  
      /**
       * Create new instance of class implementing 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 Object newInstance(Class spi, String defaultImplName)
          throws DiscoveryException
      {
          return newInstance(spi, DiscoverSingleton.nullProperties, defaultImplName);
      }
  
      /**
       * Create new instance of class implementing 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 Object newInstance(Class spi, Properties properties, String defaultImplName)
          throws DiscoveryException
      {
          return newInstance(spi, find(spi, properties, defaultImplName), properties);
      }
      
      /**
       * Create new instance of class implementing SPI.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param propertiesFileName The property file name.
       *        A property file is loaded using the following sequence of class
       *       loaders:
       *      <ul>
       *        <li>Thread Context Class Loader</li>
       *        <li>DiscoverSingleton's Caller's Class Loader</li>
       *        <li>SPI's Class Loader</li>
       *        <li>DiscoverSingleton's (this class) Class Loader</li>
       *        <li>System Class Loader</li>
       *      </ul>
       * 
       * @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 Object newInstance(Class spi, String propertiesFileName, String defaultImplName)
          throws DiscoveryException
      {
          SPIContext spiContext = new SPIContext(groupContext, spi);
          LoadStrategy strategy = new DefaultLoadStrategy(spiContext, rootFinderClass);
          Properties properties = strategy.loadProperties(propertiesFileName);
          return newInstance(spi,
                             strategy.loadClass(properties, defaultImplName),
                             properties);
      }
      
      /**
       * Instantiate SPI.
       *   If the loaded class implements the <code>Service</code> interface,
       *   then invoke the <code>init(Properties)</code> method, passing in the
       *   <code>Properties properties</code> parameter, if provided.
       * 
       * @param spi Service Provider Interface Class.
       * 
       * @param impl Class implementing SPI, class to instantiate.
       * 
       * @param properties if impl implements Service, call impl.init(properties).
       * 
       * @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 Object newInstance(Class spi, Class impl, Properties properties)
          throws DiscoveryException
      {
          Object service = null;
      
          try {
              service = impl.newInstance();
          } catch (Exception e) {
              throw new DiscoveryException("Unable to instantiate " + impl.getName() + " for " + spi.getName(), e);
          }
  
          if (service instanceof Service) {
              ((Service)service).init(groupContext, properties);
          }
  
          return service;
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/load/ClassLoaderUtils.java
  
  Index: ClassLoaderUtils.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.load;
  
  import java.io.InputStream;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  
  import org.apache.commons.discovery.DiscoveryException;
  
  
  /**
   * Mechanisms to locate and load a class.
   * The load methods locate a class only.
   * The find methods locate a class and verify that the
   * class implements an given interface or extends a given class.
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @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;
      
      /**
       * Load the class <code>serviceImplName</code>, no safety checking
       * 
       * @param serviceImplName The name of the class to load.
       */
      private static Class rawLoadClass(String serviceImplName, ClassLoader loader)
          throws DiscoveryException
      {
          Class clazz = null;
          
          try {
              // first the thread class loader
              clazz = loader.loadClass(serviceImplName);
          } catch (ClassNotFoundException e) {
              clazz = null;
          }
          
          return clazz;
      }
      
      /**
       * Load the class <code>serviceImplName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * 
       * @param serviceImplName The name of the class to load.
       */
      public static Class loadClass(String serviceImplName,
                                    ClassLoader[] loaders,
                                    int length)
          throws DiscoveryException
      {
          Class clazz = null;
          
          if (serviceImplName != null  &&  serviceImplName.length() > 0) {
              if (debug)
                  System.out.println("Loading class '" + serviceImplName + "'");
  
              for (int i = 0; i < length && clazz == null; i++)
              {
                  if (loaders[i] != null)
                      clazz = rawLoadClass(serviceImplName, loaders[i]);
              }
          }
          
          return clazz;
      }
  
      /**
       * Load the class <code>serviceImplName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * 
       * @param serviceImplName The name of the class to load.
       */
      public static Class loadClass(String serviceImplName, ClassLoader[] loaders)
          throws DiscoveryException
      {
          return loadClass(serviceImplName, loaders, loaders.length);
      }
      
      /**
       * Load the class <code>serviceImplName</code> using the
       * class loaders associated with the SPI's context.
       * 
       * @param serviceImplName The name of the class to load.
       */
      public static Class loadClass(String serviceImplName, SPIContext spiContext)
          throws DiscoveryException
      {
          return loadClass(serviceImplName, spiContext.getClassLoaders());
      }
  
  
      /**
       * Load the resource <code>resourceName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String resourceName,
                                                    ClassLoader[] loaders,
                                                    int length)
          throws DiscoveryException
      {
          InputStream stream = null;
          
          if (resourceName != null  &&  resourceName.length() > 0) {
              if (debug)
                  System.out.println("Loading resource '" + resourceName + "'");
  
              for (int i = 0; i < length && stream == null; i++)
              {
                  if (loaders[i] != null)
                      stream = loaders[i].getResourceAsStream(resourceName);
              }
          }
          
          return stream;
      }
  
      /**
       * Load the resource <code>resourceName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String resourceName, ClassLoader[] loaders)
          throws DiscoveryException
      {
          return getResourceAsStream(resourceName, loaders, loaders.length);
      }
      
      /**
       * Load the resource resourceName using the
       * class loaders associated with the SPI's context.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String resourceName, SPIContext spiContext)
          throws DiscoveryException
      {
          return getResourceAsStream(resourceName, spiContext.getClassLoaders());
      }
  
      /**
       * Load the resource <code>resourceName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * If all fail and <code>resouceName</code> is not absolute
       * (doesn't start with '/' character), then retry with
       * <code>packageName/resourceName</code> after changing all
       * '.' to '/'.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String packageName,
                                                    String resourceName,
                                                    ClassLoader[] loaders,
                                                    int length)
          throws DiscoveryException
      {
          InputStream stream = getResourceAsStream(resourceName, loaders, length);
          
          /**
           * If we didn't find the resource, and if the resourceName
           * isn't an 'absolute' path name, then qualify with
           * package name of the spi.
           */
          return (stream == null)
                 ? getResourceAsStream(qualifyName(packageName, resourceName),
                                       loaders, length)
                 : stream;
      }
  
      /**
       * Load the resource <code>resourceName</code>.
       * Try each classloader in succession,
       * until first succeeds, or all fail.
       * If all fail and <code>resouceName</code> is not absolute
       * (doesn't start with '/' character), then retry with
       * <code>packageName/resourceName</code> after changing all
       * '.' to '/'.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String packageName,
                                                    String resourceName,
                                                    ClassLoader[] loaders)
          throws DiscoveryException
      {
          return getResourceAsStream(packageName, resourceName, loaders, loaders.length);
      }
      
      /**
       * Load the resource resourceName using the
       * class loaders associated with the SPI's context.
       * If all fail and <code>resouceName</code> is not absolute
       * (doesn't start with '/' character), then retry with
       * <code>packageName/resourceName</code> after changing all
       * '.' to '/'.
       * 
       * @param resourceName The name of the resource to load.
       */
      public static InputStream getResourceAsStream(String packageName,
                                                    String resourceName,
                                                    SPIContext spiContext)
          throws DiscoveryException
      {
          return getResourceAsStream(packageName, resourceName, spiContext.getClassLoaders());
      }
  
      
      /**
       * Would <code>thisClassLoader</code> use <code>classLoader</code>?
       * Return <code>true</code> if <code>classLoader</code> is the same
       * as </code>thisClassLoader</code> or if <code>classLoader</code>
       * is an ancestor of </code>thisClassLoader</code>.
       */
      public static final boolean wouldUseClassLoader(ClassLoader thisClassLoader,
                                                      ClassLoader classLoader) {
          /* bootstrap classloader, at root of all trees! */
          if (BootstrapLoader.isBootstrapLoader(classLoader))
              return true;
          
          while (thisClassLoader != null) {
              if (thisClassLoader == classLoader) {
                  return true;
              }
              thisClassLoader = thisClassLoader.getParent();
          }
          
          return false;
      }
  
      /**
       * Return <code>true</code> if
       * <code>wouldUseClassLoader(list[idx], classLoader)<code>
       * is <code>true<code> for all <code>idx</code>
       * such that <code>0 <= idx < length</code>.
       */
      public static final boolean wouldUseClassLoader(ClassLoader[] list,
                                                      int length,
                                                      ClassLoader classLoader) {
          for (int idx = 0; idx < length; idx++) {
              if (wouldUseClassLoader(list[idx], classLoader))
                  return true;
          }
          return false;
      }
  
      /***
       * Remove duplicate Objects (as opposed to equivalent) from
       * array.  Also checks previous class loader parents..
       * 
       * Assumes that array is short, so (n^2)*m isn't a problem...
       * 
       * This is exposed to allow a unique array to be computed once,
       * and passed in for different tasks...
       */
      public static int uniq(ClassLoader[] array, ClassLoader[] uneek) {
          int len = 0;
          for (int lookForward = 0; lookForward < array.length; lookForward++) {
              ClassLoader fore = array[lookForward];
              
              if (fore != null  &&  !wouldUseClassLoader(uneek, len, fore)) {
                  uneek[len++] = fore;
              }
          }
          return len;
      }
  
      public static final ClassLoader[] copy(ClassLoader[] src, int first, int lastPlus) {
          int length = lastPlus - first;
          ClassLoader[] dest = new ClassLoader[length];
          System.arraycopy(src, first, dest, 0, length);
          return dest;
      }
  
      public static final ClassLoader[] compactUniq(ClassLoader[] array) {        
          ClassLoader[] uniqLoaders = new ClassLoader[array.length];
          int length = uniq(array, uniqLoaders);
          return copy(uniqLoaders, 0, length);
      }
      
      /**
       * If <code>name</code> represents an absolute path name, then return null.
       * Otherwise, prepend packageName to name, convert all '.' to '/', and
       * return results.
       */
      private static final String qualifyName(String packageName, String name) {
          return (name.charAt(0)=='/')
                 ? null
                 : packageName.replace('.','/') + "/" + name;
      }
  
      /**
       * 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 thread context class loader could not
       * be identified.
       * 
       * 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.
       */
      public static ClassLoader getThreadContextClassLoader()
          throws DiscoveryException
      {
          ClassLoader classLoader = null;
          
          try {
              // Are we running on a JDK 1.2 or later system?
              Method method = Thread.class.getMethod("getContextClassLoader", null);
      
              // Get the thread context class loader (if there is one)
              try {
                  classLoader =
                      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 (Thread.getContextClassLoader)
                   * 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.
                   */
                  if (e.getTargetException() instanceof SecurityException) {
                      classLoader = null;  // ignore
                  } else {
                      // Capture 'e.getTargetException()' exception for details
                      // alternate: log 'e.getTargetException()', and pass back 'e'.
                      throw new DiscoveryException
                          ("Unexpected InvocationTargetException",
                           e.getTargetException());
                  }
              }
          } catch (NoSuchMethodException e) {
              // Assume we are running on JDK 1.1
              classLoader = null;
          }
      
          // 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 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.
       */
      private static ClassLoader findSystemClassLoader()
          throws DiscoveryException
      {
          ClassLoader classLoader = null;
          
          try {
              // Are we running on a JDK 1.2 or later system?
              Method method = ClassLoader.class.getMethod("getSystemClassLoader", null);
      
              // Get the system class loader (if there is one)
              try {
                  classLoader =
                      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 (ClassLoader.getSystemClassLoader)
                   * throws an exception.
                   * 
                   * ClassLoader.getSystemClassLoader() throws SecurityException
                   * if security permissions are restricted.
                   */
                  if (e.getTargetException() instanceof SecurityException) {
                      classLoader = null;  // ignore
                  } else {
                      // Capture 'e.getTargetException()' exception for details
                      // alternate: log 'e.getTargetException()', and pass back 'e'.
                      throw new DiscoveryException
                          ("Unexpected InvocationTargetException",
                           e.getTargetException());
                  }
              }
          } catch (NoSuchMethodException e) {
              // Assume we are running on JDK 1.1
              classLoader = null;
          }
      
          // Return the selected class loader
          return classLoader;
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/load/Loaders.java
  
  Index: Loaders.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.load;
  
  import java.io.InputStream;
  
  import org.apache.commons.discovery.DiscoveryException;
  
  
  /**
   * Loads classes and resources.
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   */
  public class Loaders {
      /**
       * The SPI (and thread context) for which we are (presumably)
       * looking for an implementation of.
       */
      private final SPIContext spiContext;
      
      /**
       * System ClassLoaders only
       * 
       * This is NOT the same as spiContext.getClassLoaders(),
       * which includes the thread context class loader.
       */
      private final ClassLoader[] systemLoaders;
      
      private final ClassLoader[] allLoaders;
  
  
      /**
       * @param rootFinderClass a wrapper class encapsulating use of DiscoverSingleton.
       *   If DiscoverSingleton is used directly, then this would be DiscoverSingleton itself.
       *   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.
       */
      public Loaders(SPIContext spiContext, Class rootFinderClass)
      {
          this.spiContext = spiContext;
          this.systemLoaders = getSystemLoaders(spiContext, rootFinderClass);
          this.allLoaders = getAllLoaders(spiContext, rootFinderClass);
  
          //System.out.println("Finding '" + groupContext + "::" + spiContext.getSPI().getName() + "'");
      }
      
      /**
       * Load <code>className</code>.  If <code>systemOnly</code> is
       * <code>true</code>, use the  'system' class loaders: spi's,
       * root finder class's, and system.  If <code>systemOnly</code> is
       * <code>false</code>, use the following class loaders: thread context,
       * caller's, spi's, root finder class's, and system.
       *
       * @param serviceImplName Fully qualified name of the implementation class
       * 
       * @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 <code>spi</code>
       */
      public Class loadClass(String className, boolean systemOnly)
          throws DiscoveryException
      {
          Class clazz = ClassLoaderUtils.loadClass(className,
                             systemOnly ? systemLoaders : allLoaders);
              
          if (clazz != null  &&  !spiContext.getSPI().isAssignableFrom(clazz)) {
              throw new DiscoveryException("Class " + className +
                            " does not implement " + spiContext.getSPI().getName());
          }
          
          return clazz;
      }
      
      /**
       * Return the specified <code>resourceName</code> as an
       * <code>InputStream</code>.  If <code>systemOnly</code> is
       * <code>true</code>, try the 'system' class loaders: spi's,
       * root finder class's (default is DiscoverSingleton), and system.
       * If <code>systemOnly</code> is <code>false</code>, try each of
       * the following class loaders: thread context, caller's, spi's,
       * root finder class's (default is DiscoverSingleton), and system.
       *
       * @param resourceName name of the resource
       * 
       * @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 loadResourceAsStream(String resourceName)
          throws DiscoveryException
      {
          String packageName = spiContext.getSPI().getPackage().getName();
  
          InputStream stream =
              (spiContext.getGroupContext() == null)
                  ? null
                  : ClassLoaderUtils.getResourceAsStream(packageName,
                            spiContext.getGroupContext() + "." + resourceName,
                            allLoaders);
  
          if (stream == null)
              stream = ClassLoaderUtils.getResourceAsStream(packageName,
                                                            resourceName,
                                                            allLoaders);
  
          return stream;
      }
      
      /**
       * MAGIC: as in, I don't have a clue how to go about this...
       * SecurityManager.getClassContext() is out of reach at best,
       * and getting to it in a J2EE environment is likely to be beyond
       * hopeless....
       * 
       * If the caller class loader is the bootstrap classloader, then
       * it is 'wrapped' (see BootstrapLoader).  Therefore this method
       * only returns 'null' if a caller class loader could not
       * be identified.
       * 
       */
      private static final ClassLoader getCallerClassLoader(Class rootFinderClass) {
          return BootstrapLoader.wrap(null);
      }
  
      /**
       * List of 'system' class loaders to the SPI
       */
      private static final ClassLoader[] getSystemLoaders(SPIContext spiContext,
                                                          Class rootFinderClass) {
          return ClassLoaderUtils.compactUniq(
                  new ClassLoader[] {BootstrapLoader.wrap(spiContext.getSPI().getClassLoader()),
                                     BootstrapLoader.wrap(rootFinderClass.getClassLoader()),
                                     ClassLoaderUtils.getSystemClassLoader()
                                    });
      }
      
      private static final ClassLoader[] getAllLoaders(SPIContext spiContext,
                                                       Class rootFinderClass) {
          return ClassLoaderUtils.compactUniq(
                  new ClassLoader[] {spiContext.getThreadContextClassLoader(),
                                     getCallerClassLoader(rootFinderClass),
                                     BootstrapLoader.wrap(spiContext.getSPI().getClassLoader()),
                                     BootstrapLoader.wrap(rootFinderClass.getClassLoader()),
                                     ClassLoaderUtils.getSystemClassLoader()
                                    });
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/load/BootstrapLoader.java
  
  Index: BootstrapLoader.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.load;
  
  import java.io.InputStream;
  import java.net.URL;
  
  
  /**
   * A wrapper class that gives us a "bootstrap" loader.
   * For the moment, we cheat and return the system class loader.
   * Getting a wrapper for the bootstrap loader that works
   * in JDK 1.1.x may require a bit more work...
   * 
   * Expected use:  call BootstrapLoader.wrap(loader),
   * which will return loader (loader != null) or a wrapper class
   * in place of the bootstrap loader (loader == null).
   */
  public class BootstrapLoader {
      private static ClassLoader bootstrapLoader =
          null;
      
      private BootstrapLoader() {
      }
      
      public static ClassLoader wrap(ClassLoader incoming) {
          return (incoming == null) ? getBootstrapLoader() : incoming;
      }
      
      public static boolean isBootstrapLoader(ClassLoader incoming) {
          return incoming == null  ||  incoming == getBootstrapLoader();
      }
      
      public static ClassLoader getBootstrapLoader() {
          return bootstrapLoader;
      }
      
      /**
       * JDK 1.1.x compatible?
       * There is no direct way to get the system class loader
       * in 1.1.x, so work around...
       */
      private class SystemClassLoader extends ClassLoader {
          protected Class loadClass(String className, boolean resolve)
              throws ClassNotFoundException
          {
              return findSystemClass(className);
          }
          
          public URL getResource(String resName) {
              return getSystemResource(resName);
          }
          
          public InputStream getResourceAsStream(String resName) {
              return getSystemResourceAsStream(resName);
          }
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/load/SPIContext.java
  
  Index: SPIContext.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.load;
  
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  
  
  /**
   * Represents a Service Programming Interface (spi) context,
   * to include an spi and the Thread Context Class Loader for
   * the thread that created an instance of this object.
   * 
   * @author Richard A. Sitze
   */
  public class SPIContext {
      /**
       * Thread context class loader or null if not available (JDK 1.1).
       * Wrapped bootstrap classloader if classLoader == null.
       */
      private final ClassLoader threadContextClassLoader =
          ClassLoaderUtils.getThreadContextClassLoader();
  
      /**
       * List of class loaders
       */
      private final ClassLoader[] loaders;
  
      private final String groupContext;
      
      /**
       * The service programming interface: intended to be
       * an interface or abstract class, but not limited
       * to those two.
       */        
      private final Class spi;
      
  
      public SPIContext(String groupContext, Class spi) {
          this.groupContext = groupContext;
          this.spi = spi;
          this.loaders = ClassLoaderUtils.compactUniq(
              new ClassLoader[] { threadContextClassLoader,
                                  BootstrapLoader.wrap(spi.getClassLoader()),
                                  ClassLoaderUtils.getSystemClassLoader() });
      }
      
      public ClassLoader getThreadContextClassLoader() {
          return threadContextClassLoader;
      }
      
      public ClassLoader[] getClassLoaders() {
          return loaders;
      }
      
      public String getGroupContext() {
          return groupContext;
      }
      
      public Class getSPI() {
          return spi;
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/strategy/LoadStrategy.java
  
  Index: LoadStrategy.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.strategy;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Properties;
  
  import org.apache.commons.discovery.DiscoveryException;
  import org.apache.commons.discovery.load.Loaders;
  import org.apache.commons.discovery.load.SPIContext;
  
  
  /**
   * <p>Implement the search strategy.  Someday this might be pluggable..
   * </p>
   * 
   * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
   * after the SAXParserFactory and DocumentBuilderFactory implementations
   * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
   * </p>
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   * @version $Revision$ $Date$
   */
  public interface LoadStrategy {
      /**
       * Load SPI implementation's Class.
       * 
       * @param properties May use, but must pass to implementation.init() method
       *                   if implementation implements Service interface.
       * 
       * @param defaultImplName Default implementation name.
       * 
       * @return Class class implementing the SPI.
       * 
       * @exception DiscoveryException Thrown if the name of a class implementing
       *            the SPI cannot be found, or if the class cannot be loaded.
       */
      public Class loadClass(Properties properties, String defaultImplName)
          throws DiscoveryException;
      
      /**
       * Load property file.
       * 
       * @param propertiesFileName The property file name.
       * 
       * @return Properties loaded from <code>propertiesFileName</code> if found,
       *         otherwise <code>null</code>.
       * 
       * @exception DiscoveryException
       */    
      public Properties loadProperties(String propertiesFileName)
          throws DiscoveryException;
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/strategy/Utils.java
  
  Index: Utils.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.strategy;
  
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  
  import org.apache.commons.discovery.ManagedProperties;
  
  
  /**
   * Helper methods for locating resource names.
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   */
  public class Utils {
      /**
       * JDK1.3+ 'Service Provider' specification 
       * ( http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html )
       */
      private static final String SERVICE_HOME = "META-INF/services/";
  
      
      /**
       * Load the class whose name is given by the value of a (Managed)
       * System Property.
       * 
       * @see ManagedProperties
       * 
       * @param attribute the name of the system property whose value is
       *        the name of the class to load.
       */
      public static String getManagedProperty(String propertyName) {
          String value;
          try {
              value = ManagedProperties.getProperty(propertyName);
          } catch (SecurityException e) {
              value = null;
          }
          return value;
      }
  
      /**
       * Find the name of a service using the JDK 1.3 jar discovery mechanism.
       * This will allow users to plug a service implementation by just
       * placing it in the META-INF/services directory of the webapp
       * (or in CLASSPATH or equivalent).
       */
      public static String getJDK13ClassName(ClassLoader classLoader, String spiName) {
          String serviceImplName = null;
  
          // Name of J2EE application file that identifies the service implementation.
          String servicePropertyFile = SERVICE_HOME + spiName;
  
          InputStream is = (classLoader == null
                            ? ClassLoader.getSystemResourceAsStream(servicePropertyFile)
                            : classLoader.getResourceAsStream(servicePropertyFile));
  
          if( is != null ) {
              try {
                  try {
                      // This code is needed by EBCDIC and other strange systems.
                      // It's a fix for bugs reported in xerces
                      BufferedReader rd;
                      
                      try {
                          rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                      } catch (java.io.UnsupportedEncodingException e) {
                          rd = new BufferedReader(new InputStreamReader(is));
                      }
                          
                      try {
                          serviceImplName = rd.readLine();
                      } finally {
                          rd.close();
                      }
                  } finally {
                      is.close();
                  }
              } catch (IOException ioe) {
                  ; // ignore
              }
          }
          
          return serviceImplName;
      }
  }
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/strategy/DefaultLoadStrategy.java
  
  Index: DefaultLoadStrategy.java
  ===================================================================
  /*
   * $Header$
   * $Revision$
   * $Date$
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.discovery.strategy;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Properties;
  
  import org.apache.commons.discovery.DiscoveryException;
  import org.apache.commons.discovery.load.Loaders;
  import org.apache.commons.discovery.load.SPIContext;
  
  
  /**
   * <p>Implement the search strategy.  Someday this might be pluggable..
   * </p>
   * 
   * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
   * after the SAXParserFactory and DocumentBuilderFactory implementations
   * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
   * </p>
   * 
   * @author Richard A. Sitze
   * @author Craig R. McClanahan
   * @author Costin Manolache
   * @version $Revision$ $Date$
   */
  public class DefaultLoadStrategy implements LoadStrategy {
      private final SPIContext spiContext;
      private final Loaders loaders;
      
      public DefaultLoadStrategy(SPIContext spiContext, Class rootFinderClass) {
          this.spiContext = spiContext;
          this.loaders = new Loaders(spiContext, rootFinderClass);
      }
      
      /**
       * Load SPI implementation's Class.
       * 
       * The <code>loadInstance</code> methods proceed as follows:
       * </p>
       * <ul>
       *   <p><li>
       *   Get the name of an implementation class.  The name is the first
       *   non-null value obtained from the following resources:
       *   <ul>
       *     <li>ManagedProperty.getProperty(SPI.class.getName());</li>
       *     <li>properties.getProperty(SPI.class.getName());</li>
       *     <li>
       *     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 <code>SPI.class.getName()</code>.  This is implemented
       *     internally, so there is not a dependency on JDK 1.3+.
       *     </li>
       *   </ul>
       *   </li></p>
       *   <p><li>
       *   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:
       *   <ul>
       *     <li>Thread Context Class Loader</li>
       *     <li>DiscoverSingleton's Caller's Class Loader</li>
       *     <li>SPI's Class Loader</li>
       *     <li>DiscoverSingleton's (this class) Class Loader</li>
       *     <li>System Class Loader</li>
       *   </ul>
       *   An exception is thrown if the class cannot be loaded.
       *   </li></p>
       *   <p><li>
       *   If the name of the implementation class is null, AND the default
       *   implementation class name (<code>defaultImplName</code>) is null,
       *   then an exception is thrown.
       *   </li></p>
       *   <p><li>
       *   If the name of the implementation class is null, AND the default
       *   implementation class name (<code>defaultImplName</code>) is non-null,
       *   then load the default implementation class.  The class loaded is the
       *   first class loaded by the following sequence of class loaders:
       *   <ul>
       *     <li>SPI's Class Loader</li>
       *     <li>DiscoverSingleton's (this class) Class Loader</li>
       *     <li>System Class Loader</li>
       *   </ul>
       *   <p>
       *   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.
       *   </p>
       *   <p>
       *   An exception is thrown if the class cannot be loaded, or if the
       *   resulting class does not implement(or extend) the SPI.
       *   </li></p>
       * </ul>
       * 
       * @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 Class class implementing the SPI.
       * 
       * @exception DiscoveryException Thrown if the name of a class implementing
       *            the SPI cannot be found, or if the class cannot be loaded.
       */
      public Class loadClass(Properties properties, String defaultImplName)
          throws DiscoveryException
      {
          String spiName = spiContext.getSPI().getName();
          Object service = null;
  
          // First, try the (managed) system property
          String className = Utils.getManagedProperty(spiName);
  
          if (className == null) {
              // Second, try the properties parameter
              if (properties != null)
                  className = properties.getProperty(spiName);
          
              if (className == null) {
                  // Third, try to find a service by using the JDK1.3 jar
                  // discovery mechanism.
                  className = Utils.getJDK13ClassName(
                      spiContext.getThreadContextClassLoader(), spiName);
              }
          }
  
          Class clazz = null;
  
          if (className != null) {
              clazz = loaders.loadClass(className, false);
          } else {        
              // 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 was intended.
              clazz = loaders.loadClass(defaultImplName, true);
          }
  
          if (clazz == null) {
              throw new DiscoveryException("No implementation defined for " + spiName);
          }
  
          return clazz;
      }
      
      /**
       * Load property file (qualified by groupContext param to classFinder).
       * 
       * A property file is loaded using the following sequence of class loaders:
       *   <ul>
       *     <li>Thread Context Class Loader</li>
       *     <li>DiscoverSingleton's Caller's Class Loader</li>
       *     <li>SPI's Class Loader</li>
       *     <li>DiscoverSingleton's (this class) Class Loader</li>
       *     <li>System Class Loader</li>
       *   </ul>
       * 
       * @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.
       */    
      public Properties loadProperties(String propertiesFileName)
          throws DiscoveryException
      {
          Properties properties = null;
          
          if (propertiesFileName != null) {
              try {
                  InputStream stream =
                      loaders.loadResourceAsStream(propertiesFileName);
      
                  if (stream != null) {
                      properties = new Properties();
                      try {
                          properties.load(stream);
                      } finally {
                          stream.close();
                      }
                  }
              } catch (IOException e) {
                  ;  // ignore
              } catch (SecurityException e) {
                  ;  // ignore
              }
          }
          
          return properties;
      }
  }
  
  
  
  1.6       +15 -15    jakarta-commons/discovery/src/test/org/apache/commons/discovery/TestAll.java
  
  Index: TestAll.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/src/test/org/apache/commons/discovery/TestAll.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TestAll.java	5 Aug 2002 22:48:30 -0000	1.5
  +++ TestAll.java	9 Aug 2002 14:58:00 -0000	1.6
  @@ -96,7 +96,7 @@
           LogFactory factory = null;
           
           try {
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    LogFactoryImpl.class.getName());
   
               assertTrue(factory.getClass().getName() + "!=" + LogFactoryImpl.class.getName(),
  @@ -105,7 +105,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
           }
       }
       
  @@ -113,7 +113,7 @@
           LogFactory factory = null;
   
           try {
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    MyFactory.class.getName());
   
               assertTrue(factory.getClass().getName() + "!=" + MyFactory.class.getName(),
  @@ -122,7 +122,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
           }
       }
       
  @@ -130,7 +130,7 @@
           LogFactory factory = null;
           
           try {
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    LogFactoryImpl.class.getName());
   
               assertTrue("1. " + factory.getClass().getName() + "!=" + LogFactoryImpl.class.getName(),
  @@ -138,7 +138,7 @@
               
               // no release, should get cached value..
               
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    MyFactory.class.getName());
   
               // factory should be cached LogFactoryImpl
  @@ -148,7 +148,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
           }
       }
       
  @@ -156,15 +156,15 @@
           LogFactory factory = null;
           
           try {
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    LogFactoryImpl.class.getName());
   
               assertTrue("1. " + factory.getClass().getName() + "!=" + LogFactoryImpl.class.getName(),
                          factory.getClass().getName().equals(LogFactoryImpl.class.getName()));
               
  -            Discovery.release();
  +            DiscoverSingleton.release();
               
  -            factory = (LogFactory)Discovery.find(LogFactory.class,
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class,
                                                    MyFactory.class.getName());
   
               // Cache flushed, get new factory:
  @@ -174,7 +174,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
           }
       }
       
  @@ -190,7 +190,7 @@
               props.setProperty(Log.class.getName(),
                                 SimpleLog.class.getName());
               
  -            factory = (LogFactory)Discovery.find(LogFactory.class, props);
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class, props);
   
               assertTrue(factory.getClass().getName() + "!=" + MyFactory.class.getName(),
                          factory.getClass().getName().equals(MyFactory.class.getName()));
  @@ -198,7 +198,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
           }
       }
       
  @@ -212,7 +212,7 @@
               ManagedProperties.setProperty(Log.class.getName(),
                                             SimpleLog.class.getName());
               
  -            factory = (LogFactory)Discovery.find(LogFactory.class);
  +            factory = (LogFactory)DiscoverSingleton.find(LogFactory.class);
   
               assertTrue(factory.getClass().getName() + "!=" + MyFactory.class.getName(),
                          factory.getClass().getName().equals(MyFactory.class.getName()));
  @@ -220,7 +220,7 @@
               if (factory != null)
                   factory.releaseAll();
   
  -            Discovery.release();
  +            DiscoverSingleton.release();
               
               /**
                * Cleanup, don't want to affect next test..
  
  
  
  1.4       +118 -40   jakarta-commons/discovery/best-practices.html
  
  Index: best-practices.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/best-practices.html,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- best-practices.html	2 Aug 2002 15:07:41 -0000	1.3
  +++ best-practices.html	9 Aug 2002 14:58:00 -0000	1.4
  @@ -21,7 +21,8 @@
   <h3>1.  INTRODUCTION</h3>
   
   Best-practices are discussed.
  -See the javadoc for detail on the API:
  +See the javadoc, starting with <code>DiscoverySingleton</code> and <code>DiscoverClass</code>,
  +for detail on the API:
   where service implementations are looked for,
   the order in which those places are checked,
   which classloaders are used,
  @@ -31,31 +32,48 @@
   <a name="Calling Directly"></a>
   <h3>2.  CALLING DIRECTLY</h3>
   
  +<a name="Finding Singleton Instances (Factories)"></a>
  +<h4>Finding Singleton Instances (Factories)</h3>
   
  -<p>To call discovery directly from user-code, looking for a service.
  +<p>DiscoverSingleton finds, loads, and manages the lifecycle of a class implementing
  +a given interface.  It only supports classes with default (zero-argument) constructors.
  +DiscoverSingleton can pass a set of properties to the class
  +(see <a href="#Service Life Cycle Management">[Service Life Cycle Management]</a>).
  +Use of the term singleton should be applied loosely:
  +DiscoverSingleton will instantiate separate instances of a class if called with
  +different:
  +<ul>
  +  <li>thread context class loaders
  +      (for example, within different web applications in a J2EE managed environment)</li>
  +  <li>group contexts
  +      (maintain separation between different subsystems, if desired)</li>
  +</ul>
  +</p>
  +
  +<p>To call discovery directly from user-code:
   <ul>
   <pre>
  -    import org.apache.commons.discovery.Discovery;
  +    import org.apache.commons.discovery.DiscoverSingleton;
       import org.apache.commons.logging.LogFactory;
       ...
  -    LogFactory logFactory = (LogFactory)Discovery.find(LogFactory.class);
  +    LogFactory logFactory = (LogFactory)DiscoverSingleton.find(LogFactory.class);
   </pre>
   </ul>
  -Discovery looks for the value of the system property
  +DiscoverSingleton looks for the value of the system property
   <code>org.apache.commons.logging.LogFactory</code>
  -for the name of the class implementing that the <code>LogFactory</code> (abstract) class,
  -and failing that uses JDK1.3-style service discovery.
  +for the name of a class that implements the <code>LogFactory</code> (abstract) class.
  +Failing that, it uses JDK1.3-style service discovery.
   </p>
   <p>
  -Discovery also allows a <code>java.util.Properties</code>
  +DiscoverSingleton also allows a <code>java.util.Properties</code>
   parameter to be used for query for service implementation class name,
   as well as a default implementation class name:
   <ul>
   <pre>
       LogFactory factory =
  -        (LogFactory)Discovery.find(LogFactory.class,
  -                                   properties,
  -                                   LogFactory.FACTORY_DEFAULT);
  +        (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                           properties,
  +                                           LogFactory.FACTORY_DEFAULT);
   </pre>
   </ul>
   </p>
  @@ -64,9 +82,9 @@
   <ul>
   <pre>
       LogFactory factory =
  -        (LogFactory)Discovery.find(LogFactory.class,
  -                                   LogFactory.FACTORY_PROPERTIES,
  -                                   LogFactory.FACTORY_DEFAULT);
  +        (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                           LogFactory.FACTORY_PROPERTIES,
  +                                           LogFactory.FACTORY_DEFAULT);
   </pre>
   </ul>
   This last form is equivalent in function
  @@ -75,10 +93,68 @@
   </p>
   <p>
   There are a variety of <code>find</code> methods
  -provided by <code>Discovery</code>, review the javadoc for
  +provided by <code>DiscoverSingleton</code>, review the javadoc for
   other forms and options available.
   </p>
   
  +<a name="Finding Classes"></a>
  +<h4>Finding Classes</h3>
  +
  +<p>DiscoverClass finds and loads a class implementing a given interface.
  +DiscoverClass can pass a set of properties to the class if it implements
  +the <code>Service</code> interface (which doesn't support full-lifecycle
  +management as does the <code>SingletonService</code> interface).
  +</p>
  +
  +<p>
  +DiscoverClass provides API's that instantiate a class, though it currently
  +supports only classes with default (zero-argument) constructors.
  +Unlike <code>DiscoverySingleton</code>, class instances are not cached,
  +so each call will result in a new object instance.
  +</p>
  +
  +<p>
  +DiscoverClass is more oriented toward calling multiple times within similar contexts,
  +so it's use is slightly different than DiscoverSingleton: where as DiscoverSingleton provides
  +a set of static methods (no state), DiscoverClass must be instantiated before it is
  +used and maintains internal state.
  +</p>
  +
  +<p>To find a class directly from user-code:
  +[NEED BETTER EXAMPLE]
  +<ul>
  +<pre>
  +    import org.apache.commons.discovery.DiscoverClass;
  +    import org.apache.commons.logging.LogFactory;
  +    ...
  +    DiscoverClass discoverClass = new DiscoverClass();
  +    Class logFactoryClass = (LogFactory)discoverClass.find(LogFactory.class);
  +</pre>
  +</ul>
  +In this case, DiscoverClass looks for the value of the system property
  +<code>org.apache.commons.logging.LogFactory</code>
  +for the name of a class that implements the <code>LogFactory</code> (abstract) class.
  +Failing that, it uses JDK1.3-style service discovery.
  +</p>
  +
  +<p>To instantiate a class directly from user-code:
  +[NEED BETTER EXAMPLE]
  +<ul>
  +<pre>
  +    import org.apache.commons.discovery.DiscoverClass;
  +    import org.apache.commons.logging.LogFactory;
  +    ...
  +    DiscoverClass discoverClass = new DiscoverClass();
  +    LogFactory logFactoryClass = (LogFactory)discoverClass.newInstance(LogFactory.class);
  +</pre>
  +</ul>
  +</p>
  +<p>
  +As with DiscoverSingleton, DiscoverClass provides methods that
  +use <code>java.util.Properties</code> and a default implementation class name
  +to help determine the name of the class.
  +</p>
  +
   <a name="Integrating into Factories : Wrapping"></a>
   <h3>3.  INTEGRATING INTO FACTORIES : WRAPPING</h3>
   
  @@ -92,7 +168,7 @@
   <ul>
   <pre>
   import java.util.Properties;
  -import org.apache.commons.discovery.Discovery;
  +import org.apache.commons.discovery.DiscoverSingleton;
   import org.apache.commons.discovery.DiscoveryException;
   
   public abstract class LogFactory
  @@ -110,19 +186,19 @@
   
       public static LogFactory getFactory() throws ServiceException
       {
  -        return (LogFactory)Discovery.find(LogFactory.class,
  -                                          LogFactory.class,
  -                                          FACTORY_PROPERTIES,
  -                                          FACTORY_DEFAULT);
  +        return (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                                  LogFactory.class,
  +                                                  FACTORY_PROPERTIES,
  +                                                  FACTORY_DEFAULT);
       }
   
       public static LogFactory getFactory(Properties properties)
           throws ServiceException
       {
  -        return (LogFactory)Discovery.find(LogFactory.class,
  -                                          LogFactory.class,
  -                                          properties,
  -                                          FACTORY_DEFAULT);
  +        return (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                                  LogFactory.class,
  +                                                  properties,
  +                                                  FACTORY_DEFAULT);
       }
       ...
   }
  @@ -144,12 +220,12 @@
   <a name="Service Life Cycle Management"></a>
   <h3>4.  SERVICE LIFE CYCLE MANAGEMENT</h3>
   
  -<p>Discovery supports life-cycle management through the <code>Service</code> interface.
  -If service implementation implements
  -the <code>Service</code> interface then an
  +<p>Discovery supports life-cycle management through the <code>SingletonService</code> interface.
  +If a service implementation implements
  +the <code>SingletonService</code> interface then an
   <code>init(String, Properties)</code>
  -method is invoked when the class in created by Discovery.
  -Likewise, a <code>release()</code> method is invoked when Discovery
  +method is invoked when the class in created by DiscoverSingleton,
  +and a <code>release()</code> method is invoked when DiscoverSingleton
   removes the service from it's internal cache.
   </p>
   
  @@ -158,9 +234,9 @@
   
   <ul>
   <pre>
  -<b>import org.apache.commons.discovery.Service;</b>
  +<b>import org.apache.commons.discovery.SingletonService;</b>
   
  -public abstract class LogFactory <b>implements Service</b>
  +public abstract class LogFactory <b>implements SingletonService</b>
   {
       . . .
       <b>public abstract void init(String groupContext, Properties properties);</b>
  @@ -175,7 +251,7 @@
        * garbage collection.
        */
       <b>public static void releaseAll() {</b>
  -        <b>Discovery.releaseAll(LogFactory.class);</b>
  +        <b>DiscoverSingleton.releaseAll(LogFactory.class);</b>
       <b>}</b>
   }
   </pre>
  @@ -248,20 +324,22 @@
       public static LogFactory getFactory(<b>String groupContext</b>)
           throws ServiceException
       {
  -        return (LogFactory)Discovery.find(LogFactory.class,
  -                                          <b>groupContext</b>, LogFactory.class,
  -                                          FACTORY_PROPERTIES,
  -                                          FACTORY_DEFAULT);
  +        return (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                                  <b>groupContext</b>,
  +                                                  LogFactory.class,
  +                                                  FACTORY_PROPERTIES,
  +                                                  FACTORY_DEFAULT);
       }
   
       public static LogFactory getFactory(<b>String groupContext,</b>
                                           Properties properties)
           throws ServiceException
       {
  -        return (LogFactory)Discovery.find(LogFactory.class,
  -                                          <b>groupContext</b>, LogFactory.class,
  -                                          properties,
  -                                          FACTORY_DEFAULT);
  +        return (LogFactory)DiscoverSingleton.find(LogFactory.class,
  +                                                  <b>groupContext</b>,
  +                                                  LogFactory.class,
  +                                                  properties,
  +                                                  FACTORY_DEFAULT);
       }
   </pre>
   </ul>
  
  
  

--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message