Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@apache.org Received: (qmail 93058 invoked from network); 5 Aug 2002 22:06:11 -0000 Received: from unknown (HELO nagoya.betaversion.org) (192.18.49.131) by daedalus.apache.org with SMTP; 5 Aug 2002 22:06:11 -0000 Received: (qmail 23189 invoked by uid 97); 5 Aug 2002 22:06:34 -0000 Delivered-To: qmlist-jakarta-archive-commons-dev@jakarta.apache.org Received: (qmail 23165 invoked by uid 97); 5 Aug 2002 22:06:34 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 23142 invoked by uid 97); 5 Aug 2002 22:06:33 -0000 X-Antivirus: nagoya (v4198 created Apr 24 2002) Date: 5 Aug 2002 22:06:03 -0000 Message-ID: <20020805220603.29681.qmail@icarus.apache.org> From: rsitze@apache.org To: jakarta-commons-cvs@apache.org Subject: cvs commit: jakarta-commons/discovery/src/java/org/apache/commons/discovery ManagedProperties.java ClassFinder.java Discovery.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N rsitze 2002/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: *
    - *

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

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

    + *
  • *

  • * The value of a Properties properties property, if provided * as a parameter, whose name is the same as the SPI's fully qualifed class @@ -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 * . */ 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; /** *

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

    * *

    This class is one solution. *

    * *

    Manage properties according to a secure * scheme similar to that used by classloaders: *

      *
    • ClassLoaders are organized in a tree hierarchy.
    • *
    • each ClassLoader has a reference * to a parent ClassLoader.
    • *
    • the root of the tree is the bootstrap ClassLoaderer.
    • *
    • the youngest decendent is the thread context class loader.
    • *
    • properties are bound to a ClassLoader instance *
        *
      • non-default properties bound to a parent ClassLoader * instance take precedence over all properties of the same name bound * to any decendent. * Just to confuse the issue, this is the default case.
      • *
      • default properties bound to a parent ClassLoader * instance may be overriden by (default or non-default) properties of * the same name bound to any decendent. *
      • *
      *
    • *
    • System properties take precedence over all other properties
    • *
    *

    * *

    This is not a perfect solution, as it is possible that * different ClassLoaders load different instances of * ScopedProperties. The 'higher' this class is loaded * within the ClassLoader hierarchy, the more usefull * it will be. *

    * * @author Richard A. Sitze */ public class ManagedProperties { /** * Cache of Properties, keyed by (thread-context) class loaders. * Use HashMap 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 Properties 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 Properties 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 java.util.Properties 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: For additional commands, e-mail: