[prev in list] [next in list] [prev in thread] [next in thread] 

List:       jakarta-commons-dev
Subject:    Re: [HttpClient] Preferences Architecture Implementation Draft II
From:       "Mark R. Diggory" <mdiggory () latte ! harvard ! edu>
Date:       2002-09-30 18:35:50
[Download RAW message or body]



Ortwin Glück wrote:

> ___________
> Sample Code
>
> The user can set the configuration from the outside for HttpClient, 
> HttpMultiClient, HttpMethod, HttpConnection. The user should not try 
> to configure other classes directly:
> --user app
> HttpClient client = new HttpClient();
> Properties patch = new Properties();
> patch.setProperty(ConfigKeys.USER_AGENT, "My HTTP Client 0.01");
> Configuration clientConfig = new Configuration(Configuration.DEFAULT, 
> patch);
> ConfigManager.setConfiguration(client, clientConfig);

I guess I find this a little confusing. Why wouldn't you push the 
Configuration objects back behind HttpClient. HttpConnection and 
HttpMethod...

like this:

HttpClient client = new HttpClient();
Hashtable props = new Hashtable();
props.put(ConfigKeys.USER_AGENT, "My HTTP Client 0.01");
client.setConfiguration(props);

or

Hashtable props = new Hashtable();
props.put(ConfigKeys.USER_AGENT, "My HTTP Client 0.01");
HttpClient client = new HttpClient(props);

then provide some simple methods for the following:

public void client.addToConfiguration(key,value);
public void client.removeFromConfiguration(key);
public Hashtable getConfiguration();

This might lower the coding overhead.

>
> HttpClient configures HttpMethod automatically IF not yet configured. 
> The same way a HttpConnection is configured:
> --HttpClient
>  public synchronized int executeMethod(HttpMethod method) {
>    ...
>    if (!ConfigManager.isConfigured(method)) {
>     ConfigManager.setConfiguration(method, myConfig);
>    }
>    method.execute(getState(), connection);
>    ...
>  }
>
>
> Low level objects use the configuration of a higher level object. The 
> same applies to inner classes:
> --ChunkedInputStream
>  myConfig = ConfigManager.getConfiguration(method);
>  ... myConfig.getBooleanValue(ConfigKeys.IGNORE_PROTOCOL_VIOLATION);
>
> A static class must be configured by the caller using the meta object.
> Users should never try to configure low-level classes:
> --caller class
>  ConfigManager.setConfiguration(NTLM.class, myConfig);
>
> --NTLM
>  Configuration myConfig = ConfigManager.getConfiguration(NTLM.class);
>  String mySecurityProvider = 
> Configuration.getStringValue(ConfigKeys.SECURITY_PROVIDER);
>
>
> ________
> Problems
>
> As you can see this approach generates a large overhead. This is 
> mainly caused by one requirement: "Would there be a means to assign my 
> own properties object to the HttpClient, HttpConnection and HttpMethod 
> objects? So I could control the settings on a "client by client", 
> "connection by connection",  or "method by method" basis?" by Mark R. 
> Diggory, 2002-9-18.
>
> Any single object (e.g. Cookie) must therefore know which 
> Configuration applies to it. This means that the creator of an object 
> must set its configuration in the ConfigManager. For static classes  
> the requirement can not be fulfilled at all. Not so nice, is it.
>
> Please, if any of you has a good idea how to deal with this, drop me a 
> note.
>
>
> Odi

By using a "static" object to store configuration information 
(ConfigManager) , I think you've made it so that all objects need to get 
registered with it to figure out configuration information. Maybe if, 
each "Http" object simply stored its own Configuration object and was 
able to request from its parent the "parental" configuration. then the 
current configuration of any one object in a hierarchy could easily be 
resolved.

Finally,

I'm not sure how "dynamic" this really needs to be. If you update the 
configuration of a parent object, should it be the case that all 
previously generated objects get that configuration get the changes? 
This currently seems to be what happens by the use of the ConfigManager. 
Does this create alot of config tracking overhead?

I hadn't really looked upon the idea as having the ability to alter the 
config higher in the heirarchy once you had created an object below it.

Would it be more simple to only have newly constructed objects contain 
the new config information. Old objects below that point in the 
hierarchy could either get "destroyed/gc'ed" or "somehow invalidated". 
Then they would get "reinitialized" with the new configuration if this 
is really neccessary to keep them around. (ie. altering the 
configuration wipes out all current connections and when the client gets 
another call to a connection, it is instantiated through normal 
processes with the new config.)

Another note:
Now that I see this, I'm not sure how much configuration manipulation 
one would want to have below the HttpConnection level. My primary 
concern was to be able to manipulate things like proxying in such a way 
that if proxying was setup one way by default for the HttpClient. You 
could alter it for a specific HttpConnection if neccessary, so one could 
proxy 2 connections through two different proxy hosts. At the HttpMethod 
level, this sort of configuaration is Not/Applicable because you don't 
configure proxy info at that level. So is configuration really that 
important at the HttpMethod level? Maybe we should consider evaluating 
how much stuff we would want configurable at the Method level?

Hope this helps some,
-Mark

>
>
>
>
>------------------------------------------------------------------------
>
>/*
> * $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", "HttpClient", 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/>.
> *
> * [Additional notices, if required by prior licensing conditions]
> *
> */
>
>package org.apache.commons.httpclient.config;
>
>/**
>  * 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";
>
>     /** The security provider to use for crypto alogrithm implementations. */
>     public static final String SECURITY_PROVIDER = "security.provider";
>
>     /** The maximum number of simultaneous HTTP/S sockets to be opened to the same host */
>     public static final String CONNECTIONS_PER_SERVER = "simultaneous.connections.maximum";
>}
>
>------------------------------------------------------------------------
>
>/*
> * $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", "HttpClient", 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/>.
> *
> * [Additional notices, if required by prior licensing conditions]
> *
> */
>
>package org.apache.commons.httpclient.config;
>
>import java.io.File;
>import java.io.FileInputStream;
>import java.io.IOException;
>import java.io.InputStream;
>import java.util.Properties;
>
>import org.apache.commons.logging.Log;
>import org.apache.commons.logging.LogFactory;
>
>/**
>  * Holds a configuration for the httpclient package. A configuration can be
>  * hierarchically based on another configuration and only overwrite (patch) some
>  * of the values in the base configuration. Changes in the underlying base are
>  * immediately reflected in derived configurations.
>  *
>  * @author Ortwin Glück
>  *
>  * @since 2.0
>  */
>public class Configuration {
>    /**
>     * The default configuration read from file.
>     */
>    public static final Configuration DEFAULT = new Configuration();
>    /** Key of the System Property that is queried for the location of the properties file */
>    public static final String SYSTEM_PROPERTY = "org.apache.commons.httpclient.configuration";
>    /** Name of the properties file */
>    private static final String PROPERTIES_FILE = "httpclient.properties";
>    /** Location in a jar file where we expect a properies file */
>    private static final String JAR_PATH = "META-INF/services/";
>    /** The log */
>    private static final Log log = LogFactory.getLog(Configuration.class);
>    /** The actual configuration values */
>    private Properties props = new Properties();
>    /** The base configuration */
>    private Configuration base = null;
>
>    /**
>    * 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 derived configuration based on a configuration base that is modified
>     * by the patch values. Values in <tt>patch</tt> hide values in the <tt>base</tt>.
>     * <tt>DEFAULT</tt> holds the default configuration which
>     * is a good starting point.
>     *
>     * @param base The configuration base or <tt>null</tt>.
>     * @param patch Values that are replaced in the base configuration.
>     */
>    public Configuration(Configuration base, Properties patch) {
>        this.base = base;
>        patch(patch);
>    }
>
>    /**
>    * 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 void patch(Properties patch) {
>        props.putAll(patch);
>    }
>
>    /**
>     * Returns the value that is stored under <tt>key</tt>. If this is a derived
>     * configuration and there is no local value for <tt>key</tt> the base
>     * configuration will be queried.
>     *
>     * @param key The key to query
>     * @return The value as a String. Empty string if no value was found.
>     */
>    public String getStringValue(String key) {
>        if (base == null) {
>            return props.getProperty(key, "").trim();
>        } else {
>            return props.getProperty(key, base.getStringValue(key));
>        }
>    }
>
>    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("");
>    }
>}
>
>------------------------------------------------------------------------
>
>/*
> * $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", "HttpClient", 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/>.
> *
> * [Additional notices, if required by prior licensing conditions]
> *
> */
>
>package org.apache.commons.httpclient.config;
>
>import java.util.Map;
>import java.util.WeakHashMap;
>
>/**
> * Links configurations with objects. This implementation uses a WeakHashMap to
> * ensure that configurations are removed automatically when the linked object
> * is not used any more.
> *
> * @author Ortwin Glück
> *
> * @since 2.0
> */
>public class ConfigManager {
>    /** The default configuration (for convenience) */
>    public static final Configuration DEFAULT = Configuration.DEFAULT;
>
>    /** The singleton instance */
>    private static ConfigManager instance = new ConfigManager();
>
>    /** Stores (object, Configuration) pairs */
>    private static Map configs = new WeakHashMap();
>
>    /**
>     * Singleton. Use static methods only.
>     */
>    protected ConfigManager() {
>    }
>
>    /**
>     * Links a configuration to an object. It is stored until the object is reclaimed
>     * by the garbage collector.
>     * @param object The object to link <tt>configuration</tt> to.
>     * @param configuration The configuration.
>     */
>    public static void setConfiguration(Object object, Configuration configuration) {
>        configs.put(object, configuration);
>    }
>
>    /**
>     * Gets the configuration that was linked to an object with the
>     * <tt>setConfiuration</tt> method. If no configuration was set, the default
>     * configuration is returned.
>     * @param object The object whose configuration to get.
>     * @return The configuration that was linked to <tt>object</tt> or the default one.
>     */
>    public static Configuration getConfiguration(Object object) {
>        Configuration config = (Configuration) configs.get(object);
>        if (config == null)  {
>            return DEFAULT;
>        } else {
>            return config;
>        }
>    }
>
>    /**
>     * Checks if there is a specific Configuration for an object.
>     * @param object The object that should be checked.
>     * @return true if a specific Configuration exists, fals if the DEFAULT
>     * is used for this object.
>     */
>    public static boolean isConfigured(Object object) {
>        return (configs.get(object) != null);
>    }
>}
>  
>
>------------------------------------------------------------------------
>
>--
>To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
>For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>
>


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

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic