001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.client;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016import static org.apache.juneau.internal.CollectionUtils.*;
017import static org.apache.juneau.httppart.HttpPartType.*;
018import static org.apache.juneau.collections.JsonMap.*;
019import static org.apache.juneau.common.internal.IOUtils.*;
020import static org.apache.juneau.common.internal.StringUtils.*;
021import static org.apache.juneau.http.HttpEntities.*;
022import static org.apache.juneau.http.HttpHeaders.*;
023import static org.apache.juneau.rest.client.RestOperation.*;
024import static java.util.stream.Collectors.*;
025
026import java.io.*;
027import java.lang.reflect.*;
028import java.net.*;
029import java.text.*;
030import java.util.*;
031import java.util.concurrent.*;
032import java.util.function.*;
033import java.util.logging.*;
034
035import org.apache.http.*;
036import org.apache.http.ParseException;
037import org.apache.http.client.config.*;
038import org.apache.http.client.entity.*;
039import org.apache.http.client.methods.*;
040import org.apache.http.client.utils.*;
041import org.apache.http.concurrent.*;
042import org.apache.http.entity.BasicHttpEntity;
043import org.apache.http.params.*;
044import org.apache.http.protocol.*;
045import org.apache.juneau.*;
046import org.apache.juneau.collections.*;
047import org.apache.juneau.html.*;
048import org.apache.juneau.http.*;
049import org.apache.juneau.http.HttpHeaders;
050import org.apache.juneau.http.entity.*;
051import org.apache.juneau.http.header.*;
052import org.apache.juneau.http.part.*;
053import org.apache.juneau.http.resource.*;
054import org.apache.juneau.httppart.*;
055import org.apache.juneau.internal.*;
056import org.apache.juneau.json.*;
057import org.apache.juneau.msgpack.*;
058import org.apache.juneau.oapi.*;
059import org.apache.juneau.parser.*;
060import org.apache.juneau.plaintext.*;
061import org.apache.juneau.reflect.*;
062import org.apache.juneau.serializer.*;
063import org.apache.juneau.uon.*;
064import org.apache.juneau.urlencoding.*;
065import org.apache.juneau.xml.*;
066
067/**
068 * Represents a request to a remote REST resource.
069 *
070 * <p>
071 * Instances of this class are created by the various creator methods on the {@link RestClient} class.
072 *
073 * <h5 class='section'>Notes:</h5><ul>
074 *    <li class='warn'>This class is not thread safe.
075 * </ul>
076 *
077 * <h5 class='section'>See Also:</h5><ul>
078 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-client">juneau-rest-client</a>
079 * </ul>
080 */
081@FluentSetters
082public class RestRequest extends BeanSession implements HttpUriRequest, Configurable, AutoCloseable {
083
084   private static final ContentType TEXT_PLAIN = ContentType.TEXT_PLAIN;
085
086   final RestClient client;                               // The client that created this call.
087   private final HttpRequestBase request;                 // The request.
088   private RestResponse response;                         // The response.
089   List<RestCallInterceptor> interceptors = list();   // Used for intercepting and altering requests.
090
091   private HeaderList headerData;
092   private PartList queryData, formData, pathData;
093
094   private boolean ignoreErrors, suppressLogging;
095
096   private Object content;
097   private Serializer serializer;
098   private Parser parser;
099   private HttpPartSchema contentSchema;
100   private URIBuilder uriBuilder;
101   private Predicate<Integer> errorCodes;
102   private HttpHost target;
103   private HttpContext context;
104   private List<Class<? extends Throwable>> rethrow;
105   private HttpPartSerializerSession partSerializerSession;
106
107   private final Map<HttpPartSerializer,HttpPartSerializerSession> partSerializerSessions = new IdentityHashMap<>();
108
109   /**
110    * Constructs a REST call with the specified method name.
111    *
112    * @param client The client that created this request.
113    * @param uri The target URI.
114    * @param method The HTTP method name (uppercase).
115    * @param hasBody Whether this method has a body.
116    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
117    */
118   protected RestRequest(RestClient client, URI uri, String method, boolean hasBody) throws RestCallException {
119      super(client.getBeanContext().createSession());
120      this.client = client;
121      this.request = createInnerRequest(method, uri, hasBody);
122      this.errorCodes = client.errorCodes;
123      this.uriBuilder = new URIBuilder(request.getURI());
124      this.ignoreErrors = client.ignoreErrors;
125      this.headerData = client.createHeaderData();
126      this.queryData = client.createQueryData();
127      this.formData = client.createFormData();
128      this.pathData = client.createPathData();
129   }
130
131   /**
132    * Constructs the {@link HttpRequestBase} object that ends up being passed to the client execute method.
133    *
134    * <p>
135    * Subclasses can override this method to create their own request base objects.
136    *
137    * @param method The HTTP method.
138    * @param uri The HTTP URI.
139    * @param hasBody Whether the HTTP request has a body.
140    * @return A new {@link HttpRequestBase} object.
141    */
142   protected HttpRequestBase createInnerRequest(String method, URI uri, boolean hasBody) {
143      HttpRequestBase req = hasBody ? new BasicHttpEntityRequestBase(this, method) : new BasicHttpRequestBase(this, method);
144      req.setURI(uri);
145      return req;
146   }
147
148
149   //------------------------------------------------------------------------------------------------------------------
150   // Configuration
151   //------------------------------------------------------------------------------------------------------------------
152
153   /**
154    * Convenience method for specifying JSON as the marshalling transmission media type for this request only.
155    *
156    * <p>
157    * {@link JsonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
158    *    <ul>
159    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
160    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
161    *    </ul>
162    * <p>
163    *    {@link JsonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
164    *    <ul>
165    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
166    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
167    *    </ul>
168    * <p>
169    *    <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden
170    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
171    * <p>
172    *    <c>Content-Type</c> request header will be set to <js>"application/json"</js> unless overridden
173    *       {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
174    * <p>
175    *    Identical to calling <c>serializer(JsonSerializer.<jk>class</jk>).parser(JsonParser.<jk>class</jk>)</c>.
176    *
177    * @return This object.
178    */
179   @FluentSetter
180   public RestRequest json() {
181      return serializer(JsonSerializer.class).parser(JsonParser.class);
182   }
183
184   /**
185    * Convenience method for specifying Simplified JSON as the marshalling transmission media type for this request only.
186    *
187    * <p>
188    * Simplified JSON is typically useful for automated tests because you can do simple string comparison of results
189    * without having to escape lots of quotes.
190    *
191    * <p>
192    *    {@link Json5Serializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
193    *    <ul>
194    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
195    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
196    *    </ul>
197    * <p>
198    *    {@link Json5Parser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
199    *    <ul>
200    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
201    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
202    *    </ul>
203    * <p>
204    *    <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden
205    *       by {@link #header(String,Object)} or {@link #accept(String)}, or per-request via {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
206    * <p>
207    *    <c>Content-Type</c> request header will be set to <js>"application/json+simple"</js> unless overridden
208    *       by {@link #header(String,Object)} or {@link #contentType(String)}, or per-request via {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
209    * <p>
210    *    Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages.
211    *    <ul>
212    *       <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the
213    *       last-enabled language if the headers are not set.
214    *    </ul>
215    * <p>
216    *    Identical to calling <c>serializer(Json5Serializer.<jk>class</jk>).parser(Json5Parser.<jk>class</jk>)</c>.
217    *
218    * @return This object.
219    */
220   @FluentSetter
221   public RestRequest json5() {
222      return serializer(Json5Serializer.class).parser(Json5Parser.class);
223   }
224
225   /**
226    * Convenience method for specifying XML as the marshalling transmission media type for this request only.
227    *
228    * <p>
229    * {@link XmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
230    *    <ul>
231    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
232    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
233    *    </ul>
234    * <p>
235    *    {@link XmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
236    *    <ul>
237    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
238    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
239    *    </ul>
240    * <p>
241    *    <c>Accept</c> request header will be set to <js>"text/xml"</js> unless overridden
242    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
243    * <p>
244    *    <c>Content-Type</c> request header will be set to <js>"text/xml"</js> unless overridden
245    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
246    * <p>
247    *    Identical to calling <c>serializer(XmlSerializer.<jk>class</jk>).parser(XmlParser.<jk>class</jk>)</c>.
248    *
249    * @return This object.
250    */
251   @FluentSetter
252   public RestRequest xml() {
253      return serializer(XmlSerializer.class).parser(XmlParser.class);
254   }
255
256   /**
257    * Convenience method for specifying HTML as the marshalling transmission media type for this request only.
258    *
259    * <p>
260    * POJOs are converted to HTML without any sort of doc wrappers.
261    *
262    * <p>
263    *    {@link HtmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
264    *    <ul>
265    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
266    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
267    *    </ul>
268    * <p>
269    *    {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
270    *    <ul>
271    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
272    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
273    *    </ul>
274    * <p>
275    *    <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden
276    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
277    * <p>
278    *    <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden
279    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
280    * <p>
281    *    Identical to calling <c>serializer(HtmlSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>.
282    *
283    * @return This object.
284    */
285   @FluentSetter
286   public RestRequest html() {
287      return serializer(HtmlSerializer.class).parser(HtmlParser.class);
288   }
289
290   /**
291    * Convenience method for specifying HTML DOC as the marshalling transmission media type for this request only.
292    *
293    * <p>
294    * POJOs are converted to fully renderable HTML pages.
295    *
296    * <p>
297    *    {@link HtmlDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
298    *    <ul>
299    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
300    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
301    *    </ul>
302    * <p>
303    *    {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
304    *    <ul>
305    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
306    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
307    *    </ul>
308    * <p>
309    *    <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden
310    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
311    * <p>
312    *    <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden
313    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
314    * <p>
315    *    Identical to calling <c>serializer(HtmlDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>.
316    *
317    * @return This object.
318    */
319   @FluentSetter
320   public RestRequest htmlDoc() {
321      return serializer(HtmlDocSerializer.class).parser(HtmlParser.class);
322   }
323
324   /**
325    * Convenience method for specifying Stripped HTML DOC as the marshalling transmission media type for this request only.
326    *
327    * <p>
328    * Same as {@link #htmlDoc()} but without the header and body tags and page title and description.
329    *
330    * <p>
331    *    {@link HtmlStrippedDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
332    *    <ul>
333    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
334    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
335    *    </ul>
336    * <p>
337    *    {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
338    *    <ul>
339    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
340    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
341    *    </ul>
342    * <p>
343    *    <c>Accept</c> request header will be set to <js>"text/html+stripped"</js> unless overridden
344    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
345    * <p>
346    *    <c>Content-Type</c> request header will be set to <js>"text/html+stripped"</js> unless overridden
347    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
348    * <p>
349    *    Identical to calling <c>serializer(HtmlStrippedDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>.
350    *
351    * @return This object.
352    */
353   @FluentSetter
354   public RestRequest htmlStrippedDoc() {
355      return serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class);
356   }
357
358   /**
359    * Convenience method for specifying Plain Text as the marshalling transmission media type for this request only.
360    *
361    * <p>
362    * Plain text marshalling typically only works on simple POJOs that can be converted to and from strings using
363    * swaps, swap methods, etc...
364    *
365    * <p>
366    *    {@link PlainTextSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
367    *    <ul>
368    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
369    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
370    *    </ul>
371    * <p>
372    *    {@link PlainTextParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
373    *    <ul>
374    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
375    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
376    *    </ul>
377    * <p>
378    *    <c>Accept</c> request header will be set to <js>"text/plain"</js> unless overridden
379    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
380    * <p>
381    *    <c>Content-Type</c> request header will be set to <js>"text/plain"</js> unless overridden
382    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
383    * <p>
384    *    Identical to calling <c>serializer(PlainTextSerializer.<jk>class</jk>).parser(PlainTextParser.<jk>class</jk>)</c>.
385    *
386    * @return This object.
387    */
388   @FluentSetter
389   public RestRequest plainText() {
390      return serializer(PlainTextSerializer.class).parser(PlainTextParser.class);
391   }
392
393   /**
394    * Convenience method for specifying MessagePack as the marshalling transmission media type for this request only.
395    *
396    * <p>
397    * MessagePack is a binary equivalent to JSON that takes up considerably less space than JSON.
398    *
399    * <p>
400    *    {@link MsgPackSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
401    *    <ul>
402    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
403    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
404    *    </ul>
405    * <p>
406    *    {@link MsgPackParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
407    *    <ul>
408    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
409    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
410    *    </ul>
411    * <p>
412    *    <c>Accept</c> request header will be set to <js>"octal/msgpack"</js> unless overridden
413    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
414    * <p>
415    *    <c>Content-Type</c> request header will be set to <js>"octal/msgpack"</js> unless overridden
416    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
417    * <p>
418    *    Identical to calling <c>serializer(MsgPackSerializer.<jk>class</jk>).parser(MsgPackParser.<jk>class</jk>)</c>.
419    *
420    * @return This object.
421    */
422   @FluentSetter
423   public RestRequest msgPack() {
424      return serializer(MsgPackSerializer.class).parser(MsgPackParser.class);
425   }
426
427   /**
428    * Convenience method for specifying UON as the marshalling transmission media type for this request only.
429    *
430    * <p>
431    * UON is Url-Encoding Object notation that is equivalent to JSON but suitable for transmission as URL-encoded
432    * query and form post values.
433    *
434    * <p>
435    *    {@link UonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
436    *    <ul>
437    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
438    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
439    *    </ul>
440    * <p>
441    *    {@link UonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
442    *    <ul>
443    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
444    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
445    *    </ul>
446    * <p>
447    *    <c>Accept</c> request header will be set to <js>"text/uon"</js> unless overridden
448    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
449    * <p>
450    *    <c>Content-Type</c> request header will be set to <js>"text/uon"</js> unless overridden
451    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
452    * <p>
453    *    Identical to calling <c>serializer(UonSerializer.<jk>class</jk>).parser(UonParser.<jk>class</jk>)</c>.
454    *
455    * @return This object.
456    */
457   @FluentSetter
458   public RestRequest uon() {
459      return serializer(UonSerializer.class).parser(UonParser.class);
460   }
461
462   /**
463    * Convenience method for specifying URL-Encoding as the marshalling transmission media type for this request only.
464    *
465    * <p>
466    *    {@link UrlEncodingSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
467    *    <ul>
468    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
469    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
470    *       <li>This serializer is NOT used when using the {@link RestRequest#formData(String, Object)} (and related) methods for constructing
471    *          the request body.  Instead, the part serializer specified via {@link RestClient.Builder#partSerializer(Class)} is used.
472    *    </ul>
473    * <p>
474    *    {@link UrlEncodingParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
475    *    <ul>
476    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
477    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
478    *    </ul>
479    * <p>
480    *    <c>Accept</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden
481    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
482    * <p>
483    *    <c>Content-Type</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden
484    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
485    * <p>
486    *    Identical to calling <c>serializer(UrlEncodingSerializer.<jk>class</jk>).parser(UrlEncodingParser.<jk>class</jk>)</c>.
487    *
488    * @return This object.
489    */
490   @FluentSetter
491   public RestRequest urlEnc() {
492      return serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class);
493   }
494
495   /**
496    * Convenience method for specifying OpenAPI as the marshalling transmission media type for this request only.
497    *
498    * <p>
499    * OpenAPI is a language that allows serialization to formats that use {@link HttpPartSchema} objects to describe their structure.
500    *
501    * <p>
502    *    {@link OpenApiSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}.
503    *    <ul>
504    *       <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
505    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
506    *       <li>Typically the {@link RestRequest#content(Object, HttpPartSchema)} method will be used to specify the body of the request with the
507    *          schema describing it's structure.
508    *    </ul>
509    * <p>
510    *    {@link OpenApiParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}.
511    *    <ul>
512    *       <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
513    *          bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
514    *       <li>Typically the {@link ResponseContent#schema(HttpPartSchema)} method will be used to specify the structure of the response body.
515    *    </ul>
516    * <p>
517    *    <c>Accept</c> request header will be set to <js>"text/openapi"</js> unless overridden
518    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}.
519    * <p>
520    *    <c>Content-Type</c> request header will be set to <js>"text/openapi"</js> unless overridden
521    *       by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}.
522    * <p>
523    *    Identical to calling <c>serializer(OpenApiSerializer.<jk>class</jk>).parser(OpenApiParser.<jk>class</jk>)</c>.
524    *
525    * @return This object.
526    */
527   @FluentSetter
528   public RestRequest openApi() {
529      return serializer(OpenApiSerializer.class).parser(OpenApiParser.class);
530   }
531
532   /**
533    * Specifies the serializer to use on the request body.
534    *
535    * <p>
536    * Overrides the serializers specified on the {@link RestClient}.
537    *
538    * <p>
539    *    The serializer is not modified by an of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
540    *    bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
541    *
542    * <p>
543    * If the <c>Content-Type</c> header is not set on the request, it will be set to the media type of this serializer.
544    *
545    * @param serializer The serializer used to serialize POJOs to the body of the HTTP request.
546    * @return This object.
547    */
548   @FluentSetter
549   public RestRequest serializer(Serializer serializer) {
550      this.serializer = serializer;
551      return this;
552   }
553
554   /**
555    * Specifies the serializer to use on the request body.
556    *
557    * <p>
558    * Overrides the serializers specified on the {@link RestClient}.
559    *
560    * <p>
561    *    The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or
562    *    bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
563    *
564    * <p>
565    * If the <c>Content-Type</c> header is not set on the request, it will be set to the media type of this serializer.
566    *
567    * @param serializer The serializer used to serialize POJOs to the body of the HTTP request.
568    * @return This object.
569    */
570   @FluentSetter
571   public RestRequest serializer(Class<? extends Serializer> serializer) {
572      this.serializer = client.getInstance(serializer);
573      return this;
574   }
575
576   /**
577    * Specifies the parser to use on the response body.
578    *
579    * <p>
580    * Overrides the parsers specified on the {@link RestClient}.
581    *
582    * <p>
583    *    The parser is not modified by any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
584    *    bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
585    *
586    * <p>
587    * If the <c>Accept</c> header is not set on the request, it will be set to the media type of this parser.
588    *
589    * @param parser The parser used to parse POJOs from the body of the HTTP response.
590    * @return This object.
591    */
592   @FluentSetter
593   public RestRequest parser(Parser parser) {
594      this.parser = parser;
595      return this;
596   }
597
598   /**
599    * Specifies the parser to use on the response body.
600    *
601    * <p>
602    * Overrides the parsers specified on the {@link RestClient}.
603    *
604    * <p>
605    *    The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or
606    *    bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class.
607    *
608    * <p>
609    * If the <c>Accept</c> header is not set on the request, it will be set to the media type of this parser.
610    *
611    * @param parser The parser used to parse POJOs from the body of the HTTP response.
612    * @return This object.
613    */
614   @FluentSetter
615   public RestRequest parser(Class<? extends Parser> parser) {
616      this.parser = client.getInstance(parser);
617      return this;
618   }
619
620   /**
621    * Allows you to override what status codes are considered error codes that would result in a {@link RestCallException}.
622    *
623    * <p>
624    * The default error code predicate is: <code>x -&gt; x &gt;= 400</code>.
625    *
626    * @param value The new predicate for calculating error codes.
627    * @return This object.
628    */
629   @FluentSetter
630   public RestRequest errorCodes(Predicate<Integer> value) {
631      this.errorCodes = value;
632      return this;
633   }
634
635   /**
636    * Add one or more interceptors for this call only.
637    *
638    * @param interceptors The interceptors to add to this call.
639    * @return This object.
640    * @throws RestCallException If init method on interceptor threw an exception.
641    */
642   @FluentSetter
643   public RestRequest interceptors(RestCallInterceptor...interceptors) throws RestCallException {
644      try {
645         for (RestCallInterceptor i : interceptors) {
646            this.interceptors.add(i);
647            i.onInit(this);
648         }
649      } catch (RuntimeException | RestCallException e) {
650         throw e;
651      } catch (Exception e) {
652         throw new RestCallException(null, e, "Interceptor threw an exception on init.");
653      }
654
655      return this;
656   }
657
658   /**
659    * Prevent {@link RestCallException RestCallExceptions} from being thrown when HTTP status 400+ is encountered.
660    *
661    * <p>
662    * This overrides the <l>ignoreErrors</l> property on the client.
663    *
664    * @return This object.
665    */
666   @FluentSetter
667   public RestRequest ignoreErrors() {
668      this.ignoreErrors = true;
669      return this;
670   }
671
672   /**
673    * Rethrow any of the specified exception types if a matching <c>Exception-Name</c> header is found.
674    *
675    * <p>
676    * Rethrown exceptions will be set on the caused-by exception of {@link RestCallException} when
677    * thrown from the {@link #run()} method.
678    *
679    * <p>
680    * Can be called multiple times to append multiple rethrows.
681    *
682    * @param values The classes to rethrow.
683    * @return This object.
684    */
685   @SuppressWarnings("unchecked")
686   @FluentSetter
687   public RestRequest rethrow(Class<?>...values) {
688      if (rethrow == null)
689         rethrow = list();
690      for (Class<?> v : values) {
691         if (v != null && Throwable.class.isAssignableFrom(v))
692            rethrow.add((Class<? extends Throwable>)v);
693      }
694      return this;
695   }
696
697   /**
698    * Sets <c>Debug: value</c> header on this request.
699    *
700    * @return This object.
701    * @throws RestCallException Invalid input.
702    */
703   @FluentSetter
704   public RestRequest debug() throws RestCallException {
705      header("Debug", true);
706      return this;
707   }
708
709   /**
710    * Returns <jk>true</jk> if debug mode is currently enabled.
711    */
712   @Override
713   public boolean isDebug() {
714      return headerData.get("Debug", Boolean.class).orElse(false);
715   }
716
717   /**
718    * Causes logging to be suppressed for the duration of this request.
719    *
720    * <p>
721    * Overrides the {@link #debug()} setting on this request or client.
722    *
723    * @return This object.
724    */
725   @FluentSetter
726   public RestRequest suppressLogging() {
727      this.suppressLogging = true;
728      return this;
729   }
730
731   boolean isLoggingSuppressed() {
732      return suppressLogging;
733   }
734
735   /**
736    * Specifies the target host for the request.
737    *
738    * @param target The target host for the request.
739    *    Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default
740    *    target or by inspecting the request.
741    * @return This object.
742    */
743   @FluentSetter
744   public RestRequest target(HttpHost target) {
745      this.target = target;
746      return this;
747   }
748
749   /**
750    * Override the context to use for the execution.
751    *
752    * @param context The context to use for the execution, or <jk>null</jk> to use the default context.
753    * @return This object.
754    */
755   @FluentSetter
756   public RestRequest context(HttpContext context) {
757      this.context = context;
758      return this;
759   }
760
761   //------------------------------------------------------------------------------------------------------------------
762   // Part builders
763   //------------------------------------------------------------------------------------------------------------------
764
765   /**
766    * Returns the header data for the request.
767    *
768    * @return An immutable list of headers to send on the request.
769    */
770   public HeaderList getHeaders() {
771      return headerData;
772   }
773
774   /**
775    * Returns the query data for the request.
776    *
777    * @return An immutable list of query data to send on the request.
778    */
779   public PartList getQueryData() {
780      return queryData;
781   }
782
783   /**
784    * Returns the form data for the request.
785    *
786    * @return An immutable list of form data to send on the request.
787    */
788   public PartList getFormData() {
789      return formData;
790   }
791
792   /**
793    * Returns the path data for the request.
794    *
795    * @return An immutable list of path data to send on the request.
796    */
797   public PartList getPathData() {
798      return pathData;
799   }
800
801   //-----------------------------------------------------------------------------------------------------------------
802   // Parts
803   //-----------------------------------------------------------------------------------------------------------------
804
805   /**
806    * Appends a header to the request.
807    *
808    * <h5 class='section'>Example:</h5>
809    * <p class='bjava'>
810    *    <jc>// Adds header "Foo: bar".</jc>
811    *    <jv>client</jv>
812    *       .get(<jsf>URI</jsf>)
813    *       .header(Accept.<jsf>TEXT_XML</jsf>)
814    *       .run();
815    * </p>
816    *
817    * @param part
818    *    The parameter to set.
819    *    <jk>null</jk> values are ignored.
820    * @return This object.
821    */
822   @FluentSetter
823   public RestRequest header(Header part) {
824      return headers(part);
825   }
826
827   /**
828    * Appends multiple headers to the request.
829    *
830    * <h5 class='section'>Example:</h5>
831    * <p class='bjava'>
832    *    <jc>// Appends two headers to the request.</jc>
833    *    <jv>client</jv>
834    *       .get(<jsf>URI</jsf>)
835    *       .headers(
836    *          BasicHeader.<jsm>of</jsm>(<js>"Foo"</js>, <js>"bar"</js>),
837    *          Accept.<jsf>TEXT_XML</jsf>
838    *       )
839    *       .run();
840    * </p>
841    *
842    * @param parts
843    *    The parameters to set.
844    *    <jk>null</jk> values are ignored.
845    * @return This object.
846    */
847   @FluentSetter
848   public RestRequest headers(Header...parts) {
849      headerData.append(parts);
850      return this;
851   }
852
853   /**
854    * Appends multiple query parameters to the request.
855    *
856    * <h5 class='section'>Example:</h5>
857    * <p class='bjava'>
858    *    <jc>// Appends two query parameters to the request.</jc>
859    *    <jv>client</jv>
860    *       .get(<jsf>URI</jsf>)
861    *       .queryData(
862    *          BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>),
863    *          BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>)
864    *       )
865    *       .run();
866    * </p>
867    *
868    * @param parts
869    *    The parameters to set.
870    *    <jk>null</jk> values are ignored.
871    * @return This object.
872    */
873   @FluentSetter
874   public RestRequest queryData(NameValuePair...parts) {
875      queryData.append(parts);
876      return this;
877   }
878
879   /**
880    * Appends multiple form-data parameters to the request.
881    *
882    * <h5 class='section'>Example:</h5>
883    * <p class='bjava'>
884    *    <jc>// Appends two form-data parameters to the request.</jc>
885    *    <jv>client</jv>
886    *       .get(<jsf>URI</jsf>)
887    *       .formData(
888    *          BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>),
889    *          BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>)
890    *       )
891    *       .run();
892    * </p>
893    *
894    * @param parts
895    *    The parameters to set.
896    *    <jk>null</jk> values are ignored.
897    * @return This object.
898    */
899   @FluentSetter
900   public RestRequest formData(NameValuePair...parts) {
901      formData.append(parts);
902      return this;
903   }
904
905   /**
906    * Sets or replaces multiple path parameters on the request.
907    *
908    * <h5 class='section'>Example:</h5>
909    * <p class='bjava'>
910    *    <jc>// Appends two path parameters to the request.</jc>
911    *    <jv>client</jv>
912    *       .get(<jsf>URI</jsf>)
913    *       .pathData(
914    *          BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>),
915    *          BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>)
916    *       )
917    *       .run();
918    * </p>
919    *
920    * @param parts
921    *    The parameters to set.
922    *    <jk>null</jk> values are ignored.
923    * @return This object.
924    */
925   @FluentSetter
926   public RestRequest pathData(NameValuePair...parts) {
927      pathData.set(parts);
928      return this;
929   }
930
931   /**
932    * Appends a header to the request.
933    *
934    * <h5 class='section'>Example:</h5>
935    * <p class='bjava'>
936    *    <jc>// Adds header "Foo: bar".</jc>
937    *    <jv>client</jv>
938    *       .get(<jsf>URI</jsf>)
939    *       .header(<js>"Foo"</js>, <js>"bar"</js>)
940    *       .run();
941    * </p>
942    *
943    * @param name
944    *    The parameter name.
945    * @param value
946    *    The parameter value.
947    *    <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client.
948    * @return This object.
949    */
950   @FluentSetter
951   public RestRequest header(String name, Object value) {
952      headerData.append(createHeader(name, value));
953      return this;
954   }
955
956   /**
957    * Appends a query parameter to the URI of the request.
958    *
959    * <h5 class='section'>Example:</h5>
960    * <p class='bjava'>
961    *    <jc>// Adds query parameter "foo=bar".</jc>
962    *    <jv>client</jv>
963    *       .get(<jsf>URI</jsf>)
964    *       .queryData(<js>"foo"</js>, <js>"bar"</js>)
965    *       .run();
966    * </p>
967    *
968    * @param name
969    *    The parameter name.
970    * @param value
971    *    The parameter value.
972    *    <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client.
973    * @return This object.
974    */
975   @FluentSetter
976   public RestRequest queryData(String name, Object value) {
977      queryData.append(createPart(QUERY, name, value));
978      return this;
979   }
980
981   /**
982    * Adds a form-data parameter to the request body.
983    *
984    * <h5 class='section'>Example:</h5>
985    * <p class='bjava'>
986    *    <jc>// Adds form data parameter "foo=bar".</jc>
987    *    <jv>client</jv>
988    *       .formPost(<jsf>URI</jsf>)
989    *       .formData(<js>"foo"</js>, <js>"bar"</js>)
990    *       .run();
991    * </p>
992    *
993    * @param name
994    *    The parameter name.
995    * @param value
996    *    The parameter value.
997    *    <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client.
998    * @return This object.
999    */
1000   @FluentSetter
1001   public RestRequest formData(String name, Object value) {
1002      formData.append(createPart(FORMDATA, name, value));
1003      return this;
1004   }
1005
1006   /**
1007    * Sets or replaces a path parameter of the form <js>"{name}"</js> in the URI.
1008    *
1009    * <h5 class='section'>Example:</h5>
1010    * <p class='bjava'>
1011    *    <jc>// Sets path to "/bar".</jc>
1012    *    <jv>client</jv>
1013    *       .get(<js>"/{foo}"</js>)
1014    *       .pathData(<js>"foo"</js>, <js>"bar"</js>)
1015    *       .run();
1016    * </p>
1017    *
1018    * @param name
1019    *    The parameter name.
1020    * @param value
1021    *    The parameter value.
1022    *    <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client.
1023    * @return This object.
1024    */
1025   @FluentSetter
1026   public RestRequest pathData(String name, Object value) {
1027      pathData.set(createPart(PATH, name, value));
1028      return this;
1029   }
1030
1031   /**
1032    * Appends multiple headers to the request using freeform key/value pairs.
1033    *
1034    * <h5 class='section'>Example:</h5>
1035    * <p class='bjava'>
1036    *    <jc>// Adds headers "Foo: bar" and "Baz: qux".</jc>
1037    *    <jv>client</jv>
1038    *       .get(<jsf>URI</jsf>)
1039    *       .headerPairs(<js>"Foo"</js>,<js>"bar"</js>,<js>"Baz"</js>,<js>"qux"</js>)
1040    *       .run();
1041    * </p>
1042    *
1043    * @param pairs The form-data key/value pairs.
1044    * @return This object.
1045    */
1046   @FluentSetter
1047   public RestRequest headerPairs(String...pairs) {
1048      if (pairs.length % 2 != 0)
1049         throw new IllegalArgumentException("Odd number of parameters passed into headerPairs(String...)");
1050      HeaderList b = headerData;
1051      for (int i = 0; i < pairs.length; i+=2)
1052         b.append(pairs[i], pairs[i+1]);
1053      return this;
1054   }
1055
1056   /**
1057    * Adds query parameters to the URI query using free-form key/value pairs..
1058    *
1059    * <h5 class='section'>Example:</h5>
1060    * <p class='bjava'>
1061    *    <jc>// Adds query parameters "foo=bar&amp;baz=qux".</jc>
1062    *    <jv>client</jv>
1063    *       .get(<jsf>URI</jsf>)
1064    *       .queryDataPairs(<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>,<js>"qux"</js>)
1065    *       .run();
1066    * </p>
1067    *
1068    * @param pairs The query key/value pairs.
1069    *    <ul>
1070    *       <li>Values can be any POJO.
1071    *       <li>Values converted to a string using the configured part serializer.
1072    *    </ul>
1073    * @return This object.
1074    * @throws RestCallException Invalid input.
1075    */
1076   @FluentSetter
1077   public RestRequest queryDataPairs(String...pairs) throws RestCallException {
1078      if (pairs.length % 2 != 0)
1079         throw new IllegalArgumentException("Odd number of parameters passed into queryDataPairs(String...)");
1080      PartList b = queryData;
1081      for (int i = 0; i < pairs.length; i+=2)
1082         b.append(pairs[i], pairs[i+1]);
1083      return this;
1084   }
1085
1086   /**
1087    * Adds form-data parameters to the request body using free-form key/value pairs.
1088    *
1089    * <h5 class='section'>Example:</h5>
1090    * <p class='bjava'>
1091    *    <jc>// Creates form data "key1=val1&amp;key2=val2".</jc>
1092    *    <jv>client</jv>
1093    *       .formPost(<jsf>URI</jsf>)
1094    *       .formDataPairs(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>)
1095    *       .run();
1096    * </p>
1097    *
1098    * @param pairs The form-data key/value pairs.
1099    *    <ul>
1100    *       <li>Values can be any POJO.
1101    *       <li>Values converted to a string using the configured part serializer.
1102    *    </ul>
1103    * @return This object.
1104    * @throws RestCallException Invalid input.
1105    */
1106   @FluentSetter
1107   public RestRequest formDataPairs(String...pairs) throws RestCallException {
1108      if (pairs.length % 2 != 0)
1109         throw new IllegalArgumentException("Odd number of parameters passed into formDataPairs(String...)");
1110      PartList b = formData;
1111      for (int i = 0; i < pairs.length; i+=2)
1112         b.append(pairs[i], pairs[i+1]);
1113      return this;
1114   }
1115
1116   /**
1117    * Replaces path parameters of the form <js>"{name}"</js> in the URI using free-form key/value pairs.
1118    *
1119    * <h5 class='section'>Example:</h5>
1120    * <p class='bjava'>
1121    *    <jc>// Sets path to "/baz/qux".</jc>
1122    *    <jv>client</jv>
1123    *       .get(<js>"/{foo}/{bar}"</js>)
1124    *       .pathDataPairs(
1125    *          <js>"foo"</js>,<js>"baz"</js>,
1126    *          <js>"bar"</js>,<js>"qux"</js>
1127    *       )
1128    *       .run();
1129    * </p>
1130    *
1131    * @param pairs The path key/value pairs.
1132    *    <ul>
1133    *       <li>Values can be any POJO.
1134    *       <li>Values converted to a string using the configured part serializer.
1135    *    </ul>
1136    * @return This object.
1137    */
1138   @FluentSetter
1139   public RestRequest pathDataPairs(String...pairs) {
1140      if (pairs.length % 2 != 0)
1141         throw new IllegalArgumentException("Odd number of parameters passed into pathDataPairs(String...)");
1142      PartList b = pathData;
1143      for (int i = 0; i < pairs.length; i+=2)
1144         b.set(pairs[i], pairs[i+1]);
1145      return this;
1146   }
1147
1148   /**
1149    * Appends multiple headers to the request from properties defined on a Java bean.
1150    *
1151    * <p>
1152    * Uses {@link PropertyNamerDUCS} for resolving the header names from property names.
1153    *
1154    * <h5 class='section'>Example:</h5>
1155    * <p class='bjava'>
1156    *    <ja>@Bean</ja>
1157    *    <jk>public class</jk> MyHeaders {
1158    *       <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; }
1159    *       <jk>public</jk> Integer getQux() { <jk>return</jk> 123; }
1160    *    }
1161    *
1162    *    <jc>// Appends headers "Foo-Bar: baz" and "Qux: 123".</jc>
1163    *    <jv>client</jv>
1164    *       .get(<jsf>URI</jsf>)
1165    *       .headersBean(<jk>new</jk> MyHeaders())
1166    *       .run();
1167    * </p>
1168    *
1169    * @param value The bean containing the properties to set as header values.
1170    * @return This object.
1171    */
1172   @FluentSetter
1173   public RestRequest headersBean(Object value) {
1174      if (! isBean(value))
1175         throw new IllegalArgumentException("Object passed into headersBean(Object) is not a bean.");
1176      HeaderList b = headerData;
1177      toBeanMap(value, PropertyNamerDUCS.INSTANCE).forEach((k,v) -> b.append(createHeader(k, v)));
1178      return this;
1179   }
1180
1181   /**
1182    * Appends multiple query parameters to the request from properties defined on a Java bean.
1183    *
1184    * <h5 class='section'>Example:</h5>
1185    * <p class='bjava'>
1186    *    <jk>public class</jk> MyQuery {
1187    *       <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; }
1188    *       <jk>public</jk> Integer getQux() { <jk>return</jk> 123; }
1189    *    }
1190    *
1191    *    <jc>// Appends query "fooBar=baz&amp;qux=123".</jc>
1192    *    <jv>client</jv>
1193    *       .get(<jsf>URI</jsf>)
1194    *       .queryDataBean(<jk>new</jk> MyQuery())
1195    *       .run();
1196    * </p>
1197    *
1198    * @param value The bean containing the properties to set as query parameter values.
1199    * @return This object.
1200    */
1201   @FluentSetter
1202   public RestRequest queryDataBean(Object value) {
1203      if (! isBean(value))
1204         throw new IllegalArgumentException("Object passed into queryDataBean(Object) is not a bean.");
1205      PartList b = queryData;
1206      toBeanMap(value).forEach((k,v) -> b.append(createPart(QUERY, k, v)));
1207      return this;
1208   }
1209
1210   /**
1211    * Appends multiple form-data parameters to the request from properties defined on a Java bean.
1212    *
1213    * <h5 class='section'>Example:</h5>
1214    * <p class='bjava'>
1215    *    <jk>public class</jk> MyFormData {
1216    *       <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; }
1217    *       <jk>public</jk> Integer getQux() { <jk>return</jk> 123; }
1218    *    }
1219    *
1220    *    <jc>// Appends form data "fooBar=baz&amp;qux=123".</jc>
1221    *    <jv>client</jv>
1222    *       .get(<jsf>URI</jsf>)
1223    *       .formDataBean(<jk>new</jk> MyFormData())
1224    *       .run();
1225    * </p>
1226    *
1227    * @param value The bean containing the properties to set as form-data parameter values.
1228    * @return This object.
1229    */
1230   @FluentSetter
1231   public RestRequest formDataBean(Object value) {
1232      if (! isBean(value))
1233         throw new IllegalArgumentException("Object passed into formDataBean(Object) is not a bean.");
1234      PartList b = formData;
1235      toBeanMap(value).forEach((k,v) -> b.append(createPart(FORMDATA, k, v)));
1236      return this;
1237   }
1238
1239   /**
1240    * Sets multiple path parameters to the request from properties defined on a Java bean.
1241    *
1242    * <h5 class='section'>Example:</h5>
1243    * <p class='bjava'>
1244    *    <jk>public class</jk> MyPathVars {
1245    *       <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; }
1246    *       <jk>public</jk> Integer getQux() { <jk>return</jk> 123; }
1247    *    }
1248    *
1249    *    <jc>// Given path "/{fooBar}/{qux}/", gets converted to "/baz/123/".</jc>
1250    *    <jv>client</jv>
1251    *       .get(<jsf>URI</jsf>)
1252    *       .pathDataBean(<jk>new</jk> MyPathVars())
1253    *       .run();
1254    * </p>
1255    *
1256    * @param value The bean containing the properties to set as path parameter values.
1257    * @return This object.
1258    */
1259   @FluentSetter
1260   public RestRequest pathDataBean(Object value) {
1261      if (! isBean(value))
1262         throw new IllegalArgumentException("Object passed into pathDataBean(Object) is not a bean.");
1263      PartList b = pathData;
1264      toBeanMap(value).forEach((k,v) -> b.set(createPart(PATH, k, v)));
1265      return this;
1266   }
1267
1268   //------------------------------------------------------------------------------------------------------------------
1269   // URI
1270   //------------------------------------------------------------------------------------------------------------------
1271
1272   /**
1273    * Sets the URI for this request.
1274    *
1275    * <p>
1276    * Can be any of the following types:
1277    * <ul>
1278    *    <li>{@link URI}
1279    *    <li>{@link URL}
1280    *    <li>{@link URIBuilder}
1281    *    <li>Anything else converted to a string using {@link Object#toString()}.
1282    * </ul>
1283    *
1284    * <p>
1285    * Relative URI strings will be interpreted as relative to the root URI defined on the client.
1286    *
1287    * @param uri
1288    *    The URI of the remote REST resource.
1289    *    <br>This overrides the URI passed in from the client.
1290    *    <br>Can be any of the following types:
1291    *    <ul>
1292    *       <li>{@link URIBuilder}
1293    *       <li>{@link URI}
1294    *       <li>{@link URL}
1295    *       <li>{@link String}
1296    *       <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c>
1297    *    </ul>
1298    * @return This object.
1299    * @throws RestCallException Invalid URI syntax detected.
1300    */
1301   @FluentSetter
1302   public RestRequest uri(Object uri) throws RestCallException {
1303      URI x = client.toURI(uri, null);
1304      if (x.getScheme() != null)
1305         uriBuilder.setScheme(x.getScheme());
1306      if (x.getHost() != null)
1307         uriBuilder.setHost(x.getHost());
1308      if (x.getPort() != -1)
1309         uriBuilder.setPort(x.getPort());
1310      if (x.getUserInfo() != null)
1311         uriBuilder.setUserInfo(x.getUserInfo());
1312      if (x.getFragment() != null)
1313         uriBuilder.setFragment(x.getFragment());
1314      if (x.getQuery() != null)
1315         uriBuilder.setCustomQuery(x.getQuery());
1316      uriBuilder.setPath(x.getPath());
1317      return this;
1318   }
1319
1320   /**
1321    * Sets the URI scheme.
1322    *
1323    * @param scheme The new URI host.
1324    * @return This object.
1325    */
1326   @FluentSetter
1327   public RestRequest uriScheme(String scheme) {
1328      uriBuilder.setScheme(scheme);
1329      return this;
1330   }
1331
1332   /**
1333    * Sets the URI host.
1334    *
1335    * @param host The new URI host.
1336    * @return This object.
1337    */
1338   @FluentSetter
1339   public RestRequest uriHost(String host) {
1340      uriBuilder.setHost(host);
1341      return this;
1342   }
1343
1344   /**
1345    * Sets the URI port.
1346    *
1347    * @param port The new URI port.
1348    * @return This object.
1349    */
1350   @FluentSetter
1351   public RestRequest uriPort(int port) {
1352      uriBuilder.setPort(port);
1353      return this;
1354   }
1355
1356   /**
1357    * Sets the URI user info.
1358    *
1359    * @param userInfo The new URI user info.
1360    * @return This object.
1361    */
1362   @FluentSetter
1363   public RestRequest uriUserInfo(String userInfo) {
1364      uriBuilder.setUserInfo(userInfo);
1365      return this;
1366   }
1367
1368   /**
1369    * Sets the URI user info.
1370    *
1371    * @param username The new URI username.
1372    * @param password The new URI password.
1373    * @return This object.
1374    */
1375   @FluentSetter
1376   public RestRequest uriUserInfo(String username, String password) {
1377      uriBuilder.setUserInfo(username, password);
1378      return this;
1379   }
1380
1381   /**
1382    * Sets the URI fragment.
1383    *
1384    * @param fragment The URI fragment.  The value is expected to be unescaped and may contain non ASCII characters.
1385    * @return This object.
1386    */
1387   @FluentSetter
1388   public RestRequest uriFragment(String fragment) {
1389      uriBuilder.setFragment(fragment);
1390      return this;
1391   }
1392
1393   /**
1394    * Adds a free-form custom query.
1395    *
1396    * <h5 class='section'>Example:</h5>
1397    * <p class='bjava'>
1398    *    <jc>// Adds query parameter "foo=bar&amp;baz=qux".</jc>
1399    *    <jv>client</jv>
1400    *       .get(<jsf>URI</jsf>)
1401    *       .queryCustom(<js>"foo=bar&amp;baz=qux"</js>)
1402    *       .run();
1403    * </p>
1404    *
1405    * @param value The parameter value.
1406    *    <br>Can be any of the following types:
1407    *    <ul>
1408    *       <li>
1409    *          {@link CharSequence}
1410    *       <li>
1411    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
1412    *       <li>
1413    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
1414    *       <li>
1415    *          {@link PartList} - Converted to a URL-encoded query.
1416    *    </ul>
1417    * @return This object.
1418    */
1419   @FluentSetter
1420   public RestRequest queryCustom(Object value) {
1421      try {
1422         String q = null;
1423         if (value instanceof Reader)
1424            q = read((Reader)value);
1425         else if (value instanceof InputStream)
1426            q = read((InputStream)value);
1427         else
1428            q = stringify(value);  // Works for NameValuePairs.
1429         uriBuilder.setCustomQuery(q);
1430      } catch (IOException e) {
1431         throw new BasicRuntimeException(e, "Could not read custom query.");
1432      }
1433      return this;
1434   }
1435
1436   /**
1437    * Adds form-data parameters as the entire body of the request.
1438    *
1439    * <h5 class='section'>Example:</h5>
1440    * <p class='bjava'>
1441    *    <jc>// Creates form data "foo=bar&amp;baz=qux".</jc>
1442    *    <jv>client</jv>
1443    *       .formPost(<jsf>URI</jsf>)
1444    *       .formDataCustom(<js>"foo=bar&amp;baz=qux"</js>)
1445    *       .run();
1446    *
1447    *    <jc>// Creates form data "foo=bar&amp;baz=qux" using StringEntity.</jc>
1448    *    <jv>client</jv>
1449    *       .formPost(<jsf>URI</jsf>)
1450    *       .formDataCustom(<jk>new</jk> StringEntity(<js>"foo=bar&amp;baz=qux"</js>,<js>"application/x-www-form-urlencoded"</js>))
1451    *       .run();
1452    *
1453    *    <jc>// Creates form data "foo=bar&amp;baz=qux" using StringEntity and body().</jc>
1454    *    <jv>client</jv>
1455    *       .formPost(<jsf>URI</jsf>)
1456    *       .content(<jk>new</jk> StringEntity(<js>"foo=bar&amp;baz=qux"</js>,<js>"application/x-www-form-urlencoded"</js>))
1457    *       .run();
1458    * </p>
1459    *
1460    * @param value The parameter value.
1461    *    <br>Can be any of the following types:
1462    *    <ul class='spaced-list'>
1463    *       <li>
1464    *          {@link CharSequence}
1465    *       <li>
1466    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
1467    *       <li>
1468    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
1469    *       <li>
1470    *          {@link HttpResource} - Raw contents will be serialized to remote resource.  Additional headers and media type will be set on request.
1471    *       <li>
1472    *          {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
1473    *       <li>
1474    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
1475    *          {@link RestClient}.
1476    *       <li>
1477    *          {@link PartList} - Converted to a URL-encoded FORM post.
1478    *    </ul>
1479    * @return This object.
1480    */
1481   @FluentSetter
1482   public RestRequest formDataCustom(Object value) {
1483      header(ContentType.APPLICATION_FORM_URLENCODED);
1484      content(value instanceof CharSequence ? new StringReader(value.toString()) : value);
1485      return this;
1486   }
1487
1488   //------------------------------------------------------------------------------------------------------------------
1489   // Args
1490   //------------------------------------------------------------------------------------------------------------------
1491
1492   RestRequest headerArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) {
1493      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof HeaderList || isHeaderArray(value);
1494
1495      if (! isMulti) {
1496         if (! (skipIfEmpty && isEmpty(stringify(value))))
1497            return header(createHeader(name, value, serializer, schema, skipIfEmpty));
1498         return this;
1499      }
1500
1501      List<Header> l = list();
1502
1503      if (HttpHeaders.canCast(value)) {
1504         l.add(HttpHeaders.cast(value));
1505      } else if (value instanceof HeaderList) {
1506         ((HeaderList)value).forEach(x->l.add(x));
1507      } else if (value instanceof Collection) {
1508         ((Collection<?>)value).forEach(x -> l.add(HttpHeaders.cast(x)));
1509      } else if (value != null && value.getClass().isArray()) {
1510         for (int i = 0; i < Array.getLength(value); i++)
1511            l.add(HttpHeaders.cast(Array.get(value, i)));
1512      } else if (value instanceof Map) {
1513         toMap(value).forEach((k,v) -> l.add(createHeader(stringify(k), v, serializer, schema, skipIfEmpty)));
1514      } else if (isBean(value)) {
1515         toBeanMap(value).forEach((k,v) -> l.add(createHeader(k, v, serializer, schema, skipIfEmpty)));
1516      } else if (value != null) {
1517         throw new BasicRuntimeException("Invalid value type for header arg ''{0}'': {1}", name, className(value));
1518      }
1519
1520      if (skipIfEmpty)
1521         l.removeIf(x -> isEmpty(x.getValue()));
1522
1523      headerData.append(l);
1524
1525      return this;
1526   }
1527
1528   RestRequest queryArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) {
1529      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value);
1530
1531      if (! isMulti) {
1532         if (! (skipIfEmpty && isEmpty(stringify(value))))
1533            return queryData(createPart(name, value, QUERY, serializer, schema, skipIfEmpty));
1534         return this;
1535      }
1536
1537      List<NameValuePair> l = list();
1538
1539      if (HttpParts.canCast(value)) {
1540         l.add(HttpParts.cast(value));
1541      } else if (value instanceof PartList) {
1542         ((PartList)value).forEach(x->l.add(x));
1543      } else if (value instanceof Collection) {
1544         ((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x)));
1545      } else if (value != null && value.getClass().isArray()) {
1546         for (int i = 0; i < Array.getLength(value); i++)
1547            l.add(HttpParts.cast(Array.get(value, i)));
1548      } else if (value instanceof Map) {
1549         toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, QUERY, serializer, schema, skipIfEmpty)));
1550      } else if (isBean(value)) {
1551         toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, QUERY, serializer, schema, skipIfEmpty)));
1552      } else if (value != null) {
1553         queryCustom(value);
1554         return this;
1555      }
1556
1557      if (skipIfEmpty)
1558         l.removeIf(x -> isEmpty(x.getValue()));
1559
1560      queryData.append(l);
1561
1562      return this;
1563   }
1564
1565   RestRequest formDataArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) {
1566      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value);
1567
1568      if (! isMulti) {
1569         if (! (skipIfEmpty && isEmpty(stringify(value))))
1570            return formData(createPart(name, value, FORMDATA, serializer, schema, skipIfEmpty));
1571         return this;
1572      }
1573
1574      List<NameValuePair> l = list();
1575
1576      if (HttpParts.canCast(value)) {
1577         l.add(HttpParts.cast(value));
1578      } else if (value instanceof PartList) {
1579         ((PartList)value).forEach(x->l.add(x));
1580      } else if (value instanceof Collection) {
1581         ((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x)));
1582      } else if (value != null && value.getClass().isArray()) {
1583         for (int i = 0; i < Array.getLength(value); i++)
1584            l.add(HttpParts.cast(Array.get(value, i)));
1585      } else if (value instanceof Map) {
1586         toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, FORMDATA, serializer, schema, skipIfEmpty)));
1587      } else if (isBean(value)) {
1588         toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, FORMDATA, serializer, schema, skipIfEmpty)));
1589      } else if (value != null) {
1590         formDataCustom(value);
1591         return this;
1592      }
1593
1594      if (skipIfEmpty)
1595         l.removeIf(x -> isEmpty(x.getValue()));
1596
1597      formData.append(l);
1598
1599      return this;
1600   }
1601
1602   RestRequest pathArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer) {
1603      boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value);
1604
1605      if (! isMulti)
1606         return pathData(createPart(name, value, PATH, serializer, schema, false));
1607
1608      List<NameValuePair> l = list();
1609
1610      if (HttpParts.canCast(value)) {
1611         l.add(HttpParts.cast(value));
1612      } else if (value instanceof PartList) {
1613         ((PartList)value).forEach(x->l.add(x));
1614      } else if (value instanceof Collection) {
1615         ((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x)));
1616      } else if (value != null && value.getClass().isArray()) {
1617         for (int i = 0; i < Array.getLength(value); i++)
1618            l.add(HttpParts.cast(Array.get(value, i)));
1619      } else if (value instanceof Map) {
1620         toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, PATH, serializer, schema, false)));
1621      } else if (isBean(value)) {
1622         toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, PATH, serializer, schema, false)));
1623      } else if (value != null) {
1624         throw new BasicRuntimeException("Invalid value type for path arg ''{0}'': {1}", name, className(value));
1625      }
1626
1627      pathData.append(l);
1628
1629      return this;
1630   }
1631
1632   //------------------------------------------------------------------------------------------------------------------
1633   // Request body
1634   //------------------------------------------------------------------------------------------------------------------
1635
1636   /**
1637    * Sets the body of this request.
1638    *
1639    * @param value
1640    *    The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests.
1641    *    <br>Can be of the following types:
1642    *    <ul class='spaced-list'>
1643    *       <li>
1644    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
1645    *       <li>
1646    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
1647    *       <li>
1648    *          {@link HttpResource} - Raw contents will be serialized to remote resource.  Additional headers and media type will be set on request.
1649    *       <li>
1650    *          {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
1651    *       <li>
1652    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
1653    *          {@link RestClient}.
1654    *       <li>
1655    *          {@link PartList} - Converted to a URL-encoded FORM post.
1656    *       <li>
1657    *          A {@link Supplier} of anything on this list.
1658    *    </ul>
1659    * @return This object.
1660    */
1661   @FluentSetter
1662   public RestRequest content(Object value) {
1663      this.content = value;
1664      return this;
1665   }
1666
1667   /**
1668    * Sets the body of this request as straight text bypassing the serializer.
1669    *
1670    * <p class='bjava'>
1671    *    <jv>client</jv>
1672    *       .put(<js>"/foo"</js>)
1673    *       .content(<jk>new</jk> StringReader(<js>"foo"</js>))
1674    *       .contentType(<js>"text/foo"</js>)
1675    *       .run();
1676    *
1677    * <jv>client</jv>
1678    *       .put(<js>"/foo"</js>)
1679    *       .bodyString(<js>"foo"</js>)
1680    *       .run();
1681    * </p>
1682    *
1683    * <p>
1684    * Note that this is different than the following which will serialize <l>foo</l> as a JSON string <l>"foo"</l>.
1685    * <p class='bjava'>
1686    *    <jv>client</jv>
1687    *       .put(<js>"/foo"</js>)
1688    *       .json()
1689    *       .content(<js>"foo"</js>)
1690    *       .run();
1691    * </p>
1692    *
1693    * @param input
1694    *    The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests.
1695    * @return This object.
1696    * @throws RestCallException If a retry was attempted, but the entity was not repeatable.
1697    */
1698   @FluentSetter
1699   public RestRequest contentString(Object input) throws RestCallException {
1700      return content(input == null ? null : new StringReader(stringify(input)));
1701   }
1702
1703   /**
1704    * Sets the body of this request.
1705    *
1706    * @param input
1707    *    The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests.
1708    *    <br>Can be of the following types:
1709    *    <ul class='spaced-list'>
1710    *       <li>
1711    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
1712    *       <li>
1713    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
1714    *       <li>
1715    *          {@link HttpResource} - Raw contents will be serialized to remote resource.  Additional headers and media type will be set on request.
1716    *       <li>
1717    *          {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
1718    *       <li>
1719    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
1720    *          {@link RestClient}.
1721    *       <li>
1722    *          {@link PartList} - Converted to a URL-encoded FORM post.
1723    *       <li>
1724    *          A {@link Supplier} of anything on this list.
1725    *    </ul>
1726    * @param schema The schema object that defines the format of the output.
1727    *    <ul>
1728    *       <li>If <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}.
1729    *       <li>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}).
1730    *    </ul>
1731    * @return This object.
1732    */
1733   @FluentSetter
1734   public RestRequest content(Object input, HttpPartSchema schema) {
1735      this.content = input;
1736      this.contentSchema = schema;
1737      return this;
1738   }
1739
1740   //------------------------------------------------------------------------------------------------------------------
1741   // Specialized headers.
1742   //------------------------------------------------------------------------------------------------------------------
1743
1744   /**
1745    * Sets the value for the <c>Accept</c> request header.
1746    *
1747    * <p>
1748    * This overrides the media type specified on the parser, but is overridden by calling
1749    * <code>header(<js>"Accept"</js>, value);</code>
1750    *
1751    * @param value The new header value.
1752    * @return This object.
1753    * @throws RestCallException Invalid input.
1754    */
1755   @FluentSetter
1756   public RestRequest accept(String value) throws RestCallException {
1757      return header(Accept.of(value));
1758   }
1759
1760   /**
1761    * Sets the value for the <c>Accept-Charset</c> request header.
1762    *
1763    * <p>
1764    * This is a shortcut for calling <code>header(<js>"Accept-Charset"</js>, value);</code>
1765    *
1766    * @param value The new header value.
1767    * @return This object.
1768    * @throws RestCallException Invalid input.
1769    */
1770   @FluentSetter
1771   public RestRequest acceptCharset(String value) throws RestCallException {
1772      return header(AcceptCharset.of(value));
1773   }
1774
1775   /**
1776    * Sets the value for the <c>Content-Type</c> request header.
1777    *
1778    * <p>
1779    * This overrides the media type specified on the serializer, but is overridden by calling
1780    * <code>header(<js>"Content-Type"</js>, value);</code>
1781    *
1782    * @param value The new header value.
1783    * @return This object.
1784    * @throws RestCallException Invalid input.
1785    */
1786   @FluentSetter
1787   public RestRequest contentType(String value) throws RestCallException {
1788      return header(ContentType.of(value));
1789   }
1790
1791   /**
1792    * Shortcut for setting the <c>Accept</c> and <c>Content-Type</c> headers on a request.
1793    *
1794    * @param value The new header values.
1795    * @return This object.
1796    * @throws RestCallException Invalid input.
1797    */
1798   @FluentSetter
1799   public RestRequest mediaType(String value) throws RestCallException {
1800      return header(Accept.of(value)).header(ContentType.of(value));
1801   }
1802
1803   /**
1804    * When called, <c>No-Trace: true</c> is added to requests.
1805    *
1806    * <p>
1807    * This gives the opportunity for the servlet to not log errors on invalid requests.
1808    * This is useful for testing purposes when you don't want your log file to show lots of errors that are simply the
1809    * results of testing.
1810    *
1811    * @return This object.
1812    * @throws RestCallException Invalid input.
1813    */
1814   @FluentSetter
1815   public RestRequest noTrace() throws RestCallException {
1816      return header(NoTrace.TRUE);
1817   }
1818
1819   //------------------------------------------------------------------------------------------------------------------
1820   // Execution methods.
1821   //------------------------------------------------------------------------------------------------------------------
1822
1823   /**
1824    * Runs this request and returns the resulting response object.
1825    *
1826    * <h5 class='section'>Example:</h5>
1827    * <p class='bjava'>
1828    *    <jk>try</jk> {
1829    *       <jk>int</jk> <jv>rc</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).execute().getResponseStatus();
1830    *       <jc>// Succeeded!</jc>
1831    *    } <jk>catch</jk> (RestCallException <jv>e</jv>) {
1832    *       <jc>// Failed!</jc>
1833    *    }
1834    * </p>
1835    *
1836    * <h5 class='section'>Notes:</h5><ul>
1837    *    <li class='note'>Calling this method multiple times will return the same original response object.
1838    *    <li class='note'>You must close the returned object if you do not consume the response or execute a method that consumes
1839    *       the response.
1840    *    <li class='note'>If you are only interested in the response code, use the {@link #complete()} method which will automatically
1841    *       consume the response so that you don't need to call {@link InputStream#close()} on the response body.
1842    * </ul>
1843    *
1844    * @return The response object.
1845    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
1846    */
1847   public RestResponse run() throws RestCallException {
1848      if (response != null)
1849         throw new RestCallException(response, null, "run() already called.");
1850
1851      try {
1852
1853         queryData.stream().map(SimpleQuery::new).filter(SimplePart::isValid).forEach(
1854            x -> uriBuilder.addParameter(x.name, x.value)
1855         );
1856
1857         pathData.stream().map(SimplePath::new).forEach(x ->
1858            {
1859               String path = uriBuilder.getPath();
1860               String name = x.name, value = x.value;
1861               String var = "{" + name + "}";
1862               if (path.indexOf(var) == -1 && ! name.equals("/*"))
1863                  throw new IllegalStateException("Path variable {" + name + "} was not found in path.");
1864               if (name.equals("/*"))
1865                  path = path.replaceAll("\\/\\*$", "/" + value);
1866               else
1867                  path = path.replace(var, String.valueOf(value));
1868               uriBuilder.setPath(path);
1869            }
1870         );
1871
1872         HttpEntityEnclosingRequestBase request2 = request instanceof HttpEntityEnclosingRequestBase ? (HttpEntityEnclosingRequestBase)request : null;
1873         request.setURI(uriBuilder.build());
1874
1875         // Pick the serializer if it hasn't been overridden.
1876         HeaderList hl = headerData;
1877         Optional<Header> h = hl.getLast("Content-Type");
1878         String contentType = h.isPresent() ? h.get().getValue() : null;
1879         Serializer serializer = this.serializer;
1880         if (serializer == null)
1881            serializer = client.getMatchingSerializer(contentType);
1882         if (contentType == null && serializer != null)
1883            contentType = serializer.getPrimaryMediaType().toString();
1884
1885         // Pick the parser if it hasn't been overridden.
1886         h = hl.getLast("Accept");
1887         String accept = h.isPresent() ? h.get().getValue() : null;
1888         Parser parser = this.parser;
1889         if (parser == null)
1890            parser = client.getMatchingParser(accept);
1891         if (accept == null && parser != null)
1892            hl.set(Accept.of( parser.getPrimaryMediaType()));
1893
1894         headerData.stream().map(SimpleHeader::new).filter(SimplePart::isValid).forEach(x -> request.addHeader(x));
1895
1896         if (request2 == null && content != NO_BODY)
1897            throw new RestCallException(null, null, "Method does not support content entity.  Method={0}, URI={1}", getMethod(), getURI());
1898
1899         if (request2 != null) {
1900
1901            Object input2 = null;
1902            if (content != NO_BODY) {
1903               input2 = content;
1904            } else {
1905               input2 = new UrlEncodedFormEntity(formData.stream().map(SimpleFormData::new).filter(SimplePart::isValid).collect(toList()));
1906            }
1907
1908            if (input2 instanceof Supplier)
1909               input2 = ((Supplier<?>)input2).get();
1910
1911            HttpEntity entity = null;
1912            if (input2 instanceof PartList)
1913               entity = new UrlEncodedFormEntity(((PartList)input2).stream().map(SimpleFormData::new).filter(SimplePart::isValid).collect(toList()));
1914            else if (input2 instanceof HttpResource) {
1915               HttpResource r = (HttpResource)input2;
1916               r.getHeaders().forEach(x -> request.addHeader(x));
1917               entity = (HttpEntity)input2;
1918            }
1919            else if (input2 instanceof HttpEntity) {
1920               if (input2 instanceof SerializedEntity) {
1921                  entity = ((SerializedEntity)input2).copyWith(serializer, contentSchema);
1922               } else {
1923                  entity = (HttpEntity)input2;
1924               }
1925            }
1926            else if (input2 instanceof Reader)
1927               entity = readerEntity((Reader)input2, getRequestContentType(TEXT_PLAIN));
1928            else if (input2 instanceof InputStream)
1929               entity = streamEntity((InputStream)input2, -1, getRequestContentType(ContentType.APPLICATION_OCTET_STREAM));
1930            else if (serializer != null)
1931               entity = serializedEntity(input2, serializer, contentSchema).setContentType(contentType);
1932            else {
1933               if (client.hasSerializers()) {
1934                  if (contentType == null)
1935                     throw new RestCallException(null, null, "Content-Type not specified on request.  Cannot match correct serializer.  Use contentType(String) or mediaType(String) to specify transport language.");
1936                  throw new RestCallException(null, null, "No matching serializer for media type ''{0}''", contentType);
1937               }
1938               entity = stringEntity(input2 == null ? "" : BeanContext.DEFAULT.getClassMetaForObject(input2).toString(input2), getRequestContentType(TEXT_PLAIN));
1939            }
1940
1941            request2.setEntity(entity);
1942         }
1943
1944         try {
1945            response = client.createResponse(this, client.run(target, request, context), parser);
1946         } catch (Exception e) {
1947            throw e;
1948         }
1949
1950         if (isDebug() || client.logRequests == DetailLevel.FULL)
1951            response.cacheContent();
1952
1953         for (RestCallInterceptor rci : interceptors)
1954            rci.onConnect(this, response);
1955         client.onCallConnect(this, response);
1956
1957         String method = getMethod();
1958         int sc = response.getStatusCode();
1959
1960         Thrown thrown = response.getHeader("Thrown").asHeader(Thrown.class);
1961         if (thrown.isPresent() && rethrow != null) {
1962            Thrown.Part thrownPart = thrown.asParts().get().get(0);
1963            String className = thrownPart.getClassName();
1964            String message = thrownPart.getMessage();
1965            for (Class<? extends Throwable> t : rethrow) {
1966               if (t.getName().equals(className)) {
1967                  ConstructorInfo c = null;
1968                  ClassInfo ci = ClassInfo.of(t);
1969                  c = ci.getPublicConstructor(x -> x.hasParamTypes(HttpResponse.class));
1970                  if (c != null)
1971                     throw c.<Throwable>invoke(response);
1972                  c = ci.getPublicConstructor(x -> x.hasParamTypes(String.class));
1973                  if (c != null)
1974                     throw c.<Throwable>invoke(message != null ? message : response.getContent().asString());
1975                  c = ci.getPublicConstructor(x -> x.hasParamTypes(String.class,Throwable.class));
1976                  if (c != null)
1977                     throw c.<Throwable>invoke(message != null ? message : response.getContent().asString(), null);
1978                  c = ci.getPublicConstructor(ConstructorInfo::hasNoParams);
1979                  if (c != null)
1980                     throw c.<Throwable>invoke();
1981               }
1982            }
1983         }
1984
1985         if (errorCodes.test(sc) && ! ignoreErrors) {
1986            throw new RestCallException(response, null, "HTTP method ''{0}'' call to ''{1}'' caused response code ''{2}, {3}''.\nResponse: \n{4}",
1987               method, getURI(), sc, response.getReasonPhrase(), response.getContent().asAbbreviatedString(1000));
1988         }
1989
1990      } catch (RuntimeException | RestCallException e) {
1991         if (response != null)
1992            response.close();
1993         throw e;
1994      } catch (Throwable e) {
1995         if (response != null)
1996            response.close();
1997         throw new RestCallException(response, e, "Call failed.");
1998      }
1999
2000      return this.response;
2001   }
2002
2003   /**
2004    * Same as {@link #run()} but allows you to run the call asynchronously.
2005    *
2006    * <h5 class='section'>Example:</h5>
2007    * <p class='bjava'>
2008    *    Future&lt;RestResponse&gt; <jv>future</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).runFuture();
2009    *
2010    *    <jc>// Do some other stuff</jc>
2011    *
2012    *    <jk>try</jk> {
2013    *       String <jv>body</jv> = <jv>future</jv>.get().getContent().asString();
2014    *       <jc>// Succeeded!</jc>
2015    *    } <jk>catch</jk> (RestCallException <jv>e</jv>) {
2016    *       <jc>// Failed!</jc>
2017    *    }
2018    * </p>
2019    *
2020    * <h5 class='section'>Notes:</h5><ul>
2021    *    <li class='note'>Use the {@link RestClient.Builder#executorService(ExecutorService, boolean)} method to customize the
2022    *       executor service used for creating {@link Future Futures}.
2023    * </ul>
2024    *
2025    * @return The HTTP status code.
2026    * @throws RestCallException If the executor service was not defined.
2027    */
2028    public Future<RestResponse> runFuture() throws RestCallException {
2029        return client.getExecutorService().submit(this::run);
2030    }
2031
2032   /**
2033    * Same as {@link #run()} but immediately calls {@link RestResponse#consume()} to clean up the response.
2034    *
2035    * <p>
2036    * Use this method if you're only interested in the status line of the response and not the response entity.
2037    * Attempts to call any of the methods on the response object that retrieve the body (e.g. {@link ResponseContent#asReader()}
2038    * will cause a {@link RestCallException} to be thrown.
2039    *
2040    * <h5 class='section'>Notes:</h5><ul>
2041    *    <li class='note'>You do not need to execute {@link InputStream#close()} on the response body to consume the response.
2042    * </ul>
2043    *
2044    * <h5 class='section'>Example:</h5>
2045    * <p class='bjava'>
2046    *  <jc>// Get the response code.
2047    *  // No need to call close() on the RestResponse object.</jc>
2048    *  <jk>int</jk> <jv>rc</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getResponseCode();
2049    * </p>
2050    *
2051    * @return The response object.
2052    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
2053    */
2054   public RestResponse complete() throws RestCallException {
2055      return run().consume();
2056   }
2057
2058   /**
2059    * Same as {@link #complete()} but allows you to run the call asynchronously.
2060    *
2061    * <h5 class='section'>Example:</h5>
2062    * <p class='bjava'>
2063    *    Future&lt;RestResponse&gt; <jv>future</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).completeFuture();
2064    *
2065    *    <jc>// Do some other stuff</jc>
2066    *
2067    *    <jk>int</jk> <jv>rc</jv> = <jv>future</jv>.get().getResponseStatus();
2068    * </p>
2069    *
2070    * <h5 class='section'>Notes:</h5><ul>
2071    *    <li class='note'>Use the {@link RestClient.Builder#executorService(ExecutorService, boolean)} method to customize the
2072    *       executor service used for creating {@link Future Futures}.
2073    *    <li class='note'>You do not need to execute {@link InputStream#close()} on the response body to consume the response.
2074    * </ul>
2075    *
2076    * @return The HTTP status code.
2077    * @throws RestCallException If the executor service was not defined.
2078    */
2079   public Future<RestResponse> completeFuture() throws RestCallException {
2080      return client.getExecutorService().submit(
2081         this::complete
2082      );
2083   }
2084
2085   /**
2086    * A shortcut for calling <c>run().getContent().asString()</c>.
2087    *
2088    * @return The response content as a simple string.
2089    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
2090    */
2091   public String getResponseAsString() throws RestCallException {
2092      return run().getContent().asString();
2093   }
2094
2095   /**
2096    * A shortcut for calling <c>run().getContent().as(<js>type</js>)</c>.
2097    *
2098    * @param type The object type to create.
2099    * @param <T> The object type to create.
2100    * @see ResponseContent#as(Class)
2101    * @return The response content as a simple string.
2102    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
2103    */
2104   public <T> T getResponse(Class<T> type) throws RestCallException {
2105      return run().getContent().as(type);
2106   }
2107
2108   /**
2109    * A shortcut for calling <c>run().getContent().as(<js>type</js>,<js>args</js>)</c>.
2110    *
2111    * @param <T>
2112    *    The object type to create.
2113    * @param type
2114    *    The object type to create.
2115    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2116    * @param args
2117    *    The type arguments of the class if it's a collection or map.
2118    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
2119    *    <br>Ignored if the main type is not a map or collection.
2120    * @see ResponseContent#as(Type,Type...)
2121    * @return The response content as a simple string.
2122    * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
2123    */
2124   public <T> T getResponse(Type type, Type...args) throws RestCallException {
2125      return run().getContent().as(type, args);
2126   }
2127
2128   /**
2129    * Returns <jk>true</jk> if this request has a body.
2130    *
2131    * @return <jk>true</jk> if this request has a body.
2132    */
2133   public boolean hasHttpEntity() {
2134      return request instanceof HttpEntityEnclosingRequestBase;
2135   }
2136
2137   /**
2138    * Returns the body of this request.
2139    *
2140    * @return The body of this request, or <jk>null</jk> if it doesn't have a body.
2141    */
2142   public HttpEntity getHttpEntity() {
2143      return hasHttpEntity() ? ((HttpEntityEnclosingRequestBase)request).getEntity() : null;
2144   }
2145
2146   /**
2147    * Logs a message.
2148    *
2149    * @param level The log level.
2150    * @param t The throwable cause.
2151    * @param msg The message with {@link MessageFormat}-style arguments.
2152    * @param args The arguments.
2153    * @return This object.
2154    */
2155   public RestRequest log(Level level, Throwable t, String msg, Object...args) {
2156      client.log(level, t, msg, args);
2157      return this;
2158   }
2159
2160   /**
2161    * Logs a message.
2162    *
2163    * @param level The log level.
2164    * @param msg The message with {@link MessageFormat}-style arguments.
2165    * @param args The arguments.
2166    * @return This object.
2167    */
2168   public RestRequest log(Level level, String msg, Object...args) {
2169      client.log(level, msg, args);
2170      return this;
2171   }
2172
2173   //-----------------------------------------------------------------------------------------------------------------
2174   // HttpRequestBase pass-through methods.
2175   //-----------------------------------------------------------------------------------------------------------------
2176
2177   /**
2178    * Sets the actual request configuration.
2179    *
2180    * @param value The new value.
2181    * @return This object.
2182    */
2183   @FluentSetter
2184   public RestRequest config(RequestConfig value) {
2185      request.setConfig(value);
2186      return this;
2187   }
2188
2189   /**
2190    * Sets {@link Cancellable} for the ongoing operation.
2191    *
2192    * @param cancellable The cancellable object.
2193    * @return This object.
2194    */
2195   @FluentSetter
2196   public RestRequest cancellable(Cancellable cancellable) {
2197      request.setCancellable(cancellable);
2198      return this;
2199   }
2200
2201   /**
2202    * Sets the protocol version for this request.
2203    *
2204    * @param version The protocol version for this request.
2205    * @return This object.
2206    */
2207   @FluentSetter
2208   public RestRequest protocolVersion(ProtocolVersion version) {
2209      request.setProtocolVersion(version);
2210      return this;
2211   }
2212
2213   /**
2214    * Used in combination with {@link #cancellable(Cancellable)}.
2215    *
2216    * @return This object.
2217    */
2218   @Deprecated
2219   public RestRequest completed() {
2220      request.completed();
2221      return this;
2222   }
2223
2224   // -----------------------------------------------------------------------------------------------------------------
2225   // HttpUriRequest pass-through methods.
2226   // -----------------------------------------------------------------------------------------------------------------
2227
2228   /**
2229    * Returns the HTTP method this request uses, such as GET, PUT, POST, or other.
2230    *
2231    * @return The HTTP method this request uses, such as GET, PUT, POST, or other.
2232    */
2233   @Override /* HttpUriRequest */
2234   public String getMethod() {
2235      return request.getMethod();
2236   }
2237
2238   /**
2239    * Returns the original request URI.
2240    *
2241    * <h5 class='section'>Notes:</h5><ul>
2242    *    <li class='note'>URI remains unchanged in the course of request execution and is not updated if the request is redirected to another location.
2243    * </ul>
2244    *
2245    * @return The original request URI.
2246    */
2247   @Override /* HttpUriRequest */
2248   public URI getURI() {
2249      return request.getURI();
2250   }
2251
2252   /**
2253    * Aborts this http request. Any active execution of this method should return immediately.
2254    *
2255    * If the request has not started, it will abort after the next execution.
2256    * <br>Aborting this request will cause all subsequent executions with this request to fail.
2257    */
2258   @Override /* HttpUriRequest */
2259   public void abort() throws UnsupportedOperationException {
2260      request.abort();
2261   }
2262
2263   @Override /* HttpUriRequest */
2264   public boolean isAborted() {
2265      return request.isAborted();
2266   }
2267
2268   /**
2269    * Returns the request line of this request.
2270    *
2271    * @return The request line.
2272    */
2273   @Override /* HttpRequest */
2274   public RequestLine getRequestLine() {
2275      return request.getRequestLine();
2276   }
2277
2278   /**
2279    * Returns the protocol version this message is compatible with.
2280    *
2281    * @return The protocol version.
2282    */
2283   @Override /* HttpMessage */
2284   public ProtocolVersion getProtocolVersion() {
2285      return request.getProtocolVersion();
2286   }
2287
2288   /**
2289    * Checks if a certain header is present in this message.
2290    *
2291    * Header values are ignored.
2292    *
2293    * @param name The header name to check for.
2294    * @return <jk>true</jk> if at least one header with this name is present.
2295    */
2296   @Override /* HttpMessage */
2297   public boolean containsHeader(String name) {
2298      return headerData.contains(name);
2299   }
2300
2301   /**
2302    * Returns all the headers with a specified name of this message.
2303    *
2304    * Header values are ignored.
2305    * <br>Headers are ordered in the sequence they will be sent over a connection.
2306    *
2307    * @param name The name of the headers to return.
2308    * @return The headers whose name property equals name.
2309    */
2310   @Override /* HttpMessage */
2311   public Header[] getHeaders(String name) {
2312      return headerData.getAll(name);
2313   }
2314
2315   /**
2316    * Returns the first header with a specified name of this message.
2317    *
2318    * Header values are ignored.
2319    * <br>If there is more than one matching header in the message the first element of {@link #getHeaders(String)} is returned.
2320    * <br>If there is no matching header in the message <jk>null</jk> is returned.
2321    *
2322    * @param name The name of the header to return.
2323    * @return The first header whose name property equals name or <jk>null</jk> if no such header could be found.
2324    */
2325   @Override /* HttpMessage */
2326   public Header getFirstHeader(String name) {
2327      return headerData.getFirst(name).orElse(null);
2328   }
2329
2330   /**
2331    * Returns the last header with a specified name of this message.
2332    *
2333    * Header values are ignored.
2334    * <br>If there is more than one matching header in the message the last element of {@link #getHeaders(String)} is returned.
2335    * <br>If there is no matching header in the message null is returned.
2336    *
2337    * @param name The name of the header to return.
2338    * @return The last header whose name property equals name or <jk>null</jk> if no such header could be found.
2339    */
2340   @Override /* HttpMessage */
2341   public Header getLastHeader(String name) {
2342      return headerData.getLast(name).orElse(null);
2343   }
2344
2345   /**
2346    * Returns all the headers of this message.
2347    *
2348    * Headers are ordered in the sequence they will be sent over a connection.
2349    *
2350    * @return All the headers of this message
2351    */
2352   @Override /* HttpMessage */
2353   public Header[] getAllHeaders() {
2354      return headerData.getAll();
2355   }
2356
2357   /**
2358    * Adds a header to this message.
2359    *
2360    * The header will be appended to the end of the list.
2361    *
2362    * <h5 class='section'>Notes:</h5><ul>
2363    *    <li class='note'>{@link #header(Header)} is an equivalent method and the preferred method for fluent-style coding.
2364    * </ul>
2365    *
2366    * @param header The header to append.
2367    */
2368   @Override /* HttpMessage */
2369   public void addHeader(Header header) {
2370      headerData.append(header);
2371   }
2372
2373   /**
2374    * Adds a header to this message.
2375    *
2376    * The header will be appended to the end of the list.
2377    *
2378    * <h5 class='section'>Notes:</h5><ul>
2379    *    <li class='note'>{@link #header(String,Object)} is an equivalent method and the preferred method for fluent-style coding.
2380    * </ul>
2381    *
2382    * @param name The name of the header.
2383    * @param value The value of the header.
2384    */
2385   @Override /* HttpMessage */
2386   public void addHeader(String name, String value) {
2387      headerData.append(stringHeader(name, value));
2388   }
2389
2390   /**
2391    * Overwrites the first header with the same name.
2392    *
2393    * The new header will be appended to the end of the list, if no header with the given name can be found.
2394    *
2395    * @param header The header to set.
2396    */
2397   @Override /* HttpMessage */
2398   public void setHeader(Header header) {
2399      headerData.set(header);
2400   }
2401
2402   /**
2403    * Overwrites the first header with the same name.
2404    *
2405    * The new header will be appended to the end of the list, if no header with the given name can be found.
2406    *
2407    * @param name The name of the header.
2408    * @param value The value of the header.
2409    */
2410   @Override /* HttpMessage */
2411   public void setHeader(String name, String value) {
2412      headerData.set(stringHeader(name, value));
2413   }
2414
2415   /**
2416    * Overwrites all the headers in the message.
2417    *
2418    * @param headers The array of headers to set.
2419    */
2420   @Override /* HttpMessage */
2421   public void setHeaders(Header[] headers) {
2422      headerData.set(headers);
2423   }
2424
2425   /**
2426    * Removes a header from this message.
2427    *
2428    * @param header The header to remove.
2429    */
2430   @Override /* HttpMessage */
2431   public void removeHeader(Header header) {
2432      headerData.remove(header);
2433   }
2434
2435   /**
2436    * Removes all headers with a certain name from this message.
2437    *
2438    * @param name The name of the headers to remove.
2439    */
2440   @Override /* HttpMessage */
2441   public void removeHeaders(String name) {
2442      headerData.remove(name);
2443   }
2444
2445   /**
2446    * Returns an iterator of all the headers.
2447    *
2448    * @return Iterator that returns {@link Header} objects in the sequence they are sent over a connection.
2449    */
2450   @Override /* HttpMessage */
2451   public HeaderIterator headerIterator() {
2452      return headerData.headerIterator();
2453   }
2454
2455   /**
2456    * Returns an iterator of the headers with a given name.
2457    *
2458    * @param name the name of the headers over which to iterate, or <jk>null</jk> for all headers.
2459    * @return Iterator that returns {@link Header} objects with the argument name in the sequence they are sent over a connection.
2460    */
2461   @Override /* HttpMessage */
2462   public HeaderIterator headerIterator(String name) {
2463      return headerData.headerIterator(name);
2464   }
2465
2466   /**
2467    * Returns the parameters effective for this message as set by {@link #setParams(HttpParams)}.
2468    *
2469    * @return The parameters effective for this message as set by {@link #setParams(HttpParams)}.
2470    * @deprecated Use constructor parameters of configuration API provided by HttpClient.
2471    */
2472   @Override /* HttpMessage */
2473   @Deprecated
2474   public HttpParams getParams() {
2475      return request.getParams();
2476   }
2477
2478   /**
2479    * Provides parameters to be used for the processing of this message.
2480    *
2481    * @param params The parameters.
2482    * @deprecated Use constructor parameters of configuration API provided by HttpClient.
2483    */
2484   @Override /* HttpMessage */
2485   @Deprecated
2486   public void setParams(HttpParams params) {
2487      request.setParams(params);
2488   }
2489
2490   /**
2491    * Returns the actual request configuration.
2492    *
2493    * @return The actual request configuration.
2494    */
2495   @Override /* Configurable */
2496   public RequestConfig getConfig() {
2497      return request.getConfig();
2498   }
2499
2500   // -----------------------------------------------------------------------------------------------------------------
2501   // Utility methods
2502   // -----------------------------------------------------------------------------------------------------------------
2503
2504   private ContentType getRequestContentType(ContentType def) {
2505      Header h = request.getFirstHeader("Content-Type");
2506      if (h != null) {
2507         String s = h.getValue();
2508         if (! isEmpty(s))
2509            return ContentType.of(s);
2510      }
2511      return def;
2512   }
2513
2514   @SuppressWarnings("unchecked")
2515   private static Map<Object,Object> toMap(Object o) {
2516      return (Map<Object,Object>)o;
2517   }
2518
2519   /**
2520    * Creates a new header.
2521    *
2522    * @param name The header name.
2523    * @param value The header value.
2524    * @param serializer The part serializer to use, or <jk>null</jk> to use the part serializer defined on the client.
2525    * @param schema Optional HTTP part schema to provide to the part serializer.
2526    * @param skipIfEmpty If <jk>true</jk>, empty string values will be ignored on the request.
2527    * @return A new header.
2528    */
2529   protected Header createHeader(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema, Boolean skipIfEmpty) {
2530      if (isEmpty(name))
2531         return null;
2532      if (skipIfEmpty == null)
2533         skipIfEmpty = client.isSkipEmptyHeaderData();
2534      if (serializer == null)
2535         serializer = client.getPartSerializer();
2536      return new SerializedHeader(name, value, getPartSerializerSession(serializer), schema, skipIfEmpty);
2537   }
2538
2539   private Header createHeader(String name, Object value) {
2540      return createHeader(name, value, null, null, null);
2541   }
2542
2543   /**
2544    * Creates a new query/form-data/path part.
2545    *
2546    * @param name The part name.
2547    * @param value The part value.
2548    * @param type The HTTP part type.
2549    * @param serializer The part serializer to use, or <jk>null</jk> to use the part serializer defined on the client.
2550    * @param schema Optional HTTP part schema to provide to the part serializer.
2551    * @param skipIfEmpty If <jk>true</jk>, empty string values will be ignored on the request.
2552    * @return A new part.
2553    */
2554   protected NameValuePair createPart(String name, Object value, HttpPartType type, HttpPartSerializer serializer, HttpPartSchema schema, Boolean skipIfEmpty) {
2555      if (isEmpty(name))
2556         return null;
2557      if (skipIfEmpty == null) {
2558         if (type == QUERY)
2559            skipIfEmpty = client.isSkipEmptyQueryData();
2560         else if (type == FORMDATA)
2561            skipIfEmpty = client.isSkipEmptyFormData();
2562         else
2563            skipIfEmpty = false;
2564      }
2565      if (serializer == null)
2566         serializer = client.getPartSerializer();
2567      return new SerializedPart(name, value, type, getPartSerializerSession(serializer), schema, skipIfEmpty);
2568   }
2569
2570   private NameValuePair createPart(HttpPartType type, String name, Object value) {
2571      return createPart(name, value, type, null, null, null);
2572   }
2573
2574   private HttpPartSerializerSession getPartSerializerSession(HttpPartSerializer serializer) {
2575      if (serializer == null)
2576         serializer = client.getPartSerializer();
2577      HttpPartSerializerSession s = partSerializerSessions.get(serializer);
2578      if (s == null) {
2579         s = serializer.getPartSession();
2580         partSerializerSessions.put(serializer, s);
2581      }
2582      return s;
2583   }
2584
2585   HttpPartSerializerSession getPartSerializerSession() {
2586      if (partSerializerSession == null)
2587         partSerializerSession = getPartSerializerSession(null);
2588      return partSerializerSession;
2589   }
2590
2591   private static boolean isNameValuePairArray(Object o) {
2592      if (o == null || ! o.getClass().isArray())
2593         return false;
2594      if (NameValuePair.class.isAssignableFrom(o.getClass().getComponentType()))
2595         return true;
2596      return false;
2597   }
2598
2599   private static boolean isHeaderArray(Object o) {
2600      if (o == null || ! o.getClass().isArray())
2601         return false;
2602      if (Header.class.isAssignableFrom(o.getClass().getComponentType()))
2603         return true;
2604      return false;
2605   }
2606
2607   //-----------------------------------------------------------------------------------------------------------------
2608   // Simple parts
2609   //-----------------------------------------------------------------------------------------------------------------
2610
2611   private class SimplePart implements NameValuePair {
2612      final String name;
2613      final String value;
2614
2615      SimplePart(NameValuePair x, boolean skipIfEmpty) {
2616         name = x.getName();
2617         if (x instanceof SerializedHeader) {
2618            value = ((SerializedHeader)x).copyWith(getPartSerializerSession(), null).getValue();
2619         } else if (x instanceof SerializedPart) {
2620            value = ((SerializedPart)x).copyWith(getPartSerializerSession(), null).getValue();
2621         } else {
2622            String v = x.getValue();
2623            value = (isEmpty(v) && skipIfEmpty) ? null : v;
2624         }
2625      }
2626
2627      boolean isValid() {
2628         if (isEmpty(name) || value == null)
2629            return false;
2630         return true;
2631      }
2632
2633      @Override
2634      public String getName() {
2635         return name;
2636      }
2637
2638      @Override
2639      public String getValue() {
2640         return value;
2641      }
2642   }
2643
2644   private class SimpleQuery extends SimplePart {
2645      SimpleQuery(NameValuePair x) {
2646         super(x, client.isSkipEmptyQueryData());
2647      }
2648   }
2649
2650   private class SimpleFormData extends SimplePart {
2651      SimpleFormData(NameValuePair x) {
2652         super(x, client.isSkipEmptyFormData());
2653      }
2654   }
2655
2656   private class SimplePath extends SimplePart {
2657      SimplePath(NameValuePair x) {
2658         super(x, false);
2659      }
2660   }
2661
2662   private class SimpleHeader extends SimplePart implements Header {
2663
2664      SimpleHeader(NameValuePair x) {
2665         super(x, client.isSkipEmptyHeaderData());
2666      }
2667
2668      @Override
2669      public HeaderElement[] getElements() throws ParseException {
2670         return null;
2671      }
2672   }
2673
2674   //-----------------------------------------------------------------------------------------------------------------
2675   // Fluent setters
2676   //-----------------------------------------------------------------------------------------------------------------
2677
2678   // <FluentSetters>
2679
2680   // </FluentSetters>
2681
2682   //-----------------------------------------------------------------------------------------------------------------
2683   // Other methods
2684   //-----------------------------------------------------------------------------------------------------------------
2685
2686   @Override
2687   public void close() throws Exception {
2688      complete();
2689   }
2690
2691   @Override /* ContextSession */
2692   protected JsonMap properties() {
2693      return filteredMap()
2694         .append("client", client.properties())
2695         .append("ignoreErrors", ignoreErrors)
2696         .append("interceptors", interceptors)
2697         .append("requestBodySchema", contentSchema)
2698         .append("response", response)
2699         .append("serializer", serializer);
2700   }
2701}