1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.http.nio.entity;
28
29 import java.io.IOException;
30 import java.nio.ByteBuffer;
31 import java.nio.channels.SeekableByteChannel;
32 import java.nio.file.Files;
33 import java.nio.file.OpenOption;
34 import java.nio.file.Path;
35 import java.util.Collections;
36 import java.util.Objects;
37 import java.util.Set;
38 import java.util.concurrent.atomic.AtomicReference;
39
40 import org.apache.hc.core5.http.ContentType;
41 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
42 import org.apache.hc.core5.http.nio.DataStreamChannel;
43 import org.apache.hc.core5.io.Closer;
44 import org.apache.hc.core5.util.Args;
45 import org.apache.hc.core5.util.Asserts;
46
47
48
49
50
51
52 public final class PathEntityProducer implements AsyncEntityProducer {
53
54 private static final int BUFFER_SIZE = 8192;
55 private final Path file;
56 private final OpenOption[] openOptions;
57 private final ByteBuffer byteBuffer;
58 private final long length;
59 private final ContentType contentType;
60 private final boolean chunked;
61 private final AtomicReference<Exception> exception;
62 private final AtomicReference<SeekableByteChannel> channelRef;
63 private boolean eof;
64
65 public PathEntityProducer(final Path file, final ContentType contentType, final boolean chunked,
66 final OpenOption... openOptions) throws IOException {
67 this(file, BUFFER_SIZE, contentType, chunked, openOptions);
68 }
69
70 public PathEntityProducer(final Path file, final ContentType contentType, final OpenOption... openOptions)
71 throws IOException {
72 this(file, contentType, false, openOptions);
73 }
74
75 public PathEntityProducer(final Path file, final int bufferSize, final ContentType contentType,
76 final boolean chunked, final OpenOption... openOptions) throws IOException {
77 this.file = Args.notNull(file, "file");
78 this.openOptions = openOptions;
79 this.length = Files.size(file);
80 this.byteBuffer = ByteBuffer.allocate(bufferSize);
81 this.contentType = contentType;
82 this.chunked = chunked;
83 this.channelRef = new AtomicReference<>();
84 this.exception = new AtomicReference<>();
85 }
86
87 public PathEntityProducer(final Path file, final OpenOption... openOptions) throws IOException {
88 this(file, ContentType.APPLICATION_OCTET_STREAM, openOptions);
89 }
90
91 @Override
92 public int available() {
93 return Integer.MAX_VALUE;
94 }
95
96 @Override
97 public void failed(final Exception cause) {
98 if (exception.compareAndSet(null, cause)) {
99 releaseResources();
100 }
101 }
102
103 @Override
104 public String getContentEncoding() {
105 return null;
106 }
107
108 @Override
109 public long getContentLength() {
110 return length;
111 }
112
113 @Override
114 public String getContentType() {
115 return Objects.toString(contentType, null);
116 }
117
118 public Exception getException() {
119 return exception.get();
120 }
121
122 @Override
123 public Set<String> getTrailerNames() {
124 return Collections.emptySet();
125 }
126
127 @Override
128 public boolean isChunked() {
129 return chunked;
130 }
131
132 @Override
133 public boolean isRepeatable() {
134 return true;
135 }
136
137 @Override
138 public void produce(final DataStreamChannel dataStreamChannel) throws IOException {
139 SeekableByteChannel seekableByteChannel = channelRef.get();
140 if (seekableByteChannel == null) {
141 seekableByteChannel = Files.newByteChannel(file, openOptions);
142 Asserts.check(channelRef.getAndSet(seekableByteChannel) == null, "Illegal producer state");
143 }
144 if (!eof) {
145 final int bytesRead = seekableByteChannel.read(byteBuffer);
146 if (bytesRead < 0) {
147 eof = true;
148 }
149 }
150 if (byteBuffer.position() > 0) {
151 byteBuffer.flip();
152 dataStreamChannel.write(byteBuffer);
153 byteBuffer.compact();
154 }
155 if (eof && byteBuffer.position() == 0) {
156 dataStreamChannel.endStream();
157 releaseResources();
158 }
159 }
160
161 @Override
162 public void releaseResources() {
163 eof = false;
164 Closer.closeQuietly(channelRef.getAndSet(null));
165 }
166
167 }