001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.client;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016import static org.apache.juneau.internal.IOUtils.*;
017import static org.apache.juneau.internal.StringUtils.*;
018import static org.apache.juneau.httppart.HttpPartType.*;
019import static org.apache.http.HttpStatus.*;
020
021import java.io.*;
022import java.lang.reflect.*;
023import java.lang.reflect.Proxy;
024import java.net.*;
025import java.util.*;
026import java.util.concurrent.*;
027import java.util.logging.*;
028import java.util.regex.*;
029
030import org.apache.http.*;
031import org.apache.http.Header;
032import org.apache.http.client.*;
033import org.apache.http.client.config.*;
034import org.apache.http.client.entity.*;
035import org.apache.http.client.methods.*;
036import org.apache.http.client.utils.*;
037import org.apache.http.conn.*;
038import org.apache.http.entity.*;
039import org.apache.http.entity.ContentType;
040import org.apache.http.impl.client.*;
041import org.apache.http.util.*;
042import org.apache.juneau.*;
043import org.apache.juneau.encoders.*;
044import org.apache.juneau.http.*;
045import org.apache.juneau.http.annotation.*;
046import org.apache.juneau.httppart.*;
047import org.apache.juneau.httppart.bean.*;
048import org.apache.juneau.internal.*;
049import org.apache.juneau.oapi.*;
050import org.apache.juneau.parser.*;
051import org.apache.juneau.parser.ParseException;
052import org.apache.juneau.serializer.*;
053import org.apache.juneau.utils.*;
054
055/**
056 * Represents a connection to a remote REST resource.
057 *
058 * <p>
059 * Instances of this class are created by the various {@code doX()} methods on the {@link RestClient} class.
060 *
061 * <p>
062 * This class uses only Java standard APIs.  Requests can be built up using a fluent interface with method chaining,
063 * like so...
064 * <p class='bcode w800'>
065 *    RestClient client = <jk>new</jk> RestClient();
066 *    RestCall c = client.doPost(<jsf>URL</jsf>).setInput(o).setHeader(x,y);
067 *    MyBean b = c.getResponse(MyBean.<jk>class</jk>);
068 * </p>
069 *
070 * <p>
071 * The actual connection and request/response transaction occurs when calling one of the <code>getResponseXXX()</code>
072 * methods.
073 *
074 * <h5 class='section'>See Also:</h5>
075 * <ul>
076 *    <li class='link'>{@doc juneau-rest-client}
077 * </ul>
078 */
079@SuppressWarnings({ "unchecked" })
080public final class RestCall extends BeanSession implements Closeable {
081
082   private static final ContentType TEXT_PLAIN = ContentType.create("text/plain");
083
084   private final RestClient client;                       // The client that created this call.
085   private final HttpRequestBase request;                 // The request.
086   private HttpResponse response;                         // The response.
087   private List<RestCallInterceptor> interceptors = new ArrayList<>();               // Used for intercepting and altering requests.
088
089   private boolean isConnected = false;                   // connect() has been called.
090   private boolean allowRedirectsOnPosts;
091   private int retries = 1;
092   private int redirectOnPostsTries = 5;
093   private long retryInterval = -1;
094   private RetryOn retryOn;
095   private boolean ignoreErrors;
096   private boolean byLines = false;
097   private TeeWriter writers = new TeeWriter();
098   private StringWriter capturedResponseWriter;
099   private String capturedResponse;
100   private TeeOutputStream outputStreams = new TeeOutputStream();
101   private boolean isClosed = false;
102   private boolean isFailed = false;
103   private Object input;
104   private boolean hasInput;  // input() was called, even if it's setting 'null'.
105   private Serializer serializer;
106   private Parser parser;
107   private HttpPartSerializer partSerializer;
108   private HttpPartParser partParser;
109   private HttpPartSchema requestBodySchema, responseBodySchema;
110   private URIBuilder uriBuilder;
111   private NameValuePairs formData;
112   private boolean softClose = false;  // If true, don't consume response and set isClosed flag, but do call listeners.
113
114   /**
115    * Constructs a REST call with the specified method name.
116    *
117    * @param client The client that created this request.
118    * @param request The wrapped Apache HTTP client request object.
119    * @param uri The URI for this call.
120    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
121    */
122   protected RestCall(RestClient client, HttpRequestBase request, URI uri) throws RestCallException {
123      super(client, BeanSessionArgs.DEFAULT);
124      this.client = client;
125      this.request = request;
126      for (RestCallInterceptor i : this.client.interceptors)
127         interceptor(i);
128      this.retryOn = client.retryOn;
129      this.retries = client.retries;
130      this.retryInterval = client.retryInterval;
131      this.serializer = client.serializer;
132      this.parser = client.parser;
133      this.partSerializer = client.getPartSerializer();
134      this.partParser = client.getPartParser();
135      uriBuilder = new URIBuilder(uri);
136   }
137
138   /**
139    * Sets the URI for this call.
140    *
141    * <p>
142    * Can be any of the following types:
143    * <ul>
144    *    <li>{@link URI}
145    *    <li>{@link URL}
146    *    <li>{@link URIBuilder}
147    *    <li>Anything else converted to a string using {@link Object#toString()}.
148    * </ul>
149    *
150    * <p>
151    * Relative URL strings will be interpreted as relative to the root URL defined on the client.
152    *
153    * @param uri
154    *    The URI to use for this call.
155    *    This overrides the URI passed in from the client.
156    * @return This object (for method chaining).
157    * @throws RestCallException
158    */
159   public RestCall uri(Object uri) throws RestCallException {
160      try {
161         if (uri != null)
162            uriBuilder = new URIBuilder(client.toURI(uri));
163         return this;
164      } catch (URISyntaxException e) {
165         throw new RestCallException(e);
166      }
167   }
168
169   /**
170    * Sets the URI scheme.
171    *
172    * @param scheme The new URI host.
173    * @return This object (for method chaining).
174    */
175   public RestCall scheme(String scheme) {
176      uriBuilder.setScheme(scheme);
177      return this;
178   }
179
180   /**
181    * Sets the URI host.
182    *
183    * @param host The new URI host.
184    * @return This object (for method chaining).
185    */
186   public RestCall host(String host) {
187      uriBuilder.setHost(host);
188      return this;
189   }
190
191   /**
192    * Sets the URI port.
193    *
194    * @param port The new URI port.
195    * @return This object (for method chaining).
196    */
197   public RestCall port(int port) {
198      uriBuilder.setPort(port);
199      return this;
200   }
201
202   /**
203    * Adds a query parameter to the URI query.
204    *
205    * @param name
206    *    The parameter name.
207    *    Can be null/blank/* if the value is a {@link Map}, {@link String}, {@link NameValuePairs}, or bean.
208    * @param value
209    *    The parameter value converted to a string using UON notation.
210    *    Can also be {@link Map}, {@link String}, {@link NameValuePairs}, or bean if the name is null/blank/*.
211    *    If a {@link String} and the name is null/blank/*, then calls {@link URIBuilder#setCustomQuery(String)}.
212    * @param skipIfEmpty Don't add the pair if the value is empty.
213    * @param serializer
214    *    The serializer to use for serializing the value to a string value.
215    *    If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
216    * @param schema
217    *    The schema object that defines the format of the output.
218    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
219    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
220    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
221    * @return This object (for method chaining).
222    * @throws RestCallException
223    */
224   public RestCall query(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
225      if (serializer == null)
226         serializer = client.getPartSerializer();
227      if (schema == null)
228         schema = HttpPartSchema.DEFAULT;
229      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof NameValuePairs;
230      if (! isMulti) {
231         if (canAdd(value, schema, skipIfEmpty))
232            try {
233               uriBuilder.addParameter(name, serializer.serialize(QUERY, schema, value));
234            } catch (SchemaValidationException e) {
235               throw new RestCallException(e, "Validation error on request query parameter ''{0}''=''{1}''", name, value);
236            } catch (SerializeException e) {
237               throw new RestCallException(e, "Serialization error on request query parameter ''{0}''", name);
238            }
239      } else if (value instanceof NameValuePairs) {
240         for (NameValuePair p : (NameValuePairs)value) {
241            String n = p.getName();
242            String v = p.getValue();
243            HttpPartSchema s = schema.getProperty(n);
244            if (canAdd(v, s, skipIfEmpty))
245               query(n, v, skipIfEmpty, serializer, s);
246         }
247      } else if (value instanceof Map) {
248         for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) {
249            String n = p.getKey();
250            Object v = p.getValue();
251            HttpPartSchema s = schema.getProperty(n);
252            if (canAdd(v, s, skipIfEmpty))
253               query(n, v, skipIfEmpty, serializer, s);
254         }
255      } else if (isBean(value)) {
256         return query(name, toBeanMap(value), skipIfEmpty, serializer, schema);
257      } else if (value instanceof Reader || value instanceof InputStream) {
258         try {
259            uriBuilder.setCustomQuery(read(value));
260         } catch (IOException e) {
261            throw new RestCallException(e);
262         }
263      } else if (value instanceof CharSequence) {
264         String s = value.toString();
265         if (isNotEmpty(s))
266            uriBuilder.setCustomQuery(s);
267      } else {
268         throw new RestCallException("Invalid name ''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value));
269      }
270      return this;
271   }
272
273   /**
274    * Adds a query parameter to the URI query.
275    *
276    * @param name The parameter name.
277    * @param value The parameter value converted to a string using UON notation.
278    * @return This object (for method chaining).
279    * @throws RestCallException
280    */
281   public RestCall query(String name, Object value) throws RestCallException {
282      return query(name, value, false, null, null);
283   }
284
285   /**
286    * Adds query parameters to the URI query.
287    *
288    * @param params The parameters.  Values are converted to a string using UON notation.
289    * @return This object (for method chaining).
290    * @throws RestCallException
291    */
292   public RestCall query(Map<String,Object> params) throws RestCallException {
293      return query(null, params);
294   }
295
296   /**
297    * Adds a query parameter to the URI query if the parameter value is not <jk>null</jk> or an empty string.
298    *
299    * <p>
300    * NE = "not empty"
301    *
302    * @param name The parameter name.
303    * @param value The parameter value converted to a string using UON notation.
304    * @return This object (for method chaining).
305    * @throws RestCallException
306    */
307   public RestCall queryIfNE(String name, Object value) throws RestCallException {
308      return query(name, value, true, null, null);
309   }
310
311   /**
312    * Adds query parameters to the URI for any parameters that aren't null/empty.
313    *
314    * <p>
315    * NE = "not empty"
316    *
317    * @param params The parameters.  Values are converted to a string using UON notation.
318    * @return This object (for method chaining).
319    * @throws RestCallException
320    */
321   public RestCall queryIfNE(Map<String,Object> params) throws RestCallException {
322      return query(null, params, true, null, null);
323   }
324
325   /**
326    * Sets a custom URI query.
327    *
328    * @param query The new URI query string.
329    * @return This object (for method chaining).
330    */
331   public RestCall query(String query) {
332      uriBuilder.setCustomQuery(query);
333      return this;
334   }
335
336   /**
337    * Adds a form data pair to this request to perform a URL-encoded form post.
338    *
339    * @param name
340    *    The parameter name.
341    *    Can be null/blank/* if the value is a {@link Map}, {@link NameValuePairs}, or bean.
342    * @param value
343    *    The parameter value converted to a string using UON notation.
344    *    Can also be {@link Map}, {@link NameValuePairs}, or bean if the name is null/blank/*.
345    * @param skipIfEmpty Don't add the pair if the value is empty.
346    * @param serializer
347    *    The serializer to use for serializing the value to a string value.
348    *    If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
349    * @param schema
350    *    The schema object that defines the format of the output.
351    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
352    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
353    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
354    * @return This object (for method chaining).
355    * @throws RestCallException
356    */
357   public RestCall formData(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
358      if (formData == null)
359         formData = new NameValuePairs();
360      if (serializer == null)
361         serializer = client.getPartSerializer();
362      if (schema == null)
363         schema = HttpPartSchema.DEFAULT;
364      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof NameValuePairs;
365      if (! isMulti) {
366         if (canAdd(value, schema, skipIfEmpty))
367            formData.add(new SerializedNameValuePair(name, value, serializer, schema));
368      } else if (value instanceof NameValuePairs) {
369         for (NameValuePair p : (NameValuePairs)value) {
370            String n = p.getName();
371            String v = p.getValue();
372            HttpPartSchema s = schema.getProperty(n);
373            if (canAdd(v, s, skipIfEmpty))
374               formData.add(p);
375         }
376      } else if (value instanceof Map) {
377         for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) {
378            String n = p.getKey();
379            Object v = p.getValue();
380            HttpPartSchema s = schema.getProperty(n);
381            if (canAdd(v, s, skipIfEmpty))
382               formData(n, v, skipIfEmpty, serializer, s);
383         }
384      } else if (isBean(value)) {
385         return formData(name, toBeanMap(value), skipIfEmpty, serializer, schema);
386      } else if (value instanceof Reader || value instanceof InputStream) {
387         contentType("application/x-www-form-urlencoded");
388         body(value);
389      } else if (value instanceof CharSequence) {
390         try {
391            contentType("application/x-www-form-urlencoded");
392            body(new StringEntity(value.toString()));
393         } catch (UnsupportedEncodingException e) {}
394      } else {
395         throw new FormattedRuntimeException("Invalid name ''{0}'' passed to formData(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value));
396      }
397      return this;
398   }
399
400   /**
401    * Adds a form data pair to this request to perform a URL-encoded form post.
402    *
403    * @param name
404    *    The parameter name.
405    *    Can be null/blank if the value is a {@link Map} or {@link NameValuePairs}.
406    * @param value
407    *    The parameter value converted to a string using UON notation.
408    *    Can also be a {@link Map} or {@link NameValuePairs}.
409    * @return This object (for method chaining).
410    * @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}.
411    */
412   public RestCall formData(String name, Object value) throws RestCallException {
413      return formData(name, value, false, null, null);
414   }
415
416   /**
417    * Adds form data pairs to this request to perform a URL-encoded form post.
418    *
419    * @param nameValuePairs The name-value pairs of the request.
420    * @return This object (for method chaining).
421    * @throws RestCallException
422    */
423   public RestCall formData(NameValuePairs nameValuePairs) throws RestCallException {
424      return formData(null, nameValuePairs);
425   }
426
427   /**
428    * Adds form data pairs to this request to perform a URL-encoded form post.
429    *
430    * @param params The parameters.  Values are converted to a string using UON notation.
431    * @return This object (for method chaining).
432    * @throws RestCallException If name was null/blank and value wasn't a {@link Map} or {@link NameValuePairs}.
433    */
434   public RestCall formData(Map<String,Object> params) throws RestCallException {
435      return formData(null, params);
436   }
437
438   /**
439    * Adds a form data pair to the request if the parameter value is not <jk>null</jk> or an empty string.
440    *
441    * <p>
442    * NE = "not empty"
443    *
444    * @param name The parameter name.
445    * @param value The parameter value converted to a string using UON notation.
446    * @return This object (for method chaining).
447    * @throws RestCallException
448    */
449   public RestCall formDataIfNE(String name, Object value) throws RestCallException {
450      return formData(name, value, true, null, null);
451   }
452
453   /**
454    * Adds form data parameters to the request for any parameters that aren't null/empty.
455    *
456    * <p>
457    * NE = "not empty"
458    *
459    * @param params The parameters.  Values are converted to a string using UON notation.
460    * @return This object (for method chaining).
461    * @throws RestCallException
462    */
463   public RestCall formDataIfNE(Map<String,Object> params) throws RestCallException {
464      return formData(null, params, true, null, null);
465   }
466
467   /**
468    * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value.
469    *
470    * @param name The path variable name.
471    * @param value The replacement value.
472    * @param serializer
473    *    The serializer to use for serializing the value to a string value.
474    *    If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
475    * @param schema
476    *    The schema object that defines the format of the output.
477    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
478    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
479    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
480    * @return This object (for method chaining).
481    * @throws RestCallException If variable could not be found in path.
482    */
483   public RestCall path(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
484      String path = uriBuilder.getPath();
485      if (serializer == null)
486         serializer = client.getPartSerializer();
487      if (schema == null)
488         schema = HttpPartSchema.DEFAULT;
489      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof NameValuePairs;
490      if (! isMulti) {
491         String var = "{" + name + "}";
492         if (path.indexOf(var) == -1 && ! name.equals("/*"))
493            throw new RestCallException("Path variable {"+name+"} was not found in path.");
494         try {
495            String p = null;
496            if (name.equals("/*"))
497               p = path.replaceAll("\\/\\*$", serializer.serialize(PATH, schema, value));
498            else
499               p = path.replace(var, serializer.serialize(PATH, schema, value));
500            uriBuilder.setPath(p);
501         } catch (SchemaValidationException e) {
502            throw new RestCallException(e, "Validation error on request path parameter ''{0}''=''{1}''", name, value);
503         } catch (SerializeException e) {
504            throw new RestCallException(e, "Serialization error on request path parameter ''{0}''", name);
505         }
506      } else if (value instanceof NameValuePairs) {
507         for (NameValuePair p : (NameValuePairs)value) {
508            String n = p.getName();
509            String v = p.getValue();
510            HttpPartSchema s = schema.getProperty(n);
511            path(n, v, serializer, s);
512         }
513      } else if (value instanceof Map) {
514         for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) {
515            String n = p.getKey();
516            Object v = p.getValue();
517            HttpPartSchema s = schema.getProperty(n);
518            path(n, v, serializer, s);
519         }
520      } else if (isBean(value)) {
521         return path(name, toBeanMap(value), serializer, schema);
522      } else if (value != null) {
523         throw new RestCallException("Invalid name ''{0}'' passed to path(name,value) for data type ''{1}''", name, getReadableClassNameForObject(value));
524      }
525      return this;
526   }
527
528   /**
529    * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value.
530    *
531    * @param name The path variable name.
532    * @param value The replacement value.
533    * @return This object (for method chaining).
534    * @throws RestCallException If variable could not be found in path.
535    */
536   public RestCall path(String name, Object value) throws RestCallException {
537      return path(name, value, null, null);
538   }
539
540   /**
541    * Sets the URI user info.
542    *
543    * @param userInfo The new URI user info.
544    * @return This object (for method chaining).
545    */
546   public RestCall userInfo(String userInfo) {
547      uriBuilder.setUserInfo(userInfo);
548      return this;
549   }
550
551   /**
552    * Sets the URI user info.
553    *
554    * @param username The new URI username.
555    * @param password The new URI password.
556    * @return This object (for method chaining).
557    */
558   public RestCall userInfo(String username, String password) {
559      uriBuilder.setUserInfo(username, password);
560      return this;
561   }
562
563   /**
564    * Specifies the part schema for the request body.
565    *
566    * <p>
567    * This is only useful for schema-aware serializers such as {@link OpenApiSerializer}.
568    *
569    * @param value
570    *    The new part schema for the request body.
571    *    <br>Can be <jk>null</jk>.
572    * @return This object (for method chaining).
573    */
574   public RestCall requestBodySchema(HttpPartSchema value) {
575      this.requestBodySchema = value;
576      return this;
577   }
578
579   /**
580    * Specifies the part schema for the response body.
581    *
582    * <p>
583    * This is only useful for schema-aware parsers such as {@link OpenApiParser}.
584    *
585    * @param value
586    *    The new part schema for the response body.
587    *    <br>Can be <jk>null</jk>.
588    * @return This object (for method chaining).
589    */
590   public RestCall responseBodySchema(HttpPartSchema value) {
591      this.responseBodySchema = value;
592      return this;
593   }
594
595   /**
596    * Sets the input for this REST call.
597    *
598    * @param input
599    *    The input to be sent to the REST resource (only valid for PUT and POST) requests.
600    *    <br>Can be of the following types:
601    *    <ul class='spaced-list'>
602    *       <li>
603    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
604    *       <li>
605    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
606    *       <li>
607    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
608    *          {@link RestClient}.
609    *       <li>
610    *          {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
611    *       <li>
612    *          {@link NameValuePairs} - Converted to a URL-encoded FORM post.
613    *    </ul>
614    * @return This object (for method chaining).
615    * @throws RestCallException If a retry was attempted, but the entity was not repeatable.
616    */
617   public RestCall body(Object input) throws RestCallException {
618      this.input = input;
619      this.hasInput = true;
620      this.formData = null;
621      return this;
622   }
623
624   /**
625    * Specifies the serializer to use on this call.
626    *
627    * <p>
628    * Overrides the serializer specified on the {@link RestClient}.
629    *
630    * @param serializer The serializer used to serialize POJOs to the body of the HTTP request.
631    * @return This object (for method chaining).
632    */
633   public RestCall serializer(Serializer serializer) {
634      this.serializer = serializer;
635      return this;
636   }
637
638   /**
639    * Specifies the parser to use on this call.
640    *
641    * <p>
642    * Overrides the parser specified on the {@link RestClient}.
643    *
644    * @param parser The parser used to parse POJOs from the body of the HTTP response.
645    * @return This object (for method chaining).
646    */
647   public RestCall parser(Parser parser) {
648      this.parser = parser;
649      return this;
650   }
651
652   //-----------------------------------------------------------------------------------------------------------------
653   // HTTP headers
654   //-----------------------------------------------------------------------------------------------------------------
655
656   /**
657    * Sets a header on the request.
658    *
659    * @param name
660    *    The header name.
661    *    The name can be null/empty if the value is a {@link Map}.
662    * @param value The header value.
663    * @param skipIfEmpty Don't add the header if the name is null/empty.
664    * @param serializer
665    *    The serializer to use for serializing the value to a string value.
666    *    If <jk>null</jk>, then the URL-encoding serializer defined on the client is used.
667    * @param schema
668    *    The schema object that defines the format of the output.
669    *    <br>If <jk>null</jk>, defaults to the schema defined on the serializer.
670    *    <br>If that's also <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
671    *    <br>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
672    * @return This object (for method chaining).
673    * @throws RestCallException
674    */
675   public RestCall header(String name, Object value, boolean skipIfEmpty, HttpPartSerializer serializer, HttpPartSchema schema) throws RestCallException {
676      if (serializer == null)
677         serializer = client.getPartSerializer();
678      if (schema == null)
679         schema = HttpPartSchema.DEFAULT;
680      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof NameValuePairs;
681      if (! isMulti) {
682         if (canAdd(value, schema, skipIfEmpty))
683            try {
684               request.setHeader(name, serializer.serialize(HEADER, schema, value));
685            } catch (SchemaValidationException e) {
686               throw new RestCallException(e, "Validation error on request header parameter ''{0}''=''{1}''", name, value);
687            } catch (SerializeException e) {
688               throw new RestCallException(e, "Serialization error on request header parameter ''{0}''", name);
689            }
690      } else if (value instanceof NameValuePairs) {
691         for (NameValuePair p : (NameValuePairs)value) {
692            String n = p.getName();
693            String v = p.getValue();
694            HttpPartSchema s = schema.getProperty(n);
695            if (canAdd(v, s, skipIfEmpty))
696               header(n, v, skipIfEmpty, serializer, s);
697         }
698      } else if (value instanceof Map) {
699         for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) {
700            String n = p.getKey();
701            Object v = p.getValue();
702            HttpPartSchema s = schema.getProperty(n);
703            if (canAdd(v, s, skipIfEmpty))
704               header(n, v, skipIfEmpty, serializer, s);
705         }
706      } else if (isBean(value)) {
707         return header(name, toBeanMap(value), skipIfEmpty, serializer, schema);
708      } else {
709         throw new RestCallException("Invalid name ''{0}'' passed to header(name,value,skipIfEmpty) for data type ''{1}''", name, getReadableClassNameForObject(value));
710      }
711      return this;
712   }
713
714
715   /**
716    * Sets a header on the request.
717    *
718    * @param name
719    *    The header name.
720    *    The name can be null/empty if the value is a {@link Map}.
721    * @param value The header value.
722    * @return This object (for method chaining).
723    * @throws RestCallException
724    */
725   public RestCall header(String name, Object value) throws RestCallException {
726      return header(name, value, false, null, null);
727   }
728
729   /**
730    * Sets headers on the request.
731    *
732    * @param values The header values.
733    * @return This object (for method chaining).
734    * @throws RestCallException
735    */
736   public RestCall headers(Map<String,Object> values) throws RestCallException {
737      return header(null, values, false, null, null);
738   }
739
740   /**
741    * Sets a header on the request if the value is not null/empty.
742    *
743    * <p>
744    * NE = "not empty"
745    *
746    * @param name
747    *    The header name.
748    *    The name can be null/empty if the value is a {@link Map}.
749    * @param value The header value.
750    * @return This object (for method chaining).
751    * @throws RestCallException
752    */
753   public RestCall headerIfNE(String name, Object value) throws RestCallException {
754      return header(name, value, true, null, null);
755   }
756
757   /**
758    * Sets headers on the request if the values are not null/empty.
759    *
760    * <p>
761    * NE = "not empty"
762    *
763    * @param values The header values.
764    * @return This object (for method chaining).
765    * @throws RestCallException
766    */
767   public RestCall headersIfNE(Map<String,Object> values) throws RestCallException {
768      return header(null, values, true, null, null);
769   }
770
771   /**
772    * Sets the value for the <code>Accept</code> request header.
773    *
774    * <p>
775    * This overrides the media type specified on the parser, but is overridden by calling
776    * <code>header(<js>"Accept"</js>, value);</code>
777    *
778    * @param value The new header value.
779    * @return This object (for method chaining).
780    * @throws RestCallException
781    */
782   public RestCall accept(Object value) throws RestCallException {
783      return header("Accept", value);
784   }
785
786   /**
787    * Sets the value for the <code>Accept-Charset</code> request header.
788    *
789    * <p>
790    * This is a shortcut for calling <code>header(<js>"Accept-Charset"</js>, value);</code>
791    *
792    * @param value The new header value.
793    * @return This object (for method chaining).
794    * @throws RestCallException
795    */
796   public RestCall acceptCharset(Object value) throws RestCallException {
797      return header("Accept-Charset", value);
798   }
799
800   /**
801    * Sets the value for the <code>Accept-Encoding</code> request header.
802    *
803    * <p>
804    * This is a shortcut for calling <code>header(<js>"Accept-Encoding"</js>, value);</code>
805    *
806    * @param value The new header value.
807    * @return This object (for method chaining).
808    * @throws RestCallException
809    */
810   public RestCall acceptEncoding(Object value) throws RestCallException {
811      return header("Accept-Encoding", value);
812   }
813
814   /**
815    * Sets the value for the <code>Accept-Language</code> request header.
816    *
817    * <p>
818    * This is a shortcut for calling <code>header(<js>"Accept-Language"</js>, value);</code>
819    *
820    * @param value The new header value.
821    * @return This object (for method chaining).
822    * @throws RestCallException
823    */
824   public RestCall acceptLanguage(Object value) throws RestCallException {
825      return header("Accept-Language", value);
826   }
827
828   /**
829    * Sets the value for the <code>Authorization</code> request header.
830    *
831    * <p>
832    * This is a shortcut for calling <code>header(<js>"Authorization"</js>, value);</code>
833    *
834    * @param value The new header value.
835    * @return This object (for method chaining).
836    * @throws RestCallException
837    */
838   public RestCall authorization(Object value) throws RestCallException {
839      return header("Authorization", value);
840   }
841
842   /**
843    * Sets the value for the <code>Cache-Control</code> request header.
844    *
845    * <p>
846    * This is a shortcut for calling <code>header(<js>"Cache-Control"</js>, value);</code>
847    *
848    * @param value The new header value.
849    * @return This object (for method chaining).
850    * @throws RestCallException
851    */
852   public RestCall cacheControl(Object value) throws RestCallException {
853      return header("Cache-Control", value);
854   }
855
856   /**
857    * Sets the value for the <code>Connection</code> request header.
858    *
859    * <p>
860    * This is a shortcut for calling <code>header(<js>"Connection"</js>, value);</code>
861    *
862    * @param value The new header value.
863    * @return This object (for method chaining).
864    * @throws RestCallException
865    */
866   public RestCall connection(Object value) throws RestCallException {
867      return header("Connection", value);
868   }
869
870   /**
871    * Sets the value for the <code>Content-Length</code> request header.
872    *
873    * <p>
874    * This is a shortcut for calling <code>header(<js>"Content-Length"</js>, value);</code>
875    *
876    * @param value The new header value.
877    * @return This object (for method chaining).
878    * @throws RestCallException
879    */
880   public RestCall contentLength(Object value) throws RestCallException {
881      return header("Content-Length", value);
882   }
883
884   /**
885    * Sets the value for the <code>Content-Type</code> request header.
886    *
887    * <p>
888    * This overrides the media type specified on the serializer, but is overridden by calling
889    * <code>header(<js>"Content-Type"</js>, value);</code>
890    *
891    * @param value The new header value.
892    * @return This object (for method chaining).
893    * @throws RestCallException
894    */
895   public RestCall contentType(Object value) throws RestCallException {
896      return header("Content-Type", value);
897   }
898
899   /**
900    * Sets the value for the <code>Date</code> request header.
901    *
902    * <p>
903    * This is a shortcut for calling <code>header(<js>"Date"</js>, value);</code>
904    *
905    * @param value The new header value.
906    * @return This object (for method chaining).
907    * @throws RestCallException
908    */
909   public RestCall date(Object value) throws RestCallException {
910      return header("Date", value);
911   }
912
913   /**
914    * Sets the value for the <code>Expect</code> request header.
915    *
916    * <p>
917    * This is a shortcut for calling <code>header(<js>"Expect"</js>, value);</code>
918    *
919    * @param value The new header value.
920    * @return This object (for method chaining).
921    * @throws RestCallException
922    */
923   public RestCall expect(Object value) throws RestCallException {
924      return header("Expect", value);
925   }
926
927   /**
928    * Sets the value for the <code>Forwarded</code> request header.
929    *
930    * <p>
931    * This is a shortcut for calling <code>header(<js>"Forwarded"</js>, value);</code>
932    *
933    * @param value The new header value.
934    * @return This object (for method chaining).
935    * @throws RestCallException
936    */
937   public RestCall forwarded(Object value) throws RestCallException {
938      return header("Forwarded", value);
939   }
940
941   /**
942    * Sets the value for the <code>From</code> request header.
943    *
944    * <p>
945    * This is a shortcut for calling <code>header(<js>"From"</js>, value);</code>
946    *
947    * @param value The new header value.
948    * @return This object (for method chaining).
949    * @throws RestCallException
950    */
951   public RestCall from(Object value) throws RestCallException {
952      return header("From", value);
953   }
954
955   /**
956    * Sets the value for the <code>Host</code> request header.
957    *
958    * <p>
959    * This is a shortcut for calling <code>header(<js>"Host"</js>, value);</code>
960    *
961    * @param value The new header value.
962    * @return This object (for method chaining).
963    * @throws RestCallException
964    */
965   public RestCall host(Object value) throws RestCallException {
966      return header("Host", value);
967   }
968
969   /**
970    * Sets the value for the <code>If-Match</code> request header.
971    *
972    * <p>
973    * This is a shortcut for calling <code>header(<js>"If-Match"</js>, value);</code>
974    *
975    * @param value The new header value.
976    * @return This object (for method chaining).
977    * @throws RestCallException
978    */
979   public RestCall ifMatch(Object value) throws RestCallException {
980      return header("If-Match", value);
981   }
982
983   /**
984    * Sets the value for the <code>If-Modified-Since</code> request header.
985    *
986    * <p>
987    * This is a shortcut for calling <code>header(<js>"If-Modified-Since"</js>, value);</code>
988    *
989    * @param value The new header value.
990    * @return This object (for method chaining).
991    * @throws RestCallException
992    */
993   public RestCall ifModifiedSince(Object value) throws RestCallException {
994      return header("If-Modified-Since", value);
995   }
996
997   /**
998    * Sets the value for the <code>If-None-Match</code> request header.
999    *
1000    * <p>
1001    * This is a shortcut for calling <code>header(<js>"If-None-Match"</js>, value);</code>
1002    *
1003    * @param value The new header value.
1004    * @return This object (for method chaining).
1005    * @throws RestCallException
1006    */
1007   public RestCall ifNoneMatch(Object value) throws RestCallException {
1008      return header("If-None-Match", value);
1009   }
1010
1011   /**
1012    * Sets the value for the <code>If-Range</code> request header.
1013    *
1014    * <p>
1015    * This is a shortcut for calling <code>header(<js>"If-Range"</js>, value);</code>
1016    *
1017    * @param value The new header value.
1018    * @return This object (for method chaining).
1019    * @throws RestCallException
1020    */
1021   public RestCall ifRange(Object value) throws RestCallException {
1022      return header("If-Range", value);
1023   }
1024
1025   /**
1026    * Sets the value for the <code>If-Unmodified-Since</code> request header.
1027    *
1028    * <p>
1029    * This is a shortcut for calling <code>header(<js>"If-Unmodified-Since"</js>, value);</code>
1030    *
1031    * @param value The new header value.
1032    * @return This object (for method chaining).
1033    * @throws RestCallException
1034    */
1035   public RestCall ifUnmodifiedSince(Object value) throws RestCallException {
1036      return header("If-Unmodified-Since", value);
1037   }
1038
1039   /**
1040    * Sets the value for the <code>Max-Forwards</code> request header.
1041    *
1042    * <p>
1043    * This is a shortcut for calling <code>header(<js>"Max-Forwards"</js>, value);</code>
1044    *
1045    * @param value The new header value.
1046    * @return This object (for method chaining).
1047    * @throws RestCallException
1048    */
1049   public RestCall maxForwards(Object value) throws RestCallException {
1050      return header("Max-Forwards", value);
1051   }
1052
1053   /**
1054    * Sets the value for the <code>Origin</code> request header.
1055    *
1056    * <p>
1057    * This is a shortcut for calling <code>header(<js>"Origin"</js>, value);</code>
1058    *
1059    * @param value The new header value.
1060    * @return This object (for method chaining).
1061    * @throws RestCallException
1062    */
1063   public RestCall origin(Object value) throws RestCallException {
1064      return header("Origin", value);
1065   }
1066
1067   /**
1068    * Sets the value for the <code>Pragma</code> request header.
1069    *
1070    * <p>
1071    * This is a shortcut for calling <code>header(<js>"Pragma"</js>, value);</code>
1072    *
1073    * @param value The new header value.
1074    * @return This object (for method chaining).
1075    * @throws RestCallException
1076    */
1077   public RestCall pragma(Object value) throws RestCallException {
1078      return header("Pragma", value);
1079   }
1080
1081   /**
1082    * Sets the value for the <code>Proxy-Authorization</code> request header.
1083    *
1084    * <p>
1085    * This is a shortcut for calling <code>header(<js>"Proxy-Authorization"</js>, value);</code>
1086    *
1087    * @param value The new header value.
1088    * @return This object (for method chaining).
1089    * @throws RestCallException
1090    */
1091   public RestCall proxyAuthorization(Object value) throws RestCallException {
1092      return header("Proxy-Authorization", value);
1093   }
1094
1095   /**
1096    * Sets the value for the <code>Range</code> request header.
1097    *
1098    * <p>
1099    * This is a shortcut for calling <code>header(<js>"Range"</js>, value);</code>
1100    *
1101    * @param value The new header value.
1102    * @return This object (for method chaining).
1103    * @throws RestCallException
1104    */
1105   public RestCall range(Object value) throws RestCallException {
1106      return header("Range", value);
1107   }
1108
1109   /**
1110    * Sets the value for the <code>Referer</code> request header.
1111    *
1112    * <p>
1113    * This is a shortcut for calling <code>header(<js>"Referer"</js>, value);</code>
1114    *
1115    * @param value The new header value.
1116    * @return This object (for method chaining).
1117    * @throws RestCallException
1118    */
1119   public RestCall referer(Object value) throws RestCallException {
1120      return header("Referer", value);
1121   }
1122
1123   /**
1124    * Sets the value for the <code>TE</code> request header.
1125    *
1126    * <p>
1127    * This is a shortcut for calling <code>header(<js>"TE"</js>, value);</code>
1128    *
1129    * @param value The new header value.
1130    * @return This object (for method chaining).
1131    * @throws RestCallException
1132    */
1133   public RestCall te(Object value) throws RestCallException {
1134      return header("TE", value);
1135   }
1136
1137   /**
1138    * Sets the value for the <code>User-Agent</code> request header.
1139    *
1140    * <p>
1141    * This is a shortcut for calling <code>header(<js>"User-Agent"</js>, value);</code>
1142    *
1143    * @param value The new header value.
1144    * @return This object (for method chaining).
1145    * @throws RestCallException
1146    */
1147   public RestCall userAgent(Object value) throws RestCallException {
1148      return header("User-Agent", value);
1149   }
1150
1151   /**
1152    * Sets the value for the <code>Upgrade</code> request header.
1153    *
1154    * <p>
1155    * This is a shortcut for calling <code>header(<js>"Upgrade"</js>, value);</code>
1156    *
1157    * @param value The new header value.
1158    * @return This object (for method chaining).
1159    * @throws RestCallException
1160    */
1161   public RestCall upgrade(Object value) throws RestCallException {
1162      return header("Upgrade", value);
1163   }
1164
1165   /**
1166    * Sets the value for the <code>Via</code> request header.
1167    *
1168    * <p>
1169    * This is a shortcut for calling <code>header(<js>"Via"</js>, value);</code>
1170    *
1171    * @param value The new header value.
1172    * @return This object (for method chaining).
1173    * @throws RestCallException
1174    */
1175   public RestCall via(Object value) throws RestCallException {
1176      return header("Via", value);
1177   }
1178
1179   /**
1180    * Sets the value for the <code>Warning</code> request header.
1181    *
1182    * <p>
1183    * This is a shortcut for calling <code>header(<js>"Warning"</js>, value);</code>
1184    *
1185    * @param value The new header value.
1186    * @return This object (for method chaining).
1187    * @throws RestCallException
1188    */
1189   public RestCall warning(Object value) throws RestCallException {
1190      return header("Warning", value);
1191   }
1192
1193   /**
1194    * Sets the client version by setting the value for the <js>"X-Client-Version"</js> header.
1195    *
1196    * @param version The version string (e.g. <js>"1.2.3"</js>)
1197    * @return This object (for method chaining).
1198    * @throws RestCallException
1199    */
1200   public RestCall clientVersion(String version) throws RestCallException {
1201      return header("X-Client-Version", version);
1202   }
1203
1204   /**
1205    * Make this call retryable if an error response (>=400) is received.
1206    *
1207    * @param retries The number of retries to attempt.
1208    * @param interval The time in milliseconds between attempts.
1209    * @param retryOn
1210    *    Optional object used for determining whether a retry should be attempted.
1211    *    If <jk>null</jk>, uses {@link RetryOn#DEFAULT}.
1212    * @return This object (for method chaining).
1213    * @throws RestCallException If current entity is not repeatable.
1214    */
1215   public RestCall retryable(int retries, long interval, RetryOn retryOn) throws RestCallException {
1216      if (request instanceof HttpEntityEnclosingRequestBase) {
1217         if (input != null && input instanceof HttpEntity) {
1218            HttpEntity e = (HttpEntity)input;
1219            if (e != null && ! e.isRepeatable())
1220               throw new RestCallException("Attempt to make call retryable, but entity is not repeatable.");
1221            }
1222         }
1223      this.retries = retries;
1224      this.retryInterval = interval;
1225      this.retryOn = (retryOn == null ? RetryOn.DEFAULT : retryOn);
1226      return this;
1227
1228   }
1229
1230   /**
1231    * For this call, allow automatic redirects when a 302 or 307 occurs when performing a POST.
1232    *
1233    * <p>
1234    * Note that this can be inefficient since the POST body needs to be serialized twice.
1235    * The preferred approach if possible is to use the {@link LaxRedirectStrategy} strategy on the underlying HTTP
1236    * client.
1237    * However, this method is provided if you don't have access to the underlying client.
1238    *
1239    * @param b Redirect flag.
1240    * @return This object (for method chaining).
1241    */
1242   public RestCall allowRedirectsOnPosts(boolean b) {
1243      this.allowRedirectsOnPosts = b;
1244      return this;
1245   }
1246
1247   /**
1248    * Specify the number of redirects to follow before throwing an exception.
1249    *
1250    * @param maxAttempts Allow a redirect to occur this number of times.
1251    * @return This object (for method chaining).
1252    */
1253   public RestCall redirectMaxAttempts(int maxAttempts) {
1254      this.redirectOnPostsTries = maxAttempts;
1255      return this;
1256   }
1257
1258   /**
1259    * Add an interceptor for this call only.
1260    *
1261    * @param interceptor The interceptor to add to this call.
1262    * @return This object (for method chaining).
1263    */
1264   public RestCall interceptor(RestCallInterceptor interceptor) {
1265      interceptors.add(interceptor);
1266      interceptor.onInit(this);
1267      return this;
1268   }
1269
1270   /**
1271    * Pipes the request output to the specified writer when {@link #run()} is called.
1272    *
1273    * <p>
1274    * The writer is not closed.
1275    *
1276    * <p>
1277    * This method can be called multiple times to pipe to multiple writers.
1278    *
1279    * @param w The writer to pipe the output to.
1280    * @return This object (for method chaining).
1281    */
1282   public RestCall pipeTo(Writer w) {
1283      return pipeTo(w, false);
1284   }
1285
1286   /**
1287    * Pipe output from response to the specified writer when {@link #run()} is called.
1288    *
1289    * <p>
1290    * This method can be called multiple times to pipe to multiple writers.
1291    *
1292    * @param w The writer to write the output to.
1293    * @param close Close the writer when {@link #close()} is called.
1294    * @return This object (for method chaining).
1295    */
1296   public RestCall pipeTo(Writer w, boolean close) {
1297      return pipeTo(null, w, close);
1298   }
1299
1300   /**
1301    * Pipe output from response to the specified writer when {@link #run()} is called and associate that writer with an
1302    * ID so it can be retrieved through {@link #getWriter(String)}.
1303    *
1304    * <p>
1305    * This method can be called multiple times to pipe to multiple writers.
1306    *
1307    * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)}
1308    * @param w The writer to write the output to.
1309    * @param close Close the writer when {@link #close()} is called.
1310    * @return This object (for method chaining).
1311    */
1312   public RestCall pipeTo(String id, Writer w, boolean close) {
1313      writers.add(id, w, close);
1314      return this;
1315   }
1316
1317   /**
1318    * Retrieves a writer associated with an ID via {@link #pipeTo(String, Writer, boolean)}
1319    *
1320    * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)}
1321    * @return The writer, or <jk>null</jk> if no writer is associated with that ID.
1322    */
1323   public Writer getWriter(String id) {
1324      return writers.getWriter(id);
1325   }
1326
1327   /**
1328    * When output is piped to writers, flush the writers after every line of output.
1329    *
1330    * @return This object (for method chaining).
1331    */
1332   public RestCall byLines() {
1333      this.byLines = true;
1334      return this;
1335   }
1336
1337   /**
1338    * Pipes the request output to the specified output stream when {@link #run()} is called.
1339    *
1340    * <p>
1341    * The output stream is not closed.
1342    *
1343    * <p>
1344    * This method can be called multiple times to pipe to multiple output streams.
1345    *
1346    * @param os The output stream to pipe the output to.
1347    * @return This object (for method chaining).
1348    */
1349   public RestCall pipeTo(OutputStream os) {
1350      return pipeTo(os, false);
1351   }
1352
1353   /**
1354    * Pipe output from response to the specified output stream when {@link #run()} is called.
1355    *
1356    * <p>
1357    * This method can be called multiple times to pipe to multiple output stream.
1358    *
1359    * @param os The output stream to write the output to.
1360    * @param close Close the output stream when {@link #close()} is called.
1361    * @return This object (for method chaining).
1362    */
1363   public RestCall pipeTo(OutputStream os, boolean close) {
1364      return pipeTo(null, os, close);
1365   }
1366
1367   /**
1368    * Pipe output from response to the specified output stream when {@link #run()} is called and associate
1369    * that output stream with an ID so it can be retrieved through {@link #getOutputStream(String)}.
1370    *
1371    * <p>
1372    * This method can be called multiple times to pipe to multiple output stream.
1373    *
1374    * @param id A string identifier that can be used to retrieve the output stream using {@link #getOutputStream(String)}
1375    * @param os The output stream to write the output to.
1376    * @param close Close the output stream when {@link #close()} is called.
1377    * @return This object (for method chaining).
1378    */
1379   public RestCall pipeTo(String id, OutputStream os, boolean close) {
1380      outputStreams.add(id, os, close);
1381      return this;
1382   }
1383
1384   /**
1385    * Retrieves an output stream associated with an ID via {@link #pipeTo(String, OutputStream, boolean)}
1386    *
1387    * @param id A string identifier that can be used to retrieve the writer using {@link #getWriter(String)}
1388    * @return The writer, or <jk>null</jk> if no writer is associated with that ID.
1389    */
1390   public OutputStream getOutputStream(String id) {
1391      return outputStreams.getOutputStream(id);
1392   }
1393
1394   /**
1395    * Prevent {@link RestCallException RestCallExceptions} from being thrown when HTTP status 400+ is encountered.
1396    *
1397    * @return This object (for method chaining).
1398    */
1399   public RestCall ignoreErrors() {
1400      this.ignoreErrors = true;
1401      return this;
1402   }
1403
1404   /**
1405    * Stores the response text so that it can later be captured using {@link #getCapturedResponse()}.
1406    *
1407    * <p>
1408    * This method should only be called once.  Multiple calls to this method are ignored.
1409    *
1410    * @return This object (for method chaining).
1411    */
1412   public RestCall captureResponse() {
1413      if (capturedResponseWriter == null) {
1414         capturedResponseWriter = new StringWriter();
1415         writers.add(capturedResponseWriter, false);
1416      }
1417      return this;
1418   }
1419
1420
1421   /**
1422    * Look for the specified regular expression pattern in the response output.
1423    *
1424    * <p>
1425    * Causes a {@link RestCallException} to be thrown if the specified pattern is found in the output.
1426    *
1427    * <p>
1428    * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output
1429    * methods such as {@link #getResponseAsString()}.
1430    *
1431    * <h5 class='section'>Example:</h5>
1432    * <p class='bcode w800'>
1433    *    <jc>// Throw a RestCallException if FAILURE or ERROR is found in the output.</jc>
1434    *    restClient.doGet(<jsf>URL</jsf>)
1435    *       .failurePattern(<js>"FAILURE|ERROR"</js>)
1436    *       .run();
1437    * </p>
1438    *
1439    * @param errorPattern A regular expression to look for in the response output.
1440    * @return This object (for method chaining).
1441    */
1442   public RestCall failurePattern(final String errorPattern) {
1443      responsePattern(
1444         new ResponsePattern(errorPattern) {
1445            @Override
1446            public void onMatch(RestCall rc, Matcher m) throws RestCallException {
1447               throw new RestCallException("Failure pattern detected.");
1448            }
1449         }
1450      );
1451      return this;
1452   }
1453
1454   /**
1455    * Look for the specified regular expression pattern in the response output.
1456    *
1457    * <p>
1458    * Causes a {@link RestCallException} to be thrown if the specified pattern is not found in the output.
1459    *
1460    * <p>
1461    * This method uses {@link #getCapturedResponse()} to read the response text and so does not affect the other output
1462    * methods such as {@link #getResponseAsString()}.
1463    *
1464    * <h5 class='section'>Example:</h5>
1465    * <p class='bcode w800'>
1466    *    <jc>// Throw a RestCallException if SUCCESS is not found in the output.</jc>
1467    *    restClient.doGet(<jsf>URL</jsf>)
1468    *       .successPattern(<js>"SUCCESS"</js>)
1469    *       .run();
1470    * </p>
1471    *
1472    * @param successPattern A regular expression to look for in the response output.
1473    * @return This object (for method chaining).
1474    */
1475   public RestCall successPattern(String successPattern) {
1476      responsePattern(
1477         new ResponsePattern(successPattern) {
1478            @Override
1479            public void onNoMatch(RestCall rc) throws RestCallException {
1480               throw new RestCallException("Success pattern not detected.");
1481            }
1482         }
1483      );
1484      return this;
1485   }
1486
1487   /**
1488    * Adds a response pattern finder to look for regular expression matches in the response output.
1489    *
1490    * <p>
1491    * This method can be called multiple times to add multiple response pattern finders.
1492    *
1493    * <p>
1494    * {@link ResponsePattern ResponsePatterns} use the {@link #getCapturedResponse()} to read the response text and so
1495    * does not affect the other output methods such as {@link #getResponseAsString()}.
1496    *
1497    * @param responsePattern The response pattern finder.
1498    * @return This object (for method chaining).
1499    */
1500   public RestCall responsePattern(final ResponsePattern responsePattern) {
1501      captureResponse();
1502      interceptor(
1503         new RestCallInterceptor() {
1504            @Override
1505            public void onClose(RestCall restCall) throws RestCallException {
1506               responsePattern.match(RestCall.this);
1507            }
1508         }
1509      );
1510      return this;
1511   }
1512
1513   /**
1514    * Set configuration settings on this request.
1515    *
1516    * <p>
1517    * Use {@link RequestConfig#custom()} to create configuration parameters for the request.
1518    *
1519    * @param config The new configuration settings for this request.
1520    * @return This object (for method chaining).
1521    */
1522   public RestCall setConfig(RequestConfig config) {
1523      this.request.setConfig(config);
1524      return this;
1525   }
1526
1527   /**
1528    * Method used to execute an HTTP response where you're only interested in the HTTP response code.
1529    *
1530    * <p>
1531    * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
1532    * output stream or writer.
1533    *
1534    * <h5 class='section'>Example:</h5>
1535    * <p class='bcode w800'>
1536    *    <jk>try</jk> {
1537    *       RestClient client = <jk>new</jk> RestClient();
1538    *       <jk>int</jk> rc = client.doGet(url).execute();
1539    *       <jc>// Succeeded!</jc>
1540    *    } <jk>catch</jk> (RestCallException e) {
1541    *       <jc>// Failed!</jc>
1542    *    }
1543    * </p>
1544    *
1545    * @return The HTTP status code.
1546    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
1547    */
1548   public int run() throws RestCallException {
1549      connect();
1550      try {
1551         StatusLine status = response.getStatusLine();
1552         int sc = status.getStatusCode();
1553         if (sc >= 400 && ! ignoreErrors)
1554            throw new RestCallException(sc, status.getReasonPhrase(), request.getMethod(), request.getURI(), getResponseAsString()).setHttpResponse(response);
1555         if (outputStreams.size() > 0 || writers.size() > 0)
1556            getReader();
1557         return sc;
1558      } catch (RestCallException e) {
1559         isFailed = true;
1560         throw e;
1561      } catch (IOException e) {
1562         isFailed = true;
1563         throw new RestCallException(e).setHttpResponse(response);
1564      } finally {
1565         close();
1566      }
1567   }
1568
1569   /**
1570    * Same as {@link #run()} but allows you to run the call asynchronously.
1571    *
1572    * @return The HTTP status code.
1573    * @throws RestCallException If the executor service was not defined.
1574    * @see RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating
1575    * {@link Future Futures}.
1576    */
1577   public Future<Integer> runFuture() throws RestCallException {
1578      return client.getExecutorService(true).submit(
1579         new Callable<Integer>() {
1580            @Override /* Callable */
1581            public Integer call() throws Exception {
1582               return run();
1583            }
1584         }
1585      );
1586   }
1587
1588   /**
1589    * Connects to the REST resource.
1590    *
1591    * <p>
1592    * If this is a <code>PUT</code> or <code>POST</code>, also sends the input to the remote resource.<br>
1593    *
1594    * <p>
1595    * Typically, you would only call this method if you're not interested in retrieving the body of the HTTP response.
1596    * Otherwise, you're better off just calling one of the {@link #getReader()}/{@link #getResponse(Class)}/{@link #pipeTo(Writer)}
1597    * methods directly which automatically call this method already.
1598    *
1599    * @return This object (for method chaining).
1600    * @throws RestCallException If an exception or <code>400+</code> HTTP status code occurred during the connection attempt.
1601    */
1602   public RestCall connect() throws RestCallException {
1603      return connect(null);
1604   }
1605
1606   private RestCall connect(ClassMeta<?> bodyType) throws RestCallException {
1607
1608      if (isConnected)
1609         return this;
1610      isConnected = true;
1611
1612      try {
1613
1614         request.setURI(uriBuilder.build());
1615
1616         if (hasInput || formData != null) {
1617
1618            if (hasInput && formData != null)
1619               throw new RestCallException("Both input and form data found on same request.");
1620
1621            if (! (request instanceof HttpEntityEnclosingRequestBase))
1622               throw new RestCallException(0, "Method does not support content entity.", request.getMethod(), request.getURI(), null);
1623
1624            HttpEntity entity = null;
1625            if (formData != null)
1626               entity = new UrlEncodedFormEntity(formData);
1627            else if (input instanceof NameValuePairs)
1628               entity = new UrlEncodedFormEntity((NameValuePairs)input);
1629            else if (input instanceof HttpEntity)
1630               entity = (HttpEntity)input;
1631            else if (input instanceof Reader)
1632               entity = new StringEntity(IOUtils.read((Reader)input), getRequestContentType(TEXT_PLAIN));
1633            else if (input instanceof InputStream)
1634               entity = new InputStreamEntity((InputStream)input, getRequestContentType(ContentType.APPLICATION_OCTET_STREAM));
1635            else if (serializer != null)
1636               entity = new RestRequestEntity(input, serializer, requestBodySchema);
1637            else if (partSerializer != null)
1638               entity = new StringEntity(partSerializer.serialize((HttpPartSchema)null, input), getRequestContentType(TEXT_PLAIN));
1639            else
1640               entity = new StringEntity(getBeanContext().getClassMetaForObject(input).toString(input), getRequestContentType(TEXT_PLAIN));
1641
1642            if (retries > 1 && ! entity.isRepeatable())
1643               throw new RestCallException("Rest call set to retryable, but entity is not repeatable.");
1644
1645            ((HttpEntityEnclosingRequestBase)request).setEntity(entity);
1646         }
1647
1648         int sc = 0;
1649         while (retries > 0) {
1650            retries--;
1651            Exception ex = null;
1652            try {
1653               response = client.execute(request);
1654               sc = (response == null || response.getStatusLine() == null) ? -1 : response.getStatusLine().getStatusCode();
1655            } catch (Exception e) {
1656               ex = e;
1657               sc = -1;
1658               if (response != null)
1659                  EntityUtils.consumeQuietly(response.getEntity());
1660            }
1661            if (! retryOn.onResponse(response))
1662               retries = 0;
1663            if (retries > 0) {
1664               for (RestCallInterceptor rci : interceptors)
1665                  rci.onRetry(this, sc, request, response, ex);
1666               request.reset();
1667               long w = retryInterval;
1668               synchronized(this) {
1669                  wait(w);
1670               }
1671            } else if (ex != null) {
1672               throw ex;
1673            }
1674         }
1675         for (RestCallInterceptor rci : interceptors)
1676            rci.onConnect(this, sc, request, response);
1677         if (response == null)
1678            throw new RestCallException("HttpClient returned a null response");
1679         StatusLine sl = response.getStatusLine();
1680         String method = request.getMethod();
1681         sc = sl.getStatusCode(); // Read it again in case it was changed by one of the interceptors.
1682
1683         int[] expected = new int[0];
1684         if (bodyType != null && bodyType.hasAnnotation(Response.class))
1685            expected = bodyType.getAnnotation(Response.class).code();
1686
1687         if (sc >= 400 && ! ignoreErrors && ! ArrayUtils.contains(sc, expected)) {
1688            throw new RestCallException(sc, sl.getReasonPhrase(), method, request.getURI(), getResponseAsString())
1689               .setServerException(response.getFirstHeader("Exception-Name"), response.getFirstHeader("Exception-Message"), response.getFirstHeader("Exception-Trace"))
1690               .setHttpResponse(response);
1691         }
1692         if ((sc == 307 || sc == 302) && allowRedirectsOnPosts && method.equalsIgnoreCase("POST") && ! ArrayUtils.contains(sc, expected)) {
1693            if (redirectOnPostsTries-- < 1)
1694               throw new RestCallException(sc, "Maximum number of redirects occurred.  Location header: " + response.getFirstHeader("Location"), method, request.getURI(), getResponseAsString());
1695            Header h = response.getFirstHeader("Location");
1696            if (h != null) {
1697               reset();
1698               request.setURI(URI.create(h.getValue()));
1699               retries++;  // Redirects should affect retries.
1700               connect();
1701            }
1702         }
1703
1704      } catch (RestCallException e) {
1705         isFailed = true;
1706         close();
1707         throw e;
1708      } catch (Exception e) {
1709         isFailed = true;
1710         close();
1711         throw new RestCallException(e).setHttpResponse(response);
1712      }
1713
1714      return this;
1715   }
1716
1717   private ContentType getRequestContentType(ContentType def) {
1718      Header h = request.getFirstHeader("Content-Type");
1719      if (h != null) {
1720         String s = h.getValue();
1721         if (! isEmpty(s))
1722            return ContentType.create(s);
1723      }
1724      return def;
1725   }
1726
1727   private void reset() {
1728      if (response != null)
1729         EntityUtils.consumeQuietly(response.getEntity());
1730      request.reset();
1731      isConnected = false;
1732      isClosed = false;
1733      isFailed = false;
1734      if (capturedResponseWriter != null)
1735         capturedResponseWriter.getBuffer().setLength(0);
1736   }
1737
1738   /**
1739    * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP
1740    * response message body as a reader.
1741    *
1742    * <p>
1743    * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream will be
1744    * wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>).
1745    *
1746    * <p>
1747    * If present, automatically handles the <code>charset</code> value in the <code>Content-Type</code> response header.
1748    *
1749    * <p>
1750    * <b>IMPORTANT:</b>  It is your responsibility to close this reader once you have finished with it.
1751    *
1752    * @return
1753    *    The HTTP response message body reader.
1754    *    <jk>null</jk> if response was successful but didn't contain a body (e.g. HTTP 204).
1755    * @throws IOException If an exception occurred while streaming was already occurring.
1756    */
1757   public Reader getReader() throws IOException {
1758      InputStream is = getInputStream();
1759      if (is == null)
1760         return null;
1761
1762      // Figure out what the charset of the response is.
1763      String cs = null;
1764      Header contentType = response.getLastHeader("Content-Type");
1765      String ct = contentType == null ? null : contentType.getValue();
1766
1767      // First look for "charset=" in Content-Type header of response.
1768      if (ct != null && ct.contains("charset="))
1769         cs = ct.substring(ct.indexOf("charset=")+8).trim();
1770
1771      if (cs == null)
1772         cs = "UTF-8";
1773
1774      if (writers.size() > 0) {
1775         try (Reader isr = new InputStreamReader(is, cs)) {
1776            StringWriter sw = new StringWriter();
1777            writers.add(sw, true);
1778            IOPipe.create(isr, writers).byLines(byLines).run();
1779            return new StringReader(sw.toString());
1780         }
1781      }
1782
1783      return new InputStreamReader(is, cs);
1784   }
1785
1786   /**
1787    * Returns the response text as a string if {@link #captureResponse()} was called on this object.
1788    *
1789    * <p>
1790    * Note that while similar to {@link #getResponseAsString()}, this method can be called multiple times to retrieve
1791    * the response text multiple times.
1792    *
1793    * <p>
1794    * Note that this method returns <jk>null</jk> if you have not called one of the methods that cause the response to
1795    * be processed.  (e.g. {@link #run()}, {@link #getResponse()}, {@link #getResponseAsString()}.
1796    *
1797    * @return The captured response, or <jk>null</jk> if {@link #captureResponse()} has not been called.
1798    * @throws IllegalStateException If trying to call this method before the response is consumed.
1799    */
1800   public String getCapturedResponse() {
1801      if (! isClosed)
1802         throw new IllegalStateException("This method cannot be called until the response has been consumed.");
1803      if (capturedResponse == null && capturedResponseWriter != null && capturedResponseWriter.getBuffer().length() > 0)
1804         capturedResponse = capturedResponseWriter.toString();
1805      return capturedResponse;
1806   }
1807
1808   /**
1809    * Returns the value of the <code>Content-Length</code> header.
1810    *
1811    * @return The value of the <code>Content-Length</code> header, or <code>-1</code> if header is not present.
1812    * @throws IOException
1813    */
1814   public int getContentLength() throws IOException {
1815      connect();
1816      Header h = response.getLastHeader("Content-Length");
1817      if (h == null)
1818         return -1;
1819      long l = Long.parseLong(h.getValue());
1820      if (l > Integer.MAX_VALUE)
1821         return Integer.MAX_VALUE;
1822      return (int)l;
1823   }
1824
1825   /**
1826    * Connects to the remote resource (if <code>connect()</code> hasn't already been called) and returns the HTTP
1827    * response message body as an input stream.
1828    *
1829    * <p>
1830    * If an {@link Encoder} has been registered with the {@link RestClient}, then the underlying input stream will be
1831    * wrapped in the encoded stream (e.g. a <code>GZIPInputStream</code>).
1832    *
1833    * <p>
1834    * <b>IMPORTANT:</b>  It is your responsibility to close this reader once you have finished with it.
1835    *
1836    * @return
1837    *    The HTTP response message body input stream. <jk>null</jk> if response was successful but didn't contain
1838    *    a body (e.g. HTTP 204).
1839    * @throws IOException If an exception occurred while streaming was already occurring.
1840    * @throws IllegalStateException If an attempt is made to read the response more than once.
1841    */
1842   @SuppressWarnings("resource")
1843   public InputStream getInputStream() throws IOException {
1844      if (isClosed)
1845         throw new IllegalStateException("Method cannot be called.  Response has already been consumed.");
1846      connect();
1847      if (response == null)
1848         throw new RestCallException("Response was null");
1849      if (response.getEntity() == null)  // HTTP 204 results in no content.
1850         return null;
1851
1852      softClose();
1853
1854      InputStream is = new EofSensorInputStream(response.getEntity().getContent(), new EofSensorWatcher() {
1855         @Override
1856         public boolean eofDetected(InputStream wrapped) throws IOException {
1857            RestCall.this.forceClose();
1858            return true;
1859         }
1860         @Override
1861         public boolean streamClosed(InputStream wrapped) throws IOException {
1862            RestCall.this.forceClose();
1863            return true;
1864         }
1865         @Override
1866         public boolean streamAbort(InputStream wrapped) throws IOException {
1867            RestCall.this.forceClose();
1868            return true;
1869         }
1870      });
1871
1872      if (outputStreams.size() > 0) {
1873         ByteArrayInOutStream baios = new ByteArrayInOutStream();
1874         outputStreams.add(baios, true);
1875         IOPipe.create(is, baios).run();
1876         is.close();
1877         return baios.getInputStream();
1878      }
1879      return is;
1880   }
1881
1882   /**
1883    * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response
1884    * message body as plain text.
1885    *
1886    * <p>
1887    * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
1888    * output stream or writer.
1889    *
1890    * @return The response as a string.
1891    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
1892    * @throws IOException If an exception occurred while streaming was already occurring.
1893    */
1894   public String getResponseAsString() throws IOException {
1895      try (Reader r = getReader()) {
1896         return read(r).toString();
1897      } catch (IOException e) {
1898         isFailed = true;
1899         close();
1900         throw e;
1901      }
1902   }
1903
1904   /**
1905    * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the value of
1906    * an HTTP header on the response.
1907    *
1908    * <p>
1909    * Useful if you're only interested in a particular header value from the response and not the body of the response.
1910    *
1911    * <p>
1912    * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
1913    * output stream or writer.
1914    *
1915    * @param name The header name.
1916    * @return The response header as a string, or <jk>null</jk> if the header was not found.
1917    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
1918    * @throws IOException If an exception occurred while streaming was already occurring.
1919    */
1920   public String getResponseHeader(String name) throws IOException {
1921      try {
1922         HttpResponse r = getResponse();
1923         Header h = r.getFirstHeader(name);
1924         return h == null ? null : h.getValue();
1925      } catch (IOException e) {
1926         isFailed = true;
1927         close();
1928         throw e;
1929      }
1930   }
1931
1932   /**
1933    * Same as {@link #getResponseHeader(String)} except parses the header value using the specified part parser and schema.
1934    *
1935    * @param name The header name.
1936    * @param partParser The part parser to use for parsing the header.
1937    * @param schema The part schema.  Can be <jk>null</jk>.
1938    * @param c The type to convert the part into.
1939    * @return The parsed part.
1940    * @throws IOException
1941    * @throws ParseException
1942    */
1943   public <T> T getResponseHeader(HttpPartParser partParser, HttpPartSchema schema, String name, Class<T> c) throws IOException, ParseException {
1944      return getResponseHeader(partParser, schema, name, (Type)c);
1945   }
1946
1947   /**
1948    * Same as {@link #getResponseHeader(String)} except parses the header value using the specified part parser and schema.
1949    *
1950    * @param name The header name.
1951    * @param partParser The part parser to use for parsing the header.
1952    * @param schema The part schema.  Can be <jk>null</jk>.
1953    * @param type The type to convert the part into.
1954    * @param args The type arguments to convert the part into.
1955    * @return The parsed part.
1956    * @throws IOException
1957    * @throws ParseException
1958    */
1959   public <T> T getResponseHeader(HttpPartParser partParser, HttpPartSchema schema, String name, Type type, Type...args) throws IOException, ParseException {
1960      try {
1961         HttpResponse r = getResponse();
1962         Header h = r.getFirstHeader(name);
1963         if (h == null)
1964            return null;
1965         String hs = h.getValue();
1966         if (partParser == null)
1967            partParser = client.getPartParser();
1968         return partParser.parse(schema, hs, type, args);
1969      } catch (IOException e) {
1970         isFailed = true;
1971         close();
1972         throw e;
1973      }
1974   }
1975
1976   /**
1977    * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response code.
1978    *
1979    * <p>
1980    * Useful if you're only interested in the status code and not the body of the response.
1981    *
1982    * <p>
1983    * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
1984    * output stream or writer.
1985    *
1986    * @return The response code.
1987    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
1988    * @throws IOException If an exception occurred while streaming was already occurring.
1989    */
1990   public int getResponseCode() throws IOException {
1991      return run();
1992   }
1993
1994   /**
1995    * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously.
1996    *
1997    * @return The response as a string.
1998    * @throws RestCallException If the executor service was not defined.
1999    * @see
2000    *    RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating
2001    *    {@link Future Futures}.
2002    */
2003   public Future<String> getResponseAsStringFuture() throws RestCallException {
2004      return client.getExecutorService(true).submit(
2005         new Callable<String>() {
2006            @Override /* Callable */
2007            public String call() throws Exception {
2008               return getResponseAsString();
2009            }
2010         }
2011      );
2012   }
2013
2014   /**
2015    * Same as {@link #getResponse(Type, Type...)} except optimized for a non-parameterized class.
2016    *
2017    * <p>
2018    * This is the preferred parse method for simple types since you don't need to cast the results.
2019    *
2020    * <h5 class='section'>Examples:</h5>
2021    * <p class='bcode w800'>
2022    *    <jc>// Parse into a string.</jc>
2023    *    String s = restClient.doGet(url).getResponse(String.<jk>class</jk>);
2024    *
2025    *    <jc>// Parse into a bean.</jc>
2026    *    MyBean b = restClient.doGet(url).getResponse(MyBean.<jk>class</jk>);
2027    *
2028    *    <jc>// Parse into a bean array.</jc>
2029    *    MyBean[] ba = restClient.doGet(url).getResponse(MyBean[].<jk>class</jk>);
2030    *
2031    *    <jc>// Parse into a linked-list of objects.</jc>
2032    *    List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>);
2033    *
2034    *    <jc>// Parse into a map of object keys/values.</jc>
2035    *    Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>);
2036    * </p>
2037    *
2038    * <h5 class='section'>Notes:</h5>
2039    * <ul class='spaced-list'>
2040    *    <li>
2041    *       You can also specify any of the following types:
2042    *       <ul>
2043    *          <li>{@link HttpResponse} - Returns the raw <code>HttpResponse</code> returned by the inner <code>HttpClient</code>.
2044    *          <li>{@link Reader} - Returns access to the raw reader of the response.
2045    *          <li>{@link InputStream} - Returns access to the raw input stream of the response.
2046    *       </ul>
2047    * </ul>
2048    *
2049    * @param <T>
2050    *    The class type of the object being created.
2051    *    See {@link #getResponse(Type, Type...)} for details.
2052    * @param type The object type to create.
2053    * @return The parsed object.
2054    * @throws ParseException
2055    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
2056    * @throws IOException If a connection error occurred.
2057    */
2058   public <T> T getResponse(Class<T> type) throws IOException, ParseException {
2059      BeanContext bc = parser;
2060      if (bc == null)
2061         bc = BeanContext.DEFAULT;
2062      return getResponseInner(bc.getClassMeta(type));
2063   }
2064
2065   /**
2066    * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously.
2067    *
2068    * @param <T>
2069    *    The class type of the object being created.
2070    *    See {@link #getResponse(Type, Type...)} for details.
2071    * @param type The object type to create.
2072    * @return The parsed object.
2073    * @throws RestCallException If the executor service was not defined.
2074    * @see
2075    *    RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating
2076    *    {@link Future Futures}.
2077    */
2078   public <T> Future<T> getResponseFuture(final Class<T> type) throws RestCallException {
2079      return client.getExecutorService(true).submit(
2080         new Callable<T>() {
2081            @Override /* Callable */
2082            public T call() throws Exception {
2083               return getResponse(type);
2084            }
2085         }
2086      );
2087   }
2088
2089   /**
2090    * Parses HTTP body into the specified object type.
2091    *
2092    * <p>
2093    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
2094    *
2095    * <h5 class='section'>Examples:</h5>
2096    * <p class='bcode w800'>
2097    *    <jc>// Parse into a linked-list of strings.</jc>
2098    *    List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
2099    *
2100    *    <jc>// Parse into a linked-list of beans.</jc>
2101    *    List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
2102    *
2103    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
2104    *    List l = restClient.doGet(url).getResponse(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
2105    *
2106    *    <jc>// Parse into a map of string keys/values.</jc>
2107    *    Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
2108    *
2109    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
2110    *    Map m = restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
2111    * </p>
2112    *
2113    * <p>
2114    * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type.
2115    *
2116    * <p>
2117    * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
2118    *
2119    * <p>
2120    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
2121    *
2122    * <h5 class='section'>Notes:</h5>
2123    * <ul class='spaced-list'>
2124    *    <li>
2125    *       Use the {@link #getResponse(Class)} method instead if you don't need a parameterized map/collection.
2126    *    <li>
2127    *       You can also specify any of the following types:
2128    *       <ul>
2129    *          <li>{@link HttpResponse} - Returns the raw <code>HttpResponse</code> returned by the inner <code>HttpClient</code>.
2130    *          <li>{@link Reader} - Returns access to the raw reader of the response.
2131    *          <li>{@link InputStream} - Returns access to the raw input stream of the response.
2132    *       </ul>
2133    * </ul>
2134    *
2135    * @param <T> The class type of the object to create.
2136    * @param type
2137    *    The object type to create.
2138    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2139    * @param args
2140    *    The type arguments of the class if it's a collection or map.
2141    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2142    *    <br>Ignored if the main type is not a map or collection.
2143    * @return The parsed object.
2144    * @throws ParseException
2145    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
2146    * @throws IOException If a connection error occurred.
2147    * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections.
2148    */
2149   public <T> T getResponse(Type type, Type...args) throws IOException, ParseException {
2150      BeanContext bc = parser;
2151      if (bc == null)
2152         bc = BeanContext.DEFAULT;
2153      return (T)getResponseInner(bc.getClassMeta(type, args));
2154   }
2155
2156   /**
2157    * Same as {@link #getResponse(Type, Type...)} but allows you to specify a part parser to use for parsing the response.
2158    *
2159    * @param <T> The class type of the object to create.
2160    * @param type
2161    *    The object type to create.
2162    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2163    * @param args
2164    *    The type arguments of the class if it's a collection or map.
2165    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2166    *    <br>Ignored if the main type is not a map or collection.
2167    * @return The parsed object.
2168    * @throws ParseException
2169    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
2170    * @throws IOException If a connection error occurred.
2171    * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections.
2172    */
2173   public <T> T getResponseBody(Type type, Type...args) throws IOException, ParseException {
2174      BeanContext bc = parser;
2175      if (bc == null)
2176         bc = BeanContext.DEFAULT;
2177      return (T)getResponseInner(bc.getClassMeta(type, args));
2178   }
2179
2180   /**
2181    * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously.
2182    *
2183    * @param <T>
2184    *    The class type of the object being created.
2185    *    See {@link #getResponse(Type, Type...)} for details.
2186    * @param type
2187    *    The object type to create.
2188    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2189    * @param args
2190    *    The type arguments of the class if it's a collection or map.
2191    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2192    *    <br>Ignored if the main type is not a map or collection.
2193    * @return The parsed object.
2194    * @throws RestCallException If the executor service was not defined.
2195    * @see
2196    *    RestClientBuilder#executorService(ExecutorService, boolean) for defining the executor service for creating
2197    *    {@link Future Futures}.
2198    */
2199   public <T> Future<T> getResponseFuture(final Type type, final Type...args) throws RestCallException {
2200      return client.getExecutorService(true).submit(
2201         new Callable<T>() {
2202            @Override /* Callable */
2203            public T call() throws Exception {
2204               return getResponse(type, args);
2205            }
2206         }
2207      );
2208   }
2209
2210   /**
2211    * Parses the output from the connection into the specified type and then wraps that in a {@link PojoRest}.
2212    *
2213    * <p>
2214    * Useful if you want to quickly retrieve a single value from inside of a larger JSON document.
2215    *
2216    * @param innerType The class type of the POJO being wrapped.
2217    * @return The parsed output wrapped in a {@link PojoRest}.
2218    * @throws IOException If a connection error occurred.
2219    * @throws ParseException
2220    *    If the input contains a syntax error or is malformed for the <code>Content-Type</code> header.
2221    */
2222   public PojoRest getResponsePojoRest(Class<?> innerType) throws IOException, ParseException {
2223      return new PojoRest(getResponse(innerType));
2224   }
2225
2226   /**
2227    * Converts the output from the connection into an {@link ObjectMap} and then wraps that in a {@link PojoRest}.
2228    *
2229    * <p>
2230    * Useful if you want to quickly retrieve a single value from inside of a larger JSON document.
2231    *
2232    * @return The parsed output wrapped in a {@link PojoRest}.
2233    * @throws IOException If a connection error occurred.
2234    * @throws ParseException
2235    *    If the input contains a syntax error or is malformed for the <code>Content-Type</code> header.
2236    */
2237   public PojoRest getResponsePojoRest() throws IOException, ParseException {
2238      return getResponsePojoRest(ObjectMap.class);
2239   }
2240
2241   <T> T getResponseInner(ClassMeta<T> type) throws IOException, ParseException {
2242      try {
2243         if (response == null)
2244            connect(type);
2245
2246         Class<?> ic = type.getInnerClass();
2247
2248         if (ic.equals(HttpResponse.class)) {
2249            softClose();
2250            return (T)response;
2251         }
2252         if (ic.equals(Reader.class))
2253            return (T)getReader();
2254         if (ic.equals(InputStream.class))
2255            return (T)getInputStream();
2256         if (type.isType(ReaderResource.class) || type.isType(StreamResource.class)) {
2257            String mediaType = null;
2258            ObjectMap headers = new ObjectMap();
2259            for (Header h : response.getAllHeaders()) {
2260               if (h.getName().equalsIgnoreCase("Content-Type"))
2261                  mediaType = h.getValue();
2262               else
2263                  headers.put(h.getName(), h.getValue());
2264            }
2265            if (type.isType(ReaderResource.class))
2266               return (T)ReaderResource.create().headers(headers).mediaType(mediaType).contents(getReader()).build();
2267            return (T)StreamResource.create().headers(headers).mediaType(mediaType).contents(getInputStream()).build();
2268         }
2269
2270         connect(type);
2271         Header h = response.getFirstHeader("Content-Type");
2272         int sc = response.getStatusLine().getStatusCode();
2273         String ct = firstNonEmpty(h == null ? null : h.getValue(), "text/plain");
2274
2275         MediaType mt = MediaType.forString(ct);
2276
2277         if (parser == null || (mt.toString().equals("text/plain") && ! parser.canHandle(ct))) {
2278            if (type.hasStringTransform())
2279               return type.getStringTransform().transform(getResponseAsString());
2280         }
2281
2282         if (parser != null) {
2283            try (Closeable in = parser.isReaderParser() ? getReader() : getInputStream()) {
2284
2285               // HttpClient automatically ignores the content body for certain HTTP status codes.
2286               // So instantiate the object anyway if it has a no-arg constructor.
2287               // This allows a remote resource method to return a NoContent object for example.
2288               if (in == null && (sc < SC_OK || sc == SC_NO_CONTENT || sc == SC_NOT_MODIFIED || sc == SC_RESET_CONTENT)) {
2289                  Constructor<T> c = ClassUtils.findNoArgConstructor(type.getInnerClass(), Visibility.PUBLIC);
2290                  if (c != null) {
2291                     try {
2292                        return c.newInstance();
2293                     } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
2294                        throw new ParseException((Throwable)e);
2295                     }
2296                  }
2297               }
2298
2299               ParserSessionArgs pArgs = new ParserSessionArgs(this.getProperties(), null, response.getLocale(), null, mt, responseBodySchema, false, null);
2300               return parser.createSession(pArgs).parse(in, type);
2301            }
2302         }
2303
2304         if (type.hasReaderTransform())
2305            return type.getReaderTransform().transform(getReader());
2306
2307         if (type.hasInputStreamTransform())
2308            return type.getInputStreamTransform().transform(getInputStream());
2309
2310         throw new ParseException(
2311            "Unsupported media-type in request header ''Content-Type'': ''{0}''\n\tSupported media-types: {1}",
2312            getResponseHeader("Content-Type"), parser == null ? null : parser.getMediaTypes()
2313         );
2314
2315      } catch (ParseException | IOException e) {
2316         isFailed = true;
2317         close();
2318         throw e;
2319      }
2320   }
2321
2322   BeanContext getBeanContext() {
2323      BeanContext bc = parser;
2324      if (bc == null)
2325         bc = BeanContext.DEFAULT;
2326      return bc;
2327   }
2328
2329   /**
2330    * Converts the response from this call into a response bean.
2331    *
2332    * @param rbm The metadata used to construct the response bean.
2333    * @return A new response bean.
2334    */
2335   public <T> T getResponse(final ResponseBeanMeta rbm) {
2336      try {
2337         softClose();
2338         Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass();
2339         final RestClient rc = this.client;
2340         final HttpPartParser p = ObjectUtils.firstNonNull(partParser, rc.getPartParser());
2341         return (T)Proxy.newProxyInstance(
2342            c.getClassLoader(),
2343            new Class[] { c },
2344            new InvocationHandler() {
2345               @Override /* InvocationHandler */
2346               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
2347                  ResponseBeanPropertyMeta pm = rbm.getProperty(method.getName());
2348                  if (pm != null) {
2349                     HttpPartParser pp = pm.getParser(p);
2350                     HttpPartSchema schema = pm.getSchema();
2351                     String name = pm.getPartName();
2352                     ClassMeta<?> type = rc.getClassMeta(method.getGenericReturnType());
2353                     HttpPartType pt = pm.getPartType();
2354                     if (pt == RESPONSE_BODY) {
2355                        responseBodySchema(schema);
2356                        return getResponseBody(type);
2357                     }
2358                     if (pt == RESPONSE_HEADER)
2359                        return getResponseHeader(pp, schema, name, type);
2360                     if (pt == RESPONSE_STATUS)
2361                        return getResponseCode();
2362                  }
2363                  return null;
2364               }
2365
2366         });
2367      } catch (Exception e) {
2368         throw new RuntimeException(e);
2369      }
2370   }
2371
2372   /**
2373    * Returns access to the {@link HttpUriRequest} passed to {@link HttpClient#execute(HttpUriRequest)}.
2374    *
2375    * @return The {@link HttpUriRequest} object.
2376    */
2377   public HttpUriRequest getRequest() {
2378      return request;
2379   }
2380
2381   /**
2382    * Returns access to the {@link HttpResponse} returned by {@link HttpClient#execute(HttpUriRequest)}.
2383    *
2384    * <p>
2385    * Returns <jk>null</jk> if {@link #connect()} has not yet been called.
2386    *
2387    * @return The HTTP response object.
2388    * @throws IOException
2389    */
2390   public HttpResponse getResponse() throws IOException {
2391      connect();
2392      return response;
2393   }
2394
2395   /**
2396    * Shortcut for calling <code>getRequest().setHeader(header)</code>
2397    *
2398    * @param header The header to set on the request.
2399    * @return This object (for method chaining).
2400    */
2401   public RestCall header(Header header) {
2402      request.setHeader(header);
2403      return this;
2404   }
2405
2406   /** Use close() */
2407   @Deprecated
2408   public void consumeResponse() {
2409      if (response != null)
2410         EntityUtils.consumeQuietly(response.getEntity());
2411   }
2412
2413   /**
2414    * Cleans up this HTTP call.
2415    *
2416    * @throws RestCallException Can be thrown by one of the {@link RestCallInterceptor#onClose(RestCall)} calls.
2417    */
2418   @Override /* Closeable */
2419   public void close() throws RestCallException {
2420      if (response != null && ! softClose)
2421         EntityUtils.consumeQuietly(response.getEntity());
2422      if (! softClose)
2423         isClosed = true;
2424      if (! isFailed)
2425         for (RestCallInterceptor r : interceptors)
2426            r.onClose(this);
2427   }
2428
2429   void forceClose() throws RestCallException {
2430      softClose = false;
2431      close();
2432   }
2433
2434   /**
2435    * Adds a {@link RestCallLogger} to the list of interceptors on this class.
2436    *
2437    * @param level The log level to log events at.
2438    * @param log The logger.
2439    * @return This object (for method chaining).
2440    */
2441   public RestCall logTo(Level level, Logger log) {
2442      interceptor(new RestCallLogger(level, log));
2443      return this;
2444   }
2445
2446   /**
2447    * Sets <code>Debug: value</code> header on this request.
2448    *
2449    * @return This object (for method chaining).
2450    * @throws RestCallException
2451    */
2452   public RestCall debug() throws RestCallException {
2453      header("Debug", true);
2454      return this;
2455   }
2456
2457   /**
2458    * If called, the underlying response stream will not be closed when you call {@link #close()}.
2459    *
2460    * <p>
2461    * This is useful in cases where you want access to that stream after you've already cleaned up this object.
2462    * However, it is your responsibility to close that stream yourself.
2463    *
2464    * @return This object (for method chaining).
2465    */
2466   public RestCall softClose() {
2467      this.softClose = true;
2468      return this;
2469   }
2470
2471   //-----------------------------------------------------------------------------------------------------------------
2472   // Utility methods
2473   //-----------------------------------------------------------------------------------------------------------------
2474
2475   /**
2476    * Specifies that the following value can be added as an HTTP part.
2477    */
2478   private boolean canAdd(Object value, HttpPartSchema schema, boolean skipIfEmpty) {
2479      if (value != null) {
2480         if (ObjectUtils.isEmpty(value) && skipIfEmpty)
2481            return false;
2482         return true;
2483      }
2484      if (schema == null)
2485         return false;
2486      if (schema.isRequired())
2487         return true;
2488      String def = schema.getDefault();
2489      if (def == null)
2490         return false;
2491      if (StringUtils.isEmpty(def) && skipIfEmpty)
2492         return false;
2493      return true;
2494   }
2495
2496   /**
2497    * @deprecated Use {@link #run()}
2498    */
2499   @SuppressWarnings("javadoc")
2500   @Deprecated
2501   public int execute() throws RestCallException {
2502      return run();
2503   }
2504
2505   /**
2506    * @deprecated Use {@link #formData(String, Object, boolean, HttpPartSerializer, HttpPartSchema)}
2507    */
2508   @SuppressWarnings("javadoc")
2509   @Deprecated
2510   public RestCall formData(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
2511      return formData(name, value, skipIfEmpty, partSerializer, null);
2512   }
2513
2514   /**
2515    * @deprecated Use {@link #header(String, Object, boolean, HttpPartSerializer, HttpPartSchema)}
2516    */
2517   @SuppressWarnings("javadoc")
2518   @Deprecated
2519   public RestCall header(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
2520      return header(name, value, skipIfEmpty, partSerializer, null);
2521   }
2522
2523   /**
2524    * @deprecated Use {@link #body(Object)}
2525    */
2526   @SuppressWarnings("javadoc")
2527   @Deprecated
2528   public RestCall input(final Object input) throws RestCallException {
2529      return body(input);
2530   }
2531
2532   /**
2533    * @deprecated Use {@link #path(String, Object, HttpPartSerializer, HttpPartSchema)}
2534    */
2535   @SuppressWarnings("javadoc")
2536   @Deprecated
2537   public RestCall path(String name, Object value, HttpPartSerializer partSerializer) throws RestCallException {
2538      return path(name, value, partSerializer, null);
2539   }
2540
2541   /**
2542    * @deprecated Use {@link #query(String, Object, boolean, HttpPartSerializer, HttpPartSchema)}
2543    */
2544   @SuppressWarnings("javadoc")
2545   @Deprecated
2546   public RestCall query(String name, Object value, boolean skipIfEmpty, HttpPartSerializer partSerializer) throws RestCallException {
2547      return query(name, value, skipIfEmpty, partSerializer, null);
2548   }
2549}