diff --git a/projects/samskivert/src/java/com/samskivert/util/Config.java b/projects/samskivert/src/java/com/samskivert/util/Config.java
index 6fb7fe79..f8d4168b 100644
--- a/projects/samskivert/src/java/com/samskivert/util/Config.java
+++ b/projects/samskivert/src/java/com/samskivert/util/Config.java
@@ -1,5 +1,5 @@
//
-// $Id: Config.java,v 1.3 2001/07/21 00:50:28 shaper Exp $
+// $Id: Config.java,v 1.4 2001/08/08 23:46:00 mdb Exp $
package com.samskivert.util;
@@ -43,11 +43,14 @@ public class Config
* somewhere in the classpath. For example: foo/bar/baz
* would indicate a file named "foo/bar/baz.properties" living in the
* classpath.
+ * @param inherit if true, the properties file will be loaded using
+ * {@link ConfigUtil#loadInheritedProperties} rather than {@link
+ * ConfigUtil#loadProperties}.
*
* @exception IOException thrown if an error occurrs loading the
* properties file (like it doesn't exist or cannot be accessed).
*/
- public void bindProperties (String name, String path)
+ public void bindProperties (String name, String path, boolean inherit)
throws IOException
{
// append the file suffix onto the path
@@ -57,10 +60,22 @@ public class Config
if (props == null) {
throw new IOException("Unable to load properties file: " + path);
}
- // put it into the hashtable with the specified name
+ // bind the properties instance
_props.put(name, props);
}
+ /**
+ * A backwards compatibility method that does not use inherited
+ * properties loading.
+ *
+ * @see #bindProperties(String,String,boolean)
+ */
+ public void bindProperties (String name, String path)
+ throws IOException
+ {
+ bindProperties(name, path, false);
+ }
+
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
diff --git a/projects/samskivert/src/java/com/samskivert/util/ConfigUtil.java b/projects/samskivert/src/java/com/samskivert/util/ConfigUtil.java
index 78b545d5..5e242ec2 100644
--- a/projects/samskivert/src/java/com/samskivert/util/ConfigUtil.java
+++ b/projects/samskivert/src/java/com/samskivert/util/ConfigUtil.java
@@ -1,12 +1,17 @@
//
-// $Id: ConfigUtil.java,v 1.2 2001/02/15 01:15:02 mdb Exp $
+// $Id: ConfigUtil.java,v 1.3 2001/08/08 23:46:00 mdb Exp $
package com.samskivert.util;
import java.io.InputStream;
import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.Properties;
+import com.samskivert.Log;
+
/**
* The config util class provides routines for loading configuration
* information out of a file that lives somewhere in the classpath.
@@ -26,7 +31,7 @@ public class ConfigUtil
*
* @param path The path to the properties file, relative to the root
* of the classpath entry from which it will be loaded
- * (e.g. /conf/foo.properties or perhaps just bar.properties).
+ * (e.g. /conf/foo.properties).
*
* @return A properties object loaded with the contents of the
* specified file if the file could be found, null otherwise.
@@ -38,25 +43,16 @@ public class ConfigUtil
}
/**
- * Loads a properties file from the named file that exists somewhere
- * in the classpath. A full path should be supplied, but variations
- * including and not including a leading slash will be used because
- * JVMs differ on their opinion of whether this is necessary.
+ * Like the other version of {@link #loadProperties(String)}
+ * but this one uses the supplied class loader rather than the class
+ * loader used to load the ConfigUtil class.
*
- *
The supplied classloader is searched first, followed by the - * system classloader. - * - * @param path The path to the properties file, relative to the root - * of the classpath entry from which it will be loaded - * (e.g. /conf/foo.properties or perhaps just bar.properties). - * - * @return A properties object loaded with the contents of the - * specified file if the file could be found, null otherwise. + * @see #loadProperties(String) */ - public static Properties loadProperties (String name, ClassLoader loader) + public static Properties loadProperties (String path, ClassLoader loader) throws IOException { - InputStream in = getStream(name, loader); + InputStream in = getStream(path, loader); Properties props = null; if (in != null) { @@ -67,6 +63,90 @@ public class ConfigUtil return props; } + /** + * Creates a properties instance by combining properties files loaded + * using the specified classpath-relative property file path. A + * leading slash should be supplied, but variations including and not + * including a leading slash will be used because JVMs differ on their + * opinion of whether this is necessary. + * + *
The inheritance works like so: the file will be searched for in + * the classpath from farthest to nearest. Near and far in the + * classpath are defined by the class loading search order. Normal + * class loading searches from nearest to farthest. Beginning with the + * farthest copy of the properties file, sucessively nearer copies + * will be overlaid onto those properties to achieve a sort of + * inheritance. Properties specified in nearer versions of the file + * will override those in farther versions of the file, but properties + * not specified in nearer versions will be "inherited" from the + * farther versions. Using this mechanism, a standard set of defaults + * can be provided and users need only place a properties file with + * their preferred overrides somewhere nearer in the classpath to have + * those overrides properly combined with the original + * defaults. Because the entire classpath is searched, this process + * can cascade up through a set of properties files and provide a + * powerful mechanism for inheriting configuration information. + * + *
The classloader that loaded the ConfigUtil class
+ * is searched first, followed by the system classpath. If you wish to
+ * provide an additional classloader, use the version of this function
+ * that takes a classloader as an argument.
+ *
+ * @param path The path to the properties file, relative to the root
+ * of the classpath entries from which it will be loaded
+ * (e.g. /conf/foo.properties).
+ *
+ * @return A properties object loaded with the contents of the
+ * specified file if the file could be found, null otherwise.
+ */
+ public static Properties loadInheritedProperties (String path)
+ throws IOException
+ {
+ return loadInheritedProperties(
+ path, ConfigUtil.class.getClassLoader());
+ }
+
+ /**
+ * Like the other version of {@link #loadInheritedProperties(String)}
+ * but this one uses the supplied class loader rather than the class
+ * loader used to load the ConfigUtil class.
+ *
+ * @see #loadInheritedProperties(String)
+ */
+ public static Properties loadInheritedProperties (
+ String path, ClassLoader loader)
+ throws IOException
+ {
+ // first look for the files in the supplied class loader
+ Enumeration enum = getResources(path, loader);
+ if (!enum.hasMoreElements()) {
+ // if we couldn't find anything there, try the system class
+ // loader (but only if that's not where we were already
+ // looking)
+ ClassLoader sysloader = ClassLoader.getSystemClassLoader();
+ if (sysloader != loader) {
+ enum = getResources(path, sysloader);
+ }
+ }
+
+ // we need to process the resources in reverse order, so we put
+ // them all into an array first
+ ArrayList rsrcs = new ArrayList();
+ while (enum.hasMoreElements()) {
+ rsrcs.add(enum.nextElement());
+ }
+
+ // now load each file in turn into our properties object
+ Properties props = new Properties();
+ for (int i = rsrcs.size()-1; i >= 0; i--) {
+ URL rurl = (URL)rsrcs.get(i);
+ InputStream in = rurl.openStream();
+ props.load(in);
+ }
+
+ return props;
+ }
+
/**
* Returns an input stream referencing a file that exists somewhere in
* the classpath. A full path (relative to the classpath directories)
@@ -81,8 +161,8 @@ public class ConfigUtil
*
* @param path The path to the file, relative to the root of the
* classpath directory from which it will be loaded
- * (e.g. /conf/foo.gif or perhaps just bar.gif if the file is at the
- * top level).
+ * (e.g. /conf/foo.gif or perhaps just
+ * /bar.gif if the file is at the top level).
*/
public static InputStream getStream (String path)
{
@@ -101,32 +181,62 @@ public class ConfigUtil
*
* @param path The path to the file, relative to the root of the
* classpath directory from which it will be loaded
- * (e.g. /conf/foo.gif or perhaps just bar.gif if the file is at the
- * top level).
+ * (e.g. /conf/foo.gif or perhaps just
+ * /bar.gif if the file is at the top level).
*/
public static InputStream getStream (String path, ClassLoader loader)
{
// first try the supplied class loader
+ InputStream in = getResourceAsStream(path, loader);
+ if (in != null) {
+ return in;
+ }
+
+ // if that didn't work, try the system class loader (but only if
+ // it's different from the class loader we just tried)
+ ClassLoader sysloader = ClassLoader.getSystemClassLoader();
+ if (sysloader != loader) {
+ return getResourceAsStream(path, loader);
+ } else {
+ return null;
+ }
+ }
+
+ protected static InputStream getResourceAsStream (
+ String path, ClassLoader loader)
+ {
+ // make sure the class loader isn't null
+ if (loader == null) {
+// Log.debug("No loader for get resource request " +
+// "[path=" + path + "].");
+ return null;
+ }
+ // try the path as is
InputStream in = loader.getResourceAsStream(path);
if (in != null) {
return in;
}
-
// try toggling the leading slash
- String apath = togglePath(path);
- in = loader.getResourceAsStream(apath);
- if (in != null) {
- return in;
- }
+ return loader.getResourceAsStream(togglePath(path));
+ }
- // if that didn't work, try the system classloader
- in = Class.class.getResourceAsStream(path);
- if (in != null) {
- return in;
+ protected static Enumeration getResources (
+ String path, ClassLoader loader)
+ throws IOException
+ {
+ // make sure the class loader isn't null
+ if (loader == null) {
+// Log.debug("No loader for get resource request " +
+// "[path=" + path + "].");
+ return null;
+ }
+ // try the path as is
+ Enumeration enum = loader.getResources(path);
+ if (enum.hasMoreElements()) {
+ return enum;
}
-
- // help us obi wan, you're our only hope
- return Class.class.getResourceAsStream(apath);
+ // try toggling the leading slash
+ return loader.getResources(togglePath(path));
}
protected static String togglePath (String path)
@@ -137,4 +247,18 @@ public class ConfigUtil
return "/" + path;
}
}
+
+ /**
+ * Unit test driver.
+ */
+ public static void main (String[] args)
+ {
+ try {
+ String path = "/com/samskivert/util/test.properties";
+ Properties props = loadInheritedProperties(path);
+ System.out.println(props);
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ }
}