refactored HTTP support into top-level package "org.springframework.http"; revised RestTemplate facility in package "org.springframework.web.client"

This commit is contained in:
Juergen Hoeller 2009-02-24 11:46:00 +00:00
parent 882c195221
commit 760cab8fea
71 changed files with 1347 additions and 1366 deletions

View File

@ -1,144 +0,0 @@
/*
* 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.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Default implementation of {@link MultiValueMap} that wraps a plain {@code Map}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class DefaultMultiValueMap<K, V> implements MultiValueMap<K, V> {
private final Map<K, List<V>> wrappee;
/**
* Constructs a new intance of the {@code DefaultMultiValueMap} wrapping a plain {@link LinkedHashMap}.
*/
public DefaultMultiValueMap() {
this(new LinkedHashMap<K, List<V>>());
}
/**
* Constructs a new intance of the {@code DefaultMultiValueMap} wrapping the given map.
*
* @param wrappee the map to be wrapped
*/
public DefaultMultiValueMap(Map<K, List<V>> wrappee) {
Assert.notNull(wrappee, "'wrappee' must not be null");
this.wrappee = wrappee;
}
/*
* MultiValueMap implementation
*/
public void add(K key, V value) {
List<V> values = wrappee.get(key);
if (values == null) {
values = new LinkedList<V>();
wrappee.put(key, values);
}
values.add(value);
}
public V getFirst(K key) {
List<V> values = wrappee.get(key);
return values != null ? values.get(0) : null;
}
public void set(K key, V value) {
List<V> values = new LinkedList<V>();
values.add(value);
wrappee.put(key, values);
}
/*
* Map implementation
*/
public int size() {
return wrappee.size();
}
public boolean isEmpty() {
return wrappee.isEmpty();
}
public boolean containsKey(Object key) {
return wrappee.containsKey(key);
}
public boolean containsValue(Object value) {
return wrappee.containsValue(value);
}
public List<V> get(Object key) {
return wrappee.get(key);
}
public List<V> put(K key, List<V> value) {
return wrappee.put(key, value);
}
public List<V> remove(Object key) {
return wrappee.remove(key);
}
public void putAll(Map<? extends K, ? extends List<V>> m) {
wrappee.putAll(m);
}
public void clear() {
wrappee.clear();
}
public Set<K> keySet() {
return wrappee.keySet();
}
public Collection<List<V>> values() {
return wrappee.values();
}
public Set<Entry<K, List<V>>> entrySet() {
return wrappee.entrySet();
}
@Override
public int hashCode() {
return wrappee.hashCode();
}
@Override
public boolean equals(Object obj) {
return this.wrappee.equals(obj);
}
@Override
public String toString() {
return wrappee.toString();
}
}

View File

@ -0,0 +1,150 @@
/*
* 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.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Simple implementation of {@link MultiValueMap} that wraps a plain {@code Map}
* (by default a {@link LinkedHashMap}, storing multiple values in a {@link LinkedList}.
*
* <p>This Map implementation is generally not thread-safe. It is primarily designed
* for data structures exposed from request objects, for use in a single thread only.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
*/
public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V> {
private final Map<K, List<V>> targetMap;
/**
* Create a new SimpleMultiValueMap that wraps the given target Map.
* @param wrappee the target Map to wrap
*/
public LinkedMultiValueMap() {
this.targetMap = new LinkedHashMap<K, List<V>>();
}
/**
* Create a new SimpleMultiValueMap that wraps the given target Map.
* <p>Note: The given Map will be used as active underlying Map.
* Any changes in the underlying map will be reflected in the
* MultiValueMap object, and vice versa.
* @param targetMap the target Map to wrap
*/
public LinkedMultiValueMap(Map<K, List<V>> targetMap) {
Assert.notNull(targetMap, "'targetMap' must not be null");
this.targetMap = targetMap;
}
// MultiValueMap implementation
public void add(K key, V value) {
List<V> values = this.targetMap.get(key);
if (values == null) {
values = new LinkedList<V>();
this.targetMap.put(key, values);
}
values.add(value);
}
public V getFirst(K key) {
List<V> values = this.targetMap.get(key);
return (values != null ? values.get(0) : null);
}
public void set(K key, V value) {
List<V> values = new LinkedList<V>();
values.add(value);
this.targetMap.put(key, values);
}
// Map implementation
public int size() {
return this.targetMap.size();
}
public boolean isEmpty() {
return this.targetMap.isEmpty();
}
public boolean containsKey(Object key) {
return this.targetMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.targetMap.containsValue(value);
}
public List<V> get(Object key) {
return this.targetMap.get(key);
}
public List<V> put(K key, List<V> value) {
return this.targetMap.put(key, value);
}
public List<V> remove(Object key) {
return this.targetMap.remove(key);
}
public void putAll(Map<? extends K, ? extends List<V>> m) {
this.targetMap.putAll(m);
}
public void clear() {
this.targetMap.clear();
}
public Set<K> keySet() {
return this.targetMap.keySet();
}
public Collection<List<V>> values() {
return this.targetMap.values();
}
public Set<Entry<K, List<V>>> entrySet() {
return this.targetMap.entrySet();
}
@Override
public boolean equals(Object obj) {
return this.targetMap.equals(obj);
}
@Override
public int hashCode() {
return this.targetMap.hashCode();
}
@Override
public String toString() {
return this.targetMap.toString();
}
}

View File

@ -28,25 +28,22 @@ import java.util.Map;
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
/**
* Adds the given single value to the current list of values for the given key.
*
* @param key the key
* @param value the value to be added
*/
void add(K key, V value);
/**
* Returns the first value for the given key.
*
* Return the first value for the given key.
* @param key the key
* @return the first value for the specified key, or <code>null</code>
*/
V getFirst(K key);
/**
* Sets the given single value under the given key.
*
* @param key the key
* Add the given single value to the current list of values for the given key.
* @param key the key
* @param value the value to be added
*/
void add(K key, V value);
/**
* Set the given single value under the given key.
* @param key the key
* @param value the value to set
*/
void set(K key, V value);

View File

@ -29,13 +29,13 @@ import org.junit.Test;
/**
* @author Arjen Poutsma
*/
public class DefaultMultiValueMapTests {
public class LinkedMultiValueMapTests {
private DefaultMultiValueMap<String, String> map;
private LinkedMultiValueMap<String, String> map;
@Before
public void setUp() {
map = new DefaultMultiValueMap<String, String>();
map = new LinkedMultiValueMap<String, String>();
}
@Test
@ -71,7 +71,7 @@ public class DefaultMultiValueMapTests {
public void equals() {
map.set("key1", "value1");
assertEquals(map, map);
MultiValueMap<String, String> o1 = new DefaultMultiValueMap<String, String>();
MultiValueMap<String, String> o1 = new LinkedMultiValueMap<String, String>();
o1.set("key1", "value1");
assertEquals(map, o1);
assertEquals(o1, map);
@ -80,4 +80,5 @@ public class DefaultMultiValueMapTests {
assertEquals(map, o2);
assertEquals(o2, map);
}
}

View File

@ -39,7 +39,7 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MediaType;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

View File

@ -28,7 +28,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.MediaType;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.View;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
import java.net.URI;
import java.nio.charset.Charset;
@ -31,7 +31,6 @@ import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
@ -39,16 +38,18 @@ 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>
* <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 MultiValueMap<String, String> {
public class HttpHeaders implements MultiValueMap<String, String> {
private static String ACCEPT = "Accept";
@ -62,23 +63,12 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
private static String LOCATION = "Location";
private Map<String, List<String>> headers = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(5);
private final Map<String, List<String>> headers = CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(5);
/**
* Returns the list of acceptable {@linkplain 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 {@linkplain MediaType media types}, as specified by the <code>Accept</code> header.
*
* Set the list of acceptable {@linkplain MediaType media types}, as specified by the <code>Accept</code> header.
* @param acceptableMediaTypes the acceptable media types
*/
public void setAccept(List<MediaType> acceptableMediaTypes) {
@ -86,9 +76,34 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* Returns the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code>
* Return the list of acceptable {@linkplain 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());
}
/**
* Set the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code> header.
* @param acceptableCharsets the acceptable charsets
*/
public void setAcceptCharset(List<Charset> acceptableCharsets) {
StringBuilder builder = new StringBuilder();
for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
Charset charset = iterator.next();
builder.append(charset.name().toLowerCase(Locale.ENGLISH));
if (iterator.hasNext()) {
builder.append(", ");
}
}
set(ACCEPT_CHARSET, builder.toString());
}
/**
* Return the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code>
* header.
*
* @return the acceptable charsets
*/
public List<Charset> getAcceptCharset() {
@ -110,29 +125,19 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* Sets the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code> header.
*
* @param acceptableCharsets the acceptable charsets
* Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the <code>Allow</code> header.
* @param allowedMethods the allowed methods
*/
public void setAcceptCharset(List<Charset> acceptableCharsets) {
StringBuilder builder = new StringBuilder();
for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
Charset charset = iterator.next();
builder.append(charset.name().toLowerCase(Locale.ENGLISH));
if (iterator.hasNext()) {
builder.append(", ");
}
}
set(ACCEPT_CHARSET, builder.toString());
public void setAllow(Set<HttpMethod> allowedMethods) {
set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
}
/**
* 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 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() {
public Set<HttpMethod> getAllow() {
String value = getFirst(ALLOW);
if (value != null) {
List<HttpMethod> allowedMethod = new ArrayList<HttpMethod>(5);
@ -148,28 +153,7 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* 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.
*
* Set 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) {
@ -177,19 +161,17 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* 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
* Return 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 MediaType getContentType() {
String value = getFirst(CONTENT_TYPE);
return value != null ? MediaType.parseMediaType(value) : null;
public long getContentLength() {
String value = getFirst(CONTENT_LENGTH);
return (value != null ? Long.parseLong(value) : -1);
}
/**
* Sets the {@linkplain MediaType media type} of the body, as specified by the <code>Content-Type</code> header.
*
* Set 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) {
@ -199,32 +181,38 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* 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
* Return 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 URI getLocation() {
String value = getFirst(LOCATION);
return value != null ? URI.create(value) : null;
public MediaType getContentType() {
String value = getFirst(CONTENT_TYPE);
return (value != null ? MediaType.parseMediaType(value) : null);
}
/**
* Sets the (new) location of a resource, as specified by the <code>Location</code> header.
*
* Set 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
/**
* Return 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);
}
// Single string methods
/**
* Returns the first header value for the given header name, if any.
*
* Return 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>
*/
@ -234,8 +222,7 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
}
/**
* Adds the given, single header value under the given name.
*
* Add 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
@ -246,14 +233,13 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
List<String> headerValues = headers.get(headerName);
if (headerValues == null) {
headerValues = new LinkedList<String>();
headers.put(headerName, headerValues);
this.headers.put(headerName, headerValues);
}
headerValues.add(headerValue);
}
/**
* Sets the given, single header value under the given name.
*
* Set 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
@ -266,77 +252,77 @@ public final class HttpHeaders implements MultiValueMap<String, String> {
headers.put(headerName, headerValues);
}
/*
* Map implementation
*/
// Map implementation
public int size() {
return headers.size();
return this.headers.size();
}
public boolean isEmpty() {
return headers.isEmpty();
return this.headers.isEmpty();
}
public boolean containsKey(Object key) {
return headers.containsKey(key);
return this.headers.containsKey(key);
}
public boolean containsValue(Object value) {
return headers.containsValue(value);
return this.headers.containsValue(value);
}
public List<String> get(Object key) {
return headers.get(key);
return this.headers.get(key);
}
public List<String> put(String key, List<String> value) {
return headers.put(key, value);
return this.headers.put(key, value);
}
public List<String> remove(Object key) {
return headers.remove(key);
return this.headers.remove(key);
}
public void putAll(Map<? extends String, ? extends List<String>> m) {
headers.putAll(m);
this.headers.putAll(m);
}
public void clear() {
headers.clear();
this.headers.clear();
}
public Set<String> keySet() {
return headers.keySet();
return this.headers.keySet();
}
public Collection<List<String>> values() {
return headers.values();
return this.headers.values();
}
public Set<Entry<String, List<String>>> entrySet() {
return headers.entrySet();
return this.headers.entrySet();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof HttpHeaders)) {
return false;
}
HttpHeaders otherHeaders = (HttpHeaders) other;
return this.headers.equals(otherHeaders.headers);
}
@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;
return this.headers.hashCode();
}
@Override
public String toString() {
return headers.toString();
return this.headers.toString();
}
}

View File

@ -14,14 +14,16 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.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.
* 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
@ -29,8 +31,7 @@ import java.io.InputStream;
public interface HttpInputMessage extends HttpMessage {
/**
* Returns the body of the message as an input stream.
*
* Return the body of the message as an input stream.
* @return the input stream body
* @throws IOException in case of I/O Errors
*/

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
/**
* Represents the base interface for HTTP request and response messages. Consists of {@link HttpHeaders}, retrievable
@ -26,9 +26,9 @@ package org.springframework.web.http;
public interface HttpMessage {
/**
* Returns the headers of this message.
*
* @return the headers
* Return the headers of this message.
* @return a corresponding HttpHeaders object
*/
HttpHeaders getHeaders();
}

View File

@ -14,13 +14,14 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
/**
* Java 5 enumeration of HTTP request methods.
* Java 5 enumeration of HTTP request methods. Intended for use
* with {@link org.springframework.http.client.ClientHttpRequest}
* and {@link org.springframework.web.client.RestTemplate}.
*
* @author Arjen Poutsma
* @see org.springframework.web.bind.annotation.RequestMapping
* @since 3.0
*/
public enum HttpMethod {

View File

@ -14,14 +14,16 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.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.
* 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
@ -29,8 +31,7 @@ import java.io.OutputStream;
public interface HttpOutputMessage extends HttpMessage {
/**
* Returns the body of the message as an output stream.
*
* Return the body of the message as an output stream.
* @return the output stream body
* @throws IOException in case of I/O Errors
*/

View File

@ -14,10 +14,12 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
/**
* Java 5 enumeration of HTTP status codes. <p/> The HTTP status code series can be retrieved via {@link #series()}.
* 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
@ -79,10 +81,59 @@ public enum HttpStatus {
GATEWAY_TIMEOUT(504),
HTTP_VERSION_NOT_SUPPORTED(505);
private final int value;
private HttpStatus(int value) {
this.value = value;
}
/**
* Java 5 enumeration of HTTP status series. <p/> Retrievable via {@link HttpStatus#series()}.
* Return the integer value of this status code.
*/
public enum Series {
public int value() {
return this.value;
}
/**
* Returns the HTTP status series of this status code.
* @see HttpStatus.Series
*/
public Series series() {
return Series.valueOf(this);
}
/**
* Return a string representation of this status code.
*/
@Override
public String toString() {
return Integer.toString(value);
}
/**
* Return 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 + "]");
}
/**
* Java 5 enumeration of HTTP status series.
* <p>Retrievable via {@link HttpStatus#series()}.
*/
public static enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
@ -97,12 +148,10 @@ public enum HttpStatus {
}
/**
* Returns the integer value of this status series. Ranges from 1 to 5.
*
* @return the integer value
* Return the integer value of this status series. Ranges from 1 to 5.
*/
public int value() {
return value;
return this.value;
}
private static Series valueOf(HttpStatus status) {
@ -117,55 +166,4 @@ public enum HttpStatus {
}
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

@ -14,10 +14,11 @@
* limitations under the License.
*/
package org.springframework.util;
package org.springframework.http;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -26,27 +27,32 @@ import java.util.Locale;
import java.util.Map;
import org.springframework.core.CollectionFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Represents an Internet Media Type, as defined in the HTTP specification.
*
* <p>Consists of a {@linkplain #getType() type} and a {@linkplain #getSubtype() subtype}. Also has functionality to
* parse media types from a string using {@link #parseMediaType(String)}, or multiple comma-separated media types using
* {@link #parseMediaTypes(String)}.
* <p>Consists of a {@linkplain #getType() type} and a {@linkplain #getSubtype() subtype}.
* Also has functionality to parse media types from a string using {@link #parseMediaType(String)},
* or multiple comma-separated media types using {@link #parseMediaTypes(String)}.
*
* @author Arjen Poutsma
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP 1.1</a>
* @author Juergen Hoeller
* @since 3.0
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP 1.1</a>
*/
public final class MediaType implements Comparable<MediaType> {
public class MediaType implements Comparable<MediaType> {
public static final MediaType ALL = new MediaType();
public static final MediaType ALL = new MediaType("*", "*");
private static final String WILDCARD_TYPE = "*";
private static final String PARAM_QUALITY_FACTORY = "q";
private static final String PARAM_CHARSET = "charset";
private static final String WILDCARD_TYPE = "*";
private final String type;
@ -54,19 +60,10 @@ public final class MediaType implements Comparable<MediaType> {
private final Map<String, String> parameters;
/**
* Private constructor that creates a new {@link MediaType} representing <code>&#42;&#47;&#42;</code>.
*
* @see #ALL
*/
private MediaType() {
this(WILDCARD_TYPE, WILDCARD_TYPE);
}
/**
* Create a new {@link MediaType} for the given primary type. The {@linkplain #getSubtype() subtype} is set to
* <code>&#42;</code>, parameters empty.
*
* Create a new {@link MediaType} for the given primary type.
* <p>The {@linkplain #getSubtype() subtype} is set to <code>&#42;</code>, parameters empty.
* @param type the primary type
*/
public MediaType(String type) {
@ -74,9 +71,9 @@ public final class MediaType implements Comparable<MediaType> {
}
/**
* Create a new {@link MediaType} for the given primary type and subtype. The parameters are empty.
*
* @param type the primary type
* Create a new {@link MediaType} for the given primary type and subtype.
* <p>The parameters are empty.
* @param typethe primary type
* @param subtype the subtype
*/
public MediaType(String type, String subtype) {
@ -84,9 +81,8 @@ public final class MediaType implements Comparable<MediaType> {
}
/**
* Creates a new {@link MediaType} for the given type, subtype, and character set.
*
* @param type the primary type
* Create a new {@link MediaType} for the given type, subtype, and character set.
* @param type the primary type
* @param subtype the subtype
* @param charSet the character set
*/
@ -95,10 +91,9 @@ public final class MediaType implements Comparable<MediaType> {
}
/**
* Creates a new {@link MediaType} for the given type, subtype, and parameters.
*
* @param type the primary type
* @param subtype the subtype
* Create a new {@link MediaType} for the given type, subtype, and parameters.
* @param type the primary type
* @param subtype the subtype
* @param parameters the parameters, mat be <code>null</code>
*/
public MediaType(String type, String subtype, Map<String, String> parameters) {
@ -115,93 +110,30 @@ public final class MediaType implements Comparable<MediaType> {
}
}
/**
* Parses the given string into a single {@link MediaType}.
*
* @param mediaType the string to parse
* @return the media type
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static MediaType parseMediaType(String mediaType) {
Assert.hasLength(mediaType, "'mediaType' must not be empty");
String[] parts = StringUtils.tokenizeToStringArray(mediaType, ";");
Map<String, String> parameters;
if (parts.length <= 1) {
parameters = null;
}
else {
parameters = new LinkedHashMap<String, String>(parts.length - 1);
}
for (int i = 1; i < parts.length; i++) {
String part = parts[i];
int idx = part.indexOf('=');
if (idx != -1) {
String name = part.substring(0, idx);
String value = part.substring(idx + 1, part.length());
parameters.put(name, value);
}
}
String fullType = parts[0].trim();
// java.net.HttpURLConnection returns a *; q=.2 Accept header
if (WILDCARD_TYPE.equals(fullType)) {
fullType = "*/*";
}
int idx = fullType.indexOf('/');
String type = fullType.substring(0, idx);
String subtype = fullType.substring(idx + 1, fullType.length());
return new MediaType(type, subtype, parameters);
}
/**
* Parses the given, comma-seperated string into a list of {@link MediaType} objects. This method can be used to parse
* an Accept or Content-Type header.
*
* @param mediaTypes the string to parse
* @return the list of media types
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static List<MediaType> parseMediaTypes(String mediaTypes) {
Assert.hasLength(mediaTypes, "'mediaTypes' must not be empty");
String[] tokens = mediaTypes.split(",\\s*");
List<MediaType> result = new ArrayList<MediaType>(tokens.length);
for (String token : tokens) {
result.add(parseMediaType(token));
}
return result;
}
/**
* Returns the primary type.
*
* @return the type
* Return the primary type.
*/
public String getType() {
return type;
return this.type;
}
/**
* Indicates whether the {@linkplain #getType() type} is the wildcard character <code>&#42;</code> or not.
*
* @return whether the type is <code>&#42;</code>
* Indicate whether the {@linkplain #getType() type} is the wildcard character <code>&#42;</code> or not.
*/
public boolean isWildcardType() {
return WILDCARD_TYPE.equals(type);
}
/**
* Returns the subtype.
*
* @return the subtype
* Return the subtype.
*/
public String getSubtype() {
return subtype;
return this.subtype;
}
/**
* Indicates whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>&#42;</code> or not.
*
* Indicate whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>&#42;</code> or not.
* @return whether the subtype is <code>&#42;</code>
*/
public boolean isWildcardSubtype() {
@ -209,39 +141,37 @@ public final class MediaType implements Comparable<MediaType> {
}
/**
* Returns the character set, as indicated by a <code>charset</code> parameter, if any.
*
* Return the character set, as indicated by a <code>charset</code> parameter, if any.
* @return the character set; or <code>null</code> if not available
*/
public Charset getCharSet() {
String charSet = parameters.get(PARAM_CHARSET);
return charSet != null ? Charset.forName(charSet) : null;
String charSet = this.parameters.get(PARAM_CHARSET);
return (charSet != null ? Charset.forName(charSet) : null);
}
/**
* Returns the quality value, as indicated by a <code>q</code> parameter, if any. Defaults to <code>1.0</code>.
*
* Return the quality value, as indicated by a <code>q</code> parameter, if any. Defaults to <code>1.0</code>.
* @return the quality factory
*/
public double getQualityValue() {
String qualityFactory = parameters.get(PARAM_QUALITY_FACTORY);
return qualityFactory != null ? Double.parseDouble(qualityFactory) : 1D;
String qualityFactory = this.parameters.get(PARAM_QUALITY_FACTORY);
return (qualityFactory != null ? Double.parseDouble(qualityFactory) : 1D);
}
/**
* Returns a generic parameter value, given a parameter name.
*
* Return a generic parameter value, given a parameter name.
* @param name the parameter name
* @return the parameter value; or <code>null</code> if not present
*/
public String getParameter(String name) {
return parameters.get(name);
return this.parameters.get(name);
}
/**
* Indicates whether this {@link MediaType} includes the given media type. For instance, <code>text/*</code> includes
* <code>text/plain</code>, <code>text/html</code>, etc.
*
* Indicate whether this {@link MediaType} includes the given media type.
* <p>For instance, <code>text/*</code> includes <code>text/plain</code>,
* <code>text/html</code>, etc.
* @param other the reference media type with which to compare
* @return <code>true</code> if this media type includes the given media type; <code>false</code> otherwise
*/
@ -258,14 +188,13 @@ public final class MediaType implements Comparable<MediaType> {
}
/**
* Compares this {@link MediaType} to another. Sorting with this comparator follows the general rule: <blockquote>
* Compare this {@link MediaType} to another. Sorting with this comparator follows the general rule: <blockquote>
* audio/basic &lt; audio/* &lt; *&#047;* </blockquote>. That is, an explicit media type is sorted before an unspecific
* media type. Quality parameters are also considered, so that <blockquote> audio/* &lt; audio/*;q=0.7;
* audio/*;q=0.3</blockquote>.
*
* @param other the media type to compare to
* @return a negative integer, zero, or a positive integer as this media type is less than, equal to, or greater than
* the specified media type
* @return a negative integer, zero, or a positive integer as this media type is less than, equal to,
* or greater than the specified media type
*/
public int compareTo(MediaType other) {
double qVal1 = this.getQualityValue();
@ -303,23 +232,23 @@ public final class MediaType implements Comparable<MediaType> {
}
@Override
public boolean equals(Object o) {
if (this == o) {
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (o != null && o instanceof MediaType) {
MediaType other = (MediaType) o;
return this.type.equals(other.type) && this.subtype.equals(other.subtype) &&
this.parameters.equals(other.parameters);
if (!(other instanceof MediaType)) {
return false;
}
return false;
MediaType otherType = (MediaType) other;
return (this.type.equals(otherType.type) && this.subtype.equals(otherType.subtype) &&
this.parameters.equals(otherType.parameters));
}
@Override
public int hashCode() {
int result = type.hashCode();
result = 31 * result + subtype.hashCode();
result = 31 * result + parameters.hashCode();
int result = this.type.hashCode();
result = 31 * result + this.subtype.hashCode();
result = 31 * result + this.parameters.hashCode();
return result;
}
@ -330,15 +259,82 @@ public final class MediaType implements Comparable<MediaType> {
return builder.toString();
}
private void appendTo(StringBuilder builder) {
builder.append(this.type);
builder.append('/');
builder.append(this.subtype);
for (Map.Entry<String, String> entry :this. parameters.entrySet()) {
builder.append(';');
builder.append(entry.getKey());
builder.append('=');
builder.append(entry.getValue());
}
}
/**
* Returns a string representation of the given list of {@link MediaType} objects. This method can be used to for an
* Accept or Content-Type header.
*
* Parse the given String into a single {@link MediaType}.
* @param mediaType the string to parse
* @return the media type
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static MediaType parseMediaType(String mediaType) {
Assert.hasLength(mediaType, "'mediaType' must not be empty");
String[] parts = StringUtils.tokenizeToStringArray(mediaType, ";");
Map<String, String> parameters;
if (parts.length <= 1) {
parameters = null;
}
else {
parameters = new LinkedHashMap<String, String>(parts.length - 1);
}
for (int i = 1; i < parts.length; i++) {
String part = parts[i];
int idx = part.indexOf('=');
if (idx != -1) {
String name = part.substring(0, idx);
String value = part.substring(idx + 1, part.length());
parameters.put(name, value);
}
}
String fullType = parts[0].trim();
// java.net.HttpURLConnection returns a *; q=.2 Accept header
if (WILDCARD_TYPE.equals(fullType)) {
fullType = "*/*";
}
int idx = fullType.indexOf('/');
String type = fullType.substring(0, idx);
String subtype = fullType.substring(idx + 1, fullType.length());
return new MediaType(type, subtype, parameters);
}
/**
* Parse the given, comma-seperated string into a list of {@link MediaType} objects.
* <p>This method can be used to parse an Accept or Content-Type header.
* @param mediaTypes the string to parse
* @return the list of media types
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static String toString(List<MediaType> mediaTypes) {
public static List<MediaType> parseMediaTypes(String mediaTypes) {
Assert.hasLength(mediaTypes, "'mediaTypes' must not be empty");
String[] tokens = mediaTypes.split(",\\s*");
List<MediaType> result = new ArrayList<MediaType>(tokens.length);
for (String token : tokens) {
result.add(parseMediaType(token));
}
return result;
}
/**
* Return a string representation of the given list of {@link MediaType} objects.
* <p>This method can be used to for an Accept or Content-Type header.
* @param mediaTypes the string to parse
* @return the list of media types
* @throws IllegalArgumentException if the String cannot be parsed
*/
public static String toString(Collection<MediaType> mediaTypes) {
StringBuilder builder = new StringBuilder();
for (Iterator<MediaType> iterator = mediaTypes.iterator(); iterator.hasNext();) {
MediaType mediaType = iterator.next();
@ -350,15 +346,4 @@ public final class MediaType implements Comparable<MediaType> {
return builder.toString();
}
private void appendTo(StringBuilder builder) {
builder.append(type);
builder.append('/');
builder.append(subtype);
for (Map.Entry<String, String> entry : parameters.entrySet()) {
builder.append(';');
builder.append(entry.getKey());
builder.append('=');
builder.append(entry.getValue());
}
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.http;
import java.beans.PropertyEditorSupport;
import org.springframework.util.StringUtils;
/**
* {@link java.beans.PropertyEditor Editor} for {@link MediaType}
* descriptors, to automatically convert <code>String</code> specifications
* (e.g. <code>"text/html"</code>) to <code>MediaType</code> properties.
*
* @author Juergen Hoeller
* @since 3.0
* @see MediaType
*/
public class MediaTypeEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) {
if (StringUtils.hasText(text)) {
setValue(MediaType.parseMediaType(text));
}
else {
setValue(null);
}
}
@Override
public String getAsText() {
MediaType mediaType = (MediaType) getValue();
return (mediaType != null ? mediaType.toString() : "");
}
}

View File

@ -14,14 +14,14 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpHeaders;
import org.springframework.http.HttpHeaders;
/**
* Abstract base for {@link ClientHttpRequest} that makes sure that headers and body are not written multiple times.
@ -37,31 +37,36 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
private final ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream();
public final HttpHeaders getHeaders() {
checkExecuted();
return headers;
return this.headers;
}
public final OutputStream getBody() throws IOException {
checkExecuted();
return bufferedOutput;
return this.bufferedOutput;
}
public final ClientHttpResponse execute() throws IOException {
checkExecuted();
ClientHttpResponse result = executeInternal(headers, bufferedOutput.toByteArray());
executed = true;
ClientHttpResponse result = executeInternal(this.headers, this.bufferedOutput.toByteArray());
this.executed = true;
return result;
}
private void checkExecuted() {
Assert.state(!this.executed, "ClientHttpRequest already executed");
}
/**
* Abstract template method that writes the given headers and content to the HTTP request.
* @param headers the HTTP headers
* @param bufferedOutput the body content
* @return the response object for the executed request
*/
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
throws IOException;
private void checkExecuted() {
Assert.state(!executed, "ClientRequest already executed");
}
}

View File

@ -14,33 +14,33 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.IOException;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.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.
* 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
* @since 3.0
* @see ClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
*/
public interface ClientHttpRequest extends HttpOutputMessage {
/**
* Returns the HTTP method of the request.
*
* @return the HTTP method
* Return the HTTP method of the request.
* @return the HTTP method as an HttpMethod enum value
*/
HttpMethod getMethod();
/**
* Executes this request, resulting in a {@link ClientHttpResponse} that can be read.
*
* Execute 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
*/

View File

@ -14,16 +14,16 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.IOException;
import java.net.URI;
import org.springframework.web.http.HttpMethod;
import org.springframework.http.HttpMethod;
/**
* Factory for {@link ClientHttpRequest} objects. Requests are created by the {@link #createRequest(URI, HttpMethod)}
* method.
* Factory for {@link ClientHttpRequest} objects.
* Requests are created by the {@link #createRequest(URI, HttpMethod)} method.
*
* @author Arjen Poutsma
* @since 3.0
@ -31,10 +31,10 @@ import org.springframework.web.http.HttpMethod;
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
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* <p>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

View File

@ -14,16 +14,18 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.IOException;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpInputMessage;
import org.springframework.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.
* Represents a client-side HTTP response. Obtained via an calling of the {@link ClientHttpRequest#execute()}.
*
* <p>A <code>ClientHttpResponse</code> must be {@linkplain #close() closed}, typically in a
* <code>finally</code> block.
*
* @author Arjen Poutsma
* @since 3.0
@ -31,16 +33,14 @@ import org.springframework.web.http.HttpStatus;
public interface ClientHttpResponse extends HttpInputMessage {
/**
* Returns the HTTP status code of the response.
*
* @return the HTTP status
* Return the HTTP status code of the response.
* @return the HTTP status as an HttpStatus enum value
* @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 of the response.
* @return the HTTP status text
* @throws IOException in case of I/O errors
*/

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client.commons;
package org.springframework.http.client;
import java.io.IOException;
import java.util.List;
@ -26,16 +26,19 @@ import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.client.AbstractClientHttpRequest;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.AbstractClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
/**
* {@link org.springframework.web.http.client.ClientHttpRequest} implementation that uses Commons Http Client to execute
* requests. Created via the {@link CommonsClientHttpRequestFactory}.
* {@link org.springframework.http.client.ClientHttpRequest} implementation that uses
* Apache Commons HttpClient to execute requests.
*
* <p>Created via the {@link CommonsClientHttpRequestFactory}.
*
* @author Arjen Poutsma
* @since 3.0
* @see CommonsClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
*/
final class CommonsClientHttpRequest extends AbstractClientHttpRequest {
@ -44,7 +47,8 @@ final class CommonsClientHttpRequest extends AbstractClientHttpRequest {
private final HttpMethodBase httpMethod;
CommonsClientHttpRequest(HttpClient httpClient, HttpMethodBase httpMethod) {
public CommonsClientHttpRequest(HttpClient httpClient, HttpMethodBase httpMethod) {
this.httpClient = httpClient;
this.httpMethod = httpMethod;
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client.commons;
package org.springframework.http.client;
import java.io.IOException;
import java.net.URI;
@ -33,17 +33,18 @@ 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;
import org.springframework.http.HttpMethod;
/**
* {@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.
* {@link org.springframework.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
* @since 3.0
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
*/
public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
@ -51,9 +52,10 @@ public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory
private HttpClient httpClient;
/**
* Create a new instance of the <code>CommonsHttpRequestFactory</code> with a default {@link HttpClient} that uses a
* default {@link MultiThreadedHttpConnectionManager}.
* 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());
@ -61,32 +63,32 @@ public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory
}
/**
* 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
* Create a new instance of the <code>CommonsHttpRequestFactory</code> with the given
* {@link HttpClient} instance.
* @param httpClient the HttpClient instance to use for this factory
*/
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.
* Set the <code>HttpClient</code> used by this factory.
*/
public void setHttpClient(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Return the <code>HttpClient</code> used by this factory.
*/
public HttpClient getHttpClient() {
return this.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)
*/
@ -97,52 +99,59 @@ public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory
this.httpClient.getHttpConnectionManager().getParams().setSoTimeout(timeout);
}
public void destroy() throws Exception {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpMethodBase commonsHttpMethod = createCommonsHttpMethod(httpMethod, uri.toString());
postProcessCommonsHttpMethod(commonsHttpMethod);
return new CommonsClientHttpRequest(getHttpClient(), commonsHttpMethod);
}
/**
* Create a Commons HttpMethodBase object for the given HTTP method
* and URI specification.
* @param httpMethod the HTTP method
* @param uri the URI
* @return the Commons HttpMethodBase object
*/
protected HttpMethodBase createCommonsHttpMethod(HttpMethod httpMethod, String uri) {
switch (httpMethod) {
case GET:
return new GetMethod(uri);
case DELETE:
return new DeleteMethod(uri);
case HEAD:
return new HeadMethod(uri);
case OPTIONS:
return new OptionsMethod(uri);
case POST:
return new PostMethod(uri);
case PUT:
return new PutMethod(uri);
case TRACE:
return new TraceMethod(uri);
default:
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
}
}
/**
* Template method that allows for manipulating the {@link org.apache.commons.httpclient.HttpMethodBase}
* before it is returned as part of a {@link CommonsClientHttpRequest}.
* <p>The default implementation is empty.
* @param httpMethod the Commons HTTP method object to process
*/
protected void postProcessCommonsHttpMethod(HttpMethodBase httpMethod) {
}
/**
* Shutdown hook that closes the underlying {@link HttpConnectionManager}'s
* connection pool, if any.
*/
public void destroy() {
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

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client.commons;
package org.springframework.http.client;
import java.io.IOException;
import java.io.InputStream;
@ -22,15 +22,18 @@ 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;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
/**
* {@link org.springframework.web.http.client.ClientHttpResponse} implementation that uses Commons Http Client to
* execute requests. Created via the {@link CommonsClientHttpRequest}.
* {@link org.springframework.http.client.ClientHttpResponse} implementation that uses
* Apache Commons HttpClient to execute requests.
*
* <p>Created via the {@link CommonsClientHttpRequest}.
*
* @author Arjen Poutsma
* @since 3.0
* @see CommonsClientHttpRequest#execute()
*/
final class CommonsClientHttpResponse implements ClientHttpResponse {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.IOException;
import java.net.HttpURLConnection;
@ -22,27 +22,29 @@ import java.util.List;
import java.util.Map;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute requests. Created via the
* {@link SimpleClientHttpRequestFactory}.
* {@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
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
*/
final class SimpleClientHttpRequest extends AbstractClientHttpRequest {
private final HttpURLConnection connection;
SimpleClientHttpRequest(HttpURLConnection connection) {
public SimpleClientHttpRequest(HttpURLConnection connection) {
this.connection = connection;
}
public HttpMethod getMethod() {
return HttpMethod.valueOf(connection.getRequestMethod());
return HttpMethod.valueOf(this.connection.getRequestMethod());
}
@Override
@ -50,12 +52,12 @@ final class SimpleClientHttpRequest extends AbstractClientHttpRequest {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
connection.addRequestProperty(headerName, headerValue);
this.connection.addRequestProperty(headerName, headerValue);
}
}
connection.connect();
FileCopyUtils.copy(bufferedOutput, connection.getOutputStream());
return new SimpleClientHttpResponse(connection);
this.connection.connect();
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
return new SimpleClientHttpResponse(this.connection);
}
}

View File

@ -14,30 +14,28 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.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;
import org.springframework.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
* @see java.net.HttpURLConnection
* @see CommonsClientHttpRequestFactory
*/
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
URL url = uri.toURL();
URLConnection urlConnection = url.openConnection();
URLConnection urlConnection = uri.toURL().openConnection();
Assert.isInstanceOf(HttpURLConnection.class, urlConnection);
HttpURLConnection connection = (HttpURLConnection) urlConnection;
prepareConnection(connection, httpMethod.name());
@ -46,9 +44,7 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
/**
* Template method for preparing the given {@link HttpURLConnection}.
*
* <p>Default implementation prepares the connection for input and output, and sets the HTTP method.
*
* <p>The 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

View File

@ -14,19 +14,19 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.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;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/**
* {@link ClientHttpResponse} implementation that uses standard J2SE facilities. Obtained via the {@link
* SimpleClientHttpRequest#execute()}.
* {@link ClientHttpResponse} implementation that uses standard J2SE facilities.
* Obtained via the {@link SimpleClientHttpRequest#execute()}.
*
* @author Arjen Poutsma
* @since 3.0
@ -37,45 +37,44 @@ final class SimpleClientHttpResponse implements ClientHttpResponse {
private HttpHeaders headers;
SimpleClientHttpResponse(HttpURLConnection connection) {
public SimpleClientHttpResponse(HttpURLConnection connection) {
this.connection = connection;
}
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(connection.getResponseCode());
return HttpStatus.valueOf(this.connection.getResponseCode());
}
public String getStatusText() throws IOException {
return connection.getResponseMessage();
return this.connection.getResponseMessage();
}
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
if (this.headers == null) {
this.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);
String name = this.connection.getHeaderFieldKey(i);
if (!StringUtils.hasLength(name)) {
break;
}
headers.add(name, connection.getHeaderField(i));
this.headers.add(name, this.connection.getHeaderField(i));
i++;
}
}
return headers;
return this.headers;
}
public InputStream getBody() throws IOException {
if (connection.getErrorStream() == null) {
return connection.getInputStream();
}
else {
return connection.getErrorStream();
}
InputStream errorStream = this.connection.getErrorStream();
return (errorStream != null ? errorStream : this.connection.getInputStream());
}
public void close() {
connection.disconnect();
this.connection.disconnect();
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.client.support;
package org.springframework.http.client.support;
import java.io.IOException;
import java.net.URI;
@ -23,19 +23,21 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
import org.springframework.web.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
/**
* Base class for {@link org.springframework.web.client.core.RestTemplate} and other HTTP accessing gateway helpers, defining
* common properties such as the {@link ClientHttpRequestFactory} to operate on.
* <p/>
* Not intended to be used directly. See {@link org.springframework.web.client.core.RestTemplate}.
* Base class for {@link org.springframework.web.client.RestTemplate}
* and other HTTP accessing gateway helpers, defining common properties
* such as the {@link ClientHttpRequestFactory} to operate on.
*
* <p>Not intended to be used directly. See {@link org.springframework.web.client.RestTemplate}.
*
* @author Arjen Poutsma
* @see org.springframework.web.client.core.RestTemplate
* @since 3.0
* @see org.springframework.web.client.RestTemplate
*/
public abstract class HttpAccessor {
@ -46,15 +48,9 @@ public abstract class HttpAccessor {
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
/**
* Returns the request factory that this accessor uses for obtaining {@link ClientHttpRequest HttpRequests}
*/
public ClientHttpRequestFactory getRequestFactory() {
return requestFactory;
}
/**
* Sets the request factory that this accessor uses for obtaining {@link ClientHttpRequest HttpRequests}
* Set the request factory that this accessor uses for obtaining {@link ClientHttpRequest HttpRequests}.
*/
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "'requestFactory' must not be null");
@ -62,9 +58,16 @@ public abstract class HttpAccessor {
}
/**
* Creates a new {@link ClientHttpRequest} via this template's {@link ClientHttpRequestFactory}.
*
* @param url the URL to connect to
* Return the request factory that this accessor uses for obtaining {@link ClientHttpRequest HttpRequests}.
*/
public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
}
/**
* Create a new {@link ClientHttpRequest} via this template's {@link ClientHttpRequestFactory}.
* @param url the URL to connect to
* @param method the HTTP method to exectute (GET, POST, etc.)
* @return the created request
* @throws IOException in case of I/O errors
@ -76,4 +79,5 @@ public abstract class HttpAccessor {
}
return request;
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
This package provides generic HTTP support classes,
to be used by higher-level classes like RestTemplate.
</body>
</html>

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.IOException;
import java.util.ArrayList;
@ -26,69 +26,66 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.MediaType;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
/**
* Abstract base class for most {@link HttpMessageConverter} implementations.
*
* <p>This base class adds support for setting supported {@code MediaTypes}, through the {@link
* #setSupportedMediaTypes(List) supportedMediaTypes} bean property. It also adds support for {@code Content-Type} and
* {@code Content-Length} when writing to output messages.
* #setSupportedMediaTypes(List) supportedMediaTypes} bean property. It also adds support for
* {@code Content-Type} and {@code Content-Length} when writing to output messages.
*
* @author Arjen Poutsma
* @since 3.0
*/
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
/**
* Logger available to subclasses.
*/
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private List<MediaType> supportedMediaTypes = Collections.emptyList();
/**
* Constructs an {@code AbstractHttpMessageConverter} with no supported media types.
*
* @see #setSupportedMediaTypes(List)
* Construct an {@code AbstractHttpMessageConverter} with no supported media types.
* @see #setSupportedMediaTypes
*/
protected AbstractHttpMessageConverter() {
}
/**
* Constructs an {@code AbstractHttpMessageConverter} with one supported media type.
* Construct an {@code AbstractHttpMessageConverter} with one supported media type.
*/
protected AbstractHttpMessageConverter(MediaType supportedMediaType) {
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
this.supportedMediaTypes = Collections.singletonList(supportedMediaType);
}
/**
* Constructs an {@code AbstractHttpMessageConverter} with multiple supported media type.
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
*/
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) {
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
this.supportedMediaTypes = Arrays.asList(supportedMediaTypes);
}
public List<MediaType> getSupportedMediaTypes() {
return Collections.unmodifiableList(supportedMediaTypes);
}
/**
* Sets the list of {@link MediaType} objects supported by this converter.
* Set the list of {@link MediaType} objects supported by this converter.
*/
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
Assert.notEmpty(supportedMediaTypes, "'supportedMediaTypes' must not be empty");
this.supportedMediaTypes = new ArrayList<MediaType>(supportedMediaTypes);
}
public List<MediaType> getSupportedMediaTypes() {
return Collections.unmodifiableList(this.supportedMediaTypes);
}
/**
* {@inheritDoc}
*
* <p>This implementation delegates to {@link #getContentType(Object)} and {@link #getContentLength(Object)}, and sets
* the corresponding headers on the output message. It then calls {@link #writeToInternal(Object, HttpOutputMessage)}.
*
* <p>This implementation delegates to {@link #getContentType(Object)} and {@link #getContentLength(Object)},
* and sets the corresponding headers on the output message. It then calls
* {@link #writeToInternal(Object, HttpOutputMessage)}.
* @throws HttpMessageConversionException in case of conversion errors
*/
public final void write(T t, HttpOutputMessage outputMessage) throws IOException {
@ -107,23 +104,19 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
/**
* Returns the content type for the given type.
*
* <p>By default, this returns the first element of the {@link #setSupportedMediaTypes(List) supportedMediaTypes}
* property, if any. Can be overriden in subclasses.
*
* @param t the type to return the content type for
* @return the content type, or <code>null</code> if not known
*/
protected MediaType getContentType(T t) {
List<MediaType> mediaTypes = getSupportedMediaTypes();
return !mediaTypes.isEmpty() ? mediaTypes.get(0) : null;
return (!mediaTypes.isEmpty() ? mediaTypes.get(0) : null);
}
/**
* Returns the content length for the given type.
*
* <p>By default, this returns <code>null</code>. Can be overriden in subclasses.
*
* @param t the type to return the content length for
* @return the content length, or <code>null</code> if not known
*/
@ -133,10 +126,9 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
/**
* Abstract template method that writes the actualy body. Invoked from {@link #write(Object, HttpOutputMessage)}.
*
* @param t the object to write to the output message
* @param t the object to write to the output message
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws IOException in case of I/O errors
* @throws HttpMessageConversionException in case of conversion errors
*/
protected abstract void writeToInternal(T t, HttpOutputMessage outputMessage) throws IOException;

View File

@ -14,15 +14,15 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MediaType;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
/**
* Implementation of {@link HttpMessageConverter} that can read and write byte arrays.
@ -45,7 +45,7 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
}
public boolean supports(Class<? extends byte[]> clazz) {
return byte[].class == clazz;
return byte[].class.equals(clazz);
}
public byte[] read(Class<byte[]> clazz, HttpInputMessage inputMessage) throws IOException {
@ -75,5 +75,4 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
FileCopyUtils.copy(bytes, outputMessage.getBody());
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import org.springframework.core.NestedRuntimeException;
@ -28,7 +28,6 @@ public class HttpMessageConversionException extends NestedRuntimeException {
/**
* Create a new MessageConversionException.
*
* @param msg the detail message
*/
public HttpMessageConversionException(String msg) {
@ -37,11 +36,11 @@ public class HttpMessageConversionException extends NestedRuntimeException {
/**
* Create a new MessageConversionException.
*
* @param msg the detail message
* @param msg the detail message
* @param cause the root cause (if any)
*/
public HttpMessageConversionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -14,17 +14,17 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.IOException;
import java.util.List;
import org.springframework.util.MediaType;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
/**
* Strategy interface that specifies a converter can convert from and to HTTP request and responses.
* Strategy interface that specifies a converter can convert from and to HTTP requests and responses.
*
* @author Arjen Poutsma
* @since 3.0
@ -32,39 +32,34 @@ import org.springframework.web.http.HttpOutputMessage;
public interface HttpMessageConverter<T> {
/**
* Indicates whether the given class is supported by this converter.
*
* <p>Typically implemented using an {@code instanceof} check.
*
* Indicate whether the given class is supported by this converter.
* @param clazz the class to test for support
* @return <code>true</code> if supported; <code>false</code> otherwise
*/
boolean supports(Class<? extends T> clazz);
/**
* Returns the list of {@link MediaType} objects supported by this converter.
* Return the list of {@link MediaType} objects supported by this converter.
*/
List<MediaType> getSupportedMediaTypes();
/**
* Reads an object of the given type form the given input message, and returns it.
*
* @param clazz the type of object to return
* Read an object of the given type form the given input message, and returns it.
* @param clazz the type of object to return
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws IOException in case of I/O errors
* @throws HttpMessageConversionException in case of conversion errors
*/
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException;
/**
* Writes an given object to the given output message.
*
* @param t the object to write to the output message
* Write an given object to the given output message.
* @param t the object to write to the output message
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws IOException in case of I/O errors
* @throws HttpMessageConversionException in case of conversion errors
*/
void write(T t, HttpOutputMessage outputMessage) throws IOException;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.IOException;
import java.io.InputStreamReader;
@ -25,9 +25,9 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MediaType;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
/**
* Implementation of {@link HttpMessageConverter} that can read and write strings.
@ -45,11 +45,13 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
private final List<Charset> availableCharsets;
public StringHttpMessageConverter() {
super(new MediaType("text", "plain", DEFAULT_CHARSET), new MediaType("text", "*"));
availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
this.availableCharsets = new ArrayList<Charset>(Charset.availableCharsets().values());
}
public boolean supports(Class<? extends String> clazz) {
return String.class.equals(clazz);
}
@ -86,14 +88,12 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
}
/**
* Returns the list of supported {@link Charset}.
*
* Return the list of supported {@link Charset}.
* <p>By default, returns {@link Charset#availableCharsets()}. Can be overridden in subclasses.
*
* @return the list of accepted charsets
*/
protected List<Charset> getAcceptedCharsets() {
return availableCharsets;
return this.availableCharsets;
}
}

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> interfaces.
</body>
</html>

View File

@ -14,10 +14,10 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import org.springframework.web.http.HttpInputMessage;
import org.springframework.web.http.HttpMethod;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
/**
* Represents a server-side HTTP request.
@ -28,9 +28,8 @@ import org.springframework.web.http.HttpMethod;
public interface ServerHttpRequest extends HttpInputMessage {
/**
* Returns the HTTP method of the request.
*
* @return the http method
* Return the HTTP method of the request.
* @return the HTTP method as an HttpMethod enum value
*/
HttpMethod getMethod();

View File

@ -14,10 +14,10 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import org.springframework.web.http.HttpOutputMessage;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
/**
* Represents a server-side HTTP response.
@ -28,14 +28,13 @@ import org.springframework.web.http.HttpStatus;
public interface ServerHttpResponse extends HttpOutputMessage {
/**
* Sets the HTTP status code of the response.
*
* @param status the HTTP status
* Set the HTTP status code of the response.
* @param status the HTTP status as an HttpStatus enum value
*/
void setStatusCode(HttpStatus status);
/**
* Closes this response, freeing any resources created.
* Close this response, freeing any resources created.
*/
void close();

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import java.io.IOException;
import java.io.InputStream;
@ -22,8 +22,8 @@ import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* {@link ServerHttpRequest} implementation that is based on a {@link HttpServletRequest}.
@ -37,36 +37,37 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
private HttpHeaders headers;
/**
* Constructs a new instance of the <code>ServletHttpRequest</code> based on the given {@link HttpServletRequest}
*
* @param servletRequest the HTTP Servlet request
* Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}
* @param servletRequest the HttpServletRequest
*/
public ServletServerHttpRequest(HttpServletRequest servletRequest) {
Assert.notNull(servletRequest, "'servletRequest' must not be null");
this.servletRequest = servletRequest;
}
public HttpMethod getMethod() {
return HttpMethod.valueOf(servletRequest.getMethod());
return HttpMethod.valueOf(this.servletRequest.getMethod());
}
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
for (Enumeration headerNames = servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
if (this.headers == null) {
this.headers = new HttpHeaders();
for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
String headerName = (String) headerNames.nextElement();
for (Enumeration headerValues = servletRequest.getHeaders(headerName);
headerValues.hasMoreElements();) {
for (Enumeration headerValues = this.servletRequest.getHeaders(headerName); headerValues.hasMoreElements();) {
String headerValue = (String) headerValues.nextElement();
headers.add(headerName, headerValue);
this.headers.add(headerName, headerValue);
}
}
}
return headers;
return this.headers;
}
public InputStream getBody() throws IOException {
return servletRequest.getInputStream();
return this.servletRequest.getInputStream();
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import java.io.IOException;
import java.io.OutputStream;
@ -23,8 +23,8 @@ import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/**
* {@link ServerHttpResponse} implementation that is based on a {@link HttpServletResponse}.
@ -40,9 +40,9 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
private boolean headersWritten = false;
/**
* Constructs a new instance of the <code>ServletHttpResponse</code> based on the given {@link HttpServletResponse}
*
* Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
* @param servletResponse the HTTP Servlet response
*/
public ServletServerHttpResponse(HttpServletResponse servletResponse) {
@ -50,32 +50,34 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
this.servletResponse = servletResponse;
}
public void setStatusCode(HttpStatus status) {
servletResponse.setStatus(status.value());
this.servletResponse.setStatus(status.value());
}
public HttpHeaders getHeaders() {
return headers;
return this.headers;
}
public OutputStream getBody() throws IOException {
writeHeaders();
return servletResponse.getOutputStream();
}
private void writeHeaders() {
if (!headersWritten) {
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
servletResponse.addHeader(headerName, headerValue);
}
}
headersWritten = true;
}
return this.servletResponse.getOutputStream();
}
public void close() {
writeHeaders();
}
private void writeHeaders() {
if (!this.headersWritten) {
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
String headerName = entry.getKey();
for (String headerValue : entry.getValue()) {
this.servletResponse.addHeader(headerName, headerValue);
}
}
this.headersWritten = true;
}
}
}

View File

@ -14,28 +14,28 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpClientException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.http.HttpStatus;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
/**
* Default implementation of the {@link HttpErrorHandler} interface.
* Default implementation of the {@link ResponseErrorHandler} interface.
*
* <p>This error handler checks for the status code on the {@link ClientHttpResponse}: any code with series
* {@link HttpStatus.Series#CLIENT_ERROR} or {@link HttpStatus.Series#SERVER_ERROR} is considered to be an error.
* This behavior can be changed by overriding the {@link #hasError(HttpStatus)} method.
*
* @author Arjen Poutsma
* @see RestTemplate#setErrorHandler(HttpErrorHandler)
* @since 3.0
* @see RestTemplate#setErrorHandler
*/
public class SimpleHttpErrorHandler implements HttpErrorHandler {
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
/**
* Delegates to {@link #hasError(HttpStatus)} with the response status code.
@ -46,18 +46,16 @@ public class SimpleHttpErrorHandler implements HttpErrorHandler {
/**
* Template method called from {@link #hasError(ClientHttpResponse)}.
*
* <p>Default implementation checks if the given status code is {@link HttpStatus.Series#CLIENT_ERROR} or {@link
* HttpStatus.Series#SERVER_ERROR}. Can be overridden in subclasses.
*
* <p>The default implementation checks if the given status code is {@link HttpStatus.Series#CLIENT_ERROR}
* or {@link HttpStatus.Series#SERVER_ERROR}. Can be overridden in subclasses.
* @param statusCode the HTTP status code
* @return <code>true</code> if the response has an error; <code>false</code> otherwise
* @see HttpStatus.Series#CLIENT_ERROR
* @see HttpStatus.Series#SERVER_ERROR
*/
protected boolean hasError(HttpStatus statusCode) {
return statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
statusCode.series() == HttpStatus.Series.SERVER_ERROR;
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}
public void handleError(ClientHttpResponse response) throws IOException {
@ -68,7 +66,8 @@ public class SimpleHttpErrorHandler implements HttpErrorHandler {
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, response.getStatusText());
default:
throw new HttpClientException("Unknown status code [" + statusCode + "]");
throw new RestClientException("Unknown status code [" + statusCode + "]");
}
}
}

View File

@ -16,20 +16,19 @@
package org.springframework.web.client;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpStatus;
/**
* Exception thrown when a HTTP 4xx is received.
*
* @author Arjen Poutsma
* @see org.springframework.web.client.core.SimpleHttpErrorHandler
* @since 3.0
* @see DefaultResponseErrorHandler
*/
public class HttpClientErrorException extends HttpStatusCodeException {
/**
* Constructs a new instance of {@code HttpClientErrorException} based on a {@link HttpStatus}.
*
* Construct a new instance of {@code HttpClientErrorException} based on a {@link HttpStatus}.
* @param statusCode the status code
*/
public HttpClientErrorException(HttpStatus statusCode) {
@ -37,12 +36,12 @@ public class HttpClientErrorException extends HttpStatusCodeException {
}
/**
* Constructs a new instance of {@code HttpClientErrorException} based on a {@link HttpStatus} and status text.
*
* Construct a new instance of {@code HttpClientErrorException} based on a {@link HttpStatus} and status text.
* @param statusCode the status code
* @param statusText the status text
*/
public HttpClientErrorException(HttpStatus statusCode, String statusText) {
super(statusCode, statusText);
}
}

View File

@ -16,20 +16,19 @@
package org.springframework.web.client;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpStatus;
/**
* Exception thrown when a HTTP 5xx is received.
*
* @author Arjen Poutsma
* @see org.springframework.web.client.core.SimpleHttpErrorHandler
* @since 3.0
* @see DefaultResponseErrorHandler
*/
public class HttpServerErrorException extends HttpStatusCodeException {
/**
* Constructs a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus}.
*
* Construct a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus}.
* @param statusCode the status code
*/
public HttpServerErrorException(HttpStatus statusCode) {
@ -37,8 +36,7 @@ public class HttpServerErrorException extends HttpStatusCodeException {
}
/**
* Constructs a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus} and status text.
*
* Construct a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus} and status text.
* @param statusCode the status code
* @param statusText the status text
*/

View File

@ -16,23 +16,23 @@
package org.springframework.web.client;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpStatus;
/**
* Abstract base class for exceptions based on a {@link HttpStatus}.
* Abstract base class for exceptions based on an {@link HttpStatus}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public abstract class HttpStatusCodeException extends HttpClientException {
public abstract class HttpStatusCodeException extends RestClientException {
private final HttpStatus statusCode;
private final String statusText;
/**
* Constructs a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus}.
*
* Construct a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus}.
* @param statusCode the status code
*/
protected HttpStatusCodeException(HttpStatus statusCode) {
@ -42,8 +42,7 @@ public abstract class HttpStatusCodeException extends HttpClientException {
}
/**
* Constructs a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus} and status text.
*
* Construct a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus} and status text.
* @param statusCode the status code
* @param statusText the status text
*/
@ -53,17 +52,19 @@ public abstract class HttpStatusCodeException extends HttpClientException {
this.statusText = statusText;
}
/**
* Returns the HTTP status code.
*/
public HttpStatus getStatusCode() {
return statusCode;
return this.statusCode;
}
/**
* Returns the HTTP status text.
*/
public String getStatusText() {
return statusText;
return this.statusText;
}
}

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import org.springframework.web.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequest;
/**
* Callback interface for code that operates on a {@link ClientHttpRequest}. Allows to manipulate the request
@ -30,13 +30,12 @@ import org.springframework.web.http.client.ClientHttpRequest;
* @see RestTemplate#execute
* @since 3.0
*/
public interface HttpRequestCallback {
public interface RequestCallback {
/**
* Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}. Does not need to care about
* closing the request, handling I/O errors, or about handling errors: this will all be handled by the {@code
* RestTemplate}.
*
* Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}.
* Does not need to care about closing the request or about handling errors:
* this will all be handled by the {@code RestTemplate}.
* @param request the active HTTP request
* @throws IOException in case of I/O errors
*/

View File

@ -19,29 +19,27 @@ package org.springframework.web.client;
import java.io.IOException;
/**
* Exception thrown when a I/O error occurs.
* Exception thrown when an I/O error occurs.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class HttpIOException extends HttpClientException {
public class ResourceAccessException extends RestClientException {
/**
* Constructs a new {@code HttpIOException} with the given message.
*
* Construct a new {@code HttpIOException} with the given message.
* @param msg the message
*/
public HttpIOException(String msg) {
public ResourceAccessException(String msg) {
super(msg);
}
/**
* Constructs a new {@code HttpIOException} with the given message and {@link IOException}.
*
* Construct a new {@code HttpIOException} with the given message and {@link IOException}.
* @param msg the message
* @param ex the {@code IOException}
* @param ex the {@code IOException}
*/
public HttpIOException(String msg, IOException ex) {
public ResourceAccessException(String msg, IOException ex) {
super(msg, ex);
}

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.http.client.ClientHttpResponse;
/**
* Strategy interface used by the {@link RestTemplate} to determine whether a particular response has an error or not.
@ -26,13 +26,12 @@ import org.springframework.web.http.client.ClientHttpResponse;
* @author Arjen Poutsma
* @since 3.0
*/
public interface HttpErrorHandler {
public interface ResponseErrorHandler {
/**
* Indicates whether the given response has any errors.
*
* Implementations will typically inspect the {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
*
* Implementations will typically inspect the {@link ClientHttpResponse#getStatusCode() HttpStatus}
* of the response.
* @param response the response to inspect
* @return <code>true</code> if the response has an error; <code>false</code> otherwise
* @throws IOException in case of I/O errors
@ -41,13 +40,9 @@ public interface HttpErrorHandler {
/**
* Handles the error in the given response.
*
* This method is only called when {@link #hasError(ClientHttpResponse)} has returned <code>true</code>.
*
* @param response the response with the error
* @throws IOException in case of I/O errors
* @throws org.springframework.web.client.HttpClientException
* typically thrown by implementations of this interface
*/
void handleError(ClientHttpResponse response) throws IOException;
}

View File

@ -14,28 +14,28 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.http.client.ClientHttpResponse;
/**
* Generic callback interface used by {@link RestTemplate}'s retrieval methods. Implementations of this interface
* perform the actual work of extracting data from a {@link ClientHttpResponse}, but don't need to worry about exception
* Generic callback interface used by {@link RestTemplate}'s retrieval methods
* Implementations of this interface perform the actual work of extracting data
* from a {@link ClientHttpResponse}, but don't need to worry about exception
* handling or closing resources.
*
* <p>Used internally by the {@link RestTemplate}, but also useful for application code.
*
* @author Arjen Poutsma
* @see RestTemplate#execute
* @since 3.0
* @see RestTemplate#execute
*/
public interface HttpResponseExtractor<T> {
public interface ResponseExtractor<T> {
/**
* Extracts data from the given {@code ClientHttpResponse} and returns it.
*
* Extract data from the given {@code ClientHttpResponse} and return it.
* @param response the HTTP response
* @return the extracted data
* @throws IOException in case of I/O errors

View File

@ -19,29 +19,28 @@ package org.springframework.web.client;
import org.springframework.core.NestedRuntimeException;
/**
* Base class for exceptions thrown by the framework whenever it encounters client-side HTTP errors.
* Base class for exceptions thrown by {@link RestTemplate} whenever it encounters client-side HTTP errors.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class HttpClientException extends NestedRuntimeException {
public class RestClientException extends NestedRuntimeException {
/**
* Constructs a new instance of {@code HttpClientException} with the given message.
*
* Construct a new instance of {@code HttpClientException} with the given message.
* @param msg the message
*/
public HttpClientException(String msg) {
public RestClientException(String msg) {
super(msg);
}
/**
* Constructs a new instance of {@code HttpClientException} with the given message and exception.
*
* Construct a new instance of {@code HttpClientException} with the given message and exception.
* @param msg the message
* @param ex the exception
* @param ex the exception
*/
public HttpClientException(String msg, Throwable ex) {
public RestClientException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@ -0,0 +1,205 @@
/*
* 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.client;
import java.net.URI;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* Interface specifying a basic set of RESTful operations.
* Implemented by {@link RestTemplate}. Not often used directly, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Arjen Poutsma
* @since 3.0
* @see RestTemplate
*/
public interface RestOperations {
// GET
/**
* Retrieve a representation by doing a GET on the specified URL.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the converted object
*/
<T> T getForObject(String uri, Class<T> responseType, String... uriVariables)
throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URI template.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the converted object
*/
<T> T getForObject(String uri, Class<T> responseType, Map<String, String> uriVariables)
throws RestClientException;
// HEAD
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param uriVariables the variables to expand the template
* @return all HTTP headers of that resource
*/
HttpHeaders headForHeaders(String uri, String... uriVariables) throws RestClientException;
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param uriVariables the map containing variables for the URI template
* @return all HTTP headers of that resource
*/
HttpHeaders headForHeaders(String uri, Map<String, String> uriVariables) throws RestClientException;
// POST
/**
* Create a new resource by POSTing the given object to the URI template. The value of the <code>Location</code>,
* indicating where the new resource is stored, is returned.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param request the Object to be POSTED
* @return the value for the <code>Location</code> header
*/
URI postForLocation(String uri, Object request, String... uriVariables)
throws RestClientException;
/**
* Create a new resource by POSTing the given object to URI template. The value of the <code>Location</code>,
* indicating where the new resource is stored, is returned.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
*/
URI postForLocation(String uri, Object request, Map<String, String> uriVariables)
throws RestClientException;
// PUT
/**
* Create or update a resource by PUTting the given object to the URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
*/
void put(String uri, Object request, String... uriVariables) throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URI template.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
*/
void put(String uri, Object request, Map<String, String> uriVariables) throws RestClientException;
// DELETE
/**
* Delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param uriVariables the variables to expand in the template
*/
void delete(String uri, String... uriVariables) throws RestClientException;
/**
* Delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param uriVariables the variables to expand the template
*/
void delete(String uri, Map<String, String> uriVariables) throws RestClientException;
// OPTIONS
/**
* Return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
Set<HttpMethod> optionsForAllow(String uri, String... uriVariables)
throws RestClientException;
/**
* Return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given map.
* @param uri the URI
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
Set<HttpMethod> optionsForAllow(String uri, Map<String, String> uriVariables)
throws RestClientException;
// general execution
/**
* Execute the HTTP methods to the given URI, preparing the request with the {@link RequestCallback},
* and reading the response with a {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param uri the URI
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> T execute(String uri, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, String... uriVariables)
throws RestClientException;
/**
* Execute the HTTP methods to the given URI, preparing the request with the {@link RequestCallback},
* and reading the response with a {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables map.
* @param uri the URI
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariablesthe variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
<T> T execute(String uri, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, String> uriVariables)
throws RestClientException;
}

View File

@ -14,29 +14,27 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpAccessor;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.MediaType;
import org.springframework.web.client.HttpClientException;
import org.springframework.web.client.HttpIOException;
import org.springframework.web.client.support.HttpAccessor;
import org.springframework.web.converter.ByteArrayHttpMessageConverter;
import org.springframework.web.converter.HttpMessageConverter;
import org.springframework.web.converter.StringHttpMessageConverter;
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.ClientHttpRequestFactory;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.web.util.UriTemplate;
/**
@ -76,72 +74,67 @@ import org.springframework.web.util.UriTemplate;
* your own converter and register it via the {@link #setMessageConverters(HttpMessageConverter[]) messageConverters}
* bean property.
*
* <p>This template uses a {@link org.springframework.web.http.client.SimpleClientHttpRequestFactory} and a {@link
* SimpleHttpErrorHandler} as default strategies for for creating HTTP connections or handling HTTP errors, respectively.
* <p>This template uses a {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a {@link
* DefaultResponseErrorHandler} as default strategies for for creating HTTP connections or handling HTTP errors, respectively.
* These defaults can be overridden through the {@link #setRequestFactory(ClientHttpRequestFactory) requestFactory} and
* {@link #setErrorHandler(HttpErrorHandler) errorHandler} bean properties.
* {@link #setErrorHandler(ResponseErrorHandler) errorHandler} bean properties.
*
* @author Arjen Poutsma
* @see HttpMessageConverter
* @see HttpRequestCallback
* @see HttpResponseExtractor
* @see HttpErrorHandler
* @since 3.0
* @see HttpMessageConverter
* @see RequestCallback
* @see ResponseExtractor
* @see ResponseErrorHandler
*/
public class RestTemplate extends HttpAccessor implements RestOperations {
private final HttpResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
private HttpMessageConverter<?>[] messageConverters;
private HttpMessageConverter<?>[] messageConverters =
new HttpMessageConverter[] {new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter()};
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
private HttpErrorHandler errorHandler;
/**
* Creates a new instance of the {@link RestTemplate} using default settings.
*
* Create a new instance of the {@link RestTemplate} using default settings.
* @see #initDefaultStrategies()
*/
public RestTemplate() {
initDefaultStrategies();
}
/**
* Creates a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
*
* Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
* @param requestFactory HTTP request factory to use
* @see org.springframework.web.http.client.SimpleClientHttpRequestFactory
* @see org.springframework.web.http.client.commons.CommonsClientHttpRequestFactory
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
* @see org.springframework.http.client.CommonsClientHttpRequestFactory
*/
public RestTemplate(ClientHttpRequestFactory requestFactory) {
initDefaultStrategies();
setRequestFactory(requestFactory);
}
/**
* Initializes the default stragegies for this template.
*
* <p>Default implementation sets up the {@link SimpleHttpErrorHandler} and the {@link ByteArrayHttpMessageConverter} and
* {@link StringHttpMessageConverter}.
* Set the message body converters to use. These converters are used to convert
* from and to HTTP requests and responses.
*/
protected void initDefaultStrategies() {
errorHandler = new SimpleHttpErrorHandler();
messageConverters =
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter()};
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
}
/**
* Returns the array of message body converters. These converters are used to covert from and to HTTP requests and
* responses.
* Returnsthe message body converters. These converters are used to convert
* from and to HTTP requests and responses.
*/
public HttpMessageConverter<?>[] getMessageConverters() {
return messageConverters;
return this.messageConverters;
}
/**
* Returns the list of message body converters that support a particular type.
*
* Returns the message body converters that support a particular type.
* @param type the type to return converters for
* @return converts that support the given type
* @return converters that support the given type
*/
@SuppressWarnings("unchecked")
protected <T> List<HttpMessageConverter<T>> getSupportedMessageConverters(Class<T> type) {
@ -156,122 +149,128 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
}
/**
* Sets the array of message body converters to use. These converters are used to covert from and to HTTP requests and
* responses.
*
* <strong>Note</strong> that setting this property overrides the {@linkplain #initDefaultStrategies() default strategies}.
* Set the error handler.
*/
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
}
/**
* Returns the error handler. By default, this is the {@link SimpleHttpErrorHandler}.
*/
public HttpErrorHandler getErrorHandler() {
return errorHandler;
}
/**
* Sets the error handler.
*/
public void setErrorHandler(HttpErrorHandler errorHandler) {
public void setErrorHandler(ResponseErrorHandler errorHandler) {
Assert.notNull(errorHandler, "'errorHandler' must not be null");
this.errorHandler = errorHandler;
}
/**
* Return the error handler. By default, this is the {@link DefaultResponseErrorHandler}.
*/
public ResponseErrorHandler getErrorHandler() {
return this.errorHandler;
}
// GET
public <T> T getForObject(String url, Class<T> responseType, String... urlVariables) {
checkForSupportedEntityConverter(responseType);
public <T> T getForObject(String url, Class<T> responseType, String... urlVariables)
throws RestClientException {
checkForSupportedMessageConverter(responseType);
return execute(url, HttpMethod.GET, new GetCallback<T>(responseType),
new HttpMessageConverterExtractor<T>(responseType), urlVariables);
}
public <T> T getForObject(String url, Class<T> responseType, Map<String, String> urlVariables) {
checkForSupportedEntityConverter(responseType);
public <T> T getForObject(String url, Class<T> responseType, Map<String, String> urlVariables)
throws RestClientException {
checkForSupportedMessageConverter(responseType);
return execute(url, HttpMethod.GET, new GetCallback<T>(responseType),
new HttpMessageConverterExtractor<T>(responseType), urlVariables);
}
// POST
public URI postForLocation(String url, Object request, String... urlVariables) {
checkForSupportedEntityConverter(request.getClass());
HttpHeaders headers =
execute(url, HttpMethod.POST, new PostPutCallback(request), headersExtractor, urlVariables);
return headers.getLocation();
}
public URI postForLocation(String url, Object request, Map<String, String> urlVariables) {
checkForSupportedEntityConverter(request.getClass());
HttpHeaders headers =
execute(url, HttpMethod.POST, new PostPutCallback(request), headersExtractor, urlVariables);
return headers.getLocation();
}
// PUT
public void put(String url, Object request, String... urlVariables) {
checkForSupportedEntityConverter(request.getClass());
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
}
public void put(String url, Object request, Map<String, String> urlVariables) {
checkForSupportedEntityConverter(request.getClass());
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
}
// HEAD
public HttpHeaders headForHeaders(String url, String... urlVariables) {
return execute(url, HttpMethod.HEAD, null, headersExtractor, urlVariables);
public HttpHeaders headForHeaders(String url, String... urlVariables) throws RestClientException {
return execute(url, HttpMethod.HEAD, null, this.headersExtractor, urlVariables);
}
public HttpHeaders headForHeaders(String url, Map<String, String> urlVariables) {
return execute(url, HttpMethod.HEAD, null, headersExtractor, urlVariables);
public HttpHeaders headForHeaders(String url, Map<String, String> urlVariables) throws RestClientException {
return execute(url, HttpMethod.HEAD, null, this.headersExtractor, urlVariables);
}
// POST
public URI postForLocation(String url, Object request, String... urlVariables)
throws RestClientException {
checkForSupportedMessageConverter(request.getClass());
HttpHeaders headers =
execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables);
return headers.getLocation();
}
public URI postForLocation(String url, Object request, Map<String, String> urlVariables)
throws RestClientException {
checkForSupportedMessageConverter(request.getClass());
HttpHeaders headers =
execute(url, HttpMethod.POST, new PostPutCallback(request), this.headersExtractor, urlVariables);
return headers.getLocation();
}
// PUT
public void put(String url, Object request, String... urlVariables) throws RestClientException {
checkForSupportedMessageConverter(request.getClass());
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
}
public void put(String url, Object request, Map<String, String> urlVariables) throws RestClientException {
checkForSupportedMessageConverter(request.getClass());
execute(url, HttpMethod.PUT, new PostPutCallback(request), null, urlVariables);
}
// DELETE
public void delete(String url, String... urlVariables) {
public void delete(String url, String... urlVariables) throws RestClientException {
execute(url, HttpMethod.DELETE, null, null, urlVariables);
}
public void delete(String url, Map<String, String> urlVariables) {
public void delete(String url, Map<String, String> urlVariables) throws RestClientException {
execute(url, HttpMethod.DELETE, null, null, urlVariables);
}
// OPTIONS
public EnumSet<HttpMethod> optionsForAllow(String url, String... urlVariables) {
HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, urlVariables);
public Set<HttpMethod> optionsForAllow(String url, String... urlVariables)
throws RestClientException {
HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, this.headersExtractor, urlVariables);
return headers.getAllow();
}
public EnumSet<HttpMethod> optionsForAllow(String url, Map<String, String> urlVariables) {
HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, urlVariables);
public Set<HttpMethod> optionsForAllow(String url, Map<String, String> urlVariables)
throws RestClientException {
HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, this.headersExtractor, urlVariables);
return headers.getAllow();
}
// execute
public <T> T execute(String url,
HttpMethod method,
HttpRequestCallback requestCallback,
HttpResponseExtractor<T> responseExtractor,
String... urlVariables) {
// general execution
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, String... urlVariables)
throws RestClientException {
UriTemplate uriTemplate = new UriTemplate(url);
URI expanded = uriTemplate.expand(urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
public <T> T execute(String url,
HttpMethod method,
HttpRequestCallback requestCallback,
HttpResponseExtractor<T> responseExtractor,
Map<String, String> urlVariables) {
public <T> T execute(String url,HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, String> urlVariables)
throws RestClientException {
UriTemplate uriTemplate = new UriTemplate(url);
URI expanded = uriTemplate.expand(urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
@ -279,18 +278,16 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
/**
* Execute the given method on the provided URI. The {@link ClientHttpRequest} is processed using the {@link
* HttpRequestCallback}; the response with the {@link HttpResponseExtractor}.
*
* @param url the fully-expanded URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @param requestCallback object that prepares the request. Can be <code>null</code>.
* @param responseExtractor object that extracts the return value from the response. Can be <code>null</code>.
* @return an arbitrary object, as returned by the {@link HttpResponseExtractor}
* RequestCallback}; the response with the {@link ResponseExtractor}.
* @param url the fully-expanded URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @param requestCallback object that prepares the request (can be <code>null</code>)
* @param responseExtractor object that extracts the return value from the response (can be <code>null</code>)
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
protected <T> T doExecute(URI url,
HttpMethod method,
HttpRequestCallback requestCallback,
HttpResponseExtractor<T> responseExtractor) {
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
@ -311,7 +308,7 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
}
}
catch (IOException ex) {
throw new HttpIOException("I/O error: " + ex.getMessage(), ex);
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally {
if (response != null) {
@ -321,14 +318,13 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
}
/**
* Checks whether any of the registered {@linkplain #setMessageConverters(HttpMessageConverter[]) message body
* Check whether any of the registered {@linkplain #setMessageConverters(HttpMessageConverter[]) message body
* converters} can convert the given type.
*
* @param type the type to check for
* @throws IllegalArgumentException if no supported entity converter can be found
* @see HttpMessageConverter#supports(Class)
*/
private void checkForSupportedEntityConverter(Class type) {
private void checkForSupportedMessageConverter(Class type) {
for (HttpMessageConverter<?> entityConverter : getMessageConverters()) {
if (entityConverter.supports(type)) {
return;
@ -337,30 +333,11 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
throw new IllegalArgumentException("Could not resolve HttpMessageConverter for [" + type.getName() + "]");
}
/**
* Request callback implementation that sets the <code>Accept</code> header based on the registered {@linkplain
* HttpMessageConverter entity converters}.
* Request callback implementation that prepares the request's accept headers.
*/
private class AcceptHeaderCallback implements HttpRequestCallback {
public void doWithRequest(ClientHttpRequest request) throws IOException {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> entityConverter : getMessageConverters()) {
List<MediaType> supportedMediaTypes = entityConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
allSupportedMediaTypes.add(supportedMediaType);
}
}
Collections.sort(allSupportedMediaTypes);
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
private class GetCallback<T> implements HttpRequestCallback {
private class GetCallback<T> implements RequestCallback {
private final Class<T> responseType;
@ -370,7 +347,7 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
public void doWithRequest(ClientHttpRequest request) throws IOException {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> entityConverter : getSupportedMessageConverters(responseType)) {
for (HttpMessageConverter<?> entityConverter : getSupportedMessageConverters(this.responseType)) {
List<MediaType> supportedMediaTypes = entityConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
@ -385,10 +362,11 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
}
}
/**
* Extension of {@link AcceptHeaderCallback} that writes the given object to the request stream.
* Request callback implementation that writes the given object to the request stream.
*/
private class PostPutCallback implements HttpRequestCallback {
private class PostPutCallback implements RequestCallback {
private final Object request;
@ -398,19 +376,17 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
@SuppressWarnings("unchecked")
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
for (HttpMessageConverter entityConverter : getSupportedMessageConverters(request.getClass())) {
entityConverter.write(request, httpRequest);
break;
}
HttpMessageConverter entityConverter = getSupportedMessageConverters(this.request.getClass()).get(0);
entityConverter.write(this.request, httpRequest);
}
}
/**
* Response extractor that uses the registered {@linkplain HttpMessageConverter entity converters} to convert the
* response into a type <code>T</code>.
* Response extractor that uses the registered {@linkplain HttpMessageConverter entity converters}
* to convert the response into a type <code>T</code>.
*/
private class HttpMessageConverterExtractor<T> implements HttpResponseExtractor<T> {
private class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
private final Class<T> responseType;
@ -421,29 +397,31 @@ public class RestTemplate extends HttpAccessor implements RestOperations {
public T extractData(ClientHttpResponse response) throws IOException {
MediaType contentType = response.getHeaders().getContentType();
if (contentType == null) {
throw new HttpClientException("Cannot extract response: no Content-Type found");
throw new RestClientException("Cannot extract response: no Content-Type found");
}
for (HttpMessageConverter<T> messageConverter : getSupportedMessageConverters(responseType)) {
for (HttpMessageConverter<T> messageConverter : getSupportedMessageConverters(this.responseType)) {
for (MediaType supportedMediaType : messageConverter.getSupportedMediaTypes()) {
if (supportedMediaType.includes(contentType)) {
return messageConverter.read(responseType, response);
return messageConverter.read(this.responseType, response);
}
}
}
throw new HttpClientException(
throw new RestClientException(
"Could not extract response: no suitable HttpMessageConverter found for " + "response type [" +
responseType.getName() + "] and content type [" + contentType + "]");
this.responseType.getName() + "] and content type [" + contentType + "]");
}
}
/**
* Response extractor that extracts the response {@link HttpHeaders}.
*/
private static class HeadersExtractor implements HttpResponseExtractor<HttpHeaders> {
private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> {
public HttpHeaders extractData(ClientHttpResponse response) throws IOException {
return response.getHeaders();
}
}
}

View File

@ -1,206 +0,0 @@
/*
* 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.client.core;
import java.net.URI;
import java.util.EnumSet;
import java.util.Map;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
/**
* Interface specifying a basic set of RESTful operations. Implemented by {@link RestTemplate}. Not often used directly,
* but a useful option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Arjen Poutsma
* @see RestTemplate
* @since 3.0
*/
public interface RestOperations {
// GET
/**
* Retrieves a representation by doing a GET on the specified URL. URI Template variables are expanded using the
* given URI variables, if any.
*
* @param uri the URI to GET
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the converted object
*/
<T> T getForObject(String uri, Class<T> responseType, String... uriVariables);
/**
* Retrieves a representation by doing a GET on the URI template. URI Template variables are expanded using the
* given map.
*
* @param uri the URI to GET
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the converted object
*/
<T> T getForObject(String uri, Class<T> responseType, Map<String, String> uriVariables);
// HEAD
/**
* Retrieves all headers of the resource specified by the URI template. URI Template variables are expanded using
* the given URI variables, if any.
*
* @param uri the URI
* @param uriVariables the variables to expand the template
* @return all HTTP headers of that resource
*/
HttpHeaders headForHeaders(String uri, String... uriVariables);
/**
* Retrieves all headers of the resource specified by the URI template. URI Template variables are expanded using
* the given map.
*
* @param uri the URI
* @param uriVariables the map containing variables for the URI template
* @return all HTTP headers of that resource
*/
HttpHeaders headForHeaders(String uri, Map<String, String> uriVariables);
// POST
/**
* Creates a new resource by POSTing the given object to the URI template. The value of the <code>Location</code>,
* indicating where the new resource is stored, is returned. URI Template variables are expanded using the given URI
* variables, if any.
*
* @param uri the URI
* @param request the Object to be POSTED
* @return the value for the <code>Location</code> header
*/
URI postForLocation(String uri, Object request, String... uriVariables);
/**
* Creates a new resource by POSTing the given object to URI template. The value of the <code>Location</code>,
* indicating where the new resource is stored, is returned. URI Template variables are expanded using the given
* map.
*
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
*/
URI postForLocation(String uri, Object request, Map<String, String> uriVariables);
// PUT
/**
* Creates or updates a resource by PUTting the given object to the URI. URI Template variables are expanded using
* the given URI variables, if any.
*
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
*/
void put(String uri, Object request, String... uriVariables);
/**
* Creates a new resource by PUTting the given object to URI template. URI Template variables are expanded using the
* given map.
*
* @param uri the URI
* @param request the Object to be POSTed
* @param uriVariables the variables to expand the template
*/
void put(String uri, Object request, Map<String, String> uriVariables);
// DELETE
/**
* Deletes the resources at the specified URI. URI Template variables are expanded using the given URI variables, if
* any.
*
* @param uri the URI
* @param uriVariables the variables to expand in the template
*/
void delete(String uri, String... uriVariables);
/**
* Deletes the resources at the specified URI. URI Template variables are expanded using the given map.
*
* @param uri the URI
* @param uriVariables the variables to expand the template
*/
void delete(String uri, Map<String, String> uriVariables);
//OPTIONS
/**
* Returns value of the Allow header for the given URI. URI Template variables are expanded using the given URI
* variables, if any.
*
* @param uri the URI
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
EnumSet<HttpMethod> optionsForAllow(String uri, String... uriVariables);
/**
* Returns value of the Allow header for the given URI. URI Template variables are expanded using the given map.
*
* @param uri the URI
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
EnumSet<HttpMethod> optionsForAllow(String uri, Map<String, String> uriVariables);
/**
* Executes the HTTP methods to the given URI, preparing the request with the {@link HttpRequestCallback}, and
* reading the response with a {@link HttpResponseExtractor}. URI Template variables are expanded using the
* given URI variables, if any.
*
* @param uri the URI
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link HttpResponseExtractor}
*/
<T> T execute(String uri,
HttpMethod method,
HttpRequestCallback requestCallback,
HttpResponseExtractor<T> responseExtractor,
String... uriVariables);
/**
* Executes the HTTP methods to the given URI, preparing the request with the {@link HttpRequestCallback}, and
* reading the response with a {@link HttpResponseExtractor}. URI Template variables are expanded using the
* given URI variables map.
*
* @param uri the URI
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link HttpResponseExtractor}
*/
<T> T execute(String uri,
HttpMethod method,
HttpRequestCallback requestCallback,
HttpResponseExtractor<T> responseExtractor,
Map<String, String> uriVariables);
}

View File

@ -1,8 +0,0 @@
<html>
<body>
Core package of the client-side HTTP support.
Provides a RestTemplate class and various callback interfaces.
</body>
</html>

View File

@ -1,8 +0,0 @@
<html>
<body>
Classes supporting the org.springframework.web.client.core package.
Contains a base class for RestTemplate usage.
</body>
</html>

View File

@ -1,7 +1,8 @@
<html>
<body>
This package contains integration classes for client-side access of HTTP services.
Core package of the client-side web support.
Provides a RestTemplate class and various callback interfaces.
</body>
</html>

View File

@ -14,48 +14,42 @@
* limitations under the License.
*/
package org.springframework.web.client.core.support;
package org.springframework.web.client.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.web.client.core.RestTemplate;
import org.springframework.web.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.client.ClientHttpRequestFactory;
/**
* Convenient super class for application classes that need REST access.
*
* <p>Requires a {@link ClientHttpRequestFactory} or a {@link RestTemplate} instance to be set. It will create its own
* JmsTemplate if a ConnectionFactory is passed in. A custom JmsTemplate instance can be created for a given
* ConnectionFactory through overriding the <code>createJmsTemplate</code> method.
* <p>Requires a {@link ClientHttpRequestFactory} or a {@link RestTemplate} instance to be set.
*
* @author Arjen Poutsma
* @see #setRestTemplate(RestTemplate)
* @see RestTemplate
* @since 3.0
* @see #setRestTemplate
* @see org.springframework.web.client.RestTemplate
*/
public class RestGatewaySupport {
/**
* Logger available to subclasses.
*/
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private RestTemplate restTemplate;
/**
* Constructs a new instance of the {@link RestGatewaySupport}, with default parameters.
*
* @see RestTemplate#RestTemplate()
* Construct a new instance of the {@link RestGatewaySupport}, with default parameters.
*/
public RestGatewaySupport() {
restTemplate = new RestTemplate();
this.restTemplate = new RestTemplate();
}
/**
* Constructs a new instance of the {@link RestGatewaySupport}, with the given {@link ClientHttpRequestFactory}.
*
* Construct a new instance of the {@link RestGatewaySupport}, with the given {@link ClientHttpRequestFactory}.
* @see RestTemplate#RestTemplate(ClientHttpRequestFactory
*/
public RestGatewaySupport(ClientHttpRequestFactory requestFactory) {
@ -63,12 +57,6 @@ public class RestGatewaySupport {
this.restTemplate = new RestTemplate(requestFactory);
}
/**
* Returns the {@link RestTemplate} for the gateway.
*/
public RestTemplate getRestTemplate() {
return restTemplate;
}
/**
* Sets the {@link RestTemplate} for the gateway.
@ -78,4 +66,11 @@ public class RestGatewaySupport {
this.restTemplate = restTemplate;
}
/**
* Returns the {@link RestTemplate} for the gateway.
*/
public RestTemplate getRestTemplate() {
return this.restTemplate;
}
}

View File

@ -1,8 +1,8 @@
<html>
<body>
This package provides generic HTTP support classes,
to be used by higher-level classes like RestTemplate.
Classes supporting the <code>org.springframework.web.client</code> package.
Contains a base class for RestTemplate usage.
</body>
</html>

View File

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

View File

@ -1,8 +0,0 @@
<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

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
import java.net.URI;
import java.net.URISyntaxException;
@ -27,7 +27,7 @@ import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.MediaType;
import org.springframework.http.MediaType;
/**
* @author Arjen Poutsma

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.util;
package org.springframework.http;
import java.util.ArrayList;
import java.util.Collections;
@ -26,7 +26,7 @@ import org.junit.Test;
/**
* @author Arjen Poutsma
*/
public class MediaTypeTest {
public class MediaTypeTests {
@Test
public void includes() throws Exception {
@ -118,4 +118,5 @@ public class MediaTypeTest {
}
}
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
import java.io.ByteArrayInputStream;
import java.io.IOException;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http;
package org.springframework.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
import java.io.IOException;
import java.net.URI;
@ -38,8 +38,8 @@ 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;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
public abstract class AbstractHttpRequestFactoryTestCase {

View File

@ -14,15 +14,16 @@
* limitations under the License.
*/
package org.springframework.web.http.client.commons;
package org.springframework.http.client;
import org.springframework.web.http.client.AbstractHttpRequestFactoryTestCase;
import org.springframework.web.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.AbstractHttpRequestFactoryTestCase;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.CommonsClientHttpRequestFactory;
public class CommonsHttpRequestFactoryTest extends AbstractHttpRequestFactoryTestCase {
public class CommonsHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
@Override
protected ClientHttpRequestFactory createRequestFactory() {
return new CommonsClientHttpRequestFactory();
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.client;
package org.springframework.http.client;
public class SimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.IOException;
@ -22,9 +22,9 @@ import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.MediaType;
import org.springframework.web.http.MockHttpInputMessage;
import org.springframework.web.http.MockHttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
/**
* @author Arjen Poutsma

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.converter;
package org.springframework.http.converter;
import java.io.IOException;
import java.nio.charset.Charset;
@ -24,9 +24,9 @@ import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.MediaType;
import org.springframework.web.http.MockHttpInputMessage;
import org.springframework.web.http.MockHttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.MockHttpInputMessage;
import org.springframework.http.MockHttpOutputMessage;
/**
* @author Arjen Poutsma

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import java.util.List;
@ -24,8 +24,8 @@ import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* @author Arjen Poutsma

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.http.server;
package org.springframework.http.server;
import java.util.List;
@ -24,8 +24,8 @@ import org.junit.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/**
* @author Arjen Poutsma

View File

@ -14,12 +14,13 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.EnumSet;
import java.util.Set;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@ -37,11 +38,9 @@ import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.CommonsClientHttpRequestFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.client.commons.CommonsClientHttpRequestFactory;
/**
* @author Arjen Poutsma
@ -103,11 +102,12 @@ public class RestTemplateIntegrationTests {
@Test
public void optionsForAllow() {
EnumSet<HttpMethod> allowed = template.optionsForAllow("http://localhost:8889/get");
Set<HttpMethod> allowed = template.optionsForAllow("http://localhost:8889/get");
assertEquals("Invalid response",
EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), allowed);
}
/**
* Servlet that returns and error message for a given status code.
*/
@ -125,6 +125,7 @@ public class RestTemplateIntegrationTests {
}
}
private static class GetServlet extends HttpServlet {
private final byte[] buf;
@ -145,6 +146,7 @@ public class RestTemplateIntegrationTests {
}
}
private static class PostServlet extends HttpServlet {
private final String s;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.client.core;
package org.springframework.web.client;
import java.io.IOException;
import java.net.URI;
@ -22,31 +22,29 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.MediaType;
import org.springframework.web.client.HttpClientException;
import org.springframework.web.client.HttpIOException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.converter.ByteArrayHttpMessageConverter;
import org.springframework.web.converter.HttpMessageConverter;
import org.springframework.web.converter.StringHttpMessageConverter;
import org.springframework.web.http.HttpHeaders;
import org.springframework.web.http.HttpMethod;
import org.springframework.web.http.HttpStatus;
import org.springframework.web.http.client.ClientHttpRequest;
import org.springframework.web.http.client.ClientHttpRequestFactory;
import org.springframework.web.http.client.ClientHttpResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
/**
* @author Arjen Poutsma
*/
@SuppressWarnings("unchecked")
public class RestTemplateTest {
public class RestTemplateTests {
private RestTemplate template;
@ -56,7 +54,7 @@ public class RestTemplateTest {
private ClientHttpResponse response;
private HttpErrorHandler errorHandler;
private ResponseErrorHandler errorHandler;
private HttpMessageConverter converter;
@ -65,7 +63,7 @@ public class RestTemplateTest {
requestFactory = createMock(ClientHttpRequestFactory.class);
request = createMock(ClientHttpRequest.class);
response = createMock(ClientHttpResponse.class);
errorHandler = createMock(HttpErrorHandler.class);
errorHandler = createMock(ResponseErrorHandler.class);
converter = createMock(HttpMessageConverter.class);
template = new RestTemplate(requestFactory);
template.setErrorHandler(errorHandler);
@ -200,7 +198,7 @@ public class RestTemplateTest {
template.getForObject("http://example.com/{p}", String.class, "resource");
fail("UnsupportedMediaTypeException expected");
}
catch (HttpClientException ex) {
catch (RestClientException ex) {
// expected
}
verifyMocks();
@ -309,7 +307,7 @@ public class RestTemplateTest {
replayMocks();
EnumSet<HttpMethod> result = template.optionsForAllow("http://example.com");
Set<HttpMethod> result = template.optionsForAllow("http://example.com");
assertEquals("Invalid OPTIONS result", expected, result);
verifyMocks();
@ -330,7 +328,7 @@ public class RestTemplateTest {
template.getForObject("http://example.com/resource", String.class);
fail("RestClientException expected");
}
catch (HttpIOException ex) {
catch (ResourceAccessException ex) {
// expected
}