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 InputStream}.
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 StreamEntity extends BasicHttpEntity {
038
039   //-----------------------------------------------------------------------------------------------------------------
040   // Instance
041   //-----------------------------------------------------------------------------------------------------------------
042
043   private byte[] byteCache;
044   private String stringCache;
045
046   /**
047    * Constructor.
048    */
049   public StreamEntity() {
050   }
051
052   /**
053    * Constructor.
054    *
055    * @param contentType The entity content type.
056    * @param content The entity contents.
057    */
058   public StreamEntity(ContentType contentType, InputStream content) {
059      super(contentType, content);
060   }
061
062   /**
063    * Copy constructor.
064    *
065    * @param copyFrom The bean being copied.
066    */
067   protected StreamEntity(StreamEntity copyFrom) {
068      super(copyFrom);
069   }
070
071   @Override
072   public StreamEntity copy() {
073      return new StreamEntity(this);
074   }
075
076   //-----------------------------------------------------------------------------------------------------------------
077   // Other methods
078   //-----------------------------------------------------------------------------------------------------------------
079
080   @SuppressWarnings("resource") // Caller closes
081    private InputStream content() {
082      return Objects.requireNonNull(contentOrElse((InputStream) null), "Input stream is null.");
083   }
084
085   @Override /* AbstractHttpEntity */
086   public String asString() throws IOException {
087      if (isCached() && stringCache == null)
088         stringCache = read(content(), getCharset());
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(), getMaxLength());
098      if (byteCache != null)
099         return byteCache;
100      return readBytes(content(), getMaxLength());
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 content();
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         try (InputStream is = getContent()) {
136            pipe(is, out, getMaxLength());
137         }
138      }
139   }
140
141   @Override /* HttpEntity */
142   public boolean isStreaming() {
143      return ! isCached();
144   }
145   @Override /* Overridden from BasicHttpEntity */
146   public StreamEntity setCached() throws IOException{
147      super.setCached();
148      return this;
149   }
150
151   @Override /* Overridden from BasicHttpEntity */
152   public StreamEntity setCharset(Charset value) {
153      super.setCharset(value);
154      return this;
155   }
156
157   @Override /* Overridden from BasicHttpEntity */
158   public StreamEntity setChunked() {
159      super.setChunked();
160      return this;
161   }
162
163   @Override /* Overridden from BasicHttpEntity */
164   public StreamEntity setChunked(boolean value) {
165      super.setChunked(value);
166      return this;
167   }
168
169   @Override /* Overridden from BasicHttpEntity */
170   public StreamEntity setContent(Object value) {
171      super.setContent(value);
172      return this;
173   }
174
175   @Override /* Overridden from BasicHttpEntity */
176   public StreamEntity setContent(Supplier<?> value) {
177      super.setContent(value);
178      return this;
179   }
180
181   @Override /* Overridden from BasicHttpEntity */
182   public StreamEntity setContentEncoding(String value) {
183      super.setContentEncoding(value);
184      return this;
185   }
186
187   @Override /* Overridden from BasicHttpEntity */
188   public StreamEntity setContentEncoding(ContentEncoding value) {
189      super.setContentEncoding(value);
190      return this;
191   }
192
193   @Override /* Overridden from BasicHttpEntity */
194   public StreamEntity setContentLength(long value) {
195      super.setContentLength(value);
196      return this;
197   }
198
199   @Override /* Overridden from BasicHttpEntity */
200   public StreamEntity setContentType(String value) {
201      super.setContentType(value);
202      return this;
203   }
204
205   @Override /* Overridden from BasicHttpEntity */
206   public StreamEntity setContentType(ContentType value) {
207      super.setContentType(value);
208      return this;
209   }
210
211   @Override /* Overridden from BasicHttpEntity */
212   public StreamEntity setMaxLength(int value) {
213      super.setMaxLength(value);
214      return this;
215   }
216
217   @Override /* Overridden from BasicHttpEntity */
218   public StreamEntity setUnmodifiable() {
219      super.setUnmodifiable();
220      return this;
221   }
222}