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