You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Ortwin Glück <or...@nose.ch> on 2002/09/18 17:45:44 UTC

[HttpClient] Preferences Architecture Analysis

There have been a couple of requests in the past to allow for more fine 
grained configuration of HttpClient. At the moment there are a few 
parameters that control the behaviour of HttpClient such as 
"strict-mode" or "http11". This proposal tries to address this issue 
with a suitable architecture.

______________
Proposoal No 1
Store preferences in Properties objects. Do not use System properties.

   HTTP offers a large number of features. And HttpClient is used by 
groups of users with different (often opposed) interests. So the number 
of configuration parameters is potentially large and will most probably 
grow as HttpClient evolves and gains popularity.

It is therefore unwise to add one pair (get/set) of methods for every 
new config parameter. A more general solution is needed.

The Java API provides the java.util.Properties class which seems to suit 
well enough for this problem.

______________
Proposoal No 2
Do not hard-code default values.

   The "ease of use" requires that reasonable default values be set 
automatically. However it depends on the user's point of view what the 
term "reasonable" actually implies.

"Factory pre-set" default values could be stored in a properties file 
inside the /META-INF/ structure of the httpclient.jar file or 
alternative places.
_____________
Proposal No 3
Methods provide a means to locally patch the global preferences.

   For some config parameters it makes sense to set them globally for a 
whole session or even application. For others setting them on a 
per-request basis may make more sense.

___________________
Architecture Sketch

java.util.Properties:
  - encapsulates preferences as key/value String pairs

httpclient.Configuration:
  - singleton that holds the global preferences
  - reads its default values on initialization
  - provides static convenience methods to access individual parameters
    Samples:
    String getValue(String key, Properties localPrefs),
    boolean isEnabled(String key, Properties localPrefs)
    (localPrefs is an optional local patch for the global preferences)
  - provides methods to modify preferences

httpclient.ConfigKeys:
  - interface that defines the string constants for the keys
  - contains documentation for each key

httpclient.HttpMethodBase:
  - provides setting of per-request preferences:
    setPrefs(Properties)

____________________
Parameter Candidates

http.version / [1.0, 1.1] - whether to HTTP/1.1 or HTTP/1.0
proxy.host
proxy.port
proxy.noproxy.domains     - domains that do not require the usage of a
                             proxy
preemtive.authorization   - currently a system property
max.redirect.count        - currently hard-coded
cookie.max.lifetime       - new
keep-alive.timeout        - new
request.timeout           - new
useragent.string          - currently hard-coded in HttpMethodBase
follow.redirects          - currently getters/setters exist
allow.loops               - currently not allowed by default
http11.requires.contentlength - currently hard-coded (lenient)
exception.on.protocol.violation - new


Comments appreciated.

Odi







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


Re: [HttpClient] Preferences Architecture Implementation Draft

Posted by Ortwin Glück <or...@nose.ch>.
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) {
             }
         }

         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) {
         }
         try {
             cl = Configuration.class.getClassLoader();
         } catch(Exception e) {
         }
         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, "");
     }

     public long getLongValue(String key) {
         return Long.parseLong(getStringValue(key).trim());
     }

     public long getLongHexValue(String key) {
         return Long.parseLong(getStringValue(key).trim(), 16);
     }

     public int getIntValue(String key) {
         return Integer.parseInt(getStringValue(key).trim());
     }

     public int getIntHexValue(String key) {
         return Integer.parseInt(getStringValue(key).trim(), 16);
     }

     public float getFloatValue(String key) {
         return Float.parseFloat(getStringValue(key).trim());
     }

     public double getDoubleValue(String key) {
         return Double.parseDouble(getStringValue(key).trim());
     }

     /**
      * Returns true if the value is either yes, true, enabled or on. 
The check
      * is not case sensitive.
      * @param key The key to check
      * @return
      */
     public boolean isEnabled(String key) {
         String val = getStringValue(key);
         return (val.equalsIgnoreCase("YES") || val.equalsIgnoreCase("TRUE")
             || val.equalsIgnoreCase("ENABLED") || 
val.equalsIgnoreCase("ON"));
     }

     /**
      * 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).trim().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";
}

--------- httpclient.properties
http.version=1.1
preemtive.authorization=true
redirect.maximum=20
user.agent=Jakarta Commons-HttpClient/2.0M1



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


Re: [HttpClient] Preferences Architecture Analysis

Posted by "Mark R. Diggory" <md...@latte.harvard.edu>.
Ortwin Glück wrote:

> Mark R. Diggory wrote:
>
>> 1.) 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?
>
>
> Yes I think this makes sense. Do we really need one at the connection 
> level as well? To be discussed.

Maybe not a configuration file, just maybe the ability to hand it a 
properties object.

Its just that I tend to instantiate HttpConnection object directly using 
its constructor, then make Methods and execute them in it. I don't 
really use the HttpClient to make my HttpConnections at this time. 
(Although I probibly should consider doing it. I don't think the 
HttpClient class existed when I started developing using the commons 
httpclient library.)

>
> [I assume you mean connection as "per server" and not physical sockets. 

yes.

> Remember: A method may (re-)open a physical connection. With Http 1.1 
> several methods can reuse the same physical connection. ]
>
>> 2.) Is there a "priority" the architecture levels? ie:
>>
>> key                                     properties
>>        client   -->  connection   -->    method     =    actually used
>>
>> p1      x  --------------------------> y  ------------> y
>>
>> p2      a  ---------> b  -----------------------------> b   
>> p3                          n ------------> m  -------------> m   
>> p4      z  -------------------------------------------> z
>>
>
> Yes. This is exactly the priority system I meant by "patching" with 
> local preferences.
>
>
> --
> To unsubscribe, e-mail:   
> <ma...@jakarta.apache.org>
> For additional commands, e-mail: 
> <ma...@jakarta.apache.org>



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


Re: [HttpClient] Preferences Architecture Analysis

Posted by Jeff Dever <js...@sympatico.ca>.
Ortwin Glück wrote:

> Mark R. Diggory wrote:
> > 1.) 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?
>
> Yes I think this makes sense. Do we really need one at the connection
> level as well? To be discussed.
> [I assume you mean connection as "per server" and not physical sockets.
> Remember: A method may (re-)open a physical connection. With Http 1.1
> several methods can reuse the same physical connection. ]

For the connection issue, it is unreasonable to set properties for each socket,
but it is reasonable to set it for the HttpConnection class.

>
>
> > 2.) Is there a "priority" the architecture levels? ie:
> >
> > key                                     properties
> >        client   -->  connection   -->    method     =    actually used
> >
> > p1      x  --------------------------> y  ------------> y
> >
> > p2      a  ---------> b  -----------------------------> b
> > p3                          n ------------> m  -------------> m
> > p4      z  -------------------------------------------> z
> >
>
> Yes. This is exactly the priority system I meant by "patching" with
> local preferences.
>

Yes, this seems good.  Might also want to add a global (or default) level before
the client which would could be system wide.  This could be system properties or
in a httpclient global properties file.


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


Re: [HttpClient] Preferences Architecture Analysis

Posted by Ortwin Glück <or...@nose.ch>.
Mark R. Diggory wrote:
> 1.) 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?

Yes I think this makes sense. Do we really need one at the connection 
level as well? To be discussed.
[I assume you mean connection as "per server" and not physical sockets. 
Remember: A method may (re-)open a physical connection. With Http 1.1 
several methods can reuse the same physical connection. ]

> 2.) Is there a "priority" the architecture levels? ie:
> 
> key                                     properties
>        client   -->  connection   -->    method     =    actually used
> 
> p1      x  --------------------------> y  ------------> y
> 
> p2      a  ---------> b  -----------------------------> b   
> p3                          n ------------> m  -------------> m   
> p4      z  -------------------------------------------> z
> 

Yes. This is exactly the priority system I meant by "patching" with 
local preferences.


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


Re: [HttpClient] Preferences Architecture Analysis

Posted by "Mark R. Diggory" <md...@latte.harvard.edu>.

Ortwin Glück wrote:

>
> Proposoal No 1
> Store preferences in Properties objects. Do not use System properties.

Definitely, this is the major reason I DON'T use the default 
URL/URLConnection that Sun provides, why would anyone want to have 
properties such as proxyHost and proxyPort stuck into the System 
properties. Nothing sucks more than wanting to setup two or more http 
connections that have different proxies in this case. How do you do it? 
You can't if its configured in the static System.properties.

>
>   HTTP offers a large number of features. And HttpClient is used by 
> groups of users with different (often opposed) interests. So the 
> number of configuration parameters is potentially large and will most 
> probably grow as HttpClient evolves and gains popularity.
>
> It is therefore unwise to add one pair (get/set) of methods for every 
> new config parameter. A more general solution is needed.
>
> The Java API provides the java.util.Properties class which seems to 
> suit well enough for this problem.

Much like the same way you can build a JNDI Context off of a set of 
 properties stored in a java.util.Hastable.

>
> _____________
> Proposal No 3
> Methods provide a means to locally patch the global preferences.
>
>   For some config parameters it makes sense to set them globally for a 
> whole session or even application. For others setting them on a 
> per-request basis may make more sense.
>
> ___________________
> Architecture Sketch
>
> java.util.Properties:
>  - encapsulates preferences as key/value String pairs

>
> httpclient.Configuration:
>  - singleton that holds the global preferences
>  - reads its default values on initialization
>  - provides static convenience methods to access individual parameters
>    Samples:
>    String getValue(String key, Properties localPrefs),
>    boolean isEnabled(String key, Properties localPrefs)
>    (localPrefs is an optional local patch for the global preferences)
>  - provides methods to modify preferences
>
> httpclient.ConfigKeys:
>  - interface that defines the string constants for the keys
>  - contains documentation for each key
>
> httpclient.HttpMethodBase:
>  - provides setting of per-request preferences:
>    setPrefs(Properties)

1.) 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?

2.) Is there a "priority" the architecture levels? ie:

key                                     properties
        client   -->  connection   -->    method     =    actually used

p1      x  --------------------------> y  ------------> y

p2      a  ---------> b  -----------------------------> b    

p3                          n ------------> m  -------------> m  
  
p4      z  -------------------------------------------> z


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