Added HTTP abstraction for RestTemplate

This commit is contained in:
Arjen Poutsma 2009-02-21 11:52:38 +00:00
parent 5fed34bdb4
commit 4a02cd96ea
26 changed files with 1968 additions and 191 deletions

View File

@ -29,28 +29,44 @@
</publications>
<dependencies>
<dependency org="com.caucho" name="com.springsource.com.caucho" rev="3.1.5" conf="optional, hessian, burlap->compile"/>
<dependency org="com.caucho" name="com.springsource.com.caucho" rev="3.1.5"
conf="optional, hessian, burlap->compile"/>
<dependency org="javax.el" name="com.springsource.javax.el" rev="1.0.0" conf="provided, el->compile"/>
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.08" conf="provided, faces->compile"/>
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.08"
conf="provided, faces->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.5.0" conf="provided->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="2.1.0" conf="provided, jsp->compile"/>
<dependency org="javax.xml.rpc" name="com.springsource.javax.xml.rpc" rev="1.1.0" conf="provided, jaxrpc->compile"/>
<dependency org="javax.xml.soap" name="com.springsource.javax.xml.soap" rev="1.3.0" conf="provided, ws->compile"/>
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="2.1.0"
conf="provided, jsp->compile"/>
<dependency org="javax.xml.rpc" name="com.springsource.javax.xml.rpc" rev="1.1.0"
conf="provided, jaxrpc->compile"/>
<dependency org="javax.xml.soap" name="com.springsource.javax.xml.soap" rev="1.3.0"
conf="provided, ws->compile"/>
<dependency org="javax.xml.ws" name="com.springsource.javax.xml.ws" rev="2.1.1" conf="provided, ws->compile"/>
<dependency org="org.aopalliance" name="com.springsource.org.aopalliance" rev="1.0.0" conf="compile->compile"/>
<dependency org="org.apache.axis" name="com.springsource.org.apache.axis" rev="1.4.0" conf="optional, axis->compile"/>
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.httpclient" rev="3.1.0" conf="optional, httpclient->compile"/>
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->compile"/>
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="optional, log4j->compile"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile"/>
<dependency org="org.apache.axis" name="com.springsource.org.apache.axis" rev="1.4.0"
conf="optional, axis->compile"/>
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.httpclient" rev="3.1.0"
conf="optional, httpclient->compile"/>
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1"
conf="compile->compile"/>
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15"
conf="optional, log4j->compile"/>
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration"
conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration"
conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration"
conf="compile->compile"/>
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration"
conf="compile->compile"/>
<!-- test dependencies -->
<dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2" conf="test->runtime"/>
<dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2"
conf="test->runtime"/>
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.5.0" conf="test->runtime"/>
<dependency org="net.sourceforge.cglib" name="com.springsource.net.sf.cglib" rev="2.1.3" conf="test->compile"/>
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.3.0" conf="test->compile"/>
<dependency org="org.mortbay.jetty" name="com.springsource.org.mortbay.jetty.server" rev="6.1.9"
conf="test->compile"/>
</dependencies>
</ivy-module>

View File

@ -0,0 +1,294 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.MediaType;
import org.springframework.util.StringUtils;
/**
* Represents HTTP request and response headers, mapping string header names to list of string values.
*
* <p>In addition to the normal methods defined by {@link Map}, this class offers the following convenience methods:
* <ul> <li>{@link #getFirst(String)} returns the first value associated with a given header name</li> <li>{@link
* #add(String, String)} adds a header value to the list of values for a header name</li> <li>{@link #set(String,
* String)} sets the header value to a single string value</li> </ul>
*
* <p>Inspired by {@link com.sun.net.httpserver.Headers}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public final class HttpHeaders implements Map<String, List<String>> {
private static String ACCEPT = "Accept";
private static String ALLOW = "Allow";
private static String CONTENT_LENGTH = "Content-Length";
private static String CONTENT_TYPE = "Content-Type";
private static String LOCATION = "Location";
private Map<String, List<String>> headers = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(5);
/**
* Returns the list of acceptable {@link MediaType media types}, as specified by the <code>Accept</code> header. <p/>
* Returns an empty list when the acceptable media types are unspecified.
*
* @return the acceptable media types
*/
public List<MediaType> getAccept() {
String value = getFirst(ACCEPT);
return value != null ? MediaType.parseMediaTypes(value) : Collections.<MediaType>emptyList();
}
/**
* Sets the list of acceptable {@link MediaType media types}, as specified by the <code>Accept</code> header.
*
* @param acceptableMediaTypes the acceptable media types
*/
public void setAccept(List<MediaType> acceptableMediaTypes) {
set(ACCEPT, MediaType.toString(acceptableMediaTypes));
}
/**
* Returns the set of allowed {@link HttpMethod HTTP methods}, as specified by the <code>Allow</code> header. <p/>
* Returns an empty set when the allowed methods are unspecified.
*
* @return the allowed methods
*/
public EnumSet<HttpMethod> getAllow() {
String value = getFirst(ALLOW);
if (value != null) {
List<HttpMethod> allowedMethod = new ArrayList<HttpMethod>(5);
String[] tokens = value.split(",\\s*");
for (String token : tokens) {
allowedMethod.add(HttpMethod.valueOf(token));
}
return EnumSet.copyOf(allowedMethod);
}
else {
return EnumSet.noneOf(HttpMethod.class);
}
}
/**
* Sets the set of allowed {@link HttpMethod HTTP methods}, as specified by the <code>Allow</code> header.
*
* @param allowedMethods the allowed methods
*/
public void setAllow(EnumSet<HttpMethod> allowedMethods) {
set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
}
/**
* Returns the length of the body in bytes, as specified by the <code>Content-Length</code> header. <p/> Returns -1
* when the content-length is unknown.
*
* @return the content length
*/
public long getContentLength() {
String value = getFirst(CONTENT_LENGTH);
return value != null ? Long.parseLong(value) : -1;
}
/**
* Sets the length of the body in bytes, as specified by the <code>Content-Length</code> header.
*
* @param contentLength the content length
*/
public void setContentLength(long contentLength) {
set(CONTENT_LENGTH, Long.toString(contentLength));
}
/**
* Returns the {@linkplain MediaType media type} of the body, as specified by the <code>Content-Type</code> header.
* <p/> Returns <code>null</code> when the content-type is unknown.
*
* @return the content type
*/
public MediaType getContentType() {
String value = getFirst(CONTENT_TYPE);
return value != null ? MediaType.parseMediaType(value) : null;
}
/**
* Sets the {@linkplain MediaType media type} of the body, as specified by the <code>Content-Type</code> header.
*
* @param mediaType the media type
*/
public void setContentType(MediaType mediaType) {
Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
set(CONTENT_TYPE, mediaType.toString());
}
/**
* Returns the (new) location of a resource, as specified by the <code>Location</code> header. <p/> Returns
* <code>null</code> when the location is unknown.
*
* @return the location
*/
public URI getLocation() {
String value = getFirst(LOCATION);
return value != null ? URI.create(value) : null;
}
/**
* Sets the (new) location of a resource, as specified by the <code>Location</code> header.
*
* @param location the location
*/
public void setLocation(URI location) {
set(LOCATION, location.toASCIIString());
}
/*
* Single string methods
*/
/**
* Returns the first header value for the given header name, if any.
*
* @param headerName the header name
* @return the first header value; or <code>null</code>
*/
public String getFirst(String headerName) {
List<String> headerValues = headers.get(headerName);
return headerValues != null ? headerValues.get(0) : null;
}
/**
* Adds the given, single header value under the given name.
*
* @param headerName the header name
* @param headerValue the header value
* @throws UnsupportedOperationException if adding headers is not supported
* @see #put(String, List)
* @see #set(String, String)
*/
public void add(String headerName, String headerValue) {
List<String> headerValues = headers.get(headerName);
if (headerValues == null) {
headerValues = new LinkedList<String>();
headers.put(headerName, headerValues);
}
headerValues.add(headerValue);
}
/**
* Sets the given, single header value under the given name.
*
* @param headerName the header name
* @param headerValue the header value
* @throws UnsupportedOperationException if adding headers is not supported
* @see #put(String, List)
* @see #add(String, String)
*/
public void set(String headerName, String headerValue) {
List<String> headerValues = new LinkedList<String>();
headerValues.add(headerValue);
headers.put(headerName, headerValues);
}
/*
* Map implementation
*/
public int size() {
return headers.size();
}
public boolean isEmpty() {
return headers.isEmpty();
}
public boolean containsKey(Object key) {
return headers.containsKey(key);
}
public boolean containsValue(Object value) {
return headers.containsValue(value);
}
public List<String> get(Object key) {
return headers.get(key);
}
public List<String> put(String key, List<String> value) {
return headers.put(key, value);
}
public List<String> remove(Object key) {
return headers.remove(key);
}
public void putAll(Map<? extends String, ? extends List<String>> m) {
headers.putAll(m);
}
public void clear() {
headers.clear();
}
public Set<String> keySet() {
return headers.keySet();
}
public Collection<List<String>> values() {
return headers.values();
}
public Set<Entry<String, List<String>>> entrySet() {
return headers.entrySet();
}
@Override
public int hashCode() {
return headers.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof HttpHeaders) {
HttpHeaders other = (HttpHeaders) obj;
return this.headers.equals(other.headers);
}
return false;
}
@Override
public String toString() {
return headers.toString();
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.io.IOException;
import java.io.InputStream;
/**
* Represents a HTTP output message, consisting of {@linkplain #getHeaders() headers} and a readable {@linkplain
* #getBody() body}. <p/> Typically implemented by a HTTP request on the server-side, or a response on the client-side.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface HttpInputMessage extends HttpMessage {
/**
* Returns the body of the message as an input stream.
*
* @return the input stream body
* @throws IOException in case of I/O Errors
*/
InputStream getBody() throws IOException;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
/**
* Represents the base interface for HTTP request and response messages. Consists of {@link HttpHeaders}, retrievable
* via {@link #getHeaders()}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface HttpMessage {
/**
* Returns the headers of this message.
*
* @return the headers
*/
HttpHeaders getHeaders();
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
/**
* Java 5 enumeration of HTTP request methods.
*
* @author Arjen Poutsma
* @see org.springframework.web.bind.annotation.RequestMapping
* @since 3.0
*/
public enum HttpMethod {
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.io.IOException;
import java.io.OutputStream;
/**
* Represents a HTTP output message, consisting of {@linkplain #getHeaders() headers} and a writable {@linkplain
* #getBody() body}. <p/> Typically implemented by a HTTP request on the client-side, or a response on the server-side.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface HttpOutputMessage extends HttpMessage {
/**
* Returns the body of the message as an output stream.
*
* @return the output stream body
* @throws IOException in case of I/O Errors
*/
OutputStream getBody() throws IOException;
}

View File

@ -0,0 +1,171 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
/**
* Java 5 enumeration of HTTP status codes. <p/> The HTTP status code series can be retrieved via {@link #series()}.
*
* @author Arjen Poutsma
* @see HttpStatus.Series
*/
public enum HttpStatus {
// 1xx Informational
CONTINUE(100),
SWITCHING_PROTOCOLS(101),
// 2xx Success
OK(200),
CREATED(201),
ACCEPTED(202),
NON_AUTHORITATIVE_INFORMATION(203),
NO_CONTENT(204),
RESET_CONTENT(205),
PARTIAL_CONTENT(206),
// 3xx Redirection
MULTIPLE_CHOICES(300),
MOVED_PERMANENTLY(301),
MOVED_TEMPORARILY(302),
SEE_OTHER(303),
NOT_MODIFIED(304),
USE_PROXY(305),
TEMPORARY_REDIRECT(307),
// --- 4xx Client Error ---
BAD_REQUEST(400),
UNAUTHORIZED(401),
PAYMENT_REQUIRED(402),
FORBIDDEN(403),
NOT_FOUND(404),
METHOD_NOT_ALLOWED(405),
NOT_ACCEPTABLE(406),
PROXY_AUTHENTICATION_REQUIRED(407),
REQUEST_TIMEOUT(408),
CONFLICT(409),
GONE(410),
LENGTH_REQUIRED(411),
PRECONDITION_FAILED(412),
REQUEST_TOO_LONG(413),
REQUEST_URI_TOO_LONG(414),
UNSUPPORTED_MEDIA_TYPE(415),
REQUESTED_RANGE_NOT_SATISFIABLE(416),
EXPECTATION_FAILED(417),
// --- 5xx Server Error ---
INTERNAL_SERVER_ERROR(500),
NOT_IMPLEMENTED(501),
BAD_GATEWAY(502),
SERVICE_UNAVAILABLE(503),
GATEWAY_TIMEOUT(504),
HTTP_VERSION_NOT_SUPPORTED(505);
/**
* Java 5 enumeration of HTTP status series. <p/> Retrievable via {@link HttpStatus#series()}.
*/
public enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
REDIRECTION(3),
CLIENT_ERROR(4),
SERVER_ERROR(5);
private final int value;
private Series(int value) {
this.value = value;
}
/**
* Returns the integer value of this status series. Ranges from 1 to 5.
*
* @return the integer value
*/
public int value() {
return value;
}
private static Series valueOf(HttpStatus status) {
int seriesCode = status.value() / 100;
for (Series series : values()) {
if (series.value == seriesCode) {
return series;
}
}
throw new IllegalArgumentException("No matching constant for [" + status + "]");
}
}
private final int value;
private HttpStatus(int value) {
this.value = value;
}
/**
* Returns the integer value of this status code.
*
* @return the integer value
*/
public int value() {
return value;
}
/**
* Returns the HTTP status series of this status code.
*
* @return the series
* @see HttpStatus.Series
*/
public Series series() {
return Series.valueOf(this);
}
/**
* Returns the enum constant of this type with the specified numeric value.
*
* @param statusCode the numeric value of the enum to be returned
* @return the enum constant with the specified numeric value
* @throws IllegalArgumentException if this enum has no constant for the specified numeric value
*/
public static HttpStatus valueOf(int statusCode) {
for (HttpStatus status : values()) {
if (status.value == statusCode) {
return status;
}
}
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
}
/**
* Returns a string representation of this status code.
*
* @return a string representation
*/
@Override
public String toString() {
return Integer.toString(value);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.HttpOutputMessage;
/**
* Represents a client-side HTTP request. Created via an implementation of the {@link ClientHttpRequestFactory}. <p/> A
* <code>HttpRequest</code> can be {@linkplain #execute() executed}, getting a {@link ClientHttpResponse} which can be
* read from.
*
* @author Arjen Poutsma
* @see ClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
*/
public interface ClientHttpRequest extends HttpOutputMessage {
/**
* Returns the HTTP method of the request.
*
* @return the HTTP method
*/
HttpMethod getMethod();
/**
* Executes this request, resulting in a {@link ClientHttpResponse} that can be read.
*
* @return the response result of the execution
* @throws IOException in case of I/O errors
*/
ClientHttpResponse execute() throws IOException;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import java.net.URI;
import org.springframework.web.http.HttpMethod;
/**
* Factory for {@link ClientHttpRequest} objects. Requests are created by the {@link #createRequest(URI, HttpMethod)}
* method.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface ClientHttpRequestFactory {
/**
* Creates a new {@link ClientHttpRequest} for the specified URI and HTTP method. The returned request can be written
* to, and then executed by calling {@link ClientHttpRequest#execute()}.
*
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpStatus;
/**
* Represents a client-side HTTP response. Obtained via an calling of the {@link ClientHttpRequest#execute()}. <p/> A
* <code>HttpResponse</code> must be {@linkplain #close() closed}, typically in a <code>finally</code> block.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface ClientHttpResponse extends HttpInputMessage {
/**
* Returns the HTTP status code of the response.
*
* @return the HTTP status
* @throws IOException in case of I/O errors
*/
HttpStatus getStatusCode() throws IOException;
/**
* Returns the HTTP status text of the response.
*
* @return the HTTP status text
* @throws IOException in case of I/O errors
*/
String getStatusText() throws IOException;
/**
* Closes this response, freeing any resources created.
*/
void close();
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
/**
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute requests. Created via the
* {@link SimpleClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
* @since 3.0
*/
final class SimpleClientHttpRequest implements ClientHttpRequest {
private final HttpURLConnection connection;
private final HttpHeaders headers = new HttpHeaders();
private boolean headersWritten = false;
SimpleClientHttpRequest(HttpURLConnection connection) {
this.connection = connection;
}
public HttpMethod getMethod() {
return HttpMethod.valueOf(connection.getRequestMethod());
}
public HttpHeaders getHeaders() {
return headers;
}
public OutputStream getBody() throws IOException {
writeHeaders();
return connection.getOutputStream();
}
public ClientHttpResponse execute() throws IOException {
writeHeaders();
connection.connect();
return new SimpleClientHttpResponse(connection);
}
private void writeHeaders() {
if (!headersWritten) {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
connection.addRequestProperty(headerName, headerValue);
}
}
headersWritten = true;
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpMethod;
/**
* {@link ClientHttpRequestFactory} implementation that uses standard J2SE facilities.
*
* @author Arjen Poutsma
* @see java.net.HttpURLConnection
* @see org.springframework.web.http.client.commons.CommonsClientHttpRequestFactory
* @since 3.0
*/
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
URL url = uri.toURL();
URLConnection urlConnection = url.openConnection();
Assert.isInstanceOf(HttpURLConnection.class, urlConnection);
HttpURLConnection connection = (HttpURLConnection) urlConnection;
prepareConnection(connection, httpMethod.name());
return new SimpleClientHttpRequest(connection);
}
/**
* Template method for preparing the given {@link HttpURLConnection}.
*
* <p>Default implementation prepares the connection for input and output, and sets the HTTP method.
*
* @param connection the connection to prepare
* @param httpMethod the HTTP request method ({@code GET}, {@code POST}, etc.)
* @throws IOException in case of I/O errors
*/
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod(httpMethod);
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import org.springframework.util.StringUtils;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpStatus;
/**
* {@link ClientHttpResponse} implementation that uses standard J2SE facilities. Obtained via the {@link
* SimpleClientHttpRequest#execute()}.
*
* @author Arjen Poutsma
* @since 3.0
*/
final class SimpleClientHttpResponse implements ClientHttpResponse {
private final HttpURLConnection connection;
private HttpHeaders headers;
SimpleClientHttpResponse(HttpURLConnection connection) {
this.connection = connection;
}
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(connection.getResponseCode());
}
public String getStatusText() throws IOException {
return connection.getResponseMessage();
}
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
// Header field 0 is the status line, so we start at 1
int i = 1;
while (true) {
String name = connection.getHeaderFieldKey(i);
if (!StringUtils.hasLength(name)) {
break;
}
headers.add(name, connection.getHeaderField(i));
i++;
}
}
return headers;
}
public InputStream getBody() throws IOException {
if (connection.getErrorStream() == null) {
return connection.getInputStream();
}
else {
return connection.getErrorStream();
}
}
public void close() {
connection.disconnect();
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client.commons;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.client.ClientHttpRequest;
import org.springframework.web.http.client.ClientHttpResponse;
/**
* {@link org.springframework.web.http.client.ClientHttpRequest} implementation that uses Commons Http Client to execute
* requests. Created via the {@link CommonsClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @see CommonsClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
*/
final class CommonsClientHttpRequest implements ClientHttpRequest {
private final HttpClient httpClient;
private final HttpMethodBase httpMethod;
private final HttpHeaders headers = new HttpHeaders();
private boolean headersWritten = false;
private ByteArrayOutputStream bufferedOutput;
CommonsClientHttpRequest(HttpClient httpClient, HttpMethodBase httpMethod) {
this.httpClient = httpClient;
this.httpMethod = httpMethod;
}
public HttpHeaders getHeaders() {
return headers;
}
public OutputStream getBody() throws IOException {
writeHeaders();
Assert.isInstanceOf(EntityEnclosingMethod.class, httpMethod);
this.bufferedOutput = new ByteArrayOutputStream();
return bufferedOutput;
}
public HttpMethod getMethod() {
return HttpMethod.valueOf(httpMethod.getName());
}
public ClientHttpResponse execute() throws IOException {
writeHeaders();
if (httpMethod instanceof EntityEnclosingMethod) {
EntityEnclosingMethod entityEnclosingMethod = (EntityEnclosingMethod) httpMethod;
RequestEntity requestEntity = new ByteArrayRequestEntity(bufferedOutput.toByteArray());
entityEnclosingMethod.setRequestEntity(requestEntity);
}
httpClient.executeMethod(httpMethod);
return new CommonsClientHttpResponse(httpMethod);
}
private void writeHeaders() {
if (!headersWritten) {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
httpMethod.addRequestHeader(headerName, headerValue);
}
}
headersWritten = true;
}
}
}

View File

@ -0,0 +1,148 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client.commons;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
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.OptionsMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.TraceMethod;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.client.ClientHttpRequest;
import org.springframework.web.http.client.ClientHttpRequestFactory;
/**
* {@link org.springframework.web.http.client.ClientHttpRequestFactory} implementation that uses <a
* href="http://jakarta.apache.org/commons/httpclient">Jakarta Commons HttpClient</a> to create requests. <p/> Allows to
* use a pre-configured {@link HttpClient} instance, potentially with authentication, HTTP connection pooling, etc.
*
* @author Arjen Poutsma
* @see org.springframework.web.http.client.SimpleClientHttpRequestFactory
*/
public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
private HttpClient httpClient;
/**
* Create a new instance of the <code>CommonsHttpRequestFactory</code> with a default {@link HttpClient} that uses a
* default {@link MultiThreadedHttpConnectionManager}.
*/
public CommonsClientHttpRequestFactory() {
httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
this.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS);
}
/**
* Create a new instance of the <code>CommonsHttpRequestFactory</code> with the given {@link HttpClient} instance.
*
* @param httpClient the HttpClient instance to use for this sender
*/
public CommonsClientHttpRequestFactory(HttpClient httpClient) {
Assert.notNull(httpClient, "httpClient must not be null");
this.httpClient = httpClient;
}
/**
* Returns the <code>HttpClient</code> used by this message sender.
*/
public HttpClient getHttpClient() {
return httpClient;
}
/**
* Set the <code>HttpClient</code> used by this message sender.
*/
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Set the socket read timeout for the underlying HttpClient. A value of 0 means <em>never</em> timeout.
*
* @param timeout the timeout value in milliseconds
* @see org.apache.commons.httpclient.params.HttpConnectionManagerParams#setSoTimeout(int)
*/
public void setReadTimeout(int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be a non-negative value");
}
this.httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
}
public void destroy() throws Exception {
HttpConnectionManager connectionManager = getHttpClient().getHttpConnectionManager();
if (connectionManager instanceof MultiThreadedHttpConnectionManager) {
((MultiThreadedHttpConnectionManager) connectionManager).shutdown();
}
}
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
String uriString = uri.toString();
HttpMethodBase httpMethodBase;
switch (httpMethod) {
case GET:
httpMethodBase = new GetMethod(uriString);
break;
case DELETE:
httpMethodBase = new DeleteMethod(uriString);
break;
case HEAD:
httpMethodBase = new HeadMethod(uriString);
break;
case OPTIONS:
httpMethodBase = new OptionsMethod(uriString);
break;
case POST:
httpMethodBase = new PostMethod(uriString);
break;
case PUT:
httpMethodBase = new PutMethod(uriString);
break;
case TRACE:
httpMethodBase = new TraceMethod(uriString);
break;
default:
throw new IllegalArgumentException("Invalid method: " + httpMethod);
}
process(httpMethodBase);
return new CommonsClientHttpRequest(getHttpClient(), httpMethodBase);
}
/**
* Template method that allows for manipulating the {@link org.apache.commons.httpclient.HttpMethodBase} before it is
* returned as part of a {@link CommonsClientHttpRequest}. <p/> Default implementation is empty.
*
* @param httpMethod the Commons HTTP method to process
*/
protected void process(HttpMethodBase httpMethod) {
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client.commons;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpStatus;
import org.springframework.web.http.client.ClientHttpResponse;
/**
* {@link org.springframework.web.http.client.ClientHttpResponse} implementation that uses Commons Http Client to
* execute requests. Created via the {@link CommonsClientHttpRequest}.
*
* @author Arjen Poutsma
* @see CommonsClientHttpRequest#execute()
*/
final class CommonsClientHttpResponse implements ClientHttpResponse {
private final HttpMethod httpMethod;
private HttpHeaders headers;
CommonsClientHttpResponse(HttpMethod httpMethod) {
this.httpMethod = httpMethod;
}
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(httpMethod.getStatusCode());
}
public String getStatusText() {
return httpMethod.getStatusText();
}
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
for (Header header : httpMethod.getResponseHeaders()) {
headers.add(header.getName(), header.getValue());
}
}
return headers;
}
public InputStream getBody() throws IOException {
return httpMethod.getResponseBodyAsStream();
}
public void close() {
httpMethod.releaseConnection();
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Contains an implementation of the <code>ClientHttpRequest</code> and
<code>ClientHttpResponse</code> based on Commons HTTP Client.
</body>
</html>

View File

@ -0,0 +1,10 @@
<html>
<body>
Contains an abstraction over client-side HTTP. This package
contains the <code>ClientHttpRequest</code> and
<code>ClientHttpResponse</code>, as well as a basic implementation of these
interfaces.
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body>
Contains a basic abstraction over client/server-side HTTP. This package
contains the <code>HttpInputMessage</code> and <code>HttpOutputMessage</code>.
</body>
</html>

View File

@ -0,0 +1,87 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.MediaType;
/**
* @author Arjen Poutsma
*/
public class HttpHeadersTests {
private HttpHeaders headers;
@Before
public void setUp() {
headers = new HttpHeaders();
}
@Test
public void accept() {
MediaType mediaType1 = new MediaType("text", "html");
MediaType mediaType2 = new MediaType("text", "plain");
List<MediaType> mediaTypes = new ArrayList<MediaType>(2);
mediaTypes.add(mediaType1);
mediaTypes.add(mediaType2);
headers.setAccept(mediaTypes);
assertEquals("Invalid Accept header", mediaTypes, headers.getAccept());
assertEquals("Invalid Accept header", "text/html,text/plain", headers.getFirst("Accept"));
}
@Test
public void allow() {
EnumSet<HttpMethod> methods = EnumSet.of(HttpMethod.GET, HttpMethod.POST);
headers.setAllow(methods);
assertEquals("Invalid Allow header", methods, headers.getAllow());
assertEquals("Invalid Allow header", "GET,POST", headers.getFirst("Allow"));
}
@Test
public void contentLength() {
long length = 42L;
headers.setContentLength(length);
assertEquals("Invalid Content-Length header", length, headers.getContentLength());
assertEquals("Invalid Content-Length header", "42", headers.getFirst("Content-Length"));
}
@Test
public void contentType() {
MediaType contentType = new MediaType("text", "html", Charset.forName("UTF-8"));
headers.setContentType(contentType);
assertEquals("Invalid Content-Type header", contentType, headers.getContentType());
assertEquals("Invalid Content-Type header", "text/html;charset=UTF-8", headers.getFirst("Content-Type"));
}
@Test
public void location() throws URISyntaxException {
URI location = new URI("http://www.example.com/hotels");
headers.setLocation(location);
assertEquals("Invalid Location header", location, headers.getLocation());
assertEquals("Invalid Location header", "http://www.example.com/hotels", headers.getFirst("Location"));
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.util.Assert;
/**
* @author Arjen Poutsma
*/
public class MockHttpInputMessage implements HttpInputMessage {
private final HttpHeaders headers = new HttpHeaders();
private final InputStream body;
public MockHttpInputMessage(byte[] contents) {
Assert.notNull(contents, "'contents' must not be null");
this.body = new ByteArrayInputStream(contents);
}
public MockHttpInputMessage(InputStream body) {
Assert.notNull(body, "'body' must not be null");
this.body = body;
}
public HttpHeaders getHeaders() {
return headers;
}
public InputStream getBody() throws IOException {
return body;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* @author Arjen Poutsma
*/
public class MockHttpOutputMessage implements HttpOutputMessage {
private final HttpHeaders headers = new HttpHeaders();
private final ByteArrayOutputStream body = new ByteArrayOutputStream();
public HttpHeaders getHeaders() {
return headers;
}
public OutputStream getBody() throws IOException {
return body;
}
public byte[] getBodyAsBytes() {
return body.toByteArray();
}
public String getBodyAsString(Charset charset) {
byte[] bytes = getBodyAsBytes();
return new String(bytes, charset);
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Enumeration;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.AfterClass;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.HttpStatus;
public abstract class AbstractHttpRequestFactoryTestCase {
private ClientHttpRequestFactory factory;
private static Server jettyServer;
@BeforeClass
public static void startJettyServer() throws Exception {
jettyServer = new Server(8889);
Context jettyContext = new Context(jettyServer, "/");
jettyContext.addServlet(new ServletHolder(new EchoServlet()), "/echo");
jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound");
jettyServer.start();
}
@Before
public final void createFactory() {
factory = createRequestFactory();
}
protected abstract ClientHttpRequestFactory createRequestFactory();
@AfterClass
public static void stopJettyServer() throws Exception {
if (jettyServer != null) {
jettyServer.stop();
}
}
@Test
public void status() throws Exception {
ClientHttpRequest request =
factory.createRequest(new URI("http://localhost:8889/errors/notfound"), HttpMethod.GET);
assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod());
ClientHttpResponse response = request.execute();
assertEquals("Invalid status code", HttpStatus.NOT_FOUND, response.getStatusCode());
}
@Test
public void echo() throws Exception {
ClientHttpRequest request = factory.createRequest(new URI("http://localhost:8889/echo"), HttpMethod.PUT);
assertEquals("Invalid HTTP method", HttpMethod.PUT, request.getMethod());
String headerName = "MyHeader";
String headerValue1 = "value1";
request.getHeaders().add(headerName, headerValue1);
String headerValue2 = "value2";
request.getHeaders().add(headerName, headerValue2);
byte[] body = "Hello World".getBytes("UTF-8");
FileCopyUtils.copy(body, request.getBody());
ClientHttpResponse response = request.execute();
assertEquals("Invalid status code", HttpStatus.OK, response.getStatusCode());
assertTrue("Header not found", response.getHeaders().containsKey(headerName));
assertEquals("Header value not found", Arrays.asList(headerValue1, headerValue2),
response.getHeaders().get(headerName));
byte[] result = FileCopyUtils.copyToByteArray(response.getBody());
assertTrue("Invalid body", Arrays.equals(body, result));
}
/**
* Servlet that returns and error message for a given status code.
*/
private static class ErrorServlet extends GenericServlet {
private final int sc;
private ErrorServlet(int sc) {
this.sc = sc;
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
((HttpServletResponse) response).sendError(sc);
}
}
private static class EchoServlet extends HttpServlet {
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
echo(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
echo(request, response);
}
private void echo(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
for (Enumeration e1 = request.getHeaderNames(); e1.hasMoreElements();) {
String headerName = (String) e1.nextElement();
for (Enumeration e2 = request.getHeaders(headerName); e2.hasMoreElements();) {
String headerValue = (String) e2.nextElement();
response.addHeader(headerName, headerValue);
}
}
FileCopyUtils.copy(request.getInputStream(), response.getOutputStream());
}
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client;
public class SimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
@Override
protected ClientHttpRequestFactory createRequestFactory() {
return new SimpleClientHttpRequestFactory();
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.web.http.client.commons;
import org.springframework.web.http.client.AbstractHttpRequestFactoryTestCase;
import org.springframework.web.http.client.ClientHttpRequestFactory;
public class CommonsHttpRequestFactoryTest extends AbstractHttpRequestFactoryTestCase {
@Override
protected ClientHttpRequestFactory createRequestFactory() {
return new CommonsClientHttpRequestFactory();
}
}

View File

@ -1,177 +1,232 @@
<?xml version="1.0" encoding="UTF-8"?>
<module relativePaths="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="inheritedJdk" />
<orderEntry type="module" module-name="aop" />
<orderEntry type="module" module-name="beans" />
<orderEntry type="module" module-name="context" />
<orderEntry type="module" module-name="core" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-sources-1.0.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.taglibs/com.springsource.org.apache.taglibs.standard/1.1.2/com.springsource.org.apache.taglibs.standard-1.1.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.taglibs/com.springsource.org.apache.taglibs.standard/1.1.2/com.springsource.org.apache.taglibs.standard-sources-1.1.2.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.log4j/com.springsource.org.apache.log4j/1.2.15/com.springsource.org.apache.log4j-1.2.15.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.log4j/com.springsource.org.apache.log4j/1.2.15/com.springsource.org.apache.log4j-sources-1.2.15.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.xml.rpc/com.springsource.javax.xml.rpc/1.1.0/com.springsource.javax.xml.rpc-1.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.axis/com.springsource.org.apache.axis/1.4.0/com.springsource.org.apache.axis-1.4.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.axis/com.springsource.org.apache.axis/1.4.0/com.springsource.org.apache.axis-sources-1.4.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/com.caucho/com.springsource.com.caucho/3.1.5/com.springsource.com.caucho-3.1.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/com.caucho/com.springsource.com.caucho/3.1.5/com.springsource.com.caucho-sources-3.1.5.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-sources-1.0.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.httpclient/3.1.0/com.springsource.org.apache.commons.httpclient-3.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.httpclient/3.1.0/com.springsource.org.apache.commons.httpclient-sources-3.1.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-sources-2.5.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.faces/com.springsource.javax.faces/1.2.0.08/com.springsource.javax.faces-1.2.0.08.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.faces/com.springsource.javax.faces/1.2.0.08/com.springsource.javax.faces-sources-1.2.0.08.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-2.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-sources-2.1.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
<component name="copyright">
<Base>
<setting name="state" value="2" />
</Base>
</component>
</module>
<?xml version="1.0" encoding="UTF-8"?>
<module relativePaths="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="inheritedJdk" />
<orderEntry type="module" module-name="aop" />
<orderEntry type="module" module-name="beans" />
<orderEntry type="module" module-name="context" />
<orderEntry type="module" module-name="core" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-sources-1.0.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.taglibs/com.springsource.org.apache.taglibs.standard/1.1.2/com.springsource.org.apache.taglibs.standard-1.1.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.taglibs/com.springsource.org.apache.taglibs.standard/1.1.2/com.springsource.org.apache.taglibs.standard-sources-1.1.2.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.log4j/com.springsource.org.apache.log4j/1.2.15/com.springsource.org.apache.log4j-1.2.15.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.log4j/com.springsource.org.apache.log4j/1.2.15/com.springsource.org.apache.log4j-sources-1.2.15.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.xml.rpc/com.springsource.javax.xml.rpc/1.1.0/com.springsource.javax.xml.rpc-1.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.axis/com.springsource.org.apache.axis/1.4.0/com.springsource.org.apache.axis-1.4.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.axis/com.springsource.org.apache.axis/1.4.0/com.springsource.org.apache.axis-sources-1.4.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/com.caucho/com.springsource.com.caucho/3.1.5/com.springsource.com.caucho-3.1.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/com.caucho/com.springsource.com.caucho/3.1.5/com.springsource.com.caucho-sources-3.1.5.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-sources-1.0.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.httpclient/3.1.0/com.springsource.org.apache.commons.httpclient-3.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.httpclient/3.1.0/com.springsource.org.apache.commons.httpclient-sources-3.1.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-sources-2.5.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.faces/com.springsource.javax.faces/1.2.0.08/com.springsource.javax.faces-1.2.0.08.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.faces/com.springsource.javax.faces/1.2.0.08/com.springsource.javax.faces-sources-1.2.0.08.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-2.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-sources-2.1.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.mortbay.jetty/com.springsource.org.mortbay.jetty.server/6.1.9/com.springsource.org.mortbay.jetty.server-6.1.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.mortbay.jetty/com.springsource.org.mortbay.jetty.server/6.1.9/com.springsource.org.mortbay.jetty.server-sources-6.1.9.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.mortbay.jetty/com.springsource.org.mortbay.util/6.1.9/com.springsource.org.mortbay.util-6.1.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.mortbay.jetty/com.springsource.org.mortbay.util/6.1.9/com.springsource.org.mortbay.util-sources-6.1.9.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.codec/1.3.0/com.springsource.org.apache.commons.codec-1.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.apache.commons/com.springsource.org.apache.commons.codec/1.3.0/com.springsource.org.apache.commons.codec-sources-1.3.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/com.sun.syndication/com.springsource.com.sun.syndication/0.9.0/com.springsource.com.sun.syndication-0.9.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/com.sun.syndication/com.springsource.com.sun.syndication/0.9.0/com.springsource.com.sun.syndication-sources-0.9.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$IVY_CACHE$/org.jdom/com.springsource.org.jdom/1.0.0/com.springsource.org.jdom-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$IVY_CACHE$/org.jdom/com.springsource.org.jdom/1.0.0/com.springsource.org.jdom-sources-1.0.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
<component name="copyright">
<Base>
<setting name="state" value="1" />
</Base>
</component>
</module>