http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab15d45b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java deleted file mode 100644 index 231218e..0000000 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java +++ /dev/null @@ -1,2100 +0,0 @@ -// *************************************************************************************************************************** -// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * -// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * -// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * -// * with the License. You may obtain a copy of the License at * -// * * -// * http://www.apache.org/licenses/LICENSE-2.0 * -// * * -// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * -// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * -// * specific language governing permissions and limitations under the License. * -// *************************************************************************************************************************** -package org.apache.juneau.rest.client; - -import static org.apache.juneau.internal.ClassUtils.*; -import static org.apache.juneau.internal.IOUtils.*; -import static org.apache.juneau.internal.StringUtils.*; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import java.util.regex.*; - -import org.apache.http.*; -import org.apache.http.client.*; -import org.apache.http.client.config.*; -import org.apache.http.client.entity.*; -import org.apache.http.client.methods.*; -import org.apache.http.client.utils.*; -import org.apache.http.entity.*; -import org.apache.http.impl.client.*; -import org.apache.http.util.*; -import org.apache.juneau.*; -import org.apache.juneau.encoders.*; -import org.apache.juneau.internal.*; -import org.apache.juneau.internal.ObjectUtils; -import org.apache.juneau.parser.*; -import org.apache.juneau.parser.ParseException; -import org.apache.juneau.serializer.*; -import org.apache.juneau.urlencoding.*; -import org.apache.juneau.utils.*; - -/** - * Represents a connection to a remote REST resource. - * - * <p> - * Instances of this class are created by the various {@code doX()} methods on the {@link RestClient} class. - * - * <p> - * This class uses only Java standard APIs. Requests can be built up using a fluent interface with method chaining, - * like so... - * <p class='bcode'> - * RestClient client = <jk>new</jk> RestClient(); - * RestCall c = client.doPost(<jsf>URL</jsf>).setInput(o).setHeader(x,y); - * MyBean b = c.getResponse(MyBean.<jk>class</jk>); - * </p> - * - * <p> - * The actual connection and request/response transaction occurs when calling one of the <code>getResponseXXX()</code> - * methods. - * - * <h5 class='section'>Additional information:</h5> - * <ul> - * <li> - * <a class="doclink" href="package-summary.html#RestClient">org.apache.juneau.rest.client > REST client - * API</a> for more information and code examples. - * </ul> - */ -@SuppressWarnings({ "hiding", "unchecked" }) -public final class RestCall { - - private final RestClient client; // The client that created this call. - private final HttpRequestBase request; // The request. - private HttpResponse response; // The response. - private List<RestCallInterceptor> intercepters = new ArrayList<RestCallInterceptor>(); // Used for intercepting and altering requests. - - private boolean isConnected = false; // connect() has been called. - private boolean allowRedirectsOnPosts; - private int retries = 1; - private int redirectOnPostsTries = 5; - private long retryInterval = -1; - private RetryOn retryOn; - private boolean ignoreErrors; - private boolean byLines = false; - private TeeWriter writers = new TeeWriter(); - private StringWriter capturedResponseWriter; - private String capturedResponse; - private TeeOutputStream outputStreams = new TeeOutputStream(); - private boolean isClosed = false; - private boolean isFailed = false; - private Object input; - private boolean hasInput; // input() was called, even if it's setting 'null'. - private Serializer serializer; - private Parser parser; - private URIBuilder uriBuilder; - private NameValuePairs formData; - - /** - * Constructs a REST call with the specified method name. - * - * @param client The client that created this request. - * @param request The wrapped Apache HTTP client request object. - * @param uri The URI for this call. - * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. - */ - protected RestCall(RestClient client, HttpRequestBase request, URI uri) throws RestCallException { - this.client = client; - this.request = request; - for (RestCallInterceptor i : this.client.intercepters) - intercepter(i); - this.retryOn = client.retryOn; - this.retries = client.retries; - this.retryInterval = client.retryInterval; - this.serializer = client.serializer; - this.parser = client.parser; - uriBuilder = new URIBuilder(uri); - } - - /** - * Sets the URI for this call. - * - * <p> - * Can be any of the following types: - * <ul> - * <li>{@link URI} - * <li>{@link URL} - * <li>{@link URIBuilder} - * <li>Anything else converted to a string using {@link Object#toString()}. - * </ul> - * - * <p> - * Relative URL strings will be interpreted as relative to the root URL defined on the client. - * - * @param uri - * The URI to use for this call. - * This overrides the URI passed in from the client. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall uri(Object uri) throws RestCallException { - try { - if (uri != null) - uriBuilder = new URIBuilder(client.toURI(uri)); - return this; - } catch (URISyntaxException e) { - throw new RestCallException(e); - } - } - - /** - * Sets the URI scheme. - * - * @param scheme The new URI host. - * @return This object (for method chaining). - */ - public RestCall scheme(String scheme) { - uriBuilder.setScheme(scheme); - return this; - } - - /** - * Sets the URI host. - * - * @param host The new URI host. - * @return This object (for method chaining). - */ - public RestCall host(String host) { - uriBuilder.setHost(host); - return this; - } - - /** - * Sets the URI port. - * - * @param port The new URI port. - * @return This object (for method chaining). - */ - public RestCall port(int port) { - uriBuilder.setPort(port); - return this; - } - - /** - * Adds a query parameter to the URI query. - * - * @param name - * The parameter name. - * Can be null/blank/* if the value is a {@link Map}, {@link String}, {@link NameValuePairs}, or bean. - * @param value - * The parameter value converted to a string using UON notation. - * Can also be {@link Map}, {@link String}, {@link NameValuePairs}, or bean if the name is null/blank/*. - * If a {@link String} and the name is null/blank/*, then calls {@link URIBuilder#setCustomQuery(String)}. - * @param skipIfEmpty Don't add the pair if the value is empty. - * @param partSerializer - * The part serializer to use to convert the value to a string. - * If <jk>null</jk>, then the URL-encoding serializer defined on the client is used. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall query(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException { - if (partSerializer == null) - partSerializer = client.getPartSerializer(); - if (! ("*".equals(name) || isEmpty(name))) { - if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty)) - uriBuilder.addParameter(name, partSerializer.serialize(PartType.QUERY, value)); - } else if (value instanceof NameValuePairs) { - for (NameValuePair p : (NameValuePairs)value) - query(p.getName(), p.getValue(), skipIfEmpty, UrlEncodingSerializer.DEFAULT_PLAINTEXT); - } else if (value instanceof Map) { - for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) - query(p.getKey(), p.getValue(), skipIfEmpty, partSerializer); - } else if (isBean(value)) { - return query(name, toBeanMap(value), skipIfEmpty, partSerializer); - } else if (value instanceof Reader) { - try { - uriBuilder.setCustomQuery(read(value)); - } catch (IOException e) { - throw new RestCallException(e); - } - } else if (value instanceof CharSequence) { - String s = value.toString(); - if (! isEmpty(s)) - uriBuilder.setCustomQuery(s); - } else { - throw new FormattedRuntimeException("Invalid name ''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value)); - } - return this; - } - - /** - * Adds a query parameter to the URI query. - * - * @param name The parameter name. - * @param value The parameter value converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall query(String name, Object value) throws RestCallException { - return query(name, value, false, null); - } - - /** - * Adds query parameters to the URI query. - * - * @param params The parameters. Values are converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall query(Map<String,Object> params) throws RestCallException { - return query(null, params); - } - - /** - * Adds a query parameter to the URI query if the parameter value is not <jk>null</jk> or an empty string. - * - * <p> - * NE = "not empty" - * - * @param name The parameter name. - * @param value The parameter value converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall queryIfNE(String name, Object value) throws RestCallException { - return query(name, value, true, null); - } - - /** - * Adds query parameters to the URI for any parameters that aren't null/empty. - * - * <p> - * NE = "not empty" - * - * @param params The parameters. Values are converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall queryIfNE(Map<String,Object> params) throws RestCallException { - return query(null, params, true, null); - } - - /** - * Sets a custom URI query. - * - * @param query The new URI query string. - * @return This object (for method chaining). - */ - public RestCall query(String query) { - uriBuilder.setCustomQuery(query); - return this; - } - - /** - * Adds a form data pair to this request to perform a URL-encoded form post. - * - * @param name - * The parameter name. - * Can be null/blank/* if the value is a {@link Map}, {@link NameValuePairs}, or bean. - * @param value - * The parameter value converted to a string using UON notation. - * Can also be {@link Map}, {@link NameValuePairs}, or bean if the name is null/blank/*. - * @param skipIfEmpty Don't add the pair if the value is empty. - * @param partSerializer - * The part serializer to use to convert the value to a string. - * If <jk>null</jk>, then the URL-encoding serializer defined on the client is used. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall formData(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException { - if (formData == null) - formData = new NameValuePairs(); - if (partSerializer == null) - partSerializer = client.getPartSerializer(); - if (! ("*".equals(name) || isEmpty(name))) { - if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty)) - formData.add(new SerializedNameValuePair(name, value, partSerializer)); - } else if (value instanceof NameValuePairs) { - for (NameValuePair p : (NameValuePairs)value) - if (p.getValue() != null && ! (isEmpty(p.getValue()) && skipIfEmpty)) - formData.add(p); - } else if (value instanceof Map) { - for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) - formData(p.getKey(), p.getValue(), skipIfEmpty, partSerializer); - } else if (isBean(value)) { - return formData(name, toBeanMap(value), skipIfEmpty, partSerializer); - } else if (value instanceof Reader) { - contentType("application/x-www-form-urlencoded"); - input(value); - } else if (value instanceof CharSequence) { - try { - contentType("application/x-www-form-urlencoded"); - input(new StringEntity(value.toString())); - } catch (UnsupportedEncodingException e) {} - } else { - throw new FormattedRuntimeException("Invalid name ''{0}'' passed to formData(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value)); - } - return this; - } - - /** - * Adds a form data pair to this request to perform a URL-encoded form post. - * - * @param name - * The parameter name. - * Can be null/blank if the value is a {@link Map} or {@link NameValuePairs}. - * @param value - * The parameter value converted to a string using UON notation. - * Can also be a {@link Map} or {@link NameValuePairs}. - * @return This object (for method chaining). - * @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}. - */ - public RestCall formData(String name, Object value) throws RestCallException { - return formData(name, value, false, null); - } - - /** - * Adds form data pairs to this request to perform a URL-encoded form post. - * - * @param nameValuePairs The name-value pairs of the request. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall formData(NameValuePairs nameValuePairs) throws RestCallException { - return formData(null, nameValuePairs); - } - - /** - * Adds form data pairs to this request to perform a URL-encoded form post. - * - * @param params The parameters. Values are converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}. - */ - public RestCall formData(Map<String,Object> params) throws RestCallException { - return formData(null, params); - } - - /** - * Adds a form data pair to the request if the parameter value is not <jk>null</jk> or an empty string. - * - * <p> - * NE = "not empty" - * - * @param name The parameter name. - * @param value The parameter value converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall formDataIfNE(String name, Object value) throws RestCallException { - return formData(name, value, true, null); - } - - /** - * Adds form data parameters to the request for any parameters that aren't null/empty. - * - * <p> - * NE = "not empty" - * - * @param params The parameters. Values are converted to a string using UON notation. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall formDataIfNE(Map<String,Object> params) throws RestCallException { - return formData(null, params, true, null); - } - - /** - * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value. - * - * @param name The path variable name. - * @param value The replacement value. - * @param partSerializer - * The part serializer to use to convert the value to a string. - * If <jk>null</jk>, then the URL-encoding serializer defined on the client is used. - * @return This object (for method chaining). - * @throws RestCallException If variable could not be found in path. - */ - public RestCall path(String name, Object value, PartSerializer partSerializer) throws RestCallException { - String path = uriBuilder.getPath(); - if (partSerializer == null) - partSerializer = client.getPartSerializer(); - if (! ("*".equals(name) || isEmpty(name))) { - String var = "{" + name + "}"; - if (path.indexOf(var) == -1) - throw new RestCallException("Path variable {"+name+"} was not found in path."); - String newPath = path.replace(var, partSerializer.serialize(PartType.PATH, value)); - uriBuilder.setPath(newPath); - } else if (value instanceof NameValuePairs) { - for (NameValuePair p : (NameValuePairs)value) - path(p.getName(), p.getValue(), partSerializer); - } else if (value instanceof Map) { - for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) - path(p.getKey(), p.getValue(), partSerializer); - } else if (isBean(value)) { - return path(name, toBeanMap(value), partSerializer); - } else if (value != null) { - throw new FormattedRuntimeException("Invalid name ''{0}'' passed to path(name,value) for data type ''{1}''", name, getReadableClassNameForObject(value)); - } - return this; - } - - /** - * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value. - * - * @param name The path variable name. - * @param value The replacement value. - * @return This object (for method chaining). - * @throws RestCallException If variable could not be found in path. - */ - public RestCall path(String name, Object value) throws RestCallException { - return path(name, value, null); - } - - /** - * Sets the URI user info. - * - * @param userInfo The new URI user info. - * @return This object (for method chaining). - */ - public RestCall userInfo(String userInfo) { - uriBuilder.setUserInfo(userInfo); - return this; - } - - /** - * Sets the URI user info. - * - * @param username The new URI username. - * @param password The new URI password. - * @return This object (for method chaining). - */ - public RestCall userInfo(String username, String password) { - uriBuilder.setUserInfo(username, password); - return this; - } - - /** - * Sets the input for this REST call. - * - * @param input - * The input to be sent to the REST resource (only valid for PUT and POST) requests. <br> - * Can be of the following types: - * <ul class='spaced-list'> - * <li> - * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. - * <li> - * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. - * <li> - * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the - * {@link RestClient}. - * <li> - * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. - * <li> - * {@link NameValuePairs} - Converted to a URL-encoded FORM post. - * </ul> - * @return This object (for method chaining). - * @throws RestCallException If a retry was attempted, but the entity was not repeatable. - */ - public RestCall input(final Object input) throws RestCallException { - this.input = input; - this.hasInput = true; - this.formData = null; - return this; - } - - /** - * Specifies the serializer to use on this call. - * - * <p> - * Overrides the serializer specified on the {@link RestClient}. - * - * @param serializer The serializer used to serialize POJOs to the body of the HTTP request. - * @return This object (for method chaining). - */ - public RestCall serializer(Serializer serializer) { - this.serializer = serializer; - return this; - } - - /** - * Specifies the parser to use on this call. - * - * <p> - * Overrides the parser specified on the {@link RestClient}. - * - * @param parser The parser used to parse POJOs from the body of the HTTP response. - * @return This object (for method chaining). - */ - public RestCall parser(Parser parser) { - this.parser = parser; - return this; - } - - - //-------------------------------------------------------------------------------- - // HTTP headers - //-------------------------------------------------------------------------------- - - /** - * Sets a header on the request. - * - * @param name - * The header name. - * The name can be null/empty if the value is a {@link Map}. - * @param value The header value. - * @param skipIfEmpty Don't add the header if the name is null/empty. - * @param partSerializer - * The part serializer to use to convert the value to a string. - * If <jk>null</jk>, then the URL-encoding serializer defined on the client is used. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall header(String name, Object value, boolean skipIfEmpty, PartSerializer partSerializer) throws RestCallException { - if (partSerializer == null) - partSerializer = client.getPartSerializer(); - if (! ("*".equals(name) || isEmpty(name))) { - if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty)) - request.setHeader(name, partSerializer.serialize(PartType.HEADER, value)); - } else if (value instanceof NameValuePairs) { - for (NameValuePair p : (NameValuePairs)value) - header(p.getName(), p.getValue(), skipIfEmpty, UrlEncodingSerializer.DEFAULT_PLAINTEXT); - } else if (value instanceof Map) { - for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) - header(p.getKey(), p.getValue(), skipIfEmpty, partSerializer); - } else if (isBean(value)) { - return header(name, toBeanMap(value), skipIfEmpty, partSerializer); - } else { - throw new FormattedRuntimeException("Invalid name ''{0}'' passed to header(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value)); - } - return this; - } - - - /** - * Sets a header on the request. - * - * @param name - * The header name. - * The name can be null/empty if the value is a {@link Map}. - * @param value The header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall header(String name, Object value) throws RestCallException { - return header(name, value, false, null); - } - - /** - * Sets headers on the request. - * - * @param values The header values. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall headers(Map<String,Object> values) throws RestCallException { - return header(null, values, false, null); - } - - /** - * Sets a header on the request if the value is not null/empty. - * - * <p> - * NE = "not empty" - * - * @param name - * The header name. - * The name can be null/empty if the value is a {@link Map}. - * @param value The header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall headerIfNE(String name, Object value) throws RestCallException { - return header(name, value, true, null); - } - - /** - * Sets headers on the request if the values are not null/empty. - * - * <p> - * NE = "not empty" - * - * @param values The header values. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall headersIfNE(Map<String,Object> values) throws RestCallException { - return header(null, values, true, null); - } - - /** - * Sets the value for the <code>Accept</code> request header. - * - * <p> - * This overrides the media type specified on the parser, but is overridden by calling - * <code>header(<js>"Accept"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall accept(Object value) throws RestCallException { - return header("Accept", value); - } - - /** - * Sets the value for the <code>Accept-Charset</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Accept-Charset"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall acceptCharset(Object value) throws RestCallException { - return header("Accept-Charset", value); - } - - /** - * Sets the value for the <code>Accept-Encoding</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Accept-Encoding"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall acceptEncoding(Object value) throws RestCallException { - return header("Accept-Encoding", value); - } - - /** - * Sets the value for the <code>Accept-Language</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Accept-Language"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall acceptLanguage(Object value) throws RestCallException { - return header("Accept-Language", value); - } - - /** - * Sets the value for the <code>Authorization</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Authorization"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall authorization(Object value) throws RestCallException { - return header("Authorization", value); - } - - /** - * Sets the value for the <code>Cache-Control</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Cache-Control"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall cacheControl(Object value) throws RestCallException { - return header("Cache-Control", value); - } - - /** - * Sets the value for the <code>Connection</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Connection"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall connection(Object value) throws RestCallException { - return header("Connection", value); - } - - /** - * Sets the value for the <code>Content-Length</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Content-Length"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall contentLength(Object value) throws RestCallException { - return header("Content-Length", value); - } - - /** - * Sets the value for the <code>Content-Type</code> request header. - * - * <p> - * This overrides the media type specified on the serializer, but is overridden by calling - * <code>header(<js>"Content-Type"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall contentType(Object value) throws RestCallException { - return header("Content-Type", value); - } - - /** - * Sets the value for the <code>Date</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Date"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall date(Object value) throws RestCallException { - return header("Date", value); - } - - /** - * Sets the value for the <code>Expect</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Expect"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall expect(Object value) throws RestCallException { - return header("Expect", value); - } - - /** - * Sets the value for the <code>Forwarded</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Forwarded"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall forwarded(Object value) throws RestCallException { - return header("Forwarded", value); - } - - /** - * Sets the value for the <code>From</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"From"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall from(Object value) throws RestCallException { - return header("From", value); - } - - /** - * Sets the value for the <code>Host</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Host"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall host(Object value) throws RestCallException { - return header("Host", value); - } - - /** - * Sets the value for the <code>If-Match</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"If-Match"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall ifMatch(Object value) throws RestCallException { - return header("If-Match", value); - } - - /** - * Sets the value for the <code>If-Modified-Since</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"If-Modified-Since"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall ifModifiedSince(Object value) throws RestCallException { - return header("If-Modified-Since", value); - } - - /** - * Sets the value for the <code>If-None-Match</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"If-None-Match"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall ifNoneMatch(Object value) throws RestCallException { - return header("If-None-Match", value); - } - - /** - * Sets the value for the <code>If-Range</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"If-Range"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall ifRange(Object value) throws RestCallException { - return header("If-Range", value); - } - - /** - * Sets the value for the <code>If-Unmodified-Since</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"If-Unmodified-Since"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall ifUnmodifiedSince(Object value) throws RestCallException { - return header("If-Unmodified-Since", value); - } - - /** - * Sets the value for the <code>Max-Forwards</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Max-Forwards"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall maxForwards(Object value) throws RestCallException { - return header("Max-Forwards", value); - } - - /** - * Sets the value for the <code>Origin</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Origin"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall origin(Object value) throws RestCallException { - return header("Origin", value); - } - - /** - * Sets the value for the <code>Pragma</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Pragma"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall pragma(Object value) throws RestCallException { - return header("Pragma", value); - } - - /** - * Sets the value for the <code>Proxy-Authorization</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Proxy-Authorization"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall proxyAuthorization(Object value) throws RestCallException { - return header("Proxy-Authorization", value); - } - - /** - * Sets the value for the <code>Range</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Range"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall range(Object value) throws RestCallException { - return header("Range", value); - } - - /** - * Sets the value for the <code>Referer</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Referer"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall referer(Object value) throws RestCallException { - return header("Referer", value); - } - - /** - * Sets the value for the <code>TE</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"TE"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall te(Object value) throws RestCallException { - return header("TE", value); - } - - /** - * Sets the value for the <code>User-Agent</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"User-Agent"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall userAgent(Object value) throws RestCallException { - return header("User-Agent", value); - } - - /** - * Sets the value for the <code>Upgrade</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Upgrade"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall upgrade(Object value) throws RestCallException { - return header("Upgrade", value); - } - - /** - * Sets the value for the <code>Via</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Via"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall via(Object value) throws RestCallException { - return header("Via", value); - } - - /** - * Sets the value for the <code>Warning</code> request header. - * - * <p> - * This is a shortcut for calling <code>header(<js>"Warning"</js>, value);</code> - * - * @param value The new header value. - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall warning(Object value) throws RestCallException { - return header("Warning", value); - } - - /** - * Sets the client version by setting the value for the <js>"X-Client-Version"</js> header. - * - * @param version The version string (e.g. <js>"1.2.3"</js>) - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall clientVersion(String version) throws RestCallException { - return header("X-Client-Version", version); - } - - /** - * Make this call retryable if an error response (>=400) is received. - * - * @param retries The number of retries to attempt. - * @param interval The time in milliseconds between attempts. - * @param retryOn - * Optional object used for determining whether a retry should be attempted. - * If <jk>null</jk>, uses {@link RetryOn#DEFAULT}. - * @return This object (for method chaining). - * @throws RestCallException If current entity is not repeatable. - */ - public RestCall retryable(int retries, long interval, RetryOn retryOn) throws RestCallException { - if (request instanceof HttpEntityEnclosingRequestBase) { - if (input != null && input instanceof HttpEntity) { - HttpEntity e = (HttpEntity)input; - if (e != null && ! e.isRepeatable()) - throw new RestCallException("Attempt to make call retryable, but entity is not repeatable."); - } - } - this.retries = retries; - this.retryInterval = interval; - this.retryOn = (retryOn == null ? RetryOn.DEFAULT : retryOn); - return this; - - } - - /** - * For this call, allow automatic redirects when a 302 or 307 occurs when performing a POST. - * - * <p> - * Note that this can be inefficient since the POST body needs to be serialized twice. - * The preferred approach if possible is to use the {@link LaxRedirectStrategy} strategy on the underlying HTTP - * client. - * However, this method is provided if you don't have access to the underlying client. - * - * @param b Redirect flag. - * @return This object (for method chaining). - */ - public RestCall allowRedirectsOnPosts(boolean b) { - this.allowRedirectsOnPosts = b; - return this; - } - - /** - * Specify the number of redirects to follow before throwing an exception. - * - * @param maxAttempts Allow a redirect to occur this number of times. - * @return This object (for method chaining). - */ - public RestCall redirectMaxAttempts(int maxAttempts) { - this.redirectOnPostsTries = maxAttempts; - return this; - } - - /** - * Add an intercepter for this call only. - * - * @param intercepter The intercepter to add to this call. - * @return This object (for method chaining). - */ - public RestCall intercepter(RestCallInterceptor intercepter) { - intercepters.add(intercepter); - intercepter.onInit(this); - return this; - } - - /** - * Pipes the request output to the specified writer when {@link #run()} is called. - * - * <p> - * The writer is not closed. - * - * <p> - * This method can be called multiple times to pipe to multiple writers. - * - * @param w The writer to pipe the output to. - * @return This object (for method chaining). - */ - public RestCall pipeTo(Writer w) { - return pipeTo(w, false); - } - - /** - * Pipe output from response to the specified writer when {@link #run()} is called. - * - * <p> - * This method can be called multiple times to pipe to multiple writers. - * - * @param w The writer to write the output to. - * @param close Close the writer when {@link #close()} is called. - * @return This object (for method chaining). - */ - public RestCall pipeTo(Writer w, boolean close) { - return pipeTo(null, w, close); - } - - /** - * Pipe output from response to the specified writer when {@link #run()} is called and associate that writer with an - * ID so it can be retrieved through {@link #getWriter(String)}. - * - * <p> - * This method can be called multiple times to pipe to multiple writers. - * - * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} - * @param w The writer to write the output to. - * @param close Close the writer when {@link #close()} is called. - * @return This object (for method chaining). - */ - public RestCall pipeTo(String id, Writer w, boolean close) { - writers.add(id, w, close); - return this; - } - - /** - * Retrieves a writer associated with an ID via {@link #pipeTo(String, Writer, boolean)} - * - * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} - * @return The writer, or <jk>null</jk> if no writer is associated with that ID. - */ - public Writer getWriter(String id) { - return writers.getWriter(id); - } - - /** - * When output is piped to writers, flush the writers after every line of output. - * - * @return This object (for method chaining). - */ - public RestCall byLines() { - this.byLines = true; - return this; - } - - /** - * Pipes the request output to the specified output stream when {@link #run()} is called. - * - * <p> - * The output stream is not closed. - * - * <p> - * This method can be called multiple times to pipe to multiple output streams. - * - * @param os The output stream to pipe the output to. - * @return This object (for method chaining). - */ - public RestCall pipeTo(OutputStream os) { - return pipeTo(os, false); - } - - /** - * Pipe output from response to the specified output stream when {@link #run()} is called. - * - * <p> - * This method can be called multiple times to pipe to multiple output stream. - * - * @param os The output stream to write the output to. - * @param close Close the output stream when {@link #close()} is called. - * @return This object (for method chaining). - */ - public RestCall pipeTo(OutputStream os, boolean close) { - return pipeTo(null, os, close); - } - - /** - * Pipe output from response to the specified output stream when {@link #run()} is called and associate - * that output stream with an ID so it can be retrieved through {@link #getOutputStream(String)}. - * - * <p> - * This method can be called multiple times to pipe to multiple output stream. - * - * @param id A string identifier that can be used to retrieve the output stream using {@link #getOutputStream(String)} - * @param os The output stream to write the output to. - * @param close Close the output stream when {@link #close()} is called. - * @return This object (for method chaining). - */ - public RestCall pipeTo(String id, OutputStream os, boolean close) { - outputStreams.add(id, os, close); - return this; - } - - /** - * Retrieves an output stream associated with an ID via {@link #pipeTo(String, OutputStream, boolean)} - * - * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)} - * @return The writer, or <jk>null</jk> if no writer is associated with that ID. - */ - public OutputStream getOutputStream(String id) { - return outputStreams.getOutputStream(id); - } - - /** - * Prevent {@link RestCallException RestCallExceptions} from being thrown when HTTP status 400+ is encountered. - * - * @return This object (for method chaining). - */ - public RestCall ignoreErrors() { - this.ignoreErrors = true; - return this; - } - - /** - * Stores the response text so that it can later be captured using {@link #getCapturedResponse()}. - * - * <p> - * This method should only be called once. Multiple calls to this method are ignored. - * - * @return This object (for method chaining). - */ - public RestCall captureResponse() { - if (capturedResponseWriter == null) { - capturedResponseWriter = new StringWriter(); - writers.add(capturedResponseWriter, false); - } - return this; - } - - - /** - * Look for the specified regular expression pattern in the response output. - * - * <p> - * Causes a {@link RestCallException} to be thrown if the specified pattern is found in the output. - * - * <p> - * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output - * methods such as {@link #getResponseAsString()}. - * - * <h5 class='section'>Example:</h5> - * <p class='bcode'> - * <jc>// Throw a RestCallException if FAILURE or ERROR is found in the output.</jc> - * restClient.doGet(<jsf>URL</jsf>) - * .failurePattern(<js>"FAILURE|ERROR"</js>) - * .run(); - * </p> - * - * @param errorPattern A regular expression to look for in the response output. - * @return This object (for method chaining). - */ - public RestCall failurePattern(final String errorPattern) { - responsePattern( - new ResponsePattern(errorPattern) { - @Override - public void onMatch(RestCall rc, Matcher m) throws RestCallException { - throw new RestCallException("Failure pattern detected."); - } - } - ); - return this; - } - - /** - * Look for the specified regular expression pattern in the response output. - * - * <p> - * Causes a {@link RestCallException} to be thrown if the specified pattern is not found in the output. - * - * <p> - * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output - * methods such as {@link #getResponseAsString()}. - * - * <h5 class='section'>Example:</h5> - * <p class='bcode'> - * <jc>// Throw a RestCallException if SUCCESS is not found in the output.</jc> - * restClient.doGet(<jsf>URL</jsf>) - * .successPattern(<js>"SUCCESS"</js>) - * .run(); - * </p> - * - * @param successPattern A regular expression to look for in the response output. - * @return This object (for method chaining). - */ - public RestCall successPattern(String successPattern) { - responsePattern( - new ResponsePattern(successPattern) { - @Override - public void onNoMatch(RestCall rc) throws RestCallException { - throw new RestCallException("Success pattern not detected."); - } - } - ); - return this; - } - - /** - * Adds a response pattern finder to look for regular expression matches in the response output. - * - * <p> - * This method can be called multiple times to add multiple response pattern finders. - * - * <p> - * {@link ResponsePattern ResponsePatterns} use the {@link #getCapturedResponse()} to read the response text and so - * does not affect the other output methods such as {@link #getResponseAsString()}. - * - * @param responsePattern The response pattern finder. - * @return This object (for method chaining). - */ - public RestCall responsePattern(final ResponsePattern responsePattern) { - captureResponse(); - intercepter( - new RestCallInterceptor() { - @Override - public void onClose(RestCall restCall) throws RestCallException { - responsePattern.match(RestCall.this); - } - } - ); - return this; - } - - /** - * Set configuration settings on this request. - * - * <p> - * Use {@link RequestConfig#custom()} to create configuration parameters for the request. - * - * @param config The new configuration settings for this request. - * @return This object (for method chaining). - */ - public RestCall setConfig(RequestConfig config) { - this.request.setConfig(config); - return this; - } - - /** - * @return The HTTP response code. - * @throws RestCallException - * @deprecated Use {@link #run()}. - */ - @Deprecated - public int execute() throws RestCallException { - return run(); - } - - /** - * Method used to execute an HTTP response where you're only interested in the HTTP response code. - * - * <p> - * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an - * output stream or writer. - * - * <h5 class='section'>Example:</h5> - * <p class='bcode'> - * <jk>try</jk> { - * RestClient client = <jk>new</jk> RestClient(); - * <jk>int</jk> rc = client.doGet(url).execute(); - * <jc>// Succeeded!</jc> - * } <jk>catch</jk> (RestCallException e) { - * <jc>// Failed!</jc> - * } - * </p> - * - * @return The HTTP status code. - * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. - */ - public int run() throws RestCallException { - connect(); - try { - StatusLine status = response.getStatusLine(); - int sc = status.getStatusCode(); - if (sc >= 400 && ! ignoreErrors) - throw new RestCallException(sc, status.getReasonPhrase(), request.getMethod(), request.getURI(), getResponseAsString()).setHttpResponse(response); - if (outputStreams.size() > 0 || writers.size() > 0) - getReader(); - return sc; - } catch (RestCallException e) { - isFailed = true; - throw e; - } catch (IOException e) { - isFailed = true; - throw new RestCallException(e).setHttpResponse(response); - } finally { - close(); - } - } - - /** - * Same as {@link #run()} but allows you to run the call asynchronously. - * - * @return The HTTP status code. - * @throws RestCallException If the executor service was not defined. - * @see RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating - * {@link Future Futures}. - */ - public Future<Integer> runFuture() throws RestCallException { - return client.getExecutorService(true).submit( - new Callable<Integer>() { - @Override /* Callable */ - public Integer call() throws Exception { - return run(); - } - } - ); - } - - /** - * Connects to the REST resource. - * - * <p> - * If this is a <code>PUT</code> or <code>POST</code>, also sends the input to the remote resource.<br> - * - * <p> - * Typically, you would only call this method if you're not interested in retrieving the body of the HTTP response. - * Otherwise, you're better off just calling one of the {@link #getReader()}/{@link #getResponse(Class)}/{@link #pipeTo(Writer)} - * methods directly which automatically call this method already. - * - * @return This object (for method chaining). - * @throws RestCallException If an exception or <code>400+</code> HTTP status code occurred during the connection attempt. - */ - public RestCall connect() throws RestCallException { - - if (isConnected) - return this; - isConnected = true; - - try { - - request.setURI(uriBuilder.build()); - - if (hasInput || formData != null) { - - if (hasInput && formData != null) - throw new RestCallException("Both input and form data found on same request."); - - if (! (request instanceof HttpEntityEnclosingRequestBase)) - throw new RestCallException(0, "Method does not support content entity.", request.getMethod(), request.getURI(), null); - - HttpEntity entity = null; - if (formData != null) - entity = new UrlEncodedFormEntity(formData); - else if (input instanceof NameValuePairs) - entity = new UrlEncodedFormEntity((NameValuePairs)input); - else if (input instanceof HttpEntity) - entity = (HttpEntity)input; - else - entity = new RestRequestEntity(input, getSerializer()); - - if (retries > 1 && ! entity.isRepeatable()) - throw new RestCallException("Rest call set to retryable, but entity is not repeatable."); - - ((HttpEntityEnclosingRequestBase)request).setEntity(entity); - } - - int sc = 0; - while (retries > 0) { - retries--; - Exception ex = null; - try { - response = client.execute(request); - sc = (response == null || response.getStatusLine() == null) ? -1 : response.getStatusLine().getStatusCode(); - } catch (Exception e) { - ex = e; - sc = -1; - if (response != null) - EntityUtils.consumeQuietly(response.getEntity()); - } - if (! retryOn.onResponse(response)) - retries = 0; - if (retries > 0) { - for (RestCallInterceptor rci : intercepters) - rci.onRetry(this, sc, request, response, ex); - request.reset(); - long w = retryInterval; - synchronized(this) { - wait(w); - } - } else if (ex != null) { - throw ex; - } - } - for (RestCallInterceptor rci : intercepters) - rci.onConnect(this, sc, request, response); - if (response == null) - throw new RestCallException("HttpClient returned a null response"); - StatusLine sl = response.getStatusLine(); - String method = request.getMethod(); - sc = sl.getStatusCode(); // Read it again in case it was changed by one of the intercepters. - if (sc >= 400 && ! ignoreErrors) - throw new RestCallException(sc, sl.getReasonPhrase(), method, request.getURI(), getResponseAsString()) - .setServerException(response.getFirstHeader("Exception-Name"), response.getFirstHeader("Exception-Message"), response.getFirstHeader("Exception-Trace")) - .setHttpResponse(response); - if ((sc == 307 || sc == 302) && allowRedirectsOnPosts && method.equalsIgnoreCase("POST")) { - if (redirectOnPostsTries-- < 1) - throw new RestCallException(sc, "Maximum number of redirects occurred. Location header: " + response.getFirstHeader("Location"), method, request.getURI(), getResponseAsString()); - Header h = response.getFirstHeader("Location"); - if (h != null) { - reset(); - request.setURI(URI.create(h.getValue())); - retries++; // Redirects should affect retries. - connect(); - } - } - - } catch (RestCallException e) { - isFailed = true; - try { - close(); - } catch (RestCallException e2) { /* Ignore */ } - throw e; - } catch (Exception e) { - isFailed = true; - close(); - throw new RestCallException(e).setHttpResponse(response); - } - - return this; - } - - private void reset() { - if (response != null) - EntityUtils.consumeQuietly(response.getEntity()); - request.reset(); - isConnected = false; - isClosed = false; - isFailed = false; - if (capturedResponseWriter != null) - capturedResponseWriter.getBuffer().setLength(0); - } - - /** - * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP - * response message body as a reader. - * - * <p> - * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream will be - * wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>). - * - * <p> - * If present, automatically handles the <code>charset</code> value in the <code>Content-Type</code> response header. - * - * <p> - * <b>IMPORTANT:</b> It is your responsibility to close this reader once you have finished with it. - * - * @return - * The HTTP response message body reader. - * <jk>null</jk> if response was successful but didn't contain a body (e.g. HTTP 204). - * @throws IOException If an exception occurred while streaming was already occurring. - */ - public Reader getReader() throws IOException { - InputStream is = getInputStream(); - if (is == null) - return null; - - // Figure out what the charset of the response is. - String cs = null; - Header contentType = response.getLastHeader("Content-Type"); - String ct = contentType == null ? null : contentType.getValue(); - - // First look for "charset=" in Content-Type header of response. - if (ct != null && ct.contains("charset=")) - cs = ct.substring(ct.indexOf("charset=")+8).trim(); - - if (cs == null) - cs = "UTF-8"; - - Reader isr = new InputStreamReader(is, cs); - - if (writers.size() > 0) { - StringWriter sw = new StringWriter(); - writers.add(sw, true); - IOPipe.create(isr, writers).byLines(byLines).run(); - return new StringReader(sw.toString()); - } - - return new InputStreamReader(is, cs); - } - - /** - * Returns the response text as a string if {@link #captureResponse()} was called on this object. - * - * <p> - * Note that while similar to {@link #getResponseAsString()}, this method can be called multiple times to retrieve - * the response text multiple times. - * - * <p> - * Note that this method returns <jk>null</jk> if you have not called one of the methods that cause the response to - * be processed. (e.g. {@link #run()}, {@link #getResponse()}, {@link #getResponseAsString()}. - * - * @return The captured response, or <jk>null</jk> if {@link #captureResponse()} has not been called. - * @throws IllegalStateException If trying to call this method before the response is consumed. - */ - public String getCapturedResponse() { - if (! isClosed) - throw new IllegalStateException("This method cannot be called until the response has been consumed."); - if (capturedResponse == null && capturedResponseWriter != null && capturedResponseWriter.getBuffer().length() > 0) - capturedResponse = capturedResponseWriter.toString(); - return capturedResponse; - } - - /** - * Returns the parser specified on the client to use for parsing HTTP response bodies. - * - * @return The parser. - * @throws RestCallException If no parser was defined on the client. - */ - protected Parser getParser() throws RestCallException { - if (parser == null) - throw new RestCallException(0, "No parser defined on client", request.getMethod(), request.getURI(), null); - return parser; - } - - /** - * Returns the serializer specified on the client to use for serializing HTTP request bodies. - * - * @return The serializer. - * @throws RestCallException If no serializer was defined on the client. - */ - protected Serializer getSerializer() throws RestCallException { - if (serializer == null) - throw new RestCallException(0, "No serializer defined on client", request.getMethod(), request.getURI(), null); - return serializer; - } - - /** - * Returns the value of the <code>Content-Length</code> header. - * - * @return The value of the <code>Content-Length</code> header, or <code>-1</code> if header is not present. - * @throws IOException - */ - public int getContentLength() throws IOException { - connect(); - Header h = response.getLastHeader("Content-Length"); - if (h == null) - return -1; - long l = Long.parseLong(h.getValue()); - if (l > Integer.MAX_VALUE) - return Integer.MAX_VALUE; - return (int)l; - } - - /** - * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP - * response message body as an input stream. - * - * <p> - * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream will be - * wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>). - * - * <p> - * <b>IMPORTANT:</b> It is your responsibility to close this reader once you have finished with it. - * - * @return - * The HTTP response message body input stream. <jk>null</jk> if response was successful but didn't contain - * a body (e.g. HTTP 204). - * @throws IOException If an exception occurred while streaming was already occurring. - * @throws IllegalStateException If an attempt is made to read the response more than once. - */ - public InputStream getInputStream() throws IOException { - if (isClosed) - throw new IllegalStateException("Method cannot be called. Response has already been consumed."); - connect(); - if (response == null) - throw new RestCallException("Response was null"); - if (response.getEntity() == null) // HTTP 204 results in no content. - return null; - InputStream is = response.getEntity().getContent(); - - if (outputStreams.size() > 0) { - ByteArrayInOutStream baios = new ByteArrayInOutStream(); - outputStreams.add(baios, true); - IOPipe.create(is, baios).run(); - return baios.getInputStream(); - } - return is; - } - - /** - * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response - * message body as plain text. - * - * @return The response as a string. - * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. - * @throws IOException If an exception occurred while streaming was already occurring. - */ - public String getResponseAsString() throws IOException { - try { - Reader r = getReader(); - String s = read(r).toString(); - return s; - } catch (IOException e) { - isFailed = true; - throw e; - } finally { - close(); - } - } - - /** - * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. - * - * @return The response as a string. - * @throws RestCallException If the executor service was not defined. - * @see - * RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating - * {@link Future Futures}. - */ - public Future<String> getResponseAsStringFuture() throws RestCallException { - return client.getExecutorService(true).submit( - new Callable<String>() { - @Override /* Callable */ - public String call() throws Exception { - return getResponseAsString(); - } - } - ); - } - - /** - * Same as {@link #getResponse(Type, Type...)} except optimized for a non-parameterized class. - * - * <p> - * This is the preferred parse method for simple types since you don't need to cast the results. - * - * <h5 class='section'>Examples:</h5> - * <p class='bcode'> - * <jc>// Parse into a string.</jc> - * String s = restClient.doGet(url).getResponse(String.<jk>class</jk>); - * - * <jc>// Parse into a bean.</jc> - * MyBean b = restClient.doGet(url).getResponse(MyBean.<jk>class</jk>); - * - * <jc>// Parse into a bean array.</jc> - * MyBean[] ba = restClient.doGet(url).getResponse(MyBean[].<jk>class</jk>); - * - * <jc>// Parse into a linked-list of objects.</jc> - * List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>); - * - * <jc>// Parse into a map of object keys/values.</jc> - * Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>); - * </p> - * - * <h5 class='section'>Notes:</h5> - * <ul> - * <li> - * You can also specify any of the following types: - * <ul> - * <li>{@link HttpResponse} - Returns the raw <code>HttpResponse</code> returned by the inner <code>HttpClient</code>. - * <li>{@link Reader} - Returns access to the raw reader of the response. - * <li>{@link InputStream} - Returns access to the raw input stream of the response. - * </ul> - * </ul> - * - * @param <T> - * The class type of the object being created. - * See {@link #getResponse(Type, Type...)} for details. - * @param type The object type to create. - * @return The parsed object. - * @throws ParseException - * If the input contains a syntax error or is malformed, or is not valid for the specified type. - * @throws IOException If a connection error occurred. - */ - public <T> T getResponse(Class<T> type) throws IOException, ParseException { - BeanContext bc = getParser().getBeanContext(); - if (bc == null) - bc = BeanContext.DEFAULT; - return getResponse(bc.getClassMeta(type)); - } - - /** - * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. - * - * @param <T> - * The class type of the object being created. - * See {@link #getResponse(Type, Type...)} for details. - * @param type The object type to create. - * @return The parsed object. - * @throws RestCallException If the executor service was not defined. - * @see - * RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating - * {@link Future Futures}. - */ - public <T> Future<T> getResponseFuture(final Class<T> type) throws RestCallException { - return client.getExecutorService(true).submit( - new Callable<T>() { - @Override /* Callable */ - public T call() throws Exception { - return getResponse(type); - } - } - ); - } - - /** - * Parses HTTP body into the specified object type. - * - * <p> - * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). - * - * <h5 class='section'>Examples:</h5> - * <p class='bcode'> - * <jc>// Parse into a linked-list of strings.</jc> - * List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, String.<jk>class</jk>); - * - * <jc>// Parse into a linked-list of beans.</jc> - * List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); - * - * <jc>// Parse into a linked-list of linked-lists of strings.</jc> - * List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); - * - * <jc>// Parse into a map of string keys/values.</jc> - * Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); - * - * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> - * Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); - * </p> - * - * <p> - * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type. - * - * <p> - * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types. - * - * <p> - * The array can be arbitrarily long to indicate arbitrarily complex data structures. - * - * <h5 class='section'>Notes:</h5> - * <ul> - * <li> - * Use the {@link #getResponse(Class)} method instead if you don't need a parameterized map/collection. - * <li> - * You can also specify any of the following types: - * <ul> - * <li>{@link HttpResponse} - Returns the raw <code>HttpResponse</code> returned by the inner <code>HttpClient</code>. - * <li>{@link Reader} - Returns access to the raw reader of the response. - * <li>{@link InputStream} - Returns access to the raw input stream of the response. - * </ul> - * </ul> - * - * @param <T> The class type of the object to create. - * @param type - * The object type to create. - * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} - * @param args - * The type arguments of the class if it's a collection or map. - * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} - * <br>Ignored if the main type is not a map or collection. - * @return The parsed object. - * @throws ParseException - * If the input contains a syntax error or is malformed, or is not valid for the specified type. - * @throws IOException If a connection error occurred. - * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections. - */ - public <T> T getResponse(Type type, Type...args) throws IOException, ParseException { - BeanContext bc = getParser().getBeanContext(); - if (bc == null) - bc = BeanContext.DEFAULT; - return (T)getResponse(bc.getClassMeta(type, args)); - } - - /** - * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. - * - * @param <T> - * The class type of the object being created. - * See {@link #getResponse(Type, Type...)} for details. - * @param type - * The object type to create. - * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, - * {@link GenericArrayType} - * @param args - * The type arguments of the class if it's a collection or map. - * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, - * {@link GenericArrayType} - * <br>Ignored if the main type is not a map or collection. - * @return The parsed object. - * @throws RestCallException If the executor service was not defined. - * @see - * RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating - * {@link Future Futures}. - */ - public <T> Future<T> getResponseFuture(final Type type, final Type...args) throws RestCallException { - return client.getExecutorService(true).submit( - new Callable<T>() { - @Override /* Callable */ - public T call() throws Exception { - return getResponse(type, args); - } - } - ); - } - - /** - * Parses the output from the connection into the specified type and then wraps that in a {@link PojoRest}. - * - * <p> - * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. - * - * @param innerType The class type of the POJO being wrapped. - * @return The parsed output wrapped in a {@link PojoRest}. - * @throws IOException If a connection error occurred. - * @throws ParseException - * If the input contains a syntax error or is malformed for the <code>Content-Type</code> header. - */ - public PojoRest getResponsePojoRest(Class<?> innerType) throws IOException, ParseException { - return new PojoRest(getResponse(innerType)); - } - - /** - * Converts the output from the connection into an {@link ObjectMap} and then wraps that in a {@link PojoRest}. - * - * <p> - * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. - * - * @return The parsed output wrapped in a {@link PojoRest}. - * @throws IOException If a connection error occurred. - * @throws ParseException - * If the input contains a syntax error or is malformed for the <code>Content-Type</code> header. - */ - public PojoRest getResponsePojoRest() throws IOException, ParseException { - return getResponsePojoRest(ObjectMap.class); - } - - <T> T getResponse(ClassMeta<T> type) throws IOException, ParseException { - try { - if (type.getInnerClass().equals(HttpResponse.class)) - return (T)response; - if (type.getInnerClass().equals(Reader.class)) - return (T)getReader(); - if (type.getInnerClass().equals(InputStream.class)) - return (T)getInputStream(); - Parser p = getParser(); - T o = null; - if (! p.isReaderParser()) { - InputStream is = getInputStream(); - o = ((InputStreamParser)p).parse(is, type); - } else { - Reader r = getReader(); - o = ((ReaderParser)p).parse(r, type); - } - return o; - } catch (ParseException e) { - isFailed = true; - throw e; - } catch (IOException e) { - isFailed = true; - throw e; - } finally { - close(); - } - } - - BeanContext getBeanContext() throws RestCallException { - BeanContext bc = getParser().getBeanContext(); - if (bc == null) - bc = BeanContext.DEFAULT; - return bc; - } - - /** - * Returns access to the {@link HttpUriRequest} passed to {@link HttpClient#execute(HttpUriRequest)}. - * - * @return The {@link HttpUriRequest} object. - */ - public HttpUriRequest getRequest() { - return request; - } - - /** - * Returns access to the {@link HttpResponse} returned by {@link HttpClient#execute(HttpUriRequest)}. - * - * <p> - * Returns <jk>null</jk> if {@link #connect()} has not yet been called. - * - * @return The HTTP response object. - * @throws IOException - */ - public HttpResponse getResponse() throws IOException { - connect(); - return response; - } - - /** - * Shortcut for calling <code>getRequest().setHeader(header)</code> - * - * @param header The header to set on the request. - * @return This object (for method chaining). - */ - public RestCall header(Header header) { - request.setHeader(header); - return this; - } - - /** Use close() */ - @Deprecated - public void consumeResponse() { - if (response != null) - EntityUtils.consumeQuietly(response.getEntity()); - } - - /** - * Cleans up this HTTP call. - * - * @return This object (for method chaining). - * @throws RestCallException Can be thrown by one of the {@link RestCallInterceptor#onClose(RestCall)} calls. - */ - public RestCall close() throws RestCallException { - if (response != null) - EntityUtils.consumeQuietly(response.getEntity()); - isClosed = true; - if (! isFailed) - for (RestCallInterceptor r : intercepters) - r.onClose(this); - return this; - } - - /** - * Adds a {@link RestCallLogger} to the list of intercepters on this class. - * - * @param level The log level to log events at. - * @param log The logger. - * @return This object (for method chaining). - */ - public RestCall logTo(Level level, Logger log) { - intercepter(new RestCallLogger(level, log)); - return this; - } - - /** - * Sets <code>Debug: value</code> header on this request. - * - * @return This object (for method chaining). - * @throws RestCallException - */ - public RestCall debug() throws RestCallException { - header("Debug", true); - return this; - } - - private boolean isBean(Object o) throws RestCallException { - return getBeanContext().isBean(o); - } - - private BeanMap<?> toBeanMap(Object o) throws RestCallException { - return getBeanContext().createSession().toBeanMap(o); - } -}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab15d45b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallException.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallException.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallException.java deleted file mode 100644 index b0a01d9..0000000 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallException.java +++ /dev/null @@ -1,218 +0,0 @@ -// *************************************************************************************************************************** -// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * -// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * -// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * -// * with the License. You may obtain a copy of the License at * -// * * -// * http://www.apache.org/licenses/LICENSE-2.0 * -// * * -// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * -// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * -// * specific language governing permissions and limitations under the License. * -// *************************************************************************************************************************** -package org.apache.juneau.rest.client; - -import static java.lang.String.*; -import static org.apache.juneau.internal.ClassUtils.*; -import static org.apache.juneau.internal.IOUtils.*; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.util.regex.*; - -import org.apache.http.*; -import org.apache.http.client.*; -import org.apache.http.util.*; - -/** - * Exception representing a <code>400+</code> HTTP response code against a remote resource. - */ -public final class RestCallException extends IOException { - - private static final long serialVersionUID = 1L; - - private int responseCode; - private String response, responseStatusMessage; - HttpResponseException e; - private HttpResponse httpResponse; - - @SuppressWarnings("unused") - private String serverExceptionName, serverExceptionMessage, serverExceptionTrace; - - - /** - * Constructor. - * - * @param msg The exception message. - */ - public RestCallException(String msg) { - super(msg); - } - - /** - * Constructor. - * - * @param e The inner cause of the exception. - */ - public RestCallException(Exception e) { - super(e.getLocalizedMessage(), e); - if (e instanceof FileNotFoundException) { - responseCode = 404; - } else if (e.getMessage() != null) { - Pattern p = Pattern.compile("[^\\d](\\d{3})[^\\d]"); - Matcher m = p.matcher(e.getMessage()); - if (m.find()) - responseCode = Integer.parseInt(m.group(1)); - } - setStackTrace(e.getStackTrace()); - } - - /** - * Create an exception with a simple message and the status code and body of the specified response. - * - * @param msg The exception message. - * @param response The HTTP response object. - * @throws ParseException - * @throws IOException - */ - public RestCallException(String msg, HttpResponse response) throws ParseException, IOException { - super(format("%s%nstatus='%s'%nResponse: %n%s%n", msg, response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), UTF8))); - } - - /** - * Constructor. - * - * @param responseCode The response code. - * @param responseMsg The response message. - * @param method The HTTP method (for message purposes). - * @param url The HTTP URL (for message purposes). - * @param response The response from the server. - */ - public RestCallException(int responseCode, String responseMsg, String method, URI url, String response) { - super(format("HTTP method '%s' call to '%s' caused response code '%s,%s'.%nResponse: %n%s%n", method, url, responseCode, responseMsg, response)); - this.responseCode = responseCode; - this.responseStatusMessage = responseMsg; - this.response = response; - } - - /** - * Sets the server-side exception details. - * - * @param exceptionName The <code>Exception-Name:</code> header specifying the full name of the exception. - * @param exceptionMessage - * The <code>Exception-Message:</code> header specifying the message returned by {@link Throwable#getMessage()}. - * @param exceptionTrace The stack trace of the exception returned by {@link Throwable#printStackTrace()}. - * @return This object (for method chaining). - */ - protected RestCallException setServerException(Header exceptionName, Header exceptionMessage, Header exceptionTrace) { - if (exceptionName != null) - serverExceptionName = exceptionName.getValue(); - if (exceptionMessage != null) - serverExceptionMessage = exceptionMessage.getValue(); - if (exceptionTrace != null) - serverExceptionTrace = exceptionTrace.getValue(); - return this; - } - - /** - * Tries to reconstruct and re-throw the server-side exception. - * - * <p> - * The exception is based on the following HTTP response headers: - * <ul> - * <li><code>Exception-Name:</code> - The full class name of the exception. - * <li><code>Exception-Message:</code> - The message returned by {@link Throwable#getMessage()}. - * <li><code>Exception-Trace:</code> - The stack trace of the exception returned by {@link Throwable#printStackTrace()}. - * </ul> - * - * <p> - * Does nothing if the server-side exception could not be reconstructed. - * - * <p> - * Currently only supports <code>Throwables</code> with either a public no-arg constructor - * or a public constructor that takes in a simple string message. - * - * @param cl The classloader to use to resolve the throwable class name. - * @throws Throwable If the throwable could be reconstructed. - */ - protected void throwServerException(ClassLoader cl) throws Throwable { - if (serverExceptionName != null) { - Throwable t = null; - try { - Class<?> exceptionClass = cl.loadClass(serverExceptionName); - Constructor<?> c = findPublicConstructor(exceptionClass, String.class); - if (c != null) - t = (Throwable)c.newInstance(serverExceptionMessage); - if (t == null) { - c = findPublicConstructor(exceptionClass); - if (c != null) - t = (Throwable)c.newInstance(); - } - } catch (Exception e2) { /* Ignore */ } - if (t != null) - throw t; - } - } - - /** - * Sets the HTTP response object that caused this exception. - * - * @param httpResponse The HTTP response object. - * @return This object (for method chaining). - */ - protected RestCallException setHttpResponse(HttpResponse httpResponse) { - this.httpResponse = httpResponse; - return this; - } - - /** - * Returns the HTTP response object that caused this exception. - * - * @return - * The HTTP response object that caused this exception, or <jk>null</jk> if no response was created yet when the - * exception was thrown. - */ - public HttpResponse getHttpResponse() { - return this.httpResponse; - } - - /** - * Returns the HTTP response status code. - * - * @return The response status code. If a connection could not be made at all, returns <code>0</code>. - */ - public int getResponseCode() { - return responseCode; - } - - /** - * Returns the HTTP response message body text. - * - * @return The response message body text. - */ - public String getResponseMessage() { - return response; - } - - /** - * Returns the response status message as a plain string. - * - * @return The response status message. - */ - public String getResponseStatusMessage() { - return responseStatusMessage; - } - - /** - * Sets the inner cause for this exception. - * - * @param cause The inner cause. - * @return This object (for method chaining). - */ - @Override /* Throwable */ - public synchronized RestCallException initCause(Throwable cause) { - super.initCause(cause); - return this; - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab15d45b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallInterceptor.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallInterceptor.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallInterceptor.java deleted file mode 100644 index 54f36b2..0000000 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCallInterceptor.java +++ /dev/null @@ -1,61 +0,0 @@ -// *************************************************************************************************************************** -// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * -// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * -// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * -// * with the License. You may obtain a copy of the License at * -// * * -// * http://www.apache.org/licenses/LICENSE-2.0 * -// * * -// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * -// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * -// * specific language governing permissions and limitations under the License. * -// *************************************************************************************************************************** -package org.apache.juneau.rest.client; - -import org.apache.http.*; - -/** - * Used to intercept http connection responses to allow modification of that response before processing and for - * listening for call lifecycle events. - * - * <p> - * Useful if you want to prevent {@link RestCallException RestCallExceptions} from being thrown on error conditions. - */ -public abstract class RestCallInterceptor { - - /** - * Called when {@link RestCall} object is created. - * - * @param restCall The restCall object invoking this method. - */ - public void onInit(RestCall restCall) {} - - /** - * Called immediately after an HTTP response has been received. - * - * @param statusCode The HTTP status code received. - * @param restCall The restCall object invoking this method. - * @param req The HTTP request object. - * @param res The HTTP response object. - */ - public void onConnect(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res) {} - - /** - * Called if retry is going to be attempted. - * - * @param statusCode The HTTP status code received. - * @param restCall The restCall object invoking this method. - * @param req The HTTP request object. - * @param res The HTTP response object. - * @param ex The exception thrown from the client. - */ - public void onRetry(RestCall restCall, int statusCode, HttpRequest req, HttpResponse res, Exception ex) {} - - /** - * Called when {@link RestCall#close()} is called. - * - * @param restCall The restCall object invoking this method. - * @throws RestCallException - */ - public void onClose(RestCall restCall) throws RestCallException {} -}
