Hey Odi, I just made a few minor additions. We also have to be careful of the NumberFormatExceptions that will be thrown by the parseInt() type calls. Perhaps there should be a ConfigurationException? Also I think that these related classes should be in their own package, such as httpclient.config.
I am a little concerned about the use of the configuration object. I would expect calls to this to look like: String httpVersion = config.getStringValue("http.version"); It seems a little verbose if it has to be used frequently. At the same time, we will have to be careful not to "cache" these values in case a user changes the config value during runtime. I think that this is a pretty good start. There are some bugs for configuration that are already opened: http://issues.apache.org/bugzilla/show_bug.cgi?id=10790 http://issues.apache.org/bugzilla/show_bug.cgi?id=10791 http://issues.apache.org/bugzilla/show_bug.cgi?id=10797 These bugs are targeted for HttpClient 2.1. Question: do people want this in the 2.0 release? -----Original Message----- From: Ortwin Glück [mailto:[EMAIL PROTECTED]] Sent: Monday, September 23, 2002 2:53 PM To: Jakarta Commons Developers List Subject: Re: [HttpClient] Preferences Architecture Implementation Draft Here is some code for discussion. It differs slightly from my initial sketch but is more flexible that way. ------ Configuration.java package org.apache.commons.httpclient; import java.util.*; import java.io.*; import org.apache.commons.logging.*; /** * Holds the configuration for the httpclient package. Instances of this class * are immutable. * * @author Ortwin Glück * * @since 2.0 */ public class Configuration { /** * The default configuration read from file. */ public static final Configuration DEFAULT = new Configuration(); public static final String SYSTEM_PROPERTY = "org.apache.commons.httpclient.configuration"; private static final String PROPERTIES_FILE = "httpclient.properties"; private static final String JAR_PATH = "META-INF/services/"; private static final Log log = LogFactory.getLog(Configuration.class); private Properties props = new Properties(); /** * Creates the default configuration. * The default values are read from the <tt>httpclient.properties</tt> which is * expected in the following locations: * 1. $JAVA_HOME/lib/ directory * 2. On the classpath * 3. In META-INF/services on the classpath * * For classpath lookups the following class loaders are probed in order: * 1. the context class loader of the current thread * 2. the class loader of this class * 3. the system class loader * * An alternative path and filename may be specified in the * <tt>org.apache.commons.httpclient.configuration</tt> System Property. */ protected Configuration() { String filename = null; try { filename = System.getProperty(SYSTEM_PROPERTY); } catch(SecurityException e) { } if (filename == null) { String javahome = System.getProperty("java.home"); filename = javahome + File.separator + "lib" + File.separator + PROPERTIES_FILE; } InputStream in = null; File file = new File(filename); if (file.exists()) { try { log.debug("Trying "+filename); in = new FileInputStream(file); } catch(Exception e) { log.error("Unable to load configuration file", e); } } if (in == null) { try { ClassLoader cl = getClassLoader(); if (cl == null) { log.debug("Trying last ressort class loader"); in = ClassLoader.getSystemResourceAsStream(JAR_PATH + PROPERTIES_FILE); } else { log.debug("Trying class loader "+cl.toString()); in = cl.getResourceAsStream(JAR_PATH + PROPERTIES_FILE); } } catch(Exception e) { log.error("Error while probing class loaders", e); } } if (in != null) { try { props.load(in); } catch (IOException e) { log.error("Could not load "+ PROPERTIES_FILE, e); } } else { log.warn(PROPERTIES_FILE +" not found. No default values available."); } } /** * Returns the best class loader. * @return */ private ClassLoader getClassLoader() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); if (cl != null) return cl; } catch(Exception e) { log.warn("ClassLoader Exception: " + e.getMessage()); } try { cl = Configuration.class.getClassLoader(); } catch(Exception e) { log.warn("ClassLoader Exception: " + e.getMessage()); } return cl; } /** * Creates a configuration based on a configuration base that is modified * by the patch values. The <tt>base</tt> is first copied into the new * configuration. Afterwards all values from the <tt>patch</tt> Properties are * stored in the new configuration overwriting existing ones. * * @param base The configuration base * @param patch Values that are replaced in the base configuration. */ public Configuration(Configuration base, Properties patch) { //copy props.putAll(base.props); //patch Enumeration keys = patch.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); String value = patch.getProperty(key, ""); props.setProperty(key, value); } } /** * Convenience method to generate a patched configuration based on the current one. * @param patch Values that are replaced in the base configuration. * @return new Configuration(this, patch) */ public Configuration patch(Properties patch) { return new Configuration(this, patch); } public String getStringValue(String key) { return props.getProperty(key, "").trim(); } public long getLongValue(String key) { return Long.parseLong(getStringValue(key)); } public long getLongHexValue(String key) { return Long.parseLong(getStringValue(key), 16); } public int getIntValue(String key) { return Integer.parseInt(getStringValue(key)); } public int getIntHexValue(String key) { return Integer.parseInt(getStringValue(key), 16); } public float getFloatValue(String key) { return Float.parseFloat(getStringValue(key)); } public double getDoubleValue(String key) { return Double.parseDouble(getStringValue(key)); } public boolean getBooleanValue(String key) { return isEnabled(key); } /** * Returns true if the value is either yes, true, enabled, on or 1. The check * is not case sensitive. * @param key The key to check * @return */ public boolean isEnabled(String key) { String val = getStringValue(key).toUpperCase(); return (val.equals("YES") || val.equals("TRUE") || val.equals("ENABLED") || val.equals("ON") || val.equals("1")); } /** * Checks if a key is empty. * @param key * @return false if a key does not exist, it is the empty string or consits * solely of whitespace; true otherwise. */ public boolean isEmpty(String key) { return getStringValue(key).equals(""); } } ------------ ConfigKeys.java package org.apache.commons.httpclient; /** * Holds the property keys used to configure HttpClient. * @see Configuration * * @author Ortwin Glück * * @since 2.0 */ public interface ConfigKeys { /** The HTTP version to use. 1.0 means HTTP/1.0, 1.1 means HTTP/1.1 */ public static final String HTTP_VERSION = "http.version"; /** Whether to use preemtive authorization or not. Boolean. */ public static final String PREEMPT_AUTH = "preemtive.authorization"; /** The maximum number of Location redirects until an Exception is thrown. Integer. */ public static final String MAX_REDIRECT = "redirect.maximum"; /** The user-agent string used to identify the client against the web server. String. */ public static final String USER_AGENT = "user.agent"; /** The default port for http requests. public static final String HTTP_PORT = "http.default.port" /** The default port for secure https requests. public static final String HTTPS_PORT = "https.default.port" } --------- httpclient.properties http.version=1.1 http.default.port=80 https.default.port=443 authorization.preemptive=true redirect.maximum=20 user.agent=Jakarta Commons-HttpClient/2.0M1 -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>