All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.rest.client.RestCall Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.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.
 *
 * 

* Instances of this class are created by the various {@code doX()} methods on the {@link RestClient} class. * *

* This class uses only Java standard APIs. Requests can be built up using a fluent interface with method chaining, * like so... *

* RestClient client = new RestClient(); * RestCall c = client.doPost(URL).setInput(o).setHeader(x,y); * MyBean b = c.getResponse(MyBean.class); *

* *

* The actual connection and request/response transaction occurs when calling one of the getResponseXXX() * methods. * *

Additional information:
* */ @SuppressWarnings({ "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 intercepters = new ArrayList(); // 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. * *

* Can be any of the following types: *

    *
  • {@link URI} *
  • {@link URL} *
  • {@link URIBuilder} *
  • Anything else converted to a string using {@link Object#toString()}. *
* *

* 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 null, 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 p : ((Map) 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 params) throws RestCallException { return query(null, params); } /** * Adds a query parameter to the URI query if the parameter value is not null or an empty string. * *

* 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. * *

* 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 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 null, 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 p : ((Map) 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 params) throws RestCallException { return formData(null, params); } /** * Adds a form data pair to the request if the parameter value is not null or an empty string. * *

* 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. * *

* 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 params) throws RestCallException { return formData(null, params, true, null); } /** * Replaces a variable of the form "{name}" 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 null, 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 p : ((Map) 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 "{name}" 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.
* Can be of the following types: *

    *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the * {@link RestClient}. *
  • * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link NameValuePairs} - Converted to a URL-encoded FORM post. *
* @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. * *

* 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. * *

* 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 null, 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 p : ((Map) 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 values) throws RestCallException { return header(null, values, false, null); } /** * Sets a header on the request if the value is not null/empty. * *

* 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. * *

* NE = "not empty" * * @param values The header values. * @return This object (for method chaining). * @throws RestCallException */ public RestCall headersIfNE(Map values) throws RestCallException { return header(null, values, true, null); } /** * Sets the value for the Accept request header. * *

* This overrides the media type specified on the parser, but is overridden by calling * header("Accept", value); * * @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 Accept-Charset request header. * *

* This is a shortcut for calling header("Accept-Charset", value); * * @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 Accept-Encoding request header. * *

* This is a shortcut for calling header("Accept-Encoding", value); * * @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 Accept-Language request header. * *

* This is a shortcut for calling header("Accept-Language", value); * * @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 Authorization request header. * *

* This is a shortcut for calling header("Authorization", value); * * @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 Cache-Control request header. * *

* This is a shortcut for calling header("Cache-Control", value); * * @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 Connection request header. * *

* This is a shortcut for calling header("Connection", value); * * @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 Content-Length request header. * *

* This is a shortcut for calling header("Content-Length", value); * * @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 Content-Type request header. * *

* This overrides the media type specified on the serializer, but is overridden by calling * header("Content-Type", value); * * @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 Date request header. * *

* This is a shortcut for calling header("Date", value); * * @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 Expect request header. * *

* This is a shortcut for calling header("Expect", value); * * @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 Forwarded request header. * *

* This is a shortcut for calling header("Forwarded", value); * * @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 From request header. * *

* This is a shortcut for calling header("From", value); * * @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 Host request header. * *

* This is a shortcut for calling header("Host", value); * * @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 If-Match request header. * *

* This is a shortcut for calling header("If-Match", value); * * @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 If-Modified-Since request header. * *

* This is a shortcut for calling header("If-Modified-Since", value); * * @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 If-None-Match request header. * *

* This is a shortcut for calling header("If-None-Match", value); * * @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 If-Range request header. * *

* This is a shortcut for calling header("If-Range", value); * * @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 If-Unmodified-Since request header. * *

* This is a shortcut for calling header("If-Unmodified-Since", value); * * @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 Max-Forwards request header. * *

* This is a shortcut for calling header("Max-Forwards", value); * * @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 Origin request header. * *

* This is a shortcut for calling header("Origin", value); * * @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 Pragma request header. * *

* This is a shortcut for calling header("Pragma", value); * * @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 Proxy-Authorization request header. * *

* This is a shortcut for calling header("Proxy-Authorization", value); * * @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 Range request header. * *

* This is a shortcut for calling header("Range", value); * * @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 Referer request header. * *

* This is a shortcut for calling header("Referer", value); * * @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 TE request header. * *

* This is a shortcut for calling header("TE", value); * * @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 User-Agent request header. * *

* This is a shortcut for calling header("User-Agent", value); * * @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 Upgrade request header. * *

* This is a shortcut for calling header("Upgrade", value); * * @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 Via request header. * *

* This is a shortcut for calling header("Via", value); * * @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 Warning request header. * *

* This is a shortcut for calling header("Warning", value); * * @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 "X-Client-Version" header. * * @param version The version string (e.g. "1.2.3") * @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 null, 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. * *

* 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. * *

* The writer is not closed. * *

* 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. * *

* 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)}. * *

* 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 null 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. * *

* The output stream is not closed. * *

* 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. * *

* 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)}. * *

* 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 null 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()}. * *

* 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. * *

* Causes a {@link RestCallException} to be thrown if the specified pattern is found in the output. * *

* This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output * methods such as {@link #getResponseAsString()}. * *

Example:
*

* // Throw a RestCallException if FAILURE or ERROR is found in the output. * restClient.doGet(URL) * .failurePattern("FAILURE|ERROR") * .run(); *

* * @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. * *

* Causes a {@link RestCallException} to be thrown if the specified pattern is not found in the output. * *

* This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output * methods such as {@link #getResponseAsString()}. * *

Example:
*

* // Throw a RestCallException if SUCCESS is not found in the output. * restClient.doGet(URL) * .successPattern("SUCCESS") * .run(); *

* * @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. * *

* This method can be called multiple times to add multiple response pattern finders. * *

* {@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. * *

* 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. * *

* The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an * output stream or writer. * *

Example:
*

* try { * RestClient client = new RestClient(); * int rc = client.doGet(url).execute(); * // Succeeded! * } catch (RestCallException e) { * // Failed! * } *

* * @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 runFuture() throws RestCallException { return client.getExecutorService(true).submit( new Callable() { @Override /* Callable */ public Integer call() throws Exception { return run(); } } ); } /** * Connects to the REST resource. * *

* If this is a PUT or POST, also sends the input to the remote resource.
* *

* 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 400+ 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 connect() hasn't already been called) and returns the HTTP * response message body as a reader. * *

* 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 GZIPInputStream). * *

* If present, automatically handles the charset value in the Content-Type response header. * *

* IMPORTANT: It is your responsibility to close this reader once you have finished with it. * * @return * The HTTP response message body reader. * null 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. * *

* Note that while similar to {@link #getResponseAsString()}, this method can be called multiple times to retrieve * the response text multiple times. * *

* Note that this method returns null 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 null 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 Content-Length header. * * @return The value of the Content-Length header, or -1 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 connect() hasn't already been called) and returns the HTTP * response message body as an input stream. * *

* 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 GZIPInputStream). * *

* IMPORTANT: It is your responsibility to close this reader once you have finished with it. * * @return * The HTTP response message body input stream. null 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 getResponseAsStringFuture() throws RestCallException { return client.getExecutorService(true).submit( new Callable() { @Override /* Callable */ public String call() throws Exception { return getResponseAsString(); } } ); } /** * Same as {@link #getResponse(Type, Type...)} except optimized for a non-parameterized class. * *

* This is the preferred parse method for simple types since you don't need to cast the results. * *

Examples:
*

* // Parse into a string. * String s = restClient.doGet(url).getResponse(String.class); * * // Parse into a bean. * MyBean b = restClient.doGet(url).getResponse(MyBean.class); * * // Parse into a bean array. * MyBean[] ba = restClient.doGet(url).getResponse(MyBean[].class); * * // Parse into a linked-list of objects. * List l = restClient.doGet(url).getResponse(LinkedList.class); * * // Parse into a map of object keys/values. * Map m = restClient.doGet(url).getResponse(TreeMap.class); *

* *
Notes:
*
    *
  • * You can also specify any of the following types: *
      *
    • {@link HttpResponse} - Returns the raw HttpResponse returned by the inner HttpClient. *
    • {@link Reader} - Returns access to the raw reader of the response. *
    • {@link InputStream} - Returns access to the raw input stream of the response. *
    *
* * @param * 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 getResponse(Class 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 * 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 Future getResponseFuture(final Class type) throws RestCallException { return client.getExecutorService(true).submit( new Callable() { @Override /* Callable */ public T call() throws Exception { return getResponse(type); } } ); } /** * Parses HTTP body into the specified object type. * *

* The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). * *

Examples:
*

* // Parse into a linked-list of strings. * List l = restClient.doGet(url).getResponse(LinkedList.class, String.class); * * // Parse into a linked-list of beans. * List l = restClient.doGet(url).getResponse(LinkedList.class, MyBean.class); * * // Parse into a linked-list of linked-lists of strings. * List l = restClient.doGet(url).getResponse(LinkedList.class, LinkedList.class, String.class); * * // Parse into a map of string keys/values. * Map m = restClient.doGet(url).getResponse(TreeMap.class, String.class, String.class); * * // Parse into a map containing string keys and values of lists containing beans. * Map m = restClient.doGet(url).getResponse(TreeMap.class, String.class, List.class, MyBean.class); *

* *

* Collection classes are assumed to be followed by zero or one objects indicating the element type. * *

* Map classes are assumed to be followed by zero or two meta objects indicating the key and value types. * *

* The array can be arbitrarily long to indicate arbitrarily complex data structures. * *

Notes:
*
    *
  • * Use the {@link #getResponse(Class)} method instead if you don't need a parameterized map/collection. *
  • * You can also specify any of the following types: *
      *
    • {@link HttpResponse} - Returns the raw HttpResponse returned by the inner HttpClient. *
    • {@link Reader} - Returns access to the raw reader of the response. *
    • {@link InputStream} - Returns access to the raw input stream of the response. *
    *
* * @param The class type of the object to create. * @param type * The object type to create. *
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. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
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 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 * The class type of the object being created. * See {@link #getResponse(Type, Type...)} for details. * @param type * The object type to create. *
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. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, * {@link GenericArrayType} *
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 Future getResponseFuture(final Type type, final Type...args) throws RestCallException { return client.getExecutorService(true).submit( new Callable() { @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}. * *

* 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 Content-Type 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}. * *

* 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 Content-Type header. */ public PojoRest getResponsePojoRest() throws IOException, ParseException { return getResponsePojoRest(ObjectMap.class); } T getResponse(ClassMeta 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)}. * *

* Returns null 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 getRequest().setHeader(header) * * @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 Debug: value 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); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy