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}