View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.core5.http.nio.entity;
28  
29  import java.io.File;
30  import java.io.IOException;
31  import java.io.RandomAccessFile;
32  import java.nio.ByteBuffer;
33  import java.util.Collections;
34  import java.util.Objects;
35  import java.util.Set;
36  import java.util.concurrent.atomic.AtomicReference;
37  
38  import org.apache.hc.core5.http.ContentType;
39  import org.apache.hc.core5.http.nio.AsyncEntityProducer;
40  import org.apache.hc.core5.http.nio.DataStreamChannel;
41  import org.apache.hc.core5.io.Closer;
42  import org.apache.hc.core5.util.Args;
43  import org.apache.hc.core5.util.Asserts;
44  
45  /**
46   * {@link AsyncEntityProducer} implementation that generates data stream
47   * from content of a {@link File}.
48   *
49   * @since 5.0
50   */
51  public final class FileEntityProducer implements AsyncEntityProducer {
52  
53      private final File file;
54      private final ByteBuffer byteBuffer;
55      private final long length;
56      private final ContentType contentType;
57      private final boolean chunked;
58      private final AtomicReference<Exception> exception;
59      private final AtomicReference<RandomAccessFile> accessFileRef;
60      private boolean eof;
61  
62      public FileEntityProducer(final File file, final int bufferSize, final ContentType contentType, final boolean chunked) {
63          this.file = Args.notNull(file, "File");
64          this.length = file.length();
65          this.byteBuffer = ByteBuffer.allocate(bufferSize);
66          this.contentType = contentType;
67          this.chunked = chunked;
68          this.accessFileRef = new AtomicReference<>();
69          this.exception = new AtomicReference<>();
70      }
71  
72      public FileEntityProducer(final File file, final ContentType contentType, final boolean chunked) {
73          this(file, 8192, contentType, chunked);
74      }
75  
76      public FileEntityProducer(final File file, final ContentType contentType) {
77          this(file, contentType, false);
78      }
79  
80      public FileEntityProducer(final File file) {
81          this(file, ContentType.APPLICATION_OCTET_STREAM);
82      }
83  
84      @Override
85      public boolean isRepeatable() {
86          return true;
87      }
88  
89      @Override
90      public String getContentType() {
91          return Objects.toString(contentType, null);
92      }
93  
94      @Override
95      public long getContentLength() {
96          return length;
97      }
98  
99      @Override
100     public int available() {
101         return Integer.MAX_VALUE;
102     }
103 
104     @Override
105     public String getContentEncoding() {
106         return null;
107     }
108 
109     @Override
110     public boolean isChunked() {
111         return chunked;
112     }
113 
114     @Override
115     public Set<String> getTrailerNames() {
116         return Collections.emptySet();
117     }
118 
119     @Override
120     public void produce(final DataStreamChannel channel) throws IOException {
121         @SuppressWarnings("resource")
122         RandomAccessFile accessFile = accessFileRef.get();
123         if (accessFile == null) {
124             accessFile = new RandomAccessFile(file, "r");
125             Asserts.check(accessFileRef.getAndSet(accessFile) == null, "Illegal producer state");
126         }
127         if (!eof) {
128             final int bytesRead = accessFile.getChannel().read(byteBuffer);
129             if (bytesRead < 0) {
130                 eof = true;
131             }
132         }
133         if (byteBuffer.position() > 0) {
134             byteBuffer.flip();
135             channel.write(byteBuffer);
136             byteBuffer.compact();
137         }
138         if (eof && byteBuffer.position() == 0) {
139             channel.endStream();
140             releaseResources();
141         }
142     }
143 
144     @Override
145     public void failed(final Exception cause) {
146         if (exception.compareAndSet(null, cause)) {
147             releaseResources();
148         }
149     }
150 
151     public Exception getException() {
152         return exception.get();
153     }
154 
155     @Override
156     public void releaseResources() {
157         eof = false;
158         Closer.closeQuietly(accessFileRef.getAndSet(null));
159     }
160 
161 }