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