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