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