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 repeatable entity that obtains its content from a {@link File}.
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 FileEntity extends BasicHttpEntity {
039   private byte[] byteCache;
040   private String stringCache;
041
042   /**
043    * Constructor.
044    */
045   public FileEntity() {}
046
047   /**
048    * Constructor.
049    *
050    * @param contentType The entity content type.
051    * @param content The entity contents.
052    */
053   public FileEntity(ContentType contentType, File content) {
054      super(contentType, content);
055   }
056
057   /**
058    * Copy constructor.
059    *
060    * @param copyFrom The bean being copied.
061    */
062   protected FileEntity(FileEntity 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());
073   }
074
075   @Override /* Overridden from AbstractHttpEntity */
076   public String asString() throws IOException {
077      if (isCached() && stringCache == null)
078         stringCache = read(new InputStreamReader(new FileInputStream(content()), getCharset()), getMaxLength());
079      if (nn(stringCache))
080         return stringCache;
081      return read(new InputStreamReader(new FileInputStream(content()), getCharset()), getMaxLength());
082   }
083
084   @Override
085   public FileEntity copy() {
086      return new FileEntity(this);
087   }
088
089   @Override /* Overridden from HttpEntity */
090   public InputStream getContent() throws IOException {
091      if (isCached())
092         return new ByteArrayInputStream(asBytes());
093      return new FileInputStream(content());
094   }
095
096   @Override /* Overridden from HttpEntity */
097   public long getContentLength() { return content().length(); }
098
099   @Override /* Overridden from HttpEntity */
100   public boolean isRepeatable() { return true; }
101
102   @Override /* Overridden from BasicHttpEntity */
103   public FileEntity setCached() throws IOException {
104      super.setCached();
105      return this;
106   }
107
108   @Override /* Overridden from BasicHttpEntity */
109   public FileEntity setCharset(Charset value) {
110      super.setCharset(value);
111      return this;
112   }
113
114   @Override /* Overridden from BasicHttpEntity */
115   public FileEntity setChunked() {
116      super.setChunked();
117      return this;
118   }
119
120   @Override /* Overridden from BasicHttpEntity */
121   public FileEntity setChunked(boolean value) {
122      super.setChunked(value);
123      return this;
124   }
125
126   @Override /* Overridden from BasicHttpEntity */
127   public FileEntity setContent(Object value) {
128      super.setContent(value);
129      return this;
130   }
131
132   @Override /* Overridden from BasicHttpEntity */
133   public FileEntity setContent(Supplier<?> value) {
134      super.setContent(value);
135      return this;
136   }
137
138   @Override /* Overridden from BasicHttpEntity */
139   public FileEntity setContentEncoding(ContentEncoding value) {
140      super.setContentEncoding(value);
141      return this;
142   }
143
144   @Override /* Overridden from BasicHttpEntity */
145   public FileEntity setContentEncoding(String value) {
146      super.setContentEncoding(value);
147      return this;
148   }
149
150   @Override /* Overridden from BasicHttpEntity */
151   public FileEntity setContentLength(long value) {
152      super.setContentLength(value);
153      return this;
154   }
155
156   @Override /* Overridden from BasicHttpEntity */
157   public FileEntity setContentType(ContentType value) {
158      super.setContentType(value);
159      return this;
160   }
161
162   @Override /* Overridden from BasicHttpEntity */
163   public FileEntity setContentType(String value) {
164      super.setContentType(value);
165      return this;
166   }
167
168   @Override /* Overridden from BasicHttpEntity */
169   public FileEntity setMaxLength(int value) {
170      super.setMaxLength(value);
171      return this;
172   }
173
174   @Override /* Overridden from BasicHttpEntity */
175   public FileEntity setUnmodifiable() {
176      super.setUnmodifiable();
177      return this;
178   }
179
180   @Override /* Overridden from HttpEntity */
181   public void writeTo(OutputStream out) throws IOException {
182      assertArgNotNull("out", out);
183
184      if (isCached()) {
185         out.write(asBytes());
186      } else {
187         try (var is = getContent()) {
188            pipe(is, out, getMaxLength());
189         }
190      }
191   }
192
193   private File content() {
194      var f = contentOrElse((File)null);
195      Objects.requireNonNull(f, "File");
196      if (! f.exists())
197         throw new IllegalStateException("File " + f.getAbsolutePath() + " does not exist.");
198      if (! f.canRead())
199         throw new IllegalStateException("File " + f.getAbsolutePath() + " is not readable.");
200      return f;
201   }
202}