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