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}