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(x -> x.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( 769 new Callable<T>() { 770 @Override /* Callable */ 771 public T call() throws Exception { 772 return as(type); 773 } 774 } 775 ); 776 } 777 778 /** 779 * Same as {@link #as(ClassMeta)} but allows you to run the call asynchronously. 780 * 781 * <h5 class='section'>Notes:</h5><ul> 782 * <li class='note'> 783 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 784 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 785 * with an inner {@link IllegalStateException} will be thrown. 786 * <li class='note'> 787 * The input stream is automatically closed after the execution of the future. 788 * </ul> 789 * 790 * @param <T> 791 * The class type of the object being created. 792 * See {@link #as(Type, Type...)} for details. 793 * @param type The object type to create. 794 * @return The future object. 795 * @throws RestCallException If the executor service was not defined. 796 * @see 797 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 798 * {@link Future Futures}. 799 */ 800 public <T> Future<T> asFuture(final ClassMeta<T> type) throws RestCallException { 801 return client.getExecutorService().submit( 802 new Callable<T>() { 803 @Override /* Callable */ 804 public T call() throws Exception { 805 return as(type); 806 } 807 } 808 ); 809 } 810 811 /** 812 * Same as {@link #as(Type,Type...)} but allows you to run the call asynchronously. 813 * 814 * <h5 class='section'>Notes:</h5><ul> 815 * <li class='note'> 816 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 817 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 818 * with an inner {@link IllegalStateException} will be thrown. 819 * <li class='note'> 820 * The input stream is automatically closed after the execution of the future. 821 * </ul> 822 * 823 * @param <T> 824 * The class type of the object being created. 825 * See {@link #as(Type, Type...)} for details. 826 * @param type 827 * The object type to create. 828 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 829 * @param args 830 * The type arguments of the class if it's a collection or map. 831 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 832 * <br>Ignored if the main type is not a map or collection. 833 * @return The future object. 834 * @throws RestCallException If the executor service was not defined. 835 * @see 836 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 837 * {@link Future Futures}. 838 */ 839 public <T> Future<T> asFuture(final Type type, final Type...args) throws RestCallException { 840 return client.getExecutorService().submit( 841 new Callable<T>() { 842 @Override /* Callable */ 843 public T call() throws Exception { 844 return as(type, args); 845 } 846 } 847 ); 848 } 849 850 /** 851 * Returns the contents of this body as a string. 852 * 853 * <h5 class='section'>Notes:</h5><ul> 854 * <li class='note'> 855 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 856 * <li class='note'> 857 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 858 * <li class='note'> 859 * The input stream is automatically closed after this call. 860 * </ul> 861 * 862 * @return The response as a string. 863 * @throws RestCallException 864 * <ul> 865 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 866 * <li>If a connection error occurred. 867 * </ul> 868 */ 869 public String asString() throws RestCallException { 870 cache(); 871 try (Reader r = asReader()) { 872 return read(r); 873 } catch (IOException e) { 874 response.close(); 875 throw new RestCallException(response, e, "Could not read response body."); 876 } 877 } 878 879 /** 880 * Same as {@link #asString()} but allows you to run the call asynchronously. 881 * 882 * <h5 class='section'>Notes:</h5><ul> 883 * <li class='note'> 884 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 885 * <li class='note'> 886 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 887 * <li class='note'> 888 * The input stream is automatically closed after this call. 889 * </ul> 890 * 891 * @return The future object. 892 * @throws RestCallException If the executor service was not defined. 893 * @see 894 * RestClient.Builder#executorService(ExecutorService, boolean) for defining the executor service for creating 895 * {@link Future Futures}. 896 */ 897 public Future<String> asStringFuture() throws RestCallException { 898 return client.getExecutorService().submit( 899 new Callable<String>() { 900 @Override /* Callable */ 901 public String call() throws Exception { 902 return asString(); 903 } 904 } 905 ); 906 } 907 908 /** 909 * Same as {@link #asString()} but truncates the string to the specified length. 910 * 911 * <p> 912 * If truncation occurs, the string will be suffixed with <js>"..."</js>. 913 * 914 * @param length The max length of the returned string. 915 * @return The truncated string. 916 * @throws RestCallException If a problem occurred trying to read from the reader. 917 */ 918 public String asAbbreviatedString(int length) throws RestCallException { 919 return StringUtils.abbreviate(asString(), length); 920 } 921 922 /** 923 * Returns the HTTP body content as a simple hexadecimal character string. 924 * 925 * <h5 class='section'>Example:</h5> 926 * <p class='bcode'> 927 * 0123456789ABCDEF 928 * </p> 929 * 930 * @return The incoming input from the connection as a plain string. 931 * @throws RestCallException If a problem occurred trying to read from the reader. 932 */ 933 public String asHex() throws RestCallException { 934 return toHex(asBytes()); 935 } 936 937 /** 938 * Returns the HTTP body content as a simple space-delimited hexadecimal character string. 939 * 940 * <h5 class='section'>Example:</h5> 941 * <p class='bcode'> 942 * 01 23 45 67 89 AB CD EF 943 * </p> 944 * 945 * @return The incoming input from the connection as a plain string. 946 * @throws RestCallException If a problem occurred trying to read from the reader. 947 */ 948 public String asSpacedHex() throws RestCallException { 949 return toSpacedHex(asBytes()); 950 } 951 952 /** 953 * Parses the output from the body into the specified type and then wraps that in a {@link ObjectRest}. 954 * 955 * <p> 956 * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. 957 * 958 * @param innerType The class type of the POJO being wrapped. 959 * @return The parsed output wrapped in a {@link ObjectRest}. 960 * @throws RestCallException 961 * <ul> 962 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 963 * <li>If a connection error occurred. 964 * </ul> 965 */ 966 public ObjectRest asObjectRest(Class<?> innerType) throws RestCallException { 967 return new ObjectRest(as(innerType)); 968 } 969 970 /** 971 * Converts the output from the connection into an {@link JsonMap} and then wraps that in a {@link ObjectRest}. 972 * 973 * <p> 974 * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. 975 * 976 * @return The parsed output wrapped in a {@link ObjectRest}. 977 * @throws RestCallException 978 * <ul> 979 * <li>If the input contains a syntax error or is malformed, or is not valid for the specified type. 980 * <li>If a connection error occurred. 981 * </ul> 982 */ 983 public ObjectRest asObjectRest() throws RestCallException { 984 return asObjectRest(JsonMap.class); 985 } 986 987 /** 988 * Converts the contents of the response body to a string and then matches the specified pattern against it. 989 * 990 * <h5 class='section'>Example:</h5> 991 * <p class='bjava'> 992 * <jc>// Parse response using a regular expression.</jc> 993 * Matcher <jv>matcher</jv> = <jv>client</jv> 994 * .get(<jsf>URI</jsf>) 995 * .run() 996 * .getContent().asMatcher(Pattern.<jsm>compile</jsm>(<js>"foo=(.*)"</js>)); 997 * 998 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 999 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 1000 * } 1001 * </p> 1002 * 1003 * <h5 class='section'>Notes:</h5><ul> 1004 * <li class='note'> 1005 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1006 * <li class='note'> 1007 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 1008 * <li class='note'> 1009 * The input stream is automatically closed after this call. 1010 * </ul> 1011 * 1012 * @param pattern The regular expression pattern to match. 1013 * @return The matcher. 1014 * @throws RestCallException If a connection error occurred. 1015 */ 1016 public Matcher asMatcher(Pattern pattern) throws RestCallException { 1017 return pattern.matcher(asString()); 1018 } 1019 1020 /** 1021 * Converts the contents of the response body to a string and then matches the specified pattern against it. 1022 * 1023 * <h5 class='section'>Example:</h5> 1024 * <p class='bjava'> 1025 * <jc>// Parse response using a regular expression.</jc> 1026 * Matcher <jv>matcher</jv> = <jv>client</jv> 1027 * .get(<jsf>URI</jsf>) 1028 * .run() 1029 * .getContent().asMatcher(<js>"foo=(.*)"</js>); 1030 * 1031 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 1032 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 1033 * } 1034 * </p> 1035 * 1036 * 1037 * <h5 class='section'>Notes:</h5><ul> 1038 * <li class='note'> 1039 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1040 * <li class='note'> 1041 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 1042 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 1043 * with an inner {@link IllegalStateException} will be thrown. 1044 * <li class='note'> 1045 * The input stream is automatically closed after this call. 1046 * </ul> 1047 * 1048 * @param regex The regular expression pattern to match. 1049 * @return The matcher. 1050 * @throws RestCallException If a connection error occurred. 1051 */ 1052 public Matcher asMatcher(String regex) throws RestCallException { 1053 return asMatcher(regex, 0); 1054 } 1055 1056 /** 1057 * Converts the contents of the response body to a string and then matches the specified pattern against it. 1058 * 1059 * <h5 class='section'>Example:</h5> 1060 * <p class='bjava'> 1061 * <jc>// Parse response using a regular expression.</jc> 1062 * Matcher <jv>matcher</jv> = <jv>client</jv> 1063 * .get(<jsf>URI</jsf>) 1064 * .run() 1065 * .getContent().asMatcher(<js>"foo=(.*)"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); 1066 * 1067 * <jk>if</jk> (<jv>matcher</jv>.matches()) { 1068 * String <jv>foo</jv> = <jv>matcher</jv>.group(1); 1069 * } 1070 * </p> 1071 * 1072 * 1073 * <h5 class='section'>Notes:</h5><ul> 1074 * <li class='note'> 1075 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1076 * <li class='note'> 1077 * If {@link #cache()} or {@link RestResponse#cacheContent()} has been called, this method can be can be called multiple times and/or combined with 1078 * other methods that retrieve the content of the response. Otherwise a {@link RestCallException} 1079 * with an inner {@link IllegalStateException} will be thrown. 1080 * <li class='note'> 1081 * The input stream is automatically closed after this call. 1082 * </ul> 1083 * 1084 * @param regex The regular expression pattern to match. 1085 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 1086 * @return The matcher. 1087 * @throws RestCallException If a connection error occurred. 1088 */ 1089 public Matcher asMatcher(String regex, int flags) throws RestCallException { 1090 return asMatcher(Pattern.compile(regex, flags)); 1091 } 1092 1093 //------------------------------------------------------------------------------------------------------------------ 1094 // Assertions 1095 //------------------------------------------------------------------------------------------------------------------ 1096 1097 /** 1098 * Provides the ability to perform fluent-style assertions on this response body. 1099 * 1100 * <p> 1101 * This method is called directly from the {@link RestResponse#assertContent()} method to instantiate a fluent assertions object. 1102 * 1103 * <h5 class='section'>Examples:</h5> 1104 * <p class='bjava'> 1105 * <jc>// Validates the response body equals the text "OK".</jc> 1106 * <jv>client</jv> 1107 * .get(<jsf>URI</jsf>) 1108 * .run() 1109 * .getContent().assertValue().equals(<js>"OK"</js>); 1110 * 1111 * <jc>// Validates the response body contains the text "OK".</jc> 1112 * <jv>client</jv> 1113 * .get(<jsf>URI</jsf>) 1114 * .run() 1115 * .getContent().assertValue().contains(<js>"OK"</js>); 1116 * 1117 * <jc>// Validates the response body passes a predicate test.</jc> 1118 * <jv>client</jv> 1119 * .get(<jsf>URI</jsf>) 1120 * .run() 1121 * .getContent().assertValue().is(<jv>x</jv> -> <jv>x</jv>.contains(<js>"OK"</js>)); 1122 * 1123 * <jc>// Validates the response body matches a regular expression.</jc> 1124 * <jv>client</jv> 1125 * .get(<jsf>URI</jsf>) 1126 * .run() 1127 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>); 1128 * 1129 * <jc>// Validates the response body matches a regular expression using regex flags.</jc> 1130 * <jv>client</jv> 1131 * .get(<jsf>URI</jsf>) 1132 * .run() 1133 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>, <jsf>MULTILINE</jsf> & <jsf>CASE_INSENSITIVE</jsf>); 1134 * 1135 * <jc>// Validates the response body matches a regular expression in the form of an existing Pattern.</jc> 1136 * Pattern <jv>pattern</jv> = Pattern.<jsm>compile</jsm>(<js>".*OK.*"</js>); 1137 * <jv>client</jv> 1138 * .get(<jsf>URI</jsf>) 1139 * .run() 1140 * .getContent().assertValue().isPattern(<jv>pattern</jv>); 1141 * </p> 1142 * 1143 * <p> 1144 * The assertion test returns the original response object allowing you to chain multiple requests like so: 1145 * <p class='bjava'> 1146 * <jc>// Validates the response body matches a regular expression.</jc> 1147 * MyBean <jv>bean</jv> = <jv>client</jv> 1148 * .get(<jsf>URI</jsf>) 1149 * .run() 1150 * .getContent().assertValue().isPattern(<js>".*OK.*"</js>); 1151 * .getContent().assertValue().isNotPattern(<js>".*ERROR.*"</js>) 1152 * .getContent().as(MyBean.<jk>class</jk>); 1153 * </p> 1154 * 1155 * <h5 class='section'>Notes:</h5><ul> 1156 * <li class='note'> 1157 * If no charset was found on the <code>Content-Type</code> response header, <js>"UTF-8"</js> is assumed. 1158 * <li class='note'> 1159 * This method automatically calls {@link #cache()} so that the body can be retrieved multiple times. 1160 * <li class='note'> 1161 * The input stream is automatically closed after this call. 1162 * </ul> 1163 * 1164 * @return A new fluent assertion object. 1165 */ 1166 public FluentResponseBodyAssertion<ResponseContent> assertValue() { 1167 return new FluentResponseBodyAssertion<>(this, this); 1168 } 1169 1170 /** 1171 * Shortcut for calling <c>assertValue().asString()</c>. 1172 * 1173 * @return A new fluent assertion. 1174 */ 1175 public FluentStringAssertion<ResponseContent> assertString() { 1176 return new FluentResponseBodyAssertion<>(this, this).asString(); 1177 } 1178 1179 /** 1180 * Shortcut for calling <c>assertValue().asBytes()</c>. 1181 * 1182 * @return A new fluent assertion. 1183 */ 1184 public FluentByteArrayAssertion<ResponseContent> assertBytes() { 1185 return new FluentResponseBodyAssertion<>(this, this).asBytes(); 1186 } 1187 1188 /** 1189 * Shortcut for calling <c>assertValue().as(<jv>type</jv>)</c>. 1190 * 1191 * @param <T> The object type to create. 1192 * @param type The object type to create. 1193 * @return A new fluent assertion. 1194 */ 1195 public <T> FluentAnyAssertion<T,ResponseContent> assertObject(Class<T> type) { 1196 return new FluentResponseBodyAssertion<>(this, this).as(type); 1197 } 1198 1199 /** 1200 * Shortcut for calling <c>assertValue().as(<jv>type</jv>, <jv>args</jv>)</c>. 1201 * 1202 * @param <T> The object type to create. 1203 * @param type The object type to create. 1204 * @param args Optional type arguments. 1205 * @return A new fluent assertion. 1206 */ 1207 public <T> FluentAnyAssertion<Object,ResponseContent> assertObject(Type type, Type...args) { 1208 return new FluentResponseBodyAssertion<>(this, this).as(type, args); 1209 } 1210 1211 /** 1212 * Returns the response that created this object. 1213 * 1214 * @return The response that created this object. 1215 */ 1216 public RestResponse response() { 1217 return response; 1218 } 1219 1220 //------------------------------------------------------------------------------------------------------------------ 1221 // HttpEntity passthrough methods. 1222 //------------------------------------------------------------------------------------------------------------------ 1223 1224 /** 1225 * Tells if the entity is capable of producing its data more than once. 1226 * 1227 * <p> 1228 * A repeatable entity's {@link #getContent()} and {@link #writeTo(OutputStream)} methods can be called more than 1229 * once whereas a non-repeatable entity's can not. 1230 * 1231 * <h5 class='section'>Notes:</h5><ul> 1232 * <li class='note'>This method always returns <jk>true</jk> if the response body is cached (see {@link #cache()}). 1233 * </ul> 1234 * 1235 * @return <jk>true</jk> if the entity is repeatable, <jk>false</jk> otherwise. 1236 */ 1237 @Override /* HttpEntity */ 1238 public boolean isRepeatable() { 1239 return cached || entity.isRepeatable(); 1240 } 1241 1242 /** 1243 * Tells about chunked encoding for this entity. 1244 * 1245 * <p> 1246 * The primary purpose of this method is to indicate whether chunked encoding should be used when the entity is sent. 1247 * <br>For entities that are received, it can also indicate whether the entity was received with chunked encoding. 1248 * 1249 * <p> 1250 * The behavior of wrapping entities is implementation dependent, but should respect the primary purpose. 1251 * 1252 * @return <jk>true</jk> if chunked encoding is preferred for this entity, or <jk>false</jk> if it is not. 1253 */ 1254 @Override /* HttpEntity */ 1255 public boolean isChunked() { 1256 return entity.isChunked(); 1257 } 1258 1259 /** 1260 * Tells the length of the content, if known. 1261 * 1262 * @return 1263 * The number of bytes of the content, or a negative number if unknown. 1264 * <br>If the content length is known but exceeds {@link Long#MAX_VALUE}, a negative number is returned. 1265 */ 1266 @Override /* HttpEntity */ 1267 public long getContentLength() { 1268 return body != null ? body.length : entity.getContentLength(); 1269 } 1270 1271 /** 1272 * Obtains the <c>Content-Type</c> header, if known. 1273 * 1274 * <p> 1275 * This is the header that should be used when sending the entity, or the one that was received with the entity. 1276 * It can include a charset attribute. 1277 * 1278 * @return The <c>Content-Type</c> header for this entity, or <jk>null</jk> if the content type is unknown. 1279 */ 1280 @Override /* HttpEntity */ 1281 public ResponseHeader getContentType() { 1282 return new ResponseHeader("Content-Type", request, response, entity.getContentType()); 1283 } 1284 1285 /** 1286 * Obtains the Content-Encoding header, if known. 1287 * 1288 * <p> 1289 * This is the header that should be used when sending the entity, or the one that was received with the entity. 1290 * <br>Wrapping entities that modify the content encoding should adjust this header accordingly. 1291 * 1292 * @return The <c>Content-Encoding</c> header for this entity, or <jk>null</jk> if the content encoding is unknown. 1293 */ 1294 @Override /* HttpEntity */ 1295 public ResponseHeader getContentEncoding() { 1296 return new ResponseHeader("Content-Encoding", request, response, entity.getContentEncoding()); 1297 } 1298 1299 /** 1300 * Returns a content stream of the entity. 1301 * 1302 * <h5 class='section'>Notes:</h5><ul> 1303 * <li class='note'>This method is equivalent to {@link #asInputStream()} which is the preferred method for fluent-style coding. 1304 * <li class='note'>This input stream will auto-close once the end of stream has been reached. 1305 * <li class='note'>It is up to the caller to properly close this stream if not fully consumed. 1306 * <li class='note'>This method can be called multiple times if the entity is repeatable or the cache flag is set on this object. 1307 * <li class='note'>Calling this method multiple times on a non-repeatable or cached body will throw a {@link IllegalStateException}. 1308 * Note that this is different from the HttpClient specs for this method. 1309 * </ul> 1310 * 1311 * @return Content stream of the entity. 1312 */ 1313 @Override /* HttpEntity */ 1314 public InputStream getContent() throws IOException, UnsupportedOperationException { 1315 return asInputStream(); 1316 } 1317 1318 /** 1319 * Writes the entity content out to the output stream. 1320 * 1321 * <h5 class='section'>Notes:</h5><ul> 1322 * <li class='note'>This method is equivalent to {@link #pipeTo(OutputStream)} which is the preferred method for fluent-style coding. 1323 * </ul> 1324 * 1325 * @param outstream The output stream to write entity content to. 1326 */ 1327 @Override /* HttpEntity */ 1328 public void writeTo(OutputStream outstream) throws IOException { 1329 pipeTo(outstream); 1330 } 1331 1332 /** 1333 * Tells whether this entity depends on an underlying stream. 1334 * 1335 * <h5 class='section'>Notes:</h5><ul> 1336 * <li class='note'>This method always returns <jk>false</jk> if the response body is cached (see {@link #cache()}. 1337 * </ul> 1338 * 1339 * @return <jk>true</jk> if the entity content is streamed, <jk>false</jk> otherwise. 1340 */ 1341 @Override /* HttpEntity */ 1342 public boolean isStreaming() { 1343 return cached ? false : entity.isStreaming(); 1344 } 1345 1346 /** 1347 * This method is called to indicate that the content of this entity is no longer required. 1348 * 1349 * <p> 1350 * This method is of particular importance for entities being received from a connection. 1351 * <br>The entity needs to be consumed completely in order to re-use the connection with keep-alive. 1352 * 1353 * @throws IOException If an I/O error occurs. 1354 * @deprecated Use standard java convention to ensure resource deallocation by calling {@link InputStream#close()} on 1355 * the input stream returned by {@link #getContent()} 1356 */ 1357 @Override /* HttpEntity */ 1358 @Deprecated 1359 public void consumeContent() throws IOException { 1360 entity.consumeContent(); 1361 } 1362 1363 //------------------------------------------------------------------------------------------------------------------ 1364 // Utility methods 1365 //------------------------------------------------------------------------------------------------------------------ 1366 1367 private BeanContext getBeanContext() { 1368 return parser == null ? BeanContext.DEFAULT : parser.getBeanContext(); 1369 } 1370 1371 private <T> ClassMeta<T> getClassMeta(Class<T> c) { 1372 return getBeanContext().getClassMeta(c); 1373 } 1374 1375 private <T> ClassMeta<T> getClassMeta(Type type, Type...args) { 1376 return getBeanContext().getClassMeta(type, args); 1377 } 1378 1379 @Override 1380 public String toString() { 1381 try { 1382 return asString(); 1383 } catch (RestCallException e) { 1384 return e.getLocalizedMessage(); 1385 } 1386 } 1387}