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 org.apache.juneau.common.utils.IOUtils.*; 020import static org.apache.juneau.common.utils.StringUtils.*; 021import static org.apache.juneau.common.utils.ThrowableUtils.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.nio.charset.*; 026import java.util.concurrent.*; 027import java.util.regex.*; 028 029import org.apache.http.*; 030import org.apache.http.conn.*; 031import org.apache.juneau.*; 032import org.apache.juneau.assertions.*; 033import org.apache.juneau.collections.*; 034import org.apache.juneau.common.utils.*; 035import org.apache.juneau.http.entity.*; 036import org.apache.juneau.http.resource.*; 037import org.apache.juneau.httppart.*; 038import org.apache.juneau.oapi.*; 039import org.apache.juneau.objecttools.*; 040import org.apache.juneau.parser.*; 041import org.apache.juneau.parser.ParseException; 042import org.apache.juneau.reflect.*; 043import org.apache.juneau.rest.client.assertion.*; 044 045/** 046 * Represents the body of an HTTP response. 047 * 048 * <p> 049 * An extension of an HttpClient {@link HttpEntity} that provides various support for converting the body to POJOs and 050 * other convenience methods. 051 * 052 * <h5 class='section'>See Also:</h5><ul> 053 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClientBasics">juneau-rest-client Basics</a> 054 * </ul> 055 */ 056public class ResponseContent implements HttpEntity { 057 058 private static final HttpEntity NULL_ENTITY = new HttpEntity() { 059 060 @Override 061 public boolean isRepeatable() { 062 return false; 063 } 064 065 @Override 066 public boolean isChunked() { 067 return false; 068 } 069 070 @Override 071 public long getContentLength() { 072 return -1; 073 } 074 075 @Override 076 public Header getContentType() { 077 return ResponseHeader.NULL_HEADER; 078 } 079 080 @Override 081 public Header getContentEncoding() { 082 return ResponseHeader.NULL_HEADER; 083 } 084 085 @Override 086 public InputStream getContent() throws IOException, UnsupportedOperationException { 087 return new ByteArrayInputStream(new byte[0]); 088 } 089 090 @Override 091 public void writeTo(OutputStream outstream) throws IOException {} 092 093 @Override 094 public boolean isStreaming() { 095 return false; 096 } 097 098 @Override 099 public void consumeContent() throws IOException {} 100 }; 101 102 private final RestClient client; 103 final RestRequest request; 104 final RestResponse response; 105 private final HttpEntity entity; 106 private HttpPartSchema schema; 107 private Parser parser; 108 private byte[] body; 109 private boolean cached; 110 boolean isConsumed; 111 112 /** 113 * Constructor. 114 * 115 * @param client The client used to build this request. 116 * @param request The request object. 117 * @param response The response object. 118 * @param parser The parser to use to consume the body. Can be <jk>null</jk>. 119 */ 120 public ResponseContent(RestClient client, RestRequest request, RestResponse response, Parser parser) { 121 this.client = client; 122 this.request = request; 123 this.response = response; 124 this.parser = parser; 125 this.entity = Utils.firstNonNull(response.asHttpResponse().getEntity(), NULL_ENTITY); 126 } 127 128 //------------------------------------------------------------------------------------------------------------------ 129 // Setters 130 //------------------------------------------------------------------------------------------------------------------ 131 132 /** 133 * Specifies the parser to use for this body. 134 * 135 * <p> 136 * If not specified, uses the parser defined on the client set via {@link RestClient.Builder#parser(Class)}. 137 * 138 * @param value 139 * The new part parser to use for this body. 140 * @return This object. 141 */ 142 public ResponseContent parser(Parser value) { 143 this.parser = value; 144 return this; 145 } 146 147 /** 148 * Specifies the schema for this body. 149 * 150 * <p> 151 * Used by schema-based parsers such as {@link OpenApiParser}. 152 * 153 * @param value The schema. 154 * @return This object. 155 */ 156 public ResponseContent schema(HttpPartSchema value) { 157 this.schema = value; 158 return this; 159 } 160 161 /** 162 * Causes the contents of the response body to be stored so that it can be repeatedly read. 163 * 164 * <p> 165 * Calling this method allows methods that read the response body to be called multiple times. 166 * 167 * <h5 class='section'>Notes:</h5><ul> 168 * <li class='note'> 169 * Multiple calls to this method are ignored. 170 * </ul> 171 * 172 * @return This object. 173 */ 174 public ResponseContent cache() { 175 this.cached = true; 176 return this; 177 } 178 179 //------------------------------------------------------------------------------------------------------------------ 180 // Raw streams 181 //------------------------------------------------------------------------------------------------------------------ 182 183 /** 184 * Returns the HTTP response message body as an input stream. 185 * 186 * <h5 class='section'>Notes:</h5><ul> 187 * <li class='note'> 188 * Once this input stream is exhausted, it will automatically be closed. 189 * <li class='note'> 190 * This method can be called multiple times if {@link #cache()} has been called. 191 * <li class='note'> 192 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 193 * with an inner {@link IllegalStateException} to be thrown. 194 * </ul> 195 * 196 * @return 197 * The HTTP response message body input stream, never <jk>null</jk>. 198 * <br>For responses without a body(e.g. HTTP 204), returns an empty stream. 199 * @throws IOException If a stream or illegal state exception was thrown. 200 */ 201 @SuppressWarnings("resource") 202 public InputStream asInputStream() throws IOException { 203 try { 204 if (body != null) 205 return new ByteArrayInputStream(body); 206 207 if (cached) { 208 body = readBytes(entity.getContent()); 209 response.close(); 210 return new ByteArrayInputStream(body); 211 } 212 213 if (isConsumed && ! entity.isRepeatable()) 214 throw new IllegalStateException("Method cannot be called. Response has already been consumed. Consider using the RestResponse.cacheBody() method."); 215 216 HttpEntity e = response.asHttpResponse().getEntity(); 217 InputStream is = e == null ? new ByteArrayInputStream(new byte[0]) : e.getContent(); 218 219 is = new EofSensorInputStream(is, new EofSensorWatcher() { 220 @Override 221 public boolean eofDetected(InputStream wrapped) throws IOException { 222 response.close(); 223 return true; 224 } 225 @Override 226 public boolean streamClosed(InputStream wrapped) throws IOException { 227 response.close(); 228 return true; 229 } 230 @Override 231 public boolean streamAbort(InputStream wrapped) throws IOException { 232 response.close(); 233 return true; 234 } 235 }); 236 237 isConsumed = true; 238 239 return is; 240 } catch (UnsupportedOperationException e) { 241 throw new IOException(e); 242 } 243 } 244 245 /** 246 * Returns the HTTP response message body as a reader based on the charset on the <code>Content-Type</code> response header. 247 * 248 * <h5 class='section'>Notes:</h5><ul> 249 * <li class='note'> 250 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 251 * <li class='note'> 252 * Once this input stream is exhausted, it will automatically be closed. 253 * <li class='note'> 254 * This method can be called multiple times if {@link #cache()} has been called. 255 * <li class='note'> 256 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 257 * with an inner {@link IllegalStateException} to be thrown. 258 * </ul> 259 * 260 * @return 261 * The HTTP response message body reader, never <jk>null</jk>. 262 * <br>For responses without a body(e.g. HTTP 204), returns an empty reader. 263 * @throws IOException If an exception occurred. 264 */ 265 public Reader asReader() throws IOException { 266 267 // Figure out what the charset of the response is. 268 String cs = null; 269 String ct = getContentType().orElse(null); 270 271 // First look for "charset=" in Content-Type header of response. 272 if (ct != null) 273 if (ct.contains("charset=")) 274 cs = ct.substring(ct.indexOf("charset=")+8).trim(); 275 276 return asReader(cs == null ? IOUtils.UTF8 : Charset.forName(cs)); 277 } 278 279 /** 280 * Returns the HTTP response message body as a reader using the specified charset. 281 * 282 * <h5 class='section'>Notes:</h5><ul> 283 * <li class='note'> 284 * Once this input stream is exhausted, it will automatically be closed. 285 * <li class='note'> 286 * This method can be called multiple times if {@link #cache()} has been called. 287 * <li class='note'> 288 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 289 * with an inner {@link IllegalStateException} to be thrown. 290 * </ul> 291 * 292 * @param charset 293 * The charset to use for the reader. 294 * <br>If <jk>null</jk>, <js>"UTF-8"</js> is used. 295 * @return 296 * The HTTP response message body reader, never <jk>null</jk>. 297 * <br>For responses without a body(e.g. HTTP 204), returns an empty reader. 298 * @throws IOException If an exception occurred. 299 */ 300 public Reader asReader(Charset charset) throws IOException { 301 return new InputStreamReader(asInputStream(), charset == null ? IOUtils.UTF8 : charset); 302 } 303 304 /** 305 * Returns the HTTP response message body as a byte array. 306 * 307 * The HTTP response message body reader, never <jk>null</jk>. 308 * <br>For responses without a body(e.g. HTTP 204), returns an empty array. 309 * 310 * @return The HTTP response body as a byte array. 311 * @throws RestCallException If an exception occurred. 312 */ 313 public byte[] asBytes() throws RestCallException { 314 if (body == null) { 315 try { 316 if (entity instanceof BasicHttpEntity) { 317 body = ((BasicHttpEntity)entity).asBytes(); 318 } else { 319 body = readBytes(entity.getContent()); 320 } 321 } catch (IOException e) { 322 throw new RestCallException(response, e, "Could not read response body."); 323 } finally { 324 response.close(); 325 } 326 } 327 return body; 328 } 329 330 331 /** 332 * Pipes the contents of the response to the specified output stream. 333 * 334 * <h5 class='section'>Notes:</h5><ul> 335 * <li class='note'> 336 * The output stream is not automatically closed. 337 * <li class='note'> 338 * Once the input stream is exhausted, it will automatically be closed. 339 * <li class='note'> 340 * This method can be called multiple times if {@link #cache()} has been called. 341 * <li class='note'> 342 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 343 * with an inner {@link IllegalStateException} to be thrown. 344 * </ul> 345 * 346 * @param os The output stream to pipe the output to. 347 * @return This object. 348 * @throws IOException If an IO exception occurred. 349 */ 350 public RestResponse pipeTo(OutputStream os) throws IOException { 351 pipe(asInputStream(), os); 352 return response; 353 } 354 355 /** 356 * Pipes the contents of the response to the specified writer. 357 * 358 * <h5 class='section'>Notes:</h5><ul> 359 * <li class='note'> 360 * The writer is not automatically closed. 361 * <li class='note'> 362 * Once the reader is exhausted, it will automatically be closed. 363 * <li class='note'> 364 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 365 * <li class='note'> 366 * This method can be called multiple times if {@link #cache()} has been called. 367 * <li class='note'> 368 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 369 * with an inner {@link IllegalStateException} to be thrown. 370 * </ul> 371 * 372 * @param w The writer to pipe the output to. 373 * @return This object. 374 * @throws IOException If an IO exception occurred. 375 */ 376 public RestResponse pipeTo(Writer w) throws IOException { 377 return pipeTo(w, false); 378 } 379 380 /** 381 * Pipes the contents of the response to the specified writer. 382 * 383 * <h5 class='section'>Notes:</h5><ul> 384 * <li class='note'> 385 * The writer is not automatically closed. 386 * <li class='note'> 387 * Once the reader is exhausted, it will automatically be closed. 388 * <li class='note'> 389 * This method can be called multiple times if {@link #cache()} has been called. 390 * <li class='note'> 391 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 392 * with an inner {@link IllegalStateException} to be thrown. 393 * </ul> 394 * 395 * @param w The writer to pipe the output to. 396 * @param charset 397 * The charset to use for the reader. 398 * <br>If <jk>null</jk>, <js>"UTF-8"</js> is used. 399 * @return This object. 400 * @throws IOException If an IO exception occurred. 401 */ 402 public RestResponse pipeTo(Writer w, Charset charset) throws IOException { 403 return pipeTo(w, charset, false); 404 } 405 406 /** 407 * Pipes the contents of the response to the specified writer. 408 * 409 * <h5 class='section'>Notes:</h5><ul> 410 * <li class='note'> 411 * The writer is not automatically closed. 412 * <li class='note'> 413 * Once the reader is exhausted, it will automatically be closed. 414 * <li class='note'> 415 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 416 * <li class='note'> 417 * This method can be called multiple times if {@link #cache()} has been called. 418 * <li class='note'> 419 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 420 * with an inner {@link IllegalStateException} to be thrown. 421 * </ul> 422 * 423 * @param w The writer to write the output to. 424 * @param byLines Flush the writers after every line of output. 425 * @return This object. 426 * @throws IOException If an IO exception occurred. 427 */ 428 public RestResponse pipeTo(Writer w, boolean byLines) throws IOException { 429 return pipeTo(w, null, byLines); 430 } 431 432 /** 433 * Pipes the contents of the response to the specified writer. 434 * 435 * <h5 class='section'>Notes:</h5><ul> 436 * <li class='note'> 437 * The writer is not automatically closed. 438 * <li class='note'> 439 * Once the reader is exhausted, it will automatically be closed. 440 * <li class='note'> 441 * This method can be called multiple times if {@link #cache()} has been called. 442 * <li class='note'> 443 * Calling this method multiple times without caching enabled will cause a {@link RestCallException} 444 * with an inner {@link IllegalStateException} to be thrown. 445 * </ul> 446 * 447 * @param w The writer to pipe the output to. 448 * @param byLines Flush the writers after every line of output. 449 * @param charset 450 * The charset to use for the reader. 451 * <br>If <jk>null</jk>, <js>"UTF-8"</js> is used. 452 * @return This object. 453 * @throws IOException If an IO exception occurred. 454 */ 455 public RestResponse pipeTo(Writer w, Charset charset, boolean byLines) throws IOException { 456 if (byLines) 457 pipeLines(asReader(charset), w); 458 else 459 pipe(asReader(charset), w); 460 return response; 461 } 462 463 //------------------------------------------------------------------------------------------------------------------ 464 // Retrievers 465 //------------------------------------------------------------------------------------------------------------------ 466 467 /** 468 * Parses HTTP body into the specified object type. 469 * 470 * <p> 471 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 472 * 473 * <h5 class='section'>Examples:</h5> 474 * <p class='bjava'> 475 * <jc>// Parse into a linked-list of strings.</jc> 476 * List<String> <jv>list1</jv> = <jv>client</jv> 477 * .get(<jsf>URI</jsf>) 478 * .run() 479 * .getContent().as(LinkedList.<jk>class</jk>, String.<jk>class</jk>); 480 * 481 * <jc>// Parse into a linked-list of beans.</jc> 482 * List<MyBean> <jv>list2</jv> = <jv>client</jv> 483 * .get(<jsf>URI</jsf>) 484 * .run() 485 * .getContent().as(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 486 * 487 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 488 * List<List<String>> <jv>list3</jv> = <jv>client</jv> 489 * .get(<jsf>URI</jsf>) 490 * .run() 491 * .getContent().as(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 492 * 493 * <jc>// Parse into a map of string keys/values.</jc> 494 * Map<String,String> <jv>map1</jv> = <jv>client</jv> 495 * .get(<jsf>URI</jsf>) 496 * .run() 497 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 498 * 499 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 500 * Map<String,List<MyBean>> <jv>map2</jv> = <jv>client</jv> 501 * .get(<jsf>URI</jsf>) 502 * .run() 503 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 504 * </p> 505 * 506 * <p> 507 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 508 * 509 * <p> 510 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 511 * 512 * <p> 513 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 514 * 515 * <h5 class='section'>Notes:</h5><ul> 516 * <li class='note'> 517 * Use the {@link #as(Class)} method instead if you don't need a parameterized map/collection. 518 * <li class='note'> 519 * You can also specify any of the following types: 520 * <ul class='compact'> 521 * <li>{@link ResponseContent}/{@link HttpEntity} - Returns access to this object. 522 * <li>{@link Reader} - Returns access to the raw reader of the response. 523 * <li>{@link InputStream} - Returns access to the raw input stream of the response. 524 * <li>{@link HttpResource} - Response will be converted to an {@link BasicResource}. 525 * <li>Any type that takes in an {@link HttpResponse} object. 526 * </ul> 527 * <li class='note'> 528 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 529 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 530 * with an inner {@link IllegalStateException} will be thrown. 531 * <li class='note'> 532 * The input stream is automatically closed after this call. 533 * </ul> 534 * 535 * @param <T> The class type of the object to create. 536 * @param type 537 * The object type to create. 538 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 539 * @param args 540 * The type arguments of the class if it's a collection or map. 541 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 542 * <br>Ignored if the main type is not a map or collection. 543 * @return The parsed object. 544 * @throws RestCallException 545 * <ul> 546 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 547 * <li>If a connection error occurred. 548 * </ul> 549 * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections. 550 */ 551 public <T> T as(Type type, Type...args) throws RestCallException { 552 return as(getClassMeta(type, args)); 553 } 554 555 /** 556 * Same as {@link #as(Type,Type...)} except optimized for a non-parameterized class. 557 * 558 * <p> 559 * This is the preferred parse method for simple types since you don't need to cast the results. 560 * 561 * <h5 class='section'>Examples:</h5> 562 * <p class='bjava'> 563 * <jc>// Parse into a string.</jc> 564 * String <jv>string</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(String.<jk>class</jk>); 565 * 566 * <jc>// Parse into a bean.</jc> 567 * MyBean <jv>bean</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(MyBean.<jk>class</jk>); 568 * 569 * <jc>// Parse into a bean array.</jc> 570 * MyBean[] <jv>beanArray</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(MyBean[].<jk>class</jk>); 571 * 572 * <jc>// Parse into a linked-list of objects.</jc> 573 * List <jv>list</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(LinkedList.<jk>class</jk>); 574 * 575 * <jc>// Parse into a map of object keys/values.</jc> 576 * Map <jv>map</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(TreeMap.<jk>class</jk>); 577 * </p> 578 * 579 * <h5 class='section'>Notes:</h5><ul> 580 * <li class='note'> 581 * You can also specify any of the following types: 582 * <ul class='compact'> 583 * <li>{@link ResponseContent}/{@link HttpEntity} - Returns access to this object. 584 * <li>{@link Reader} - Returns access to the raw reader of the response. 585 * <li>{@link InputStream} - Returns access to the raw input stream of the response. 586 * <li>{@link HttpResource} - Response will be converted to an {@link BasicResource}. 587 * <li>Any type that takes in an {@link HttpResponse} object. 588 * </ul> 589 * <li class='note'> 590 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 591 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 592 * with an inner {@link IllegalStateException} will be thrown. 593 * <li class='note'> 594 * The input stream is automatically closed after this call. 595 * </ul> 596 * 597 * @param <T> 598 * The class type of the object being created. 599 * See {@link #as(Type,Type...)} for details. 600 * @param type The object type to create. 601 * @return The parsed object. 602 * @throws RestCallException 603 * If the input contains a syntax error or is malformed, or is not valid for the specified type, or if a connection 604 * error occurred. 605 */ 606 public <T> T as(Class<T> type) throws RestCallException { 607 return as(getClassMeta(type)); 608 } 609 610 /** 611 * Same as {@link #as(Class)} except allows you to predefine complex data types using the {@link ClassMeta} API. 612 * 613 * <h5 class='section'>Examples:</h5> 614 * <p class='bjava'> 615 * BeanContext <jv>beanContext</jv> = BeanContext.<jsf>DEFAULT</jsf>; 616 * 617 * <jc>// Parse into a linked-list of strings.</jc> 618 * ClassMeta<List<String>> <jv>cm1</jv> = <jv>beanContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, String.<jk>class</jk>); 619 * List<String> <jv>list1</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm1</jv>); 620 * 621 * <jc>// Parse into a linked-list of beans.</jc> 622 * ClassMeta<List<String>> <jv>cm2</jv> = <jv>beanContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 623 * List<MyBean> <jv>list2</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm2</jv>); 624 * 625 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 626 * ClassMeta<List<String>> <jv>cm3</jv> = <jv>beanContext</jv>.getClassMeta(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 627 * List<List<String>> <jv>list3</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm3</jv>); 628 * 629 * <jc>// Parse into a map of string keys/values.</jc> 630 * ClassMeta<List<String>> <jv>cm4</jv> = <jv>beanContext</jv>.getClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 631 * Map<String,String> <jv>map4</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm4</jv>); 632 * 633 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 634 * ClassMeta<List<String>> <jv>cm5</jv> = <jv>beanContext</jv>.getClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 635 * Map<String,List<MyBean>> <jv>map5</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().as(<jv>cm5</jv>); 636 * </p> 637 * 638 * <h5 class='section'>Notes:</h5><ul> 639 * <li class='note'> 640 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 641 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 642 * with an inner {@link IllegalStateException} will be thrown. 643 * <li class='note'> 644 * The input stream is automatically closed after this call. 645 * </ul> 646 * 647 * @param <T> The class type of the object to create. 648 * @param type The object type to create. 649 * @return The parsed object. 650 * @throws RestCallException 651 * <ul> 652 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 653 * <li>If a connection error occurred. 654 * </ul> 655 * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections. 656 */ 657 @SuppressWarnings("unchecked") 658 public <T> T as(ClassMeta<T> type) throws RestCallException { 659 try { 660 if (type.is(ResponseContent.class) || type.is(HttpEntity.class)) 661 return (T)this; 662 663 if (type.is(Reader.class)) 664 return (T)asReader(); 665 666 if (type.is(InputStream.class)) 667 return (T)asInputStream(); 668 669 if (type.is(HttpResponse.class)) 670 return (T)response; 671 672 if (type.is(HttpResource.class)) 673 type = (ClassMeta<T>)getClassMeta(BasicResource.class); 674 675 ConstructorInfo ci = type.getInfo().getPublicConstructor(x -> x.hasParamTypes(HttpResponse.class)); 676 if (ci != null) { 677 try { 678 return (T)ci.invoke(response); 679 } catch (ExecutableException e) { 680 throw asRuntimeException(e); 681 } 682 } 683 684 String ct = firstNonEmpty(response.getHeader("Content-Type").orElse("text/plain")); 685 686 if (parser == null) 687 parser = client.getMatchingParser(ct); 688 689 MediaType mt = MediaType.of(ct); 690 691 if (parser == null || (mt.toString().contains("text/plain") && ! parser.canHandle(ct))) { 692 if (type.hasStringMutater()) 693 return type.getStringMutater().mutate(asString()); 694 } 695 696 if (parser != null) { 697 try (Closeable in = parser.isReaderParser() ? asReader() : asInputStream()) { 698 699 T t = parser 700 .createSession() 701 .properties(JsonMap.create().inner(request.getSessionProperties())) 702 .locale(response.getLocale()) 703 .mediaType(mt) 704 .schema(schema) 705 .build() 706 .parse(in, type); 707 708 // Some HTTP responses have no body, so try to create these beans if they've got no-arg constructors. 709 if (t == null && ! type.is(String.class)) { 710 ConstructorInfo c = type.getInfo().getPublicConstructor(ConstructorInfo::hasNoParams); 711 if (c != null) { 712 try { 713 return c.<T>invoke(); 714 } catch (ExecutableException e) { 715 throw new ParseException(e); 716 } 717 } 718 } 719 720 return t; 721 } 722 } 723 724 if (type.hasReaderMutater()) 725 return type.getReaderMutater().mutate(asReader()); 726 727 if (type.hasInputStreamMutater()) 728 return type.getInputStreamMutater().mutate(asInputStream()); 729 730 ct = response.getStringHeader("Content-Type").orElse(null); 731 732 if (ct == null && client.hasParsers()) 733 throw new ParseException("Content-Type not specified in response header. Cannot find appropriate parser."); 734 735 throw new ParseException("Unsupported media-type in request header ''Content-Type'': ''{0}''", ct); 736 737 } catch (ParseException | IOException e) { 738 response.close(); 739 throw new RestCallException(response, e, "Could not parse response body."); 740 } 741 } 742 743 /** 744 * Same as {@link #as(Class)} but allows you to run the call asynchronously. 745 * 746 * <h5 class='section'>Notes:</h5><ul> 747 * <li class='note'> 748 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 749 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 750 * with an inner {@link IllegalStateException} will be thrown. 751 * <li class='note'> 752 * The input stream is automatically closed after the execution of the future. 753 * </ul> 754 * 755 * @param <T> The class type of the object being created. 756 * @param type The object type to create. 757 * @return The future object. 758 * @throws RestCallException If the executor service was not defined. 759 * @see 760 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 761 * {@link Future Futures}. 762 */ 763 public <T> Future<T> asFuture(final Class<T> type) throws RestCallException { 764 return client.getExecutorService().submit(() -> as(type)); 765 } 766 767 /** 768 * Same as {@link #as(ClassMeta)} but allows you to run the call asynchronously. 769 * 770 * <h5 class='section'>Notes:</h5><ul> 771 * <li class='note'> 772 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 773 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 774 * with an inner {@link IllegalStateException} will be thrown. 775 * <li class='note'> 776 * The input stream is automatically closed after the execution of the future. 777 * </ul> 778 * 779 * @param <T> 780 * The class type of the object being created. 781 * See {@link #as(Type, Type...)} for details. 782 * @param type The object type to create. 783 * @return The future object. 784 * @throws RestCallException If the executor service was not defined. 785 * @see 786 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 787 * {@link Future Futures}. 788 */ 789 public <T> Future<T> asFuture(final ClassMeta<T> type) throws RestCallException { 790 return client.getExecutorService().submit(() -> as(type)); 791 } 792 793 /** 794 * Same as {@link #as(Type,Type...)} but allows you to run the call asynchronously. 795 * 796 * <h5 class='section'>Notes:</h5><ul> 797 * <li class='note'> 798 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 799 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 800 * with an inner {@link IllegalStateException} will be thrown. 801 * <li class='note'> 802 * The input stream is automatically closed after the execution of the future. 803 * </ul> 804 * 805 * @param <T> 806 * The class type of the object being created. 807 * See {@link #as(Type, Type...)} for details. 808 * @param type 809 * The object type to create. 810 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 811 * @param args 812 * The type arguments of the class if it's a collection or map. 813 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 814 * <br>Ignored if the main type is not a map or collection. 815 * @return The future object. 816 * @throws RestCallException If the executor service was not defined. 817 * @see 818 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 819 * {@link Future Futures}. 820 */ 821 public <T> Future<T> asFuture(final Type type, final Type... args) throws RestCallException { 822 return client.getExecutorService().submit(() -> as(type, args)); 823 } 824 825 /** 826 * Returns the contents of this body as a string. 827 * 828 * <h5 class='section'>Notes:</h5><ul> 829 * <li class='note'> 830 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 831 * <li class='note'> 832 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 833 * <li class='note'> 834 * The input stream is automatically closed after this call. 835 * </ul> 836 * 837 * @return The response as a string. 838 * @throws RestCallException 839 * <ul> 840 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 841 * <li>If a connection error occurred. 842 * </ul> 843 */ 844 public String asString() throws RestCallException { 845 cache(); 846 try (Reader r = asReader()) { 847 return read(r); 848 } catch (IOException e) { 849 response.close(); 850 throw new RestCallException(response, e, "Could not read response body."); 851 } 852 } 853 854 /** 855 * Same as {@link #asString()} but allows you to run the call asynchronously. 856 * 857 * <h5 class='section'>Notes:</h5><ul> 858 * <li class='note'> 859 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 860 * <li class='note'> 861 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 862 * <li class='note'> 863 * The input stream is automatically closed after this call. 864 * </ul> 865 * 866 * @return The future object. 867 * @throws RestCallException If the executor service was not defined. 868 * @see 869 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 870 * {@link Future Futures}. 871 */ 872 public Future<String> asStringFuture() throws RestCallException { 873 return client.getExecutorService().submit(this::asString); 874 } 875 876 /** 877 * Same as {@link #asString()} but truncates the string to the specified length. 878 * 879 * <p> 880 * If truncation occurs, the string will be suffixed with <js>"..."</js>. 881 * 882 * @param length The max length of the returned string. 883 * @return The truncated string. 884 * @throws RestCallException If a problem occurred trying to read from the reader. 885 */ 886 public String asAbbreviatedString(int length) throws RestCallException { 887 return StringUtils.abbreviate(asString(), length); 888 } 889 890 /** 891 * Returns the HTTP body content as a simple hexadecimal character string. 892 * 893 * <h5 class='section'>Example:</h5> 894 * <p class='bcode'> 895 * 0123456789ABCDEF 896 * </p> 897 * 898 * @return The incoming input from the connection as a plain string. 899 * @throws RestCallException If a problem occurred trying to read from the reader. 900 */ 901 public String asHex() throws RestCallException { 902 return toHex(asBytes()); 903 } 904 905 /** 906 * Returns the HTTP body content as a simple space-delimited hexadecimal character string. 907 * 908 * <h5 class='section'>Example:</h5> 909 * <p class='bcode'> 910 * 01 23 45 67 89 AB CD EF 911 * </p> 912 * 913 * @return The incoming input from the connection as a plain string. 914 * @throws RestCallException If a problem occurred trying to read from the reader. 915 */ 916 public String asSpacedHex() throws RestCallException { 917 return toSpacedHex(asBytes()); 918 } 919 920 /** 921 * Parses the output from the body into the specified type and then wraps that in a {@link ObjectRest}. 922 * 923 * <p> 924 * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. 925 * 926 * @param innerType The class type of the POJO being wrapped. 927 * @return The parsed output wrapped in a {@link ObjectRest}. 928 * @throws RestCallException 929 * <ul> 930 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 931 * <li>If a connection error occurred. 932 * </ul> 933 */ 934 public ObjectRest asObjectRest(Class<?> innerType) throws RestCallException { 935 return new ObjectRest(as(innerType)); 936 } 937 938 /** 939 * Converts the output from the connection into an {@link JsonMap} and then wraps that in a {@link ObjectRest}. 940 * 941 * <p> 942 * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. 943 * 944 * @return The parsed output wrapped in a {@link ObjectRest}. 945 * @throws RestCallException 946 * <ul> 947 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 948 * <li>If a connection error occurred. 949 * </ul> 950 */ 951 public ObjectRest asObjectRest() throws RestCallException { 952 return asObjectRest(JsonMap.class); 953 } 954 955 /** 956 * Converts the contents of the response body to a string and then matches the specified pattern against it. 957 * 958 * <h5 class='section'>Example:</h5> 959 * <p class='bjava'> 960 * <jc>// Parse response using a regular expression.</jc> 961 * Matcher <jv>matcher</jv> = <jv>client</jv> 962 * .get(<jsf>URI</jsf>) 963 * .run() 964 * .getContent().asMatcher(Pattern.<jsm>compile</jsm>(<js>"foo=(.*)"</js>)); 965 * 966 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 967 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 968 * } 969 * </p> 970 * 971 * <h5 class='section'>Notes:</h5><ul> 972 * <li class='note'> 973 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 974 * <li class='note'> 975 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 976 * <li class='note'> 977 * The input stream is automatically closed after this call. 978 * </ul> 979 * 980 * @param pattern The regular expression pattern to match. 981 * @return The matcher. 982 * @throws RestCallException If a connection error occurred. 983 */ 984 public Matcher asMatcher(Pattern pattern) throws RestCallException { 985 return pattern.matcher(asString()); 986 } 987 988 /** 989 * Converts the contents of the response body to a string and then matches the specified pattern against it. 990 * 991 * <h5 class='section'>Example:</h5> 992 * <p class='bjava'> 993 * <jc>// Parse response using a regular expression.</jc> 994 * Matcher <jv>matcher</jv> = <jv>client</jv> 995 * .get(<jsf>URI</jsf>) 996 * .run() 997 * .getContent().asMatcher(<js>"foo=(.*)"</js>); 998 * 999 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 1000 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 1001 * } 1002 * </p> 1003 * 1004 * 1005 * <h5 class='section'>Notes:</h5><ul> 1006 * <li class='note'> 1007 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1008 * <li class='note'> 1009 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 1010 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 1011 * with an inner {@link IllegalStateException} will be thrown. 1012 * <li class='note'> 1013 * The input stream is automatically closed after this call. 1014 * </ul> 1015 * 1016 * @param regex The regular expression pattern to match. 1017 * @return The matcher. 1018 * @throws RestCallException If a connection error occurred. 1019 */ 1020 public Matcher asMatcher(String regex) throws RestCallException { 1021 return asMatcher(regex, 0); 1022 } 1023 1024 /** 1025 * Converts the contents of the response body to a string and then matches the specified pattern against it. 1026 * 1027 * <h5 class='section'>Example:</h5> 1028 * <p class='bjava'> 1029 * <jc>// Parse response using a regular expression.</jc> 1030 * Matcher <jv>matcher</jv> = <jv>client</jv> 1031 * .get(<jsf>URI</jsf>) 1032 * .run() 1033 * .getContent().asMatcher(<js>"foo=(.*)"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); 1034 * 1035 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 1036 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 1037 * } 1038 * </p> 1039 * 1040 * 1041 * <h5 class='section'>Notes:</h5><ul> 1042 * <li class='note'> 1043 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1044 * <li class='note'> 1045 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 1046 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 1047 * with an inner {@link IllegalStateException} will be thrown. 1048 * <li class='note'> 1049 * The input stream is automatically closed after this call. 1050 * </ul> 1051 * 1052 * @param regex The regular expression pattern to match. 1053 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 1054 * @return The matcher. 1055 * @throws RestCallException If a connection error occurred. 1056 */ 1057 public Matcher asMatcher(String regex, int flags) throws RestCallException { 1058 return asMatcher(Pattern.compile(regex, flags)); 1059 } 1060 1061 //------------------------------------------------------------------------------------------------------------------ 1062 // Assertions 1063 //------------------------------------------------------------------------------------------------------------------ 1064 1065 /** 1066 * Provides the ability to perform fluent-style assertions on this response body. 1067 * 1068 * <p> 1069 * This method is called directly from the {@link RestResponse#assertContent()} method to instantiate a fluent assertions object. 1070 * 1071 * <h5 class='section'>Examples:</h5> 1072 * <p class='bjava'> 1073 * <jc>// Validates the response body equals the text "OK".</jc> 1074 * <jv>client</jv> 1075 * .get(<jsf>URI</jsf>) 1076 * .run() 1077 * .getContent().assertValue().equals(<js>"OK"</js>); 1078 * 1079 * <jc>// Validates the response body contains the text "OK".</jc> 1080 * <jv>client</jv> 1081 * .get(<jsf>URI</jsf>) 1082 * .run() 1083 * .getContent().assertValue().contains(<js>"OK"</js>); 1084 * 1085 * <jc>// Validates the response body passes a predicate test.</jc> 1086 * <jv>client</jv> 1087 * .get(<jsf>URI</jsf>) 1088 * .run() 1089 * .getContent().assertValue().is(<jv>x</jv> -> <jv>x</jv>.contains(<js>"OK"</js>)); 1090 * 1091 * <jc>// Validates the response body matches a regular expression.</jc> 1092 * <jv>client</jv> 1093 * .get(<jsf>URI</jsf>) 1094 * .run() 1095 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>); 1096 * 1097 * <jc>// Validates the response body matches a regular expression using regex flags.</jc> 1098 * <jv>client</jv> 1099 * .get(<jsf>URI</jsf>) 1100 * .run() 1101 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); 1102 * 1103 * <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc> 1104 * Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>); 1105 * <jv>client</jv> 1106 * .get(<jsf>URI</jsf>) 1107 * .run() 1108 * .getContent().assertValue().isPattern(<jv>pattern</jv>); 1109 * </p> 1110 * 1111 * <p> 1112 * The assertion test returns the original response object allowing you to chain multiple requests like so: 1113 * <p class='bjava'> 1114 * <jc>// Validates the response body matches a regular expression.</jc> 1115 * MyBean <jv>bean</jv> = <jv>client</jv> 1116 * .get(<jsf>URI</jsf>) 1117 * .run() 1118 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>); 1119 * .getContent().assertValue().isNotPattern(<js>".*ERROR.*"</js>) 1120 * .getContent().as(MyBean.<jk>class</jk>); 1121 * </p> 1122 * 1123 * <h5 class='section'>Notes:</h5><ul> 1124 * <li class='note'> 1125 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1126 * <li class='note'> 1127 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 1128 * <li class='note'> 1129 * The input stream is automatically closed after this call. 1130 * </ul> 1131 * 1132 * @return A new fluent assertion object. 1133 */ 1134 public FluentResponseBodyAssertion<ResponseContent> assertValue() { 1135 return new FluentResponseBodyAssertion<>(this, this); 1136 } 1137 1138 /** 1139 * Shortcut for calling <c>assertValue().asString()</c>. 1140 * 1141 * @return A new fluent assertion. 1142 */ 1143 public FluentStringAssertion<ResponseContent> assertString() { 1144 return new FluentResponseBodyAssertion<>(this, this).asString(); 1145 } 1146 1147 /** 1148 * Shortcut for calling <c>assertValue().asBytes()</c>. 1149 * 1150 * @return A new fluent assertion. 1151 */ 1152 public FluentByteArrayAssertion<ResponseContent> assertBytes() { 1153 return new FluentResponseBodyAssertion<>(this, this).asBytes(); 1154 } 1155 1156 /** 1157 * Shortcut for calling <c>assertValue().as(<jv>type</jv>)</c>. 1158 * 1159 * @param <T> The object type to create. 1160 * @param type The object type to create. 1161 * @return A new fluent assertion. 1162 */ 1163 public <T> FluentAnyAssertion<T,ResponseContent> assertObject(Class<T> type) { 1164 return new FluentResponseBodyAssertion<>(this, this).as(type); 1165 } 1166 1167 /** 1168 * Shortcut for calling <c>assertValue().as(<jv>type</jv>, <jv>args</jv>)</c>. 1169 * 1170 * @param <T> The object type to create. 1171 * @param type The object type to create. 1172 * @param args Optional type arguments. 1173 * @return A new fluent assertion. 1174 */ 1175 public <T> FluentAnyAssertion<Object,ResponseContent> assertObject(Type type, Type...args) { 1176 return new FluentResponseBodyAssertion<>(this, this).as(type, args); 1177 } 1178 1179 /** 1180 * Returns the response that created this object. 1181 * 1182 * @return The response that created this object. 1183 */ 1184 public RestResponse response() { 1185 return response; 1186 } 1187 1188 //------------------------------------------------------------------------------------------------------------------ 1189 // HttpEntity passthrough methods. 1190 //------------------------------------------------------------------------------------------------------------------ 1191 1192 /** 1193 * Tells if the entity is capable of producing its data more than once. 1194 * 1195 * <p> 1196 * A repeatable entity's {@link #getContent()} and {@link #writeTo(OutputStream)} methods can be called more than 1197 * once whereas a non-repeatable entity's can not. 1198 * 1199 * <h5 class='section'>Notes:</h5><ul> 1200 * <li class='note'>This method always returns <jk>true</jk> if the response body is cached (see {@link #cache()}). 1201 * </ul> 1202 * 1203 * @return <jk>true</jk> if the entity is repeatable, <jk>false</jk> otherwise. 1204 */ 1205 @Override /* HttpEntity */ 1206 public boolean isRepeatable() { 1207 return cached || entity.isRepeatable(); 1208 } 1209 1210 /** 1211 * Tells about chunked encoding for this entity. 1212 * 1213 * <p> 1214 * The primary purpose of this method is to indicate whether chunked encoding should be used when the entity is sent. 1215 * <br>For entities that are received, it can also indicate whether the entity was received with chunked encoding. 1216 * 1217 * <p> 1218 * The behavior of wrapping entities is implementation dependent, but should respect the primary purpose. 1219 * 1220 * @return <jk>true</jk> if chunked encoding is preferred for this entity, or <jk>false</jk> if it is not. 1221 */ 1222 @Override /* HttpEntity */ 1223 public boolean isChunked() { 1224 return entity.isChunked(); 1225 } 1226 1227 /** 1228 * Tells the length of the content, if known. 1229 * 1230 * @return 1231 * The number of bytes of the content, or a negative number if unknown. 1232 * <br>If the content length is known but exceeds {@link Long#MAX_VALUE}, a negative number is returned. 1233 */ 1234 @Override /* HttpEntity */ 1235 public long getContentLength() { 1236 return body != null ? body.length : entity.getContentLength(); 1237 } 1238 1239 /** 1240 * Obtains the <c>Content-Type</c> header, if known. 1241 * 1242 * <p> 1243 * This is the header that should be used when sending the entity, or the one that was received with the entity. 1244 * It can include a charset attribute. 1245 * 1246 * @return The <c>Content-Type</c> header for this entity, or <jk>null</jk> if the content type is unknown. 1247 */ 1248 @Override /* HttpEntity */ 1249 public ResponseHeader getContentType() { 1250 return new ResponseHeader("Content-Type", request, response, entity.getContentType()); 1251 } 1252 1253 /** 1254 * Obtains the Content-Encoding header, if known. 1255 * 1256 * <p> 1257 * This is the header that should be used when sending the entity, or the one that was received with the entity. 1258 * <br>Wrapping entities that modify the content encoding should adjust this header accordingly. 1259 * 1260 * @return The <c>Content-Encoding</c> header for this entity, or <jk>null</jk> if the content encoding is unknown. 1261 */ 1262 @Override /* HttpEntity */ 1263 public ResponseHeader getContentEncoding() { 1264 return new ResponseHeader("Content-Encoding", request, response, entity.getContentEncoding()); 1265 } 1266 1267 /** 1268 * Returns a content stream of the entity. 1269 * 1270 * <h5 class='section'>Notes:</h5><ul> 1271 * <li class='note'>This method is equivalent to {@link #asInputStream()} which is the preferred method for fluent-style coding. 1272 * <li class='note'>This input stream will auto-close once the end of stream has been reached. 1273 * <li class='note'>It is up to the caller to properly close this stream if not fully consumed. 1274 * <li class='note'>This method can be called multiple times if the entity is repeatable or the cache flag is set on this object. 1275 * <li class='note'>Calling this method multiple times on a non-repeatable or cached body will throw a {@link IllegalStateException}. 1276 * Note that this is different from the HttpClient specs for this method. 1277 * </ul> 1278 * 1279 * @return Content stream of the entity. 1280 */ 1281 @Override /* HttpEntity */ 1282 public InputStream getContent() throws IOException, UnsupportedOperationException { 1283 return asInputStream(); 1284 } 1285 1286 /** 1287 * Writes the entity content out to the output stream. 1288 * 1289 * <h5 class='section'>Notes:</h5><ul> 1290 * <li class='note'>This method is equivalent to {@link #pipeTo(OutputStream)} which is the preferred method for fluent-style coding. 1291 * </ul> 1292 * 1293 * @param outstream The output stream to write entity content to. 1294 */ 1295 @Override /* HttpEntity */ 1296 public void writeTo(OutputStream outstream) throws IOException { 1297 pipeTo(outstream); 1298 } 1299 1300 /** 1301 * Tells whether this entity depends on an underlying stream. 1302 * 1303 * <h5 class='section'>Notes:</h5><ul> 1304 * <li class='note'>This method always returns <jk>false</jk> if the response body is cached (see {@link #cache()}. 1305 * </ul> 1306 * 1307 * @return <jk>true</jk> if the entity content is streamed, <jk>false</jk> otherwise. 1308 */ 1309 @Override /* HttpEntity */ 1310 public boolean isStreaming() { 1311 return cached ? false : entity.isStreaming(); 1312 } 1313 1314 /** 1315 * This method is called to indicate that the content of this entity is no longer required. 1316 * 1317 * <p> 1318 * This method is of particular importance for entities being received from a connection. 1319 * <br>The entity needs to be consumed completely in order to re-use the connection with keep-alive. 1320 * 1321 * @throws IOException If an I/O error occurs. 1322 * @deprecated Use standard java convention to ensure resource deallocation by calling {@link InputStream#close()} on 1323 * the input stream returned by {@link #getContent()} 1324 */ 1325 @Override /* HttpEntity */ 1326 @Deprecated 1327 public void consumeContent() throws IOException { 1328 entity.consumeContent(); 1329 } 1330 1331 //------------------------------------------------------------------------------------------------------------------ 1332 // Utility methods 1333 //------------------------------------------------------------------------------------------------------------------ 1334 1335 private BeanContext getBeanContext() { 1336 return parser == null ? BeanContext.DEFAULT : parser.getBeanContext(); 1337 } 1338 1339 private <T> ClassMeta<T> getClassMeta(Class<T> c) { 1340 return getBeanContext().getClassMeta(c); 1341 } 1342 1343 private <T> ClassMeta<T> getClassMeta(Type type, Type...args) { 1344 return getBeanContext().getClassMeta(type, args); 1345 } 1346 1347 @Override 1348 public String toString() { 1349 try { 1350 return asString(); 1351 } catch (RestCallException e) { 1352 return e.getLocalizedMessage(); 1353 } 1354 } 1355}