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