[jbosscache-commits] JBoss Cache SVN: r5331 - in amazon-s3/trunk: src/main/java/com/amazon and 4 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Thu Feb 7 17:57:15 EST 2008


Author: genman
Date: 2008-02-07 17:57:14 -0500 (Thu, 07 Feb 2008)
New Revision: 5331

Added:
   amazon-s3/trunk/src/main/java/com/amazon/s3/GetStreamResponse.java
   amazon-s3/trunk/src/test/resources/
   amazon-s3/trunk/src/test/resources/log4j.xml
Removed:
   amazon-s3/trunk/src/main/java/com/amazon/thirdparty/
Modified:
   amazon-s3/trunk/.classpath
   amazon-s3/trunk/pom.xml
   amazon-s3/trunk/src/main/java/com/amazon/s3/Bucket.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/CallingFormat.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/CanonicalString.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/Connection.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/GetResponse.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/Headers.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/ListAllBucketsResponse.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/ListResponse.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/LocationResponse.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/Method.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/QueryGenerator.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/Response.java
   amazon-s3/trunk/src/main/java/com/amazon/s3/S3Object.java
   amazon-s3/trunk/src/test/java/com/amazon/s3/S3Test.java
Log:
Use Apache HTTP client library instead of Java URL Connection.
It's more efficient (uses the same Socket) and supports more options, like "HTTP expect" exchanges

Modified: amazon-s3/trunk/.classpath
===================================================================
--- amazon-s3/trunk/.classpath	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/.classpath	2008-02-07 22:57:14 UTC (rev 5331)
@@ -2,6 +2,7 @@
 <classpath>
 	<classpathentry kind="src" path="src/main/java"/>
 	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="src" path="src/test/resources"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
 	<classpathentry kind="output" path="bin"/>

Modified: amazon-s3/trunk/pom.xml
===================================================================
--- amazon-s3/trunk/pom.xml	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/pom.xml	2008-02-07 22:57:14 UTC (rev 5331)
@@ -1,13 +1,9 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<?xml version="1.0" encoding="UTF-8"?><project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.amazon</groupId>
   <artifactId>amazon-s3-rest</artifactId>
   <version>1.0.0.0-SNAPSHOT</version>
-  <description>
-    Cleaned-up version of the Amazon S3 REST access layer 
-  </description>
+  <description>Cleaned-up version of the Amazon S3 REST access layer</description>
   <build>
     <plugins>
       <plugin>
@@ -18,7 +14,6 @@
         </configuration>
       </plugin>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <systemProperties>
@@ -42,5 +37,21 @@
       <version>4.1</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <version>3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.14</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <version>3.0.1</version>
+    </dependency>
   </dependencies>
-</project>
+</project>
\ No newline at end of file

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/Bucket.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/Bucket.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/Bucket.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -92,7 +92,7 @@
 	 * Returns true if the bucket name is valid for the calling format.
 	 */
 	public static boolean validateBucketName(String bucketName, CallingFormat callingFormat) {
-	    if (callingFormat == CallingFormat.getPathCallingFormat())
+	    if (callingFormat == CallingFormat.PATH)
 	    {
 	        final int MIN_BUCKET_LENGTH = 3;
 	        final int MAX_BUCKET_LENGTH = 255;

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/CallingFormat.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/CallingFormat.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/CallingFormat.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -9,10 +9,13 @@
 
 package com.amazon.s3;
 
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.Map;
 
+import org.apache.commons.httpclient.HttpURL;
+import org.apache.commons.httpclient.HttpsURL;
+import org.apache.commons.httpclient.URI;
+import org.apache.commons.httpclient.URIException;
+
 /**
  * Calling formats.
  * 
@@ -20,9 +23,9 @@
  */
 public abstract class CallingFormat {
 
-	private final static CallingFormat pathCallingFormat = new PathCallingFormat();
-	private final static CallingFormat subdomainCallingFormat = new SubdomainCallingFormat();
-	private final static CallingFormat vanityCallingFormat = new VanityCallingFormat();
+	public final static CallingFormat PATH = new PathCallingFormat();
+	public final static CallingFormat SUBDOMAIN = new SubdomainCallingFormat();
+	public final static CallingFormat VANITY = new VanityCallingFormat();
 
 	public abstract boolean supportsLocatedBuckets();
 
@@ -30,71 +33,57 @@
 
 	public abstract String getPathBase(Bucket bucket, String key);
 
-	public abstract URL getURL(boolean isSecure, String server, int port,
+	public abstract URI getURI(boolean isSecure, String server, int port,
 			Bucket bucket, String key, Map<String, String> pathArgs)
-			throws MalformedURLException;
+			throws URIException;
 
 	/**
-	 * Converts the Path Arguments from a map to String which can be used in url construction
-	 * @param pathArgs a map of arguments
-	 * @return a string representation of pathArgs
+	 * Adds query parameters to the URL.
 	 */
-	public static String convertPathArgsHashToString(Map<String, String> pathArgs) {
-	    StringBuilder pathArgsString = new StringBuilder();
-	    String argumentValue;
-	    boolean firstRun = true;
-	    if (pathArgs != null) {
-	        for (Map.Entry<String, String> me : pathArgs.entrySet()) {
-	            String argument = me.getKey();
-	            if (firstRun) {
-	                firstRun = false; 
-	                pathArgsString.append("?");
-	            } else {
-	                pathArgsString.append("&");
-	            } 
-	            
-	            argumentValue = me.getValue();
-	            pathArgsString.append(argument);
-	            if (argumentValue != null) {
-	                pathArgsString.append("=");
-	                pathArgsString.append(argumentValue);
-	            }
-	        }
+	private static URI addQuery(HttpURL uri, Map<String, String> pathArgs) throws URIException {
+		if (pathArgs == null || pathArgs.isEmpty())
+			return uri;
+		int size = pathArgs.size();
+		String[] name = new String[size];
+		String[] value = new String[size];
+    	int i = 0;
+        for (Map.Entry<String, String> me : pathArgs.entrySet()) {
+        	name[i] = me.getKey();
+        	value[i] = me.getValue();
+        	if (value[i] == null)
+        		throw new NullPointerException("query cannot contain null " + pathArgs);
+        	i++;
 	    }
-	    
-	    return pathArgsString.toString();
+		uri.setQuery(name, value);
+	    return uri;
 	}
 
-	public static CallingFormat getPathCallingFormat() {
-		return pathCallingFormat;
+	private static URI url(boolean isSecure, String host, int port, String path, Map<String, String> pathArgs)
+			throws URIException
+	{
+		HttpURL url = isSecure ? new HttpsURL(host, port, "/") : new HttpURL(host, port, "/");
+		url.setEscapedPath(path);
+		return addQuery(url, pathArgs);
 	}
 
-	public static CallingFormat getSubdomainCallingFormat() {
-		return subdomainCallingFormat;
-	}
-
-	public static CallingFormat getVanityCallingFormat() {
-		return vanityCallingFormat;
-	}
-
 	static private class PathCallingFormat extends CallingFormat {
 		public boolean supportsLocatedBuckets() {
 			return false;
 		}
 
 		public String getPathBase(Bucket bucket, String key) {
-			return isBucketSpecified(bucket) ? "/" + bucket + "/" + key : "/";
+			return isBucketSpecified(bucket) ? "/" + bucket + "/" + UrlEncoder.encode(key) : "/";
 		}
 
 		public String getEndpoint(String server, int port, Bucket bucket) {
 			return server + ":" + port;
 		}
 
-		public URL getURL(boolean isSecure, String server, int port, Bucket bucket, String key, Map<String, String> pathArgs)
-				throws MalformedURLException {
-			String pathBase = isBucketSpecified(bucket) ? "/" + bucket + "/" + key : "/";
-			String pathArguments = convertPathArgsHashToString(pathArgs);
-			return new URL(isSecure ? "https" : "http", server, port, pathBase + pathArguments);
+		@Override
+		public URI getURI(boolean isSecure, String server, int port, Bucket bucket, String key, Map<String, String> pathArgs)
+				throws URIException {
+			String pathBase = isBucketSpecified(bucket) ? "/" + bucket + "/" + UrlEncoder.encode(key) : "/";
+			return url(isSecure, server, port, pathBase, pathArgs);
 		}
 
 		private boolean isBucketSpecified(Bucket bucket) {
@@ -120,22 +109,19 @@
 		}
 
 		public String getPathBase(Bucket bucket, String key) {
-			return "/" + key;
+			return "/" + UrlEncoder.encode(key);
 		}
 
-		public URL getURL(boolean isSecure, String server, int port,
+		@Override
+		public URI getURI(boolean isSecure, String server, int port,
 				Bucket bucket, String key, Map<String, String> pathArgs)
-				throws MalformedURLException {
+				throws URIException {
 			if (bucket == null || !bucket.specified()) {
-				// The bucket is null, this is listAllBuckets request
-				String pathArguments = convertPathArgsHashToString(pathArgs);
-				return new URL(isSecure ? "https" : "http", server, port, "/"
-						+ pathArguments);
+    			return url(isSecure, server, port, "", pathArgs);
 			} else {
 				String serverToUse = getServer(server, bucket);
 				String pathBase = getPathBase(bucket, key);
-				String pathArguments = convertPathArgsHashToString(pathArgs);
-				return new URL(isSecure ? "https" : "http", serverToUse, port, pathBase + pathArguments);
+    			return url(isSecure, serverToUse, port, pathBase, pathArgs);
 			}
 		}
 	}

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/CanonicalString.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/CanonicalString.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/CanonicalString.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -12,7 +12,8 @@
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 
-import com.amazon.thirdparty.Base64;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.httpclient.util.EncodingUtil;
 
 /**
  * Creates canonical strings for authorization purposes by hashing
@@ -109,7 +110,7 @@
 	    // append the key (it might be an empty string)
 	    // append a slash regardless
 	    buf.append("/");
-	    if(key != null) {
+	    if (key != null) {
 	        buf.append(key);
 	    }
 	    
@@ -164,29 +165,25 @@
 	 * @param data Data to sign
 	 * @param passcode Passcode to sign it with
 	 * @return Signature
-	 * @throws NoSuchAlgorithmException If the algorithm does not exist.  Unlikely
-	 * @throws InvalidKeyException If the key is invalid.
 	 */
 	static String encode(Key signingKey, String canonicalString,
 	                            boolean urlencode)
 	{
-	    // Acquire the MAC instance and initialize with the signing key.
-	    Mac mac = null;
+	    Mac mac;
 	    try {
 	        mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
 	    } catch (NoSuchAlgorithmException e) {
-	        // should not happen
 	        throw new RuntimeException("Could not find sha1 algorithm", e);
 	    }
 	    try {
 	        mac.init(signingKey);
 	    } catch (InvalidKeyException e) {
-	        // also should not happen
 	        throw new RuntimeException("Could not initialize the MAC algorithm", e);
 	    }
 	
-	    // Compute the HMAC on the digest, and set it.
-	    String b64 = Base64.encodeBytes(mac.doFinal(canonicalString.getBytes()));
+		byte[] b = mac.doFinal(canonicalString.getBytes());
+    	byte[] encode = Base64.encodeBase64(b);
+    	String b64 = EncodingUtil.getAsciiString(encode);
 	
 	    if (urlencode) {
 	        return UrlEncoder.encode(b64);

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/Connection.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/Connection.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/Connection.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -10,8 +10,6 @@
 package com.amazon.s3;
 
 import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.URL;
 import java.security.Key;
 import java.text.SimpleDateFormat;
 import java.util.Collections;
@@ -22,6 +20,17 @@
 import java.util.Map;
 import java.util.TimeZone;
 
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.URI;
+import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+
 /**
  * A stateless connection to the Amazon S3 system which uses the REST API.
  * <p/>
@@ -36,6 +45,11 @@
     public static final String DEFAULT_HOST = "s3.amazonaws.com";
 	public static final int INSECURE_PORT = 80;
 	public static final int SECURE_PORT = 443;
+
+	/**
+	 * Data larger than 1024 bytes will use expect headers.
+	 */
+	public static final int EXPECT_SIZE = 1024;
 	
     private String awsAccessKeyId;
     private Key awsSecretAccessKey;
@@ -44,6 +58,17 @@
     private int port;
 	private CallingFormat callingFormat;
 	
+	private MultiThreadedHttpConnectionManager connectionManager = 
+		new MultiThreadedHttpConnectionManager();
+	private HostConfiguration config;
+	private HttpClient client;
+	
+	static {
+    	String charset = URI.getDefaultProtocolCharset();
+    	if (!charset.equals("UTF-8"))
+    		throw new Error("URI charset must be UTF-8: " + charset);
+	}
+	
 	public Connection(String awsAccessKeyId, String awsSecretAccessKey) {
         this(awsAccessKeyId, awsSecretAccessKey, true);
     }
@@ -61,7 +86,7 @@
     
     public Connection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure, 
                              String server, int port) {
-        this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, port, CallingFormat.getSubdomainCallingFormat());
+        this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, port, CallingFormat.SUBDOMAIN);
         
     }
 
@@ -69,7 +94,7 @@
                              String server, CallingFormat format) {
         this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, 
              isSecure ? SECURE_PORT : INSECURE_PORT, 
-             CallingFormat.getSubdomainCallingFormat());
+             CallingFormat.SUBDOMAIN);
     }
 
     /**
@@ -92,6 +117,11 @@
         this.server = server;
         this.port = port;
         this.callingFormat = format;
+        
+        config = new HostConfiguration();
+        config.setHost(server, port, isSecure ? "http" : "https");
+        client = new HttpClient(connectionManager);
+        client.setHostConfiguration(config);
     }
     
     /**
@@ -121,14 +151,27 @@
         if (!bucket.validateName(callingFormat))
             throw new IllegalArgumentException("Invalid Bucket Name: "+bucket);
 
-        HttpURLConnection request = makeRequest(Method.PUT, bucket, headers);
+        PutMethod method = (PutMethod) makeRequest(Method.PUT, bucket, headers);
         if (body != null)
         {
-            request.setDoOutput(true);
-            request.getOutputStream().write(body.getBytes("UTF-8"));
+        	StringRequestEntity sre = new StringRequestEntity(body, "text/xml", "UTF-8");
+            method.setRequestEntity(sre);
         }
-        return new Response(request);
+        executeRelease(method);
+        return new Response(method);
     }
+    
+    private int execute(HttpMethod method) throws IOException {
+    	return client.executeMethod(method);
+    }
+    
+    private int executeRelease(HttpMethod method) throws IOException {
+    	try {
+        	return client.executeMethod(method);
+    	} finally {
+    		method.releaseConnection();
+    	}
+    }
 
 	/**
      * Creates a new bucket.
@@ -153,8 +196,8 @@
      */
     public boolean exists(Bucket bucket) throws IOException
     {
-        HttpURLConnection response  = makeRequest(Method.HEAD, bucket);
-        int httpCode = response.getResponseCode();
+        HttpMethod method = makeRequest(Method.HEAD, bucket);
+        int httpCode = executeRelease(method);
         return httpCode >= 200 && httpCode < 300;
     }
 
@@ -201,7 +244,7 @@
     }
     
     /**
-     * Lists the contents of a bucket.
+     * Lists the contents of a bucket by prefix.
      */
 	public ListResponse list(Bucket bucket, String prefix) throws IOException {
 		return list(bucket, prefix, null, null);
@@ -224,10 +267,34 @@
         throws IOException
     {
 
-        Map<String, String> pathArgs = Response.paramsForListOptions(prefix, marker, maxKeys, delimiter);
-        return new ListResponse(makeRequest(Method.GET, bucket, pathArgs, headers));
+        Map<String, String> pathArgs = paramsForListOptions(prefix, marker, maxKeys, delimiter);
+		HttpMethod method = makeRequest(Method.GET, bucket, pathArgs, headers);
+		try {
+    		execute(method);
+            return new ListResponse(method);
+		} finally {
+			method.releaseConnection();
+		}
     }
 
+	private static Map<String, String> paramsForListOptions(String prefix,
+			String marker, Integer maxKeys, String delimiter)
+	{
+		Map<String, String> argParams = new HashMap<String, String>();
+		if (prefix != null)
+			argParams.put("prefix", prefix);
+		if (marker != null)
+			argParams.put("marker", marker);
+		if (delimiter != null)
+			argParams.put("delimiter", delimiter);
+
+		if (maxKeys != null)
+			argParams.put("max-keys", Integer.toString(maxKeys.intValue()));
+
+		return argParams;
+
+	}
+
 	/**
      * Deletes a bucket.
      * @param bucket The name of the bucket to delete.
@@ -237,7 +304,9 @@
     public Response delete(Bucket bucket, Headers headers)
         throws IOException
     {
-        return new Response(makeRequest(Method.DELETE, bucket, "", null, headers));
+		HttpMethod method = makeRequest(Method.DELETE, bucket, "", null, headers);
+		executeRelease(method);
+        return new Response(method);
     }
 
     /**
@@ -260,13 +329,8 @@
     public Response put(Bucket bucket, String key, S3Object object, Headers headers)
         throws IOException
     {
-        HttpURLConnection request =
-            makeRequest(Method.PUT, bucket, key, null, headers, object);
-
-        request.setDoOutput(true);
-        request.getOutputStream().write(object.getData() == null ? new byte[] {} : object.getData());
-
-        return new Response(request);
+        PutMethod request = (PutMethod) makeRequest(Method.PUT, bucket, key, null, headers, object);
+        return execute(request, object);
     }
     
     /**
@@ -280,13 +344,18 @@
      * Reads an object from S3.
      * @param bucket The name of the bucket where the object lives.
      * @param key The name of the key to use.
-     * @param headers A Map of String to List of Strings representing the http
-     * headers to pass (can be null).
+     * @param headers HTTP headers to pass (can be null).
      */
     public GetResponse get(Bucket bucket, String key, Headers headers)
         throws IOException
     {
-        return new GetResponse(makeRequest(Method.GET, bucket, key, null, headers));
+		HttpMethod method = makeRequest(Method.GET, bucket, key, null, headers);
+		try {
+    		execute(method);
+            return new GetResponse(method);
+		} finally {
+			method.releaseConnection();
+		}
     }
     
     /**
@@ -298,6 +367,39 @@
     }
     
     /**
+     * Reads an object from S3, returning a stream to access the data.
+     * This is preferable when dealing with large objects.
+     * 
+     * @param bucket The name of the bucket where the object lives.
+     * @param key The name of the key to use.
+     * @param headers HTTP headers to pass (can be null).
+     */
+    public GetStreamResponse getStream(Bucket bucket, String key, Headers headers)
+        throws IOException
+    {
+		HttpMethod method = makeRequest(Method.GET, bucket, key, null, headers);
+		boolean ok = false;
+		try {
+    		execute(method);
+    		ok = true;
+            return new GetStreamResponse((GetMethod)method);
+		} finally {
+			if (!ok)
+    			method.releaseConnection();
+		}
+    }
+    
+    /**
+     * Reads an object from S3, returning a stream to access the data.
+     * This is preferable when dealing with large objects.
+     */
+    public GetStreamResponse getStream(Bucket bucket, String key)
+        throws IOException
+    {
+    	return getStream(bucket, key, null);
+    }
+    
+    /**
      * Returns information about an S3 object without loading it.
      * Check {@link Response#isOk()} or {@link Response#isNotFound()} to see if the object exists.
      */
@@ -309,7 +411,9 @@
      * Returns information about an S3 object without loading it.
      */
     public Response head(Bucket bucket, String key, Headers headers) throws IOException {
-        return new Response(makeRequest(Method.HEAD, bucket, key, null, headers));
+		HttpMethod method = makeRequest(Method.HEAD, bucket, key, null, headers);
+		executeRelease(method);
+        return new Response(method);
     }
 
     /**
@@ -322,7 +426,9 @@
     public Response delete(Bucket bucket, String key, Headers headers)
         throws IOException
     {
-        return new Response(makeRequest(Method.DELETE, bucket, key, null, headers));
+		HttpMethod method = makeRequest(Method.DELETE, bucket, key, null, headers);
+		executeRelease(method);
+        return new Response(method);
     }
     
     /**
@@ -342,8 +448,14 @@
     public GetResponse getBucketLogging(Bucket bucket, Headers headers)
         throws IOException
     {
-        Map<String, String> pathArgs = Collections.singletonMap("logging", null);
-        return new GetResponse(makeRequest(Method.GET, bucket, "", pathArgs, headers));
+        Map<String, String> pathArgs = Collections.singletonMap("logging", "");
+		HttpMethod method = makeRequest(Method.GET, bucket, "", pathArgs, headers);
+		try {
+    		execute(method);
+            return new GetResponse(method);
+		} finally {
+			method.releaseConnection();
+		}
     }
 
     /**
@@ -356,13 +468,18 @@
     public Response putBucketLogging(Bucket bucket, String loggingXMLDoc, Headers headers)
         throws IOException
     {
-        Map<String, String> pathArgs = Collections.singletonMap("logging", null);
+        Map<String, String> pathArgs = Collections.singletonMap("logging", "");
         S3Object object = new S3Object(loggingXMLDoc.getBytes(), null);
-        HttpURLConnection request = makeRequest(Method.PUT, bucket, "", pathArgs, headers, object);
-
-        request.setDoOutput(true);
-        request.getOutputStream().write(object.getData() == null ? new byte[] {} : object.getData());
-
+        PutMethod request = (PutMethod) makeRequest(Method.PUT, bucket, "", pathArgs, headers, object);
+        return execute(request, object);
+    }
+    
+    private Response execute(PutMethod request, S3Object object) throws IOException {
+    	if (object.getLength() > EXPECT_SIZE)
+    		request.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, true);
+        // request.setContentChunked(true);
+        request.setRequestEntity(new ByteArrayRequestEntity(object.getData()));
+        executeRelease(request);
         return new Response(request);
     }
 
@@ -391,10 +508,14 @@
         if (key == null)
         	key = "";
         
-        Map<String, String> pathArgs = Collections.singletonMap("acl", null);
-        return new GetResponse(
-                makeRequest(Method.GET, bucket, key, pathArgs, headers)
-            );
+        Map<String, String> pathArgs = Collections.singletonMap("acl", "");
+        HttpMethod method = makeRequest(Method.GET, bucket, key, pathArgs, headers);
+        try {
+            execute(method);
+            return new GetResponse(method);
+        } finally {
+        	method.releaseConnection();
+        }
     }
 
     /**
@@ -421,18 +542,12 @@
     public Response putACL(Bucket bucket, String key, String aclXMLDoc, Headers headers)
         throws IOException
     {
-        S3Object object = new S3Object(aclXMLDoc.getBytes(), null);
-        
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("acl", null);
+        S3Object object = new S3Object(aclXMLDoc);
+        Map<String, String> pathArgs = Collections.singletonMap("acl", "");
+        PutMethod request =
+            (PutMethod) makeRequest(Method.PUT, bucket, key, pathArgs, headers, object);
+        return execute(request, object);
 
-        HttpURLConnection request =
-            makeRequest(Method.PUT, bucket, key, pathArgs, headers, object);
-
-        request.setDoOutput(true);
-        request.getOutputStream().write(object.getData() == null ? new byte[] {} : object.getData());
-
-        return new Response(request);
     }
 
     /**
@@ -441,9 +556,14 @@
     public LocationResponse getLocation(Bucket bucket) 
         throws IOException 
     {
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("location", null);
-        return new LocationResponse(makeRequest(Method.GET, bucket, "", pathArgs, null));
+        Map<String, String> pathArgs = Collections.singletonMap("location", "");
+		HttpMethod method = makeRequest(Method.GET, bucket, "", pathArgs, null);
+		try {
+    		execute(method);
+            return new LocationResponse(method);
+		} finally {
+			method.releaseConnection();
+		}
     }
         
     
@@ -455,7 +575,13 @@
     public ListAllBucketsResponse listAllBuckets(Headers headers)
         throws IOException
     {
-        return new ListAllBucketsResponse(makeRequest(Method.GET, null, "", null, headers));
+		HttpMethod method = makeRequest(Method.GET, null, "", null, headers);
+		try {
+    		execute(method);
+            return new ListAllBucketsResponse(method);
+		} finally {
+			method.releaseConnection();
+		}
     }
     
     /**
@@ -467,7 +593,7 @@
     }
     
     /**
-     * Make a new HttpURLConnection without passing an S3Object parameter. 
+     * Make a new HttpMethod without passing an S3Object parameter. 
      * Use this method for key operations that do require arguments
      * @param method The method to invoke
      * @param bucketName the bucket this request is for
@@ -477,27 +603,27 @@
      * @return
      * @throws IOException
      */
-    private HttpURLConnection makeRequest(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers)
+    private HttpMethod makeRequest(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers)
         throws IOException
     {
         return makeRequest(method, bucket, key, pathArgs, headers, null);
     }
 
-    private HttpURLConnection makeRequest(Method method, Bucket bucket) throws IOException {
+    private HttpMethod makeRequest(Method method, Bucket bucket) throws IOException {
 		return makeRequest(method, bucket, null);
 	}
 
-    private HttpURLConnection makeRequest(Method method, Bucket bucket, Headers headers) throws IOException {
+    private HttpMethod makeRequest(Method method, Bucket bucket, Headers headers) throws IOException {
     	return makeRequest(method, bucket, null, headers);
 	}
 
-    private HttpURLConnection makeRequest(Method method, Bucket bucket, Map<String, String> pathArgs, Headers headers) throws IOException
+    private HttpMethod makeRequest(Method method, Bucket bucket, Map<String, String> pathArgs, Headers headers) throws IOException
     {
     	return makeRequest(method, bucket, "", pathArgs, headers);
 	}
 
     /**
-     * Make a new HttpURLConnection.
+     * Make a new HttpMethod.
      * @param method The HTTP method to use (GET, PUT, DELETE)
      * @param bucketNamePattern The bucket name this request affects
      * @param key The key this request is for, not encoded
@@ -506,89 +632,81 @@
      * headers to pass (can be null).
      * @param object The S3Object that is to be written (can be null).
      */
-    private HttpURLConnection makeRequest(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers,
+    private HttpMethod makeRequest(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers,
                                           S3Object object)
         throws IOException
     {
-    	String enckey = UrlEncoder.encode(key);
-        // build the domain based on the calling format
-        URL url = this.callingFormat.getURL(this.isSecure, server, this.port, bucket, enckey, pathArgs);
-        
-        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
-        connection.setRequestMethod(method.name());
-
-        // subdomain-style urls may encounter http redirects.  
-        // Ensure that redirects are supported.
-        if (!connection.getInstanceFollowRedirects()
-            && callingFormat.supportsLocatedBuckets())
-            throw new RuntimeException("HTTP redirect support required.");
-
-        addHeaders(connection, headers);
+    	HttpMethod httpMethod = method.createHttpMethod();
+    	
+        URI uri = this.callingFormat.getURI(this.isSecure, server, this.port, bucket, key, pathArgs);
+    	
+    	httpMethod.setURI(uri);
+        addHeaders(httpMethod, headers);
         if (object != null)
-        	addMetadataHeaders(connection, object.getMetadata());
-        addAuthHeader(connection, method, bucket, enckey, pathArgs);
-
-        return connection;
+        	addMetadataHeaders(httpMethod, object.getMetadata());
+        addAuthHeader(httpMethod, method, bucket, key, pathArgs);
+        return httpMethod;
     }
 
     /**
-     * Add the given headers to the HttpURLConnection.
-     * @param connection The HttpURLConnection to which the headers will be added.
+     * Add the given headers to the HttpMethod.
+     * @param httpMethod The HttpMethod to which the headers will be added.
      * @param headers A Map of String to List of Strings representing the http
      * headers to pass (can be null).
      */
-    private void addHeaders(HttpURLConnection connection, Headers headers) {
-        addHeaders(connection, headers, "");
+    private void addHeaders(HttpMethod httpMethod, Headers headers) {
+        addHeaders(httpMethod, headers, "");
     }
 
     /**
-     * Add the given metadata fields to the HttpURLConnection.
-     * @param connection The HttpURLConnection to which the headers will be added.
+     * Add the given metadata fields to the HttpMethod.
+     * @param httpMethod The HttpMethod to which the headers will be added.
      * @param metadata A Map of String to List of Strings representing the s3
      * metadata for this resource.
      */
-    private void addMetadataHeaders(HttpURLConnection connection, Headers metadata) {
-        addHeaders(connection, metadata, Headers.METADATA_PREFIX);
+    private void addMetadataHeaders(HttpMethod httpMethod, Headers metadata) {
+        addHeaders(httpMethod, metadata, Headers.METADATA_PREFIX);
     }
 
     /**
-     * Add the given headers to the HttpURLConnection with a prefix before the keys.
-     * @param connection The HttpURLConnection to which the headers will be added.
+     * Add the given headers to the HttpMethod with a prefix before the keys.
+     * @param httpMethod The HttpMethod to which the headers will be added.
      * @param headers A Map of String to List of Strings representing the http
      * headers to pass (can be null).
      * @param prefix The string to prepend to each key before adding it to the connection.
      */
-    private void addHeaders(HttpURLConnection connection, Headers headers, String prefix) {
+    private void addHeaders(HttpMethod httpMethod, Headers headers, String prefix) {
         if (headers != null) {
         	for (Map.Entry<String, List<String>> me : headers.getHeaders().entrySet()) {
                 String key = me.getKey();
                 for (String value : me.getValue()) {
-                    connection.addRequestProperty(prefix + key, value);
+                    httpMethod.addRequestHeader(prefix + key, value);
                 }
             }
         }
     }
 
     /**
-     * Add the appropriate Authorization header to the HttpURLConnection.
-     * @param connection The HttpURLConnection to which the header will be added.
+     * Add the appropriate Authorization header to the HttpMethod.
+     * @param httpMethod The HttpMethod to which the header will be added.
      * @param method The HTTP method to use (GET, PUT, DELETE)
      * @param bucket the bucket name this request is for
      * @param key the key this request is for (not URL encoded)
      * @param pathArgs path arguments which are part of this request
      */
-    private void addAuthHeader(HttpURLConnection connection, Method method, Bucket bucket, String enckey, Map<String, String> pathArgs) {
-        if (connection.getRequestProperty("Date") == null) {
-            connection.setRequestProperty("Date", httpDate());
+    private void addAuthHeader(HttpMethod httpMethod, Method method, Bucket bucket, String key, Map<String, String> pathArgs) {
+        if (httpMethod.getRequestHeader("Date") == null) {
+            httpMethod.setRequestHeader("Date", httpDate());
         }
-        if (connection.getRequestProperty("Content-Type") == null) {
-            connection.setRequestProperty("Content-Type", "");
+        if (httpMethod.getRequestHeader("Content-Type") == null) {
+            httpMethod.setRequestHeader("Content-Type", "");
         }
 
-        Headers prop = new Headers(connection.getRequestProperties());
+        Headers prop = new Headers(httpMethod.getRequestHeaders());
+        String enckey = UrlEncoder.encode(key);
         String canonicalString = CanonicalString.make(method, bucket, enckey, pathArgs, prop);
         String encodedCanonical = CanonicalString.encode(this.awsSecretAccessKey, canonicalString, false);
-        connection.setRequestProperty("Authorization",
+        httpMethod.setRequestHeader("Authorization",
                                       "AWS " + this.awsAccessKeyId + ":" + encodedCanonical);
     }
 
@@ -603,6 +721,13 @@
     }
     
     /**
+     * Shuts down any managed or pooled connections.
+     */
+    public void shutdown() {
+    	connectionManager.shutdown();
+    }
+    
+    /**
      * Returns a debug string.
      */
     @Override

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/GetResponse.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/GetResponse.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/GetResponse.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -12,25 +12,22 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.HttpURLConnection;
 
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodBase;
+
 /**
- * A Response object returned from AWSAuthConnection.get(). Exposes the
- * attribute object, which represents the retrieved object.
+ * Returned by the {@link Connection#get(Bucket, String)} methods.
  */
 public class GetResponse extends Response {
 
 	private S3Object object;
 
-	/**
-	 * Pulls a representation of an S3Object out of the HttpURLConnection
-	 * response.
-	 */
-	GetResponse(HttpURLConnection connection) throws IOException {
-		super(connection);
-		if (connection.getResponseCode() < 400) {
-			int len = connection.getContentLength();
-			byte[] body = slurpInputStream(connection.getInputStream(), len);
+	GetResponse(HttpMethod method) throws IOException {
+		super(method);
+		if (getResponseCode() < 400) {
+			long len = ((HttpMethodBase)method).getResponseContentLength();
+			byte[] body = toByteArray(method.getResponseBodyAsStream(), len);
 			object = new S3Object(body, getHeaders().extractMetadata());
 		}
 	}
@@ -38,22 +35,21 @@
 	/**
 	 * Read the input stream and dump it all into a big byte array
 	 */
-	private static byte[] slurpInputStream(InputStream stream, int len) throws IOException {
-		
+	private static byte[] toByteArray(InputStream stream, long len) throws IOException {
 		final int chunkSize = 4 * 1024;
 		byte[] buf = new byte[chunkSize];
-		ByteArrayOutputStream byteStream;
-		if (len > 0) {
-			byteStream = new ByteArrayOutputStream(len);
+		ByteArrayOutputStream baos;
+		if (len != -1) {
+			baos = new ByteArrayOutputStream((int)len);
 		} else {
-			byteStream = new ByteArrayOutputStream(chunkSize);
+			baos = new ByteArrayOutputStream(chunkSize);
 		}
 
 		int count;
 		while ((count = stream.read(buf)) != -1)
-			byteStream.write(buf, 0, count);
+			baos.write(buf, 0, count);
 
-		return byteStream.toByteArray();
+		return baos.toByteArray();
 	}
 
 	/**

Added: amazon-s3/trunk/src/main/java/com/amazon/s3/GetStreamResponse.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/GetStreamResponse.java	                        (rev 0)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/GetStreamResponse.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -0,0 +1,67 @@
+//  This software code is made available "AS IS" without warranties of any
+//  kind.  You may copy, display, modify and redistribute the software
+//  code either by itself or as incorporated into your code; provided that
+//  you do not remove any proprietary notices.  Your use of this software
+//  code is at your own risk and you waive any claim against Amazon
+//  Digital Services, Inc. or its affiliates with respect to your use of
+//  this software code. (c) 2006 Amazon Digital Services, Inc. or its
+//  affiliates.
+
+package com.amazon.s3;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.httpclient.methods.GetMethod;
+
+/**
+ * A Response object returned from Connection.getStream(). 
+ * This response, once read, must be released by calling {@link #release()}.
+ */
+public class GetStreamResponse extends Response {
+
+	private GetMethod method;
+
+	/**
+	 * Pulls a representation of an S3Object out of the HttpURLConnection
+	 * response.
+	 */
+	GetStreamResponse(GetMethod method) throws IOException {
+		super(method);
+		this.method = method;
+	}
+	
+	/**
+	 * Returns an input stream for the content.
+	 * Call {@link #release}) after done reading.
+	 * @throws IOException 
+	 */
+	public InputStream getInputStream() throws IOException {
+		InputStream body = method.getResponseBodyAsStream();
+		if (body == null)
+			throw new IllegalStateException("body null");
+		return body;
+	}
+	
+	/**
+	 * Returns the content length, if known. 
+	 */
+	public long getLength() {
+		return method.getResponseContentLength();
+	}
+	
+	/**
+	 * Releases this connection.
+	 */
+	public void release() {
+		method.releaseConnection();
+	}
+
+	/**
+	 * Returns a debug string.
+	 */
+	@Override
+	public String toString() {
+		return super.toString() + " method=" + method;
+	}
+}

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/Headers.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/Headers.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/Headers.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -7,6 +7,8 @@
 import java.util.Map;
 import java.util.TreeMap;
 
+import org.apache.commons.httpclient.Header;
+
 /**
  * HTTP header wrapper.
  * 
@@ -40,6 +42,13 @@
     	this(new HashMap<String, List<String>>());
     }
 	
+	public Headers(Header[] requestHeaders) {
+		this();
+		for (Header h : requestHeaders) {
+			put(h.getName(), h.getValue());
+		}
+	}
+
 	/**
 	 * Adds a header.
 	 */

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/ListAllBucketsResponse.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/ListAllBucketsResponse.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/ListAllBucketsResponse.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -10,7 +10,6 @@
 package com.amazon.s3;
 
 import java.io.IOException;
-import java.net.HttpURLConnection;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -18,6 +17,7 @@
 import java.util.List;
 import java.util.TimeZone;
 
+import org.apache.commons.httpclient.HttpMethod;
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.DefaultHandler;
 
@@ -28,8 +28,8 @@
 	
 	private List<Bucket> entries;
 
-	ListAllBucketsResponse(HttpURLConnection connection) throws IOException {
-		super(connection);
+	ListAllBucketsResponse(HttpMethod method) throws IOException {
+		super(method);
 		if (isOk()) {
     		entries = new ArrayList<Bucket>();
 			parse(new ListAllMyBucketsHandler());

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/ListResponse.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/ListResponse.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/ListResponse.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -10,13 +10,13 @@
 package com.amazon.s3;
 
 import java.io.IOException;
-import java.net.HttpURLConnection;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.TimeZone;
 
+import org.apache.commons.httpclient.HttpMethod;
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.DefaultHandler;
 
@@ -43,8 +43,8 @@
 
     private List<CommonPrefixEntry> commonPrefixEntries = null;
 
-    ListResponse(HttpURLConnection connection) throws IOException {
-        super(connection);
+    ListResponse(HttpMethod method) throws IOException {
+        super(method);
         if (isOk()) {
         	parse(new ListBucketHandler());
         }
@@ -238,13 +238,13 @@
     @Override
     public String toString() {
     	return super.toString() + 
+    		" entries=" + entries +
     		" name=" + name +
     		" prefix=" + prefix +
     		" marker=" + marker +
     		" maxKeys=" + maxKeys +
     		" isTruncated=" + isTruncated +
     		" nextMarker=" + nextMarker +
-    		" entries=" + entries +
     		" prefix=" + commonPrefixEntries;
     }
 

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/LocationResponse.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/LocationResponse.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/LocationResponse.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -10,8 +10,8 @@
 package com.amazon.s3;
 
 import java.io.IOException;
-import java.net.HttpURLConnection;
 
+import org.apache.commons.httpclient.HttpMethod;
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.DefaultHandler;
 
@@ -27,8 +27,8 @@
     /**
      * Parse the response to a ?location query.
      */
-    LocationResponse(HttpURLConnection connection) throws IOException {
-        super(connection);
+    LocationResponse(HttpMethod method) throws IOException {
+        super(method);
         if (isOk()) {
         	parse(new LocationResponseHandler());
         }

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/Method.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/Method.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/Method.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -1,10 +1,26 @@
 package com.amazon.s3;
 
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+
 /**
  * HTTP Methods.
  * 
  * @author Elias Ross
  */
 public enum Method {
-	GET, PUT, DELETE, HEAD
+	GET, PUT, DELETE, HEAD;
+	
+	public HttpMethod createHttpMethod() {
+		switch (this) {
+        case PUT: return new PutMethod();
+        case GET: return new GetMethod(); 
+        case DELETE: return new DeleteMethod();
+        case HEAD: return new HeadMethod();
+		}
+		throw new Error();
+	}
 }
\ No newline at end of file

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/QueryGenerator.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/QueryGenerator.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/QueryGenerator.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -9,12 +9,13 @@
 
 package com.amazon.s3;
 
-import java.net.MalformedURLException;
-import java.net.URL;
+import java.io.IOException;
 import java.security.Key;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.commons.httpclient.URI;
+
 /**
  * Generates URL Query Strings that can be used to perform operations.
  * These parameters include an expiration date, so that
@@ -56,7 +57,7 @@
                                     boolean isSecure, String server, int port)
     {
         this(awsAccessKeyId, awsSecretAccessKey, isSecure, server,
-             port, CallingFormat.getSubdomainCallingFormat());
+             port, CallingFormat.SUBDOMAIN);
     }
    
     public QueryGenerator(String awsAccessKeyId, String awsSecretAccessKey, 
@@ -96,32 +97,32 @@
         expires = null;
     }
 
-    public URL create(Bucket bucket, Headers headers)
+    public URI create(Bucket bucket, Headers headers)
     {
         // validate bucket name
         if (!bucket.validateName(callingFormat))
             throw new IllegalArgumentException("Invalid Bucket Name: "+bucket);
 
-        return generateURL(Method.PUT, bucket, "", headers);
+        return generateURI(Method.PUT, bucket, "", headers);
     }
 
-    public URL list(Bucket bucket, String prefix, String marker,
+    public URI list(Bucket bucket, String prefix, String marker,
                              Integer maxKeys, Headers headers){
         return list(bucket, prefix, marker, maxKeys, null, headers);
     }
 
-    public URL list(Bucket bucket, String prefix, String marker,
+    public URI list(Bucket bucket, String prefix, String marker,
                              Integer maxKeys, String delimiter, Headers headers)
     { 
-        return generateURL(Method.GET, bucket, headers);
+        return generateURI(Method.GET, bucket, headers);
     }
 
-	public URL delete(Bucket bucket, Headers headers)
+	public URI delete(Bucket bucket, Headers headers)
     {
-        return generateURL(Method.DELETE, bucket, headers);
+        return generateURI(Method.DELETE, bucket, headers);
     }
 
-    public URL put(Bucket bucket, String key, S3Object object, Headers headers) {
+    public URI put(Bucket bucket, String key, S3Object object, Headers headers) {
         Headers metadata = null;
         if (object != null) {
             metadata = object.getMetadata();
@@ -130,63 +131,61 @@
         	headers = new Headers();
         }
 
-        return generateURL(Method.PUT, bucket, UrlEncoder.encode(key), headers.mergeMetadata(metadata));
+        return generateURI(Method.PUT, bucket, key, headers.mergeMetadata(metadata));
     }
 
-    public URL get(Bucket bucket, String key, Headers headers)
+    public URI get(Bucket bucket, String key, Headers headers)
     {
-        return generateURL(Method.GET, bucket, UrlEncoder.encode(key), headers);
+        return generateURI(Method.GET, bucket, key, headers);
     }
 
-    public URL delete(Bucket bucket, String key, Headers headers)
+    public URI delete(Bucket bucket, String key, Headers headers)
     {
-        return generateURL(Method.DELETE, bucket, UrlEncoder.encode(key), headers);
+        return generateURI(Method.DELETE, bucket, key, headers);
     }
+    
+    private Map<String, String> map(String name) {
+    	HashMap<String, String> map = new HashMap<String, String>();
+    	map.put(name, "");
+    	return map;
+    }
 
-    public URL getBucketLogging(Bucket bucket, Headers headers) {
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("logging", null);
-        return generateURL(Method.GET, bucket, "", pathArgs, headers);
+    public URI getBucketLogging(Bucket bucket, Headers headers) {
+        return generateURI(Method.GET, bucket, "", map("logging"), headers);
     }
 
-    public URL putBucketLogging(Bucket bucket, String loggingXMLDoc, Headers headers) {
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("logging", null);
-        return generateURL(Method.PUT, bucket, "", pathArgs, headers);
+    public URI putBucketLogging(Bucket bucket, Headers headers) {
+        return generateURI(Method.PUT, bucket, "", map("logging"), headers);
     }
 
-    public URL getACL(Bucket bucket, Headers headers) {
+    public URI getACL(Bucket bucket, Headers headers) {
         return getACL(bucket, "", headers);
     }
 
-    public URL getACL(Bucket bucket, String key, Headers headers)
+    public URI getACL(Bucket bucket, String key, Headers headers)
     {
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("acl", null);
-        return generateURL(Method.GET, bucket, UrlEncoder.encode(key), pathArgs, headers);
+        return generateURI(Method.GET, bucket, key, map("acl"), headers);
     }
 
-    public URL putACL(Bucket bucket, String aclXMLDoc, Headers headers) {
-        return putACL(bucket, "", aclXMLDoc, headers);
+    public URI putACL(Bucket bucket, Headers headers) {
+        return putACL(bucket, "", headers);
     }
 
-    public URL putACL(Bucket bucket, String key, String aclXMLDoc, Headers headers)
+    public URI putACL(Bucket bucket, String key, Headers headers)
     {
-        Map<String, String> pathArgs = new HashMap<String, String>();
-        pathArgs.put("acl", null);
-        return generateURL(Method.PUT, bucket, UrlEncoder.encode(key), pathArgs, headers);
+        return generateURI(Method.PUT, bucket, key, map("acl"), headers);
     }
 
-    public URL listAllBuckets(Headers headers)
+    public URI listAllBuckets(Headers headers)
     {
-        return generateURL(Method.GET, headers);
+        return generateURI(Method.GET, headers);
     }
 
-	public URL listAllBuckets() {
+	public URI listAllBuckets() {
 		return listAllBuckets(null);
 	}
 
-	public String makeBareURL(Bucket bucket, String key) {
+	public String makeBareURI(Bucket bucket, String key) {
         StringBuilder buffer = new StringBuilder();
         if (this.isSecure) {
             buffer.append("https://");
@@ -200,19 +199,19 @@
     }
 
     @SuppressWarnings("unchecked")
-	private URL generateURL(Method method, Bucket bucket, String key, Headers headers) {
-		return generateURL(method, bucket, key, new HashMap(), headers);
+	private URI generateURI(Method method, Bucket bucket, String key, Headers headers) {
+		return generateURI(method, bucket, key, new HashMap(), headers);
 	}
 
-    private URL generateURL(Method method, Bucket bucket, Headers headers) {
-		return generateURL(method, bucket, "", headers);
+    private URI generateURI(Method method, Bucket bucket, Headers headers) {
+		return generateURI(method, bucket, "", headers);
 	}
 
-	private URL generateURL(Method method, Headers headers) {
-		return generateURL(method, null, headers);
+	private URI generateURI(Method method, Headers headers) {
+		return generateURI(method, null, headers);
 	}
 
-    private URL generateURL(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers) 
+    private URI generateURI(Method method, Bucket bucket, String key, Map<String, String> pathArgs, Headers headers) 
     {
         long expires = 0L;
         if (this.expiresIn != null) {
@@ -226,17 +225,18 @@
         // convert to seconds
         expires /= 1000;
 
-        String canonicalString = CanonicalString.make(method, bucket, key, pathArgs, headers, ""+expires);
-        String encodedCanonical = CanonicalString.encode(this.awsSecretAccessKey, canonicalString, true);
+        String enckey = UrlEncoder.encode(key);
+        String canonicalString = CanonicalString.make(method, bucket, enckey, pathArgs, headers, ""+expires);
+        String encodedCanonical = CanonicalString.encode(this.awsSecretAccessKey, canonicalString, false);
 
         pathArgs.put("Signature", encodedCanonical);
         pathArgs.put("Expires", Long.toString(expires));
         pathArgs.put("AWSAccessKeyId", this.awsAccessKeyId);
         
         try {
-            return this.callingFormat.getURL(this.isSecure, server, port, bucket, key, pathArgs);
-        } catch (MalformedURLException e) {
-        	throw new IllegalStateException("Unable to generate URL " + e);
+            return this.callingFormat.getURI(this.isSecure, server, port, bucket, key, pathArgs);
+        } catch (IOException e) {
+        	throw new IllegalStateException("Unable to generate URI " + e);
         }
     }
 

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/Response.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/Response.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/Response.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -10,10 +10,10 @@
 package com.amazon.s3;
 
 import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.util.HashMap;
-import java.util.Map;
+import java.io.InputStream;
 
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpMethod;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
@@ -28,35 +28,35 @@
  */
 public class Response {
 
-	private HttpURLConnection connection;
+	private HttpMethod method;
 
-	Response(HttpURLConnection connection) throws IOException {
-		this.connection = connection;
+	Response(HttpMethod method) {
+		this.method = method;
 	}
 
 	/**
 	 * Returns the HTTP response code.
 	 */
 	public int getResponseCode() throws IOException {
-		return connection.getResponseCode();
+		return method.getStatusCode();
 	}
 
 	/**
 	 * Returns the HTTP response message.
 	 */
 	public String getResponseMessage() throws IOException {
-		return connection.getResponseMessage();
+		return method.getStatusText();
 	}
 
 	/**
 	 * Returns content length of the response.
 	 */
 	public int getContentLength() {
-		return connection.getContentLength();
+		return -1; // TODO
 	}
 
 	/**
-	 * Throws an IllegalStateException if not 200 OK.
+	 * Throws an IllegalStateException if not 200-level OK.
 	 */
 	public void assertOk() {
         if (isOk())
@@ -68,7 +68,10 @@
 	 * Returns an HTTP header field from the response.
 	 */
 	public String getHeaderField(String field) {
-		return connection.getHeaderField(field);
+		Header header = method.getResponseHeader(field);
+		if (header == null)
+			return null;
+		return header.getValue();
 	}
 
 	/**
@@ -76,7 +79,7 @@
 	 * Prefer caching the return value.
 	 */
 	public Headers getHeaders() {
-		return new Headers(connection.getHeaderFields());
+		return new Headers(method.getResponseHeaders());
 	}
 
 	/**
@@ -84,7 +87,7 @@
 	 */
 	public boolean isOk() {
 		try {
-			int code = connection.getResponseCode();
+			int code = getResponseCode();
 			return code == 200 || code == 204;
 		} catch (IOException e) {
 			return false;
@@ -96,7 +99,7 @@
 	 */
 	public boolean isNotFound() {
 		try {
-			return connection.getResponseCode() == 404;
+			return getResponseCode() == 404;
 		} catch (IOException e) {
 			return false;
 		}
@@ -115,30 +118,6 @@
 		}
 	}
 
-	static Map<String, String> paramsForListOptions(String prefix,
-			String marker, Integer maxKeys) {
-		return paramsForListOptions(prefix, marker, maxKeys, null);
-	}
-
-	static Map<String, String> paramsForListOptions(String prefix,
-			String marker, Integer maxKeys, String delimiter) {
-
-		Map<String, String> argParams = new HashMap<String, String>();
-		// these three params must be url encoded
-		if (prefix != null)
-			argParams.put("prefix", UrlEncoder.encode(prefix));
-		if (marker != null)
-			argParams.put("marker", UrlEncoder.encode(marker));
-		if (delimiter != null)
-			argParams.put("delimiter", UrlEncoder.encode(delimiter));
-
-		if (maxKeys != null)
-			argParams.put("max-keys", Integer.toString(maxKeys.intValue()));
-
-		return argParams;
-
-	}
-
 	/**
 	 * Parses the response.
 	 */
@@ -162,7 +141,9 @@
 
 			};
 			xr.setErrorHandler(eh);
-			xr.parse(new InputSource(connection.getInputStream()));
+			InputStream is = method.getResponseBodyAsStream();
+			xr.parse(new InputSource(is));
+			is.close();
 		} catch (SAXException e) {
 			throw new RuntimeException("Unexpected error parsing ListBucket xml", e);
 		}

Modified: amazon-s3/trunk/src/main/java/com/amazon/s3/S3Object.java
===================================================================
--- amazon-s3/trunk/src/main/java/com/amazon/s3/S3Object.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/main/java/com/amazon/s3/S3Object.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -40,6 +40,13 @@
 	public S3Object(byte[] data) {
 		this(data, null);
 	}
+	
+    /**
+     * Constructs a new S3Object; data is converted to bytes.
+     */
+	public S3Object(String data) {
+		this(data.getBytes());
+	}
 
 	/**
 	 * Returns data.
@@ -49,6 +56,13 @@
 	}
 
 	/**
+	 * Returns the length of data.
+	 */
+	public int getLength() {
+		return data.length;
+	}
+	
+	/**
 	 * Returns metadata, unmodifiable.
 	 */
 	public Headers getMetadata() {
@@ -65,5 +79,5 @@
 	        " data.length=" + this.data.length + 
 	        " metadata=" + this.metadata + "]";
 	}
-	
+
 }

Modified: amazon-s3/trunk/src/test/java/com/amazon/s3/S3Test.java
===================================================================
--- amazon-s3/trunk/src/test/java/com/amazon/s3/S3Test.java	2008-02-07 20:21:24 UTC (rev 5330)
+++ amazon-s3/trunk/src/test/java/com/amazon/s3/S3Test.java	2008-02-07 22:57:14 UTC (rev 5331)
@@ -9,14 +9,17 @@
 //  affiliates.
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
-import java.net.URL;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.URI;
+import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -47,9 +50,9 @@
         // subdomain: http://bucket.s3.amazonaws.com/key
         // testing pure vanity domains (http://<vanity domain>/key) is not covered here
         // but is possible with some assitional setup 
-        test(CallingFormat.getSubdomainCallingFormat(), Connection.LOCATION_DEFAULT, true, Connection.DEFAULT_HOST);
-        test(CallingFormat.getPathCallingFormat(), Connection.LOCATION_DEFAULT, true, Connection.DEFAULT_HOST);
-        test(CallingFormat.getSubdomainCallingFormat(), Connection.LOCATION_EU, true, Connection.DEFAULT_HOST);
+        test(CallingFormat.SUBDOMAIN, Connection.LOCATION_DEFAULT, false, Connection.DEFAULT_HOST);
+        test(CallingFormat.PATH, Connection.LOCATION_DEFAULT, true, Connection.DEFAULT_HOST);
+        test(CallingFormat.SUBDOMAIN, Connection.LOCATION_EU, true, Connection.DEFAULT_HOST);
     }
 
     private void test(CallingFormat format, String location, boolean secure, String server) throws Exception
@@ -63,17 +66,17 @@
             new QueryGenerator(awsAccessKeyId, awsSecretAccessKey, secure, server, format);
         
         Response response = conn.create(bucket, location, null);
-        assertEquals(
-                "couldn't create bucket",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        response.assertOk();
 
-        ListResponse listBucketResponse = conn.list(bucket, null, null, null, null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
-        assertEquals("list wasn't empty", 0, listBucketResponse.getEntries().size());
+        ListResponse listBucketResponse = conn.list(bucket);
+        listBucketResponse.assertOk();
+        for (Entry entry : listBucketResponse.getEntries()) {
+        	Response delete = conn.delete(bucket, entry.getKey());
+        	System.out.println("DEL " + delete);
+        	delete.assertOk();
+        }
+        listBucketResponse = conn.list(bucket);
+        assertEquals("list wasn't empty " + listBucketResponse, 0, listBucketResponse.getEntries().size());
         System.out.println(listBucketResponse);
         verifyBucketResponseParameters(listBucketResponse, bucket, "", "", UnspecifiedMaxKeys, null, false, null);
 
@@ -84,79 +87,54 @@
         final String innerKey = "test/inner.txt";
         final String lastKey = "z-last-key.txt";
 
-        response = conn.put(bucket, key, new S3Object(text.getBytes()));
-        assertEquals(
-                "couldn't put simple object",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        response = conn.put(bucket, key, new S3Object(text));
+        response.assertOk();
 
-        response = conn.put(bucket, innerKey, new S3Object(text.getBytes()), null);
-        assertEquals(
-                "couldn't put simple object",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        response = conn.put(bucket, innerKey, new S3Object(text));
+        response.assertOk();
 
-        response = conn.put(bucket, lastKey, new S3Object(text.getBytes()), null);
-        assertEquals(
-                "couldn't put simple object",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        response = conn.put(bucket, lastKey, new S3Object(text));
+        response.assertOk();
 
         // plain list
-        listBucketResponse = conn.list(bucket, null, null, null, null);
-        assertEquals(
-                "couldn't get list",
-                listBucketResponse.getResponseCode(),
-                HttpURLConnection.HTTP_OK);
+        listBucketResponse = conn.list(bucket);
+        listBucketResponse.assertOk();
         assertEquals("Unexpected list size", 3, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 0, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "", "", UnspecifiedMaxKeys, null, false, null);
+        
+        System.out.println("LIST " + listBucketResponse.getEntries());
 
         // root "directory"
         listBucketResponse = conn.list(bucket, null, null, null, "/", null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
-        assertEquals("Unexpected list size", 2, listBucketResponse.getEntries().size());
+        listBucketResponse.assertOk();
+        assertEquals("Unexpected list size " + listBucketResponse, 2, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 1, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "", "", UnspecifiedMaxKeys, "/", false, null);
 
         // root "directory" with a max-keys of "1"
         listBucketResponse = conn.list(bucket, null, null, 1, "/", null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
+        listBucketResponse.assertOk();
         assertEquals("Unexpected list size", 1, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 0, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "", "", 1, "/", true, "example.txt");
 
         // root "directory" with a max-keys of "2"
         listBucketResponse = conn.list(bucket, null, null, 2, "/", null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
+        listBucketResponse.assertOk();
         assertEquals("Unexpected list size", 1, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 1, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "", "", 2, "/", true, "test/");
         String marker = listBucketResponse.getNextMarker();
         listBucketResponse = conn.list(bucket, null, marker, new Integer( 2 ), "/", null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
+        listBucketResponse.assertOk();
         assertEquals("Unexpected list size", 1, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 0, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "", marker, 2, "/", false, null);
 
         // test "directory"
         listBucketResponse = conn.list(bucket, "test/", null, null, "/", null);
-        assertEquals(
-                "couldn't get list",
-                HttpURLConnection.HTTP_OK,
-                listBucketResponse.getResponseCode());
+        listBucketResponse.assertOk();
         assertEquals("Unexpected list size", 1, listBucketResponse.getEntries().size());
         assertEquals("Unexpected common prefix size", 0, listBucketResponse.getCommonPrefixEntries().size());
         verifyBucketResponseParameters(listBucketResponse, bucket, "test/", "", UnspecifiedMaxKeys, "/", false, null);
@@ -179,24 +157,15 @@
         // end delimiter tests
 
         response = conn.put(bucket, key, new S3Object(text.getBytes(), null), null);
-        assertEquals(
-                "couldn't reput simple object",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        response.assertOk();
 
-        Map<String, List<String>> metadata = new HashMap<String, List<String>>();
-        metadata.put("title", Arrays.asList(new String[] { "title" }));
-        response = conn.put(bucket, key, new S3Object(text.getBytes(), new Headers(metadata)), null);
-        assertEquals(
-                "couldn't put complex object",
-                HttpURLConnection.HTTP_OK,
-                response.getResponseCode());
+        Headers metadata = new Headers();
+        metadata.put("title", "title");
+        response = conn.put(bucket, key, new S3Object(text.getBytes(), metadata), null);
+        response.assertOk();
 
         GetResponse getResponse = conn.get(bucket, key, null);
-        assertEquals(
-                "couldn't get object",
-                getResponse.getResponseCode(),
-                HttpURLConnection.HTTP_OK);
+        getResponse.assertOk();
         assertEquals("didn't get the right data back", text.getBytes(), getResponse.getObject().getData());
         assertEquals("didn't get the right metadata back", 1, getResponse.getObject().getMetadata().size());
         assertEquals(
@@ -207,8 +176,14 @@
                 "didn't get the right content-length",
                 ""+text.length(),
                 getResponse.getHeaderField("Content-Length"));
+        
+        GetStreamResponse streamResponse = conn.getStream(bucket, key);
+        InputStream is = streamResponse.getInputStream();
+        byte b[] = new byte[text.length()];
+        int len = is.read(b);
+        assertEquals("didn't get the right data back " + len, text.getBytes(), b);
+        streamResponse.release();
 
-
         String titleWithSpaces = " \t  title with leading and trailing spaces    ";
         Headers h = new Headers();
         h.put("title", titleWithSpaces);
@@ -345,73 +320,73 @@
                 buckets.size() - 1,
                 listAllMyBucketsResponse.getEntries().size());
 
-        checkURL(
+        checkURI(
                 generator.create(bucket, null),
-                "PUT",
+                Method.PUT,
                 HttpURLConnection.HTTP_OK,
                 "couldn't create bucket");
-        checkURL(
+        checkURI(
                 generator.put(bucket, key, new S3Object("test data".getBytes(), null), null),
-                "PUT",
+                Method.PUT,
                 HttpURLConnection.HTTP_OK,
                 "put object",
                 "test data");
-        checkURL(
+        checkURI(
                 generator.get(bucket, key, null),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "get object");
-        checkURL(
+        checkURI(
                 generator.list(bucket, null, null, null, null),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "list bucket");
-        checkURL(
+        checkURI(
                 generator.listAllBuckets(),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "list all my buckets");
-        checkURL(
+        checkURI(
                 generator.getACL(bucket, key, null),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "get acl");
-        checkURL(
-                generator.putACL(bucket, key, new String(acl), null),
-                "PUT",
+        checkURI(
+                generator.putACL(bucket, key, null),
+                Method.PUT,
                 HttpURLConnection.HTTP_OK,
                 "put acl",
                 new String(acl));
-        checkURL(
+        checkURI(
                 generator.getACL(bucket, null),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "get bucket acl");
-        checkURL(
-                generator.putACL(bucket, new String(bucketACL), null),
-                "PUT",
+        checkURI(
+                generator.putACL(bucket, null),
+                Method.PUT,
                 HttpURLConnection.HTTP_OK,
                 "put bucket acl",
                 new String(bucketACL));
-        checkURL(
+        checkURI(
                 generator.getBucketLogging(bucket, null),
-                "GET",
+                Method.GET,
                 HttpURLConnection.HTTP_OK,
                 "get bucket logging");
-        checkURL(
-                generator.putBucketLogging(bucket, new String(bucketLogging), null),
-                "PUT",
+        checkURI(
+                generator.putBucketLogging(bucket, null),
+                Method.PUT,
                 HttpURLConnection.HTTP_OK,
                 "put bucket logging",
                 new String(bucketLogging));
-        checkURL(
+        checkURI(
                 generator.delete(bucket, key, null),
-                "DELETE",
+                Method.DELETE,
                 HttpURLConnection.HTTP_NO_CONTENT,
                 "delete object");
-        checkURL(
+        checkURI(
                 generator.delete(bucket, null),
-                "DELETE",
+                Method.DELETE,
                 HttpURLConnection.HTTP_NO_CONTENT,
                 "delete bucket");
     }
@@ -461,29 +436,27 @@
         }
     }
 
-    private static void checkURL(URL url, String method, int code, String message)
+    private static void checkURI(URI uri, Method method, int code, String message)
         throws MalformedURLException, IOException
     {
-        checkURL(url, method, code, message, null);
+        checkURI(uri, method, code, message, null);
     }
 
-    private static void checkURL(URL url, String method, int code, String message, String data)
+    private static void checkURI(URI uri, Method method, int code, String message, String data)
         throws MalformedURLException, IOException
     {
         if (data == null) data = "";
 
-        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
-        connection.setRequestMethod(method);
-        if ("PUT".equals(method)) {
-            connection.setRequestProperty("Content-Length", ""+data.getBytes().length);
-            connection.setDoOutput(true);
-            connection.getOutputStream().write(data.getBytes());
-        } else {
-            // HttpURLConnection auto populates Content-Type, which we don't want here
-            connection.setRequestProperty("Content-Type", "");
+        HttpClient client = new HttpClient();
+        HttpMethod httpMethod = method.createHttpMethod();
+        if (method == Method.PUT) {
+        	((EntityEnclosingMethod) httpMethod).setRequestEntity(new StringRequestEntity(data));
         }
+    	httpMethod.setURI(uri);
+    	int response = client.executeMethod(httpMethod);
+    	httpMethod.releaseConnection();
 
-        assertEquals(message, code, connection.getResponseCode());
+        assertEquals(message, code, response);
     }
     
     private void readline() throws IOException {
@@ -546,7 +519,7 @@
         readline();
 
         System.out.println("\nNow try just the url without the query string arguments.  It should fail.\n");
-        System.out.println(generator.makeBareURL(bucket, keyName));
+        System.out.println(generator.makeBareURI(bucket, keyName));
         System.out.print("\npress enter> ");
         readline();
 
@@ -566,14 +539,14 @@
 
         System.out.println("----- anonymous read test -----");
         System.out.println("\nYou should be able to try this in your browser\n");
-        System.out.println(generator.makeBareURL(bucket, keyName + "-public"));
+        System.out.println(generator.makeBareURI(bucket, keyName + "-public"));
         System.out.print("\npress enter> ");
         readline();
         
         System.out.println("----- path style url example -----");
         System.out.println("\nNon-location-constrained buckets can also be specified as part of the url path.  (This was the original url style supported by S3.)");
         System.out.println("\nTry this url out in your browser (it will only be valid for 60 seconds)\n");
-        generator.setCallingFormat(CallingFormat.getPathCallingFormat());
+        generator.setCallingFormat(CallingFormat.PATH);
         // could also have been done like this:
         //  generator = new QueryStringAuthGenerator(awsAccessKeyId, awsSecretAccessKey, true, Utils.DEFAULT_HOST, CallingFormat.getPathCallingFormat());
         generator.setExpiresIn(60 * 1000);

Added: amazon-s3/trunk/src/test/resources/log4j.xml
===================================================================
--- amazon-s3/trunk/src/test/resources/log4j.xml	                        (rev 0)
+++ amazon-s3/trunk/src/test/resources/log4j.xml	2008-02-07 22:57:14 UTC (rev 5331)
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- ===================================================================== -->
+<!--                                                                       -->
+<!--  Log4j Configuration                                                  -->
+<!--                                                                       -->
+<!-- ===================================================================== -->
+
+<!-- $Id: log4j.xml,v 1.2 2008/01/24 17:52:51 rosse Exp $ -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+   <!-- ============================== -->
+   <!-- Append messages to the console -->
+   <!-- ============================== -->
+
+   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+      <param name="Target" value="System.out"/>
+
+      <layout class="org.apache.log4j.PatternLayout">
+         <!-- The default pattern: Date Priority [Category] Message\n -->
+         <param name="ConversionPattern" value="%r %-5p [%c{1}] (%t) %m%n"/>
+      </layout>
+   </appender>
+
+  <logger name="org.apache">
+      <level value="INFO"/>
+  </logger>
+  <logger name="httpclient.wire">
+      <level value="INFO"/>
+  </logger>
+  <logger name="org.apache.commons.httpclient">
+      <level value="INFO"/>
+  </logger>
+
+   <!-- ======================= -->
+   <!-- Setup the Root category -->
+   <!-- ======================= -->
+
+   <root>
+      <priority value="INFO"/>
+      <appender-ref ref="CONSOLE"/>
+   </root>
+
+</log4j:configuration>




More information about the jbosscache-commits mailing list