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