HTTPCLIENT-719: Clone support for HTTP request and cookie objects

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@659191 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2008-05-22 18:26:53 +00:00
parent a9d307fa91
commit 5cd9d169f2
9 changed files with 222 additions and 8 deletions

View File

@ -1,6 +1,9 @@
Changes since 4.0 Alpha 4
-------------------
* [HTTPCLIENT-719] Clone support for HTTP request and cookie objects.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* [HTTPCLIENT-776] Fixed concurrency issues with AbstractPoolEntry.
Contributed by Sam Berlin <sberlin at gmail.com>

View File

@ -34,6 +34,7 @@ package org.apache.http.client.methods;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.utils.CloneUtils;
import org.apache.http.protocol.HTTP;
/**
@ -66,5 +67,15 @@ abstract class HttpEntityEnclosingRequestBase
Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
}
@Override
public Object clone() throws CloneNotSupportedException {
HttpEntityEnclosingRequestBase clone =
(HttpEntityEnclosingRequestBase) super.clone();
if (this.entity != null) {
clone.entity = (HttpEntity) CloneUtils.clone(this.entity);
}
return clone;
}
}

View File

@ -38,10 +38,13 @@ import java.util.concurrent.locks.ReentrantLock;
import org.apache.http.ProtocolVersion;
import org.apache.http.RequestLine;
import org.apache.http.client.utils.CloneUtils;
import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ConnectionReleaseTrigger;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.message.BasicRequestLine;
import org.apache.http.message.HeaderGroup;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
/**
@ -54,9 +57,9 @@ import org.apache.http.params.HttpProtocolParams;
* @since 4.0
*/
abstract class HttpRequestBase extends AbstractHttpMessage
implements HttpUriRequest, AbortableHttpRequest {
implements HttpUriRequest, AbortableHttpRequest, Cloneable {
private final Lock abortLock;
private Lock abortLock;
private boolean aborted;
@ -159,5 +162,21 @@ abstract class HttpRequestBase extends AbstractHttpMessage
}
}
}
public boolean isAborted() {
return this.aborted;
}
@Override
public Object clone() throws CloneNotSupportedException {
HttpRequestBase clone = (HttpRequestBase) super.clone();
clone.abortLock = new ReentrantLock();
clone.aborted = false;
clone.releaseTrigger = null;
clone.connRequest = null;
clone.headergroup = (HeaderGroup) CloneUtils.clone(this.headergroup);
clone.params = (HttpParams) CloneUtils.clone(this.params);
return clone;
}
}

View File

@ -61,4 +61,20 @@ public interface HttpUriRequest extends HttpRequest {
*/
URI getURI();
/**
* Aborts execution of the request.
*
* @throws UnsupportedOperationException if the abort operation
* is not supported / cannot be implemented.
*/
void abort() throws UnsupportedOperationException;
/**
* Tests if the request execution has been aborted.
*
* @return <code>true</code> if the request execution has been aborted,
* <code>false</code> otherwise.
*/
boolean isAborted();
}

View File

@ -0,0 +1,75 @@
/*
* $HeadURL:$
* $Revision:$
* $Date:$
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* A collection of utilities to workaround limitations of Java clone framework.
*/
public class CloneUtils {
public static Object clone(final Object obj) throws CloneNotSupportedException {
if (obj == null) {
return null;
}
if (obj instanceof Cloneable) {
Class<?> clazz = obj.getClass ();
Method m;
try {
m = clazz.getMethod("clone", (Class[]) null);
} catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
try {
return m.invoke(obj, (Object []) null);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof CloneNotSupportedException) {
throw ((CloneNotSupportedException) cause);
} else {
throw new Error("Unexpected exception", cause);
}
} catch (IllegalAccessException ex) {
throw new IllegalAccessError(ex.getMessage());
}
} else {
throw new CloneNotSupportedException();
}
}
/**
* This class should not be instantiated.
*/
private CloneUtils() {
}
}

View File

@ -137,6 +137,14 @@ class RequestWrapper extends AbstractHttpMessage implements HttpUriRequest {
return new BasicRequestLine(method, uritext, ver);
}
public void abort() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public boolean isAborted() {
return false;
}
public HttpRequest getOriginal() {
return this.original;
}

View File

@ -57,7 +57,7 @@ import org.apache.http.cookie.SetCookie;
*
* @version $Revision$
*/
public class BasicClientCookie implements SetCookie, ClientCookie {
public class BasicClientCookie implements SetCookie, ClientCookie, Cloneable {
/**
* Default Constructor taking a name and a value. The value may be null.
@ -311,6 +311,13 @@ public class BasicClientCookie implements SetCookie, ClientCookie {
public boolean containsAttribute(final String name) {
return this.attribs.get(name) != null;
}
@Override
public Object clone() throws CloneNotSupportedException {
BasicClientCookie clone = (BasicClientCookie) super.clone();
clone.attribs = new HashMap<String, String>(this.attribs);
return clone;
}
@Override
public String toString() {
@ -342,7 +349,7 @@ public class BasicClientCookie implements SetCookie, ClientCookie {
private final String name;
/** Cookie attributes as specified by the origin server */
private final Map<String, String> attribs;
private Map<String, String> attribs;
/** Cookie value */
private String value;

View File

@ -89,6 +89,13 @@ public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2
public boolean isExpired(final Date date) {
return this.discard || super.isExpired(date);
}
@Override
public Object clone() throws CloneNotSupportedException {
BasicClientCookie2 clone = (BasicClientCookie2) super.clone();
clone.ports = this.ports.clone();
return clone;
}
}

View File

@ -1,7 +1,7 @@
/*
* $HeadURL:$
* $Revision:$
* $Date:$
* $HeadURL$
* $Revision$
* $Date$
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -30,10 +30,15 @@
package org.apache.http.client.methods;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.http.Header;
import org.apache.http.HttpVersion;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.LangUtils;
import junit.framework.Test;
import junit.framework.TestCase;
@ -72,4 +77,67 @@ public class TestHttpRequestBase extends TestCase {
assertEquals("/", httpget.getRequestLine().getUri());
}
public void testCloneBasicRequests() throws Exception {
HttpGet httpget = new HttpGet("http://host/path");
httpget.addHeader("h1", "this header");
httpget.addHeader("h2", "that header");
httpget.addHeader("h3", "all sorts of headers");
httpget.getParams().setParameter("p1", Integer.valueOf(1));
httpget.getParams().setParameter("p2", "whatever");
HttpGet clone = (HttpGet) httpget.clone();
assertEquals(httpget.getMethod(), clone.getMethod());
assertEquals(httpget.getURI(), clone.getURI());
Header[] headers1 = httpget.getAllHeaders();
Header[] headers2 = clone.getAllHeaders();
assertTrue(LangUtils.equals(headers1, headers2));
assertTrue(httpget.getParams() != clone.getParams());
assertEquals(Integer.valueOf(1), clone.getParams().getParameter("p1"));
assertEquals("whatever", clone.getParams().getParameter("p2"));
assertEquals(null, clone.getParams().getParameter("p3"));
}
public void testCloneEntityEnclosingRequests() throws Exception {
HttpPost httppost = new HttpPost("http://host/path");
httppost.addHeader("h1", "this header");
httppost.addHeader("h2", "that header");
httppost.addHeader("h3", "all sorts of headers");
httppost.getParams().setParameter("p1", Integer.valueOf(1));
httppost.getParams().setParameter("p2", "whatever");
HttpPost clone = (HttpPost) httppost.clone();
assertEquals(httppost.getMethod(), clone.getMethod());
assertEquals(httppost.getURI(), clone.getURI());
Header[] headers1 = httppost.getAllHeaders();
Header[] headers2 = clone.getAllHeaders();
assertTrue(LangUtils.equals(headers1, headers2));
assertTrue(httppost.getParams() != clone.getParams());
assertEquals(Integer.valueOf(1), clone.getParams().getParameter("p1"));
assertEquals("whatever", clone.getParams().getParameter("p2"));
assertEquals(null, clone.getParams().getParameter("p3"));
assertNull(clone.getEntity());
StringEntity e1 = new StringEntity("stuff");
httppost.setEntity(e1);
clone = (HttpPost) httppost.clone();
assertTrue(clone.getEntity() instanceof StringEntity);
assertFalse(clone.getEntity().equals(e1));
ByteArrayInputStream instream = new ByteArrayInputStream(new byte[] {});
InputStreamEntity e2 = new InputStreamEntity(instream, -1);
httppost.setEntity(e2);
try {
httppost.clone();
fail("CloneNotSupportedException should have been thrown");
} catch (CloneNotSupportedException expected) {
}
}
}