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.http.entity;
018
019import static org.apache.juneau.common.utils.IOUtils.*;
020
021import java.io.*;
022import java.nio.charset.*;
023import java.util.*;
024import java.util.function.*;
025
026import org.apache.juneau.common.utils.*;
027import org.apache.juneau.http.header.*;
028import org.apache.juneau.internal.*;
029
030/**
031 * A streamed, non-repeatable entity that obtains its content from an {@link Reader}.
032 *
033 * <h5 class='section'>See Also:</h5><ul>
034 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a>
035 * </ul>
036 */
037public class ReaderEntity extends BasicHttpEntity {
038
039   //-----------------------------------------------------------------------------------------------------------------
040   // Instance
041   //-----------------------------------------------------------------------------------------------------------------
042
043   private byte[] byteCache;
044   private String stringCache;
045
046   /**
047    * Constructor.
048    */
049   public ReaderEntity() {
050   }
051
052   /**
053    * Constructor.
054    *
055    * @param contentType The entity content type.
056    * @param content The entity contents.
057    */
058   public ReaderEntity(ContentType contentType, Reader content) {
059      super(contentType, content);
060   }
061
062   /**
063    * Copy constructor.
064    *
065    * @param copyFrom The bean being copied.
066    */
067   protected ReaderEntity(ReaderEntity copyFrom) {
068      super(copyFrom);
069   }
070
071   @Override
072   public ReaderEntity copy() {
073      return new ReaderEntity(this);
074   }
075
076   //-----------------------------------------------------------------------------------------------------------------
077   // Other methods
078   //-----------------------------------------------------------------------------------------------------------------
079
080   @SuppressWarnings("resource") // Caller closes
081    private Reader content() {
082      return Objects.requireNonNull(contentOrElse((Reader) null), "Reader is null.");
083   }
084
085   @Override /* AbstractHttpEntity */
086   public String asString() throws IOException {
087      if (isCached() && stringCache == null)
088         stringCache = read(content(), getMaxLength());
089      if (stringCache != null)
090         return stringCache;
091      return read(content());
092   }
093
094   @Override /* AbstractHttpEntity */
095   public byte[] asBytes() throws IOException {
096      if (isCached() && byteCache == null)
097         byteCache = readBytes(content());
098      if (byteCache != null)
099         return byteCache;
100      return readBytes(content());
101   }
102
103   @Override /* HttpEntity */
104   public boolean isRepeatable() {
105      return isCached();
106   }
107
108   @Override /* HttpEntity */
109   public long getContentLength() {
110      if (isCached())
111         return asSafeBytes().length;
112      return super.getContentLength();
113   }
114
115   @Override /* HttpEntity */
116   public InputStream getContent() throws IOException {
117      if (isCached())
118         return new ByteArrayInputStream(asBytes());
119      return new ReaderInputStream(content(), getCharset());
120   }
121
122   /**
123    * Writes bytes from the {@code InputStream} this entity was constructed
124    * with to an {@code OutputStream}.  The content length
125    * determines how many bytes are written.  If the length is unknown ({@code -1}), the
126    * stream will be completely consumed (to the end of the stream).
127    */
128   @Override
129   public void writeTo(OutputStream out) throws IOException {
130      Utils.assertArgNotNull("out", out);
131
132      if (isCached()) {
133         out.write(asBytes());
134      } else {
135         OutputStreamWriter osw = new OutputStreamWriter(out, getCharset());
136         pipe(content(), osw);
137         osw.flush();
138      }
139      out.flush();
140   }
141
142   @Override /* HttpEntity */
143   public boolean isStreaming() {
144      return ! isCached();
145   }
146   @Override /* Overridden from BasicHttpEntity */
147   public ReaderEntity setCached() throws IOException{
148      super.setCached();
149      return this;
150   }
151
152   @Override /* Overridden from BasicHttpEntity */
153   public ReaderEntity setCharset(Charset value) {
154      super.setCharset(value);
155      return this;
156   }
157
158   @Override /* Overridden from BasicHttpEntity */
159   public ReaderEntity setChunked() {
160      super.setChunked();
161      return this;
162   }
163
164   @Override /* Overridden from BasicHttpEntity */
165   public ReaderEntity setChunked(boolean value) {
166      super.setChunked(value);
167      return this;
168   }
169
170   @Override /* Overridden from BasicHttpEntity */
171   public ReaderEntity setContent(Object value) {
172      super.setContent(value);
173      return this;
174   }
175
176   @Override /* Overridden from BasicHttpEntity */
177   public ReaderEntity setContent(Supplier<?> value) {
178      super.setContent(value);
179      return this;
180   }
181
182   @Override /* Overridden from BasicHttpEntity */
183   public ReaderEntity setContentEncoding(String value) {
184      super.setContentEncoding(value);
185      return this;
186   }
187
188   @Override /* Overridden from BasicHttpEntity */
189   public ReaderEntity setContentEncoding(ContentEncoding value) {
190      super.setContentEncoding(value);
191      return this;
192   }
193
194   @Override /* Overridden from BasicHttpEntity */
195   public ReaderEntity setContentLength(long value) {
196      super.setContentLength(value);
197      return this;
198   }
199
200   @Override /* Overridden from BasicHttpEntity */
201   public ReaderEntity setContentType(String value) {
202      super.setContentType(value);
203      return this;
204   }
205
206   @Override /* Overridden from BasicHttpEntity */
207   public ReaderEntity setContentType(ContentType value) {
208      super.setContentType(value);
209      return this;
210   }
211
212   @Override /* Overridden from BasicHttpEntity */
213   public ReaderEntity setMaxLength(int value) {
214      super.setMaxLength(value);
215      return this;
216   }
217
218   @Override /* Overridden from BasicHttpEntity */
219   public ReaderEntity setUnmodifiable() {
220      super.setUnmodifiable();
221      return this;
222   }
223}