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