You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rs...@apache.org on 2002/08/06 00:06:03 UTC

cvs commit: jakarta-commons/discovery/src/java/org/apache/commons/discovery ManagedProperties.java ClassFinder.java Discovery.java

rsitze      2002/08/05 15:06:03

  Modified:    discovery/src/java/org/apache/commons/discovery
                        ClassFinder.java Discovery.java
  Added:       discovery/src/java/org/apache/commons/discovery
                        ManagedProperties.java
  Log:
  Added managed properties, correct BugZilla 11479.
  Not clear WHY the test-cases didn't catch THAT bug,
  I'll be looking at this a bit more..  
  
  Revision  Changes    Path
  1.7       +13 -7     jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassFinder.java
  
  Index: ClassFinder.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/ClassFinder.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- ClassFinder.java	27 Jul 2002 18:49:59 -0000	1.6
  +++ ClassFinder.java	5 Aug 2002 22:06:03 -0000	1.7
  @@ -194,15 +194,18 @@
       }
       
       /**
  -     * Load the class whose name is given by the value of a System Property.
  +     * 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 Class systemFindClass(String attribute) {
  +    public Class managedPropertyFindClass(String attribute) {
           String value;
           try {
  -            value = System.getProperty(attribute);
  +            value = ManagedProperties.getProperty(attribute);
           } catch (SecurityException e) {
               value = null;
           }
  @@ -210,11 +213,14 @@
       }
   
       /**
  -     * Load the class whose name is given by the value of a System Property,
  -     * whose name is the fully qualified name of the SPI class.
  +     * Load the class whose name is given by the value of a (Managed)
  +     * System Property, whose name is the fully qualified name of the
  +     * SPI class.
  +     * 
  +     * @see ManagedProperties
        */
  -    public Class systemFindClass() {
  -        return systemFindClass(spiContext.getSPI().getName());
  +    public Class managedPropertyFindClass() {
  +        return managedPropertyFindClass(spiContext.getSPI().getName());
       }
   
       /**
  
  
  
  1.9       +17 -12    jakarta-commons/discovery/src/java/org/apache/commons/discovery/Discovery.java
  
  Index: Discovery.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/discovery/src/java/org/apache/commons/discovery/Discovery.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- Discovery.java	1 Aug 2002 22:47:24 -0000	1.8
  +++ Discovery.java	5 Aug 2002 22:06:03 -0000	1.9
  @@ -113,10 +113,17 @@
    *   Get the name of an implementation class.  The name is the first
    *   non-null value obtained from the following resources:
    *   <ul>
  - *     <p><li>
  - *     The value of the system property whose name is the same as the SPI's
  - *     fully qualified class name (as given by SPI.class.getName()).
  - *     </li></p>
  + *     <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
  @@ -671,18 +678,16 @@
       {
           /**
            * Return previously registered service object (not class)
  -         * for this spi.  Try each class loader in succession.
  +         * for this spi, bound only to current thread context class loader.
            */
           Object service = null;
           ClassLoader[] allLoaders = classFinder.getAllLoaders();
   
  -        for (int idx = 0; service == null  &&  idx < allLoaders.length; idx++) {
  -            service = get(classFinder.getSPIContext());
  -        }
  -
  -        if (service != null) {        
  -            // First, try the system property
  -            Class clazz = classFinder.systemFindClass();
  +        service = get(classFinder.getSPIContext());
  +
  +        if (service == null) {        
  +            // First, try the (managed) system property
  +            Class clazz = classFinder.managedPropertyFindClass();
       
               if (clazz == null) {
                   // Second, try the properties parameter
  
  
  
  1.1                  jakarta-commons/discovery/src/java/org/apache/commons/discovery/ManagedProperties.java
  
  Index: ManagedProperties.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 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 acknowledgment:
   *       "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Axis" 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 name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * 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.Map;
  import java.util.Set;
  import java.util.Properties;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.Hashtable;
  
  
  /**
   * <p>Extend the concept of System properties to a hierarchical scheme
   * based around class loaders.  System properties are global in nature,
   * so using them easily violates sound architectural and design principles
   * for maintaining separation between components and runtime environments.
   * Nevertheless, there is a need for properties broader in scope than
   * class or class instance scope.
   * </p>
   * 
   * <p>This class is one solution.
   * </p>
   * 
   * <p>Manage properties according to a secure
   * scheme similar to that used by classloaders:
   * <ul>
   *   <li><code>ClassLoader</code>s are organized in a tree hierarchy.</li>
   *   <li>each <code>ClassLoader</code> has a reference
   *       to a parent <code>ClassLoader</code>.</li>
   *   <li>the root of the tree is the bootstrap <code>ClassLoader</code>er.</li>
   *   <li>the youngest decendent is the thread context class loader.</li>
   *   <li>properties are bound to a <code>ClassLoader</code> instance
   *   <ul>
   *     <li><i>non-default</i> properties bound to a parent <code>ClassLoader</code>
   *         instance take precedence over all properties of the same name bound
   *         to any decendent.
   *         Just to confuse the issue, this is the default case.</li>
   *     <li><i>default</i> properties bound to a parent <code>ClassLoader</code>
   *         instance may be overriden by (default or non-default) properties of
   *         the same name bound to any decendent.
   *         </li>
   *   </ul>
   *   </li>
   *   <li>System properties take precedence over all other properties</li>
   * </ul>
   * </p>
   * 
   * <p>This is not a perfect solution, as it is possible that
   * different <code>ClassLoader</code>s load different instances of
   * <code>ScopedProperties</code>.  The 'higher' this class is loaded
   * within the <code>ClassLoader</code> hierarchy, the more usefull
   * it will be.
   * </p>
   * 
   * @author Richard A. Sitze
   */
  public class ManagedProperties {
      /**
       * Cache of Properties, keyed by (thread-context) class loaders.
       * Use <code>HashMap</code> because it allows 'null' keys, which
       * allows us to account for the (null) bootstrap classloader.
       */
      private static final HashMap propertiesCache = new HashMap();
      
                                                          
      /**
       * Get value for property bound to the current thread context class loader.
       * 
       * @param property property name.
       * @return property value if found, otherwise default.
       */
      public static String getProperty(String propertyName) {
          return getProperty(getThreadContextClassLoader(), propertyName);
      }
      
      /**
       * Get value for property bound to the current thread context class loader.
       * If not found, then return default.
       * 
       * @param property property name.
       * @param dephault default value.
       * @return property value if found, otherwise default.
       */
      public static String getProperty(String propertyName, String dephault) {
          return getProperty(getThreadContextClassLoader(), propertyName, dephault);
      }
      
      /**
       * Get value for property bound to the class loader.
       * 
       * @param classLoader
       * @param property property name.
       * @return property value if found, otherwise default.
       */
      public static String getProperty(ClassLoader classLoader, String propertyName) {
          String value = System.getProperty(propertyName);
          if (value == null) {
              Value val = getValueProperty(classLoader, propertyName);
              if (val != null) {
                  value = val.value;
              }
          }
          return value;
      }
      
      /**
       * Get value for property bound to the class loader.
       * If not found, then return default.
       * 
       * @param classLoader
       * @param property property name.
       * @param dephault default value.
       * @return property value if found, otherwise default.
       */
      public static String getProperty(ClassLoader classLoader, String propertyName, String dephault) {
          String value = getProperty(classLoader, propertyName);
          return (value == null) ? dephault : value;
      }
  
      /**
       * Set value for property bound to the current thread context class loader.
       * @param property property name
       * @param value property value (non-default)  If null, remove the property.
       */
      public static void setProperty(String propertyName, String value) {
          setProperty(propertyName, value, false);
      }
      
      /**
       * Set value for property bound to the current thread context class loader.
       * @param property property name
       * @param value property value.  If null, remove the property.
       * @param isDefault determines if property is default or not.
       *        A non-default property cannot be overriden.
       *        A default property can be overriden by a property
       *        (default or non-default) of the same name bound to
       *        a decendent class loader.
       */
      public static void setProperty(String propertyName, String value, boolean isDefault) {
          if (propertyName != null) {
              synchronized (propertiesCache) {
                  ClassLoader classLoader = getThreadContextClassLoader();
                  HashMap properties = (HashMap)propertiesCache.get(classLoader);
                  
                  if (value == null) {
                      properties.remove(propertyName);
                  } else {
                      if (properties == null) {
                          properties = new HashMap();
                          propertiesCache.put(classLoader, properties);
                      }
                  
                      properties.put(propertyName, new Value(value, isDefault));
                  }
              }
          }
      }
      
      /**
       * Set property values for <code>Properties</code> bound to the
       * current thread context class loader.
       * 
       * @param newProperties name/value pairs to be bound
       */
      public static void setProperties(Map newProperties) {
          setProperties(newProperties, false);
      }
      
      
      /**
       * Set property values for <code>Properties</code> bound to the
       * current thread context class loader.
       * 
       * @param newProperties name/value pairs to be bound
       * @param isDefault determines if properties are default or not.
       *        A non-default property cannot be overriden.
       *        A default property can be overriden by a property
       *        (default or non-default) of the same name bound to
       *        a decendent class loader.
       */
      public static void setProperties(Map newProperties, boolean isDefault) {
          java.util.Iterator it = newProperties.entrySet().iterator();
  
          /**
           * Each entry must be mapped to a Property.
           * 'setProperty' does this for us.
           */
          while (it.hasNext()) {
              Map.Entry entry = (Map.Entry)it.next();
              setProperty( String.valueOf(entry.getKey()),
                           String.valueOf(entry.getValue()),
                           isDefault);
          }
      }
  
      
      /**
       * Return list of all property names.  This is an expensive
       * operation: ON EACH CALL it walks through all property lists 
       * associated with the current context class loader upto
       * and including the bootstrap class loader.
       */
      public static Enumeration propertyNames() {
          Hashtable allProps = new Hashtable();
  
          ClassLoader classLoader = getThreadContextClassLoader();
  
          /**
           * Order doesn't matter, we are only going to use
           * the set of all keys...
           */
          while (true) {
              HashMap properties = null;
  
              synchronized (propertiesCache) {
                  properties = (HashMap)propertiesCache.get(classLoader);
              }
  
              if (properties != null) {
                  allProps.putAll(properties);
              }
  
              if (classLoader == null) break;
              classLoader = classLoader.getParent();
          }
          
          return allProps.keys();
      }
      
      /**
       * This is an expensive operation.
       * ON EACH CALL it walks through all property lists 
       * associated with the current context class loader upto
       * and including the bootstrap class loader.
       * 
       * @return Returns a <code>java.util.Properties</code> instance
       * that is equivalent to the current state of the scoped
       * properties, in that getProperty() will return the same value.
       * However, this is a copy, so setProperty on the
       * returned value will not effect the scoped properties.
       */
      public static Properties getProperties() {
          Properties p = new Properties();
          
          Enumeration names = propertyNames();
          while (names.hasMoreElements()) {
              String name = (String)names.nextElement();
              p.put(name, getProperty(name));
          }
          
          return p;
      }
  
  
      /***************** INTERNAL IMPLEMENTATION *****************/
  
      private static class Value {
          final String value;
          final boolean isDefault;
          
          Value(String value, boolean isDefault) {
              this.value = value;
              this.isDefault = isDefault;
          }
      }
  
      /**
       * Get value for properties bound to the class loader.
       * Explore up the tree first, as higher-level class
       * loaders take precedence over lower-level class loaders.
       */
      private static final Value getValueProperty(ClassLoader classLoader, String propertyName) {
          Value value = null;
  
          if (propertyName != null) {
              /**
               * If classLoader isn't bootstrap loader (==null),
               * then get up-tree value.
               */
              if (classLoader != null) {
                  value = getValueProperty(classLoader.getParent(), propertyName);
              }
              
              if (value == null  ||  value.isDefault) {
                  synchronized (propertiesCache) {
                      HashMap properties = (HashMap)propertiesCache.get(classLoader);
                          
                      if (properties != null) {
                          Value altValue = (Value)properties.get(propertyName);
                          
                          // set value only if override exists..
                          // otherwise pass default (or null) on..
                          if (altValue != null)
                              value = altValue;
                      }
                  }
              }
          }
          
          return value;
      }
      
      private static final ClassLoader getThreadContextClassLoader() {
          return Thread.currentThread().getContextClassLoader();
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>