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.Set;
34  import java.util.concurrent.atomic.AtomicReference;
35  
36  import org.apache.hc.core5.http.ContentType;
37  import org.apache.hc.core5.http.nio.AsyncEntityProducer;
38  import org.apache.hc.core5.http.nio.DataStreamChannel;
39  import org.apache.hc.core5.util.Args;
40  import org.apache.hc.core5.util.Asserts;
41  
42  /**
43   * {@link AsyncEntityProducer} implementation that generates data stream
44   * from content of a {@link File}.
45   *
46   * @since 5.0
47   */
48  public final class FileEntityProducer implements AsyncEntityProducer {
49  
50      private final File file;
51      private final ByteBuffer byteBuffer;
52      private final long length;
53      private final ContentType contentType;
54      private final AtomicReference<Exception> exception;
55      private final AtomicReference<RandomAccessFile> accessFileRef;
56      private boolean eof;
57  
58      public FileEntityProducer(final File file, final int bufferSize, final ContentType contentType) {
59          this.file = Args.notNull(file, "File");
60          this.length = file.length();
61          this.byteBuffer = ByteBuffer.allocate(bufferSize);
62          this.contentType = contentType;
63          this.accessFileRef = new AtomicReference<>(null);
64          this.exception = new AtomicReference<>(null);
65      }
66  
67      public FileEntityProducer(final File file, final ContentType contentType) {
68          this(file, 8192, contentType);
69      }
70  
71      public FileEntityProducer(final File file) {
72          this(file, ContentType.APPLICATION_OCTET_STREAM);
73      }
74  
75      @Override
76      public boolean isRepeatable() {
77          return true;
78      }
79  
80      @Override
81      public String getContentType() {
82          return contentType != null ? contentType.toString() : null;
83      }
84  
85      @Override
86      public long getContentLength() {
87          return length;
88      }
89  
90      @Override
91      public int available() {
92          return Integer.MAX_VALUE;
93      }
94  
95      @Override
96      public String getContentEncoding() {
97          return null;
98      }
99  
100     @Override
101     public boolean isChunked() {
102         return false;
103     }
104 
105     @Override
106     public Set<String> getTrailerNames() {
107         return null;
108     }
109 
110     @Override
111     public void produce(final DataStreamChannel channel) throws IOException {
112         @SuppressWarnings("resource")
113         RandomAccessFile accessFile = accessFileRef.get();
114         if (accessFile == null) {
115             accessFile = new RandomAccessFile(file, "r");
116             Asserts.check(accessFileRef.getAndSet(accessFile) == null, "Illegal producer state");
117         }
118         if (!eof) {
119             final int bytesRead = accessFile.getChannel().read(byteBuffer);
120             if (bytesRead < 0) {
121                 eof = true;
122             }
123         }
124         if (byteBuffer.position() > 0) {
125             byteBuffer.flip();
126             channel.write(byteBuffer);
127             byteBuffer.compact();
128         }
129         if (eof && byteBuffer.position() == 0) {
130             channel.endStream();
131             releaseResources();
132         }
133     }
134 
135     @Override
136     public void failed(final Exception cause) {
137         if (exception.compareAndSet(null, cause)) {
138             releaseResources();
139         }
140     }
141 
142     public Exception getException() {
143         return exception.get();
144     }
145 
146     @Override
147     public void releaseResources() {
148         eof = false;
149         @SuppressWarnings("resource")
150         final RandomAccessFile accessFile = accessFileRef.getAndSet(null);
151         if (accessFile != null) {
152             try {
153                 accessFile.close();
154             } catch (final IOException ignore) {
155                 // ignore
156             }
157         }
158     }
159 
160 }