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