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