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