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.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
47
48
49
50 public final class PathEntityProducer implements AsyncEntityProducer {
51
52 private static final int BUFFER_SIZE = 8192;
53 private final Path file;
54 private final OpenOption[] openOptions;
55 private final ByteBuffer byteBuffer;
56 private final long length;
57 private final ContentType contentType;
58 private final boolean chunked;
59 private final AtomicReference<Exception> exception;
60 private final AtomicReference<SeekableByteChannel> channelRef;
61 private boolean eof;
62
63 public PathEntityProducer(final Path file, final ContentType contentType, final boolean chunked,
64 final OpenOption... openOptions) throws IOException {
65 this(file, BUFFER_SIZE, contentType, chunked, openOptions);
66 }
67
68 public PathEntityProducer(final Path file, final ContentType contentType, final OpenOption... openOptions)
69 throws IOException {
70 this(file, contentType, false, openOptions);
71 }
72
73 public PathEntityProducer(final Path file, final int bufferSize, final ContentType contentType,
74 final boolean chunked, final OpenOption... openOptions) throws IOException {
75 this.file = Args.notNull(file, "file");
76 this.openOptions = openOptions;
77 this.length = Files.size(file);
78 this.byteBuffer = ByteBuffer.allocate(bufferSize);
79 this.contentType = contentType;
80 this.chunked = chunked;
81 this.channelRef = new AtomicReference<>();
82 this.exception = new AtomicReference<>();
83 }
84
85 public PathEntityProducer(final Path file, final OpenOption... openOptions) throws IOException {
86 this(file, ContentType.APPLICATION_OCTET_STREAM, openOptions);
87 }
88
89 @Override
90 public int available() {
91 return Integer.MAX_VALUE;
92 }
93
94 @Override
95 public void failed(final Exception cause) {
96 if (exception.compareAndSet(null, cause)) {
97 releaseResources();
98 }
99 }
100
101 @Override
102 public String getContentEncoding() {
103 return null;
104 }
105
106 @Override
107 public long getContentLength() {
108 return length;
109 }
110
111 @Override
112 public String getContentType() {
113 return contentType != null ? contentType.toString() : null;
114 }
115
116 public Exception getException() {
117 return exception.get();
118 }
119
120 @Override
121 public Set<String> getTrailerNames() {
122 return null;
123 }
124
125 @Override
126 public boolean isChunked() {
127 return chunked;
128 }
129
130 @Override
131 public boolean isRepeatable() {
132 return true;
133 }
134
135 @Override
136 public void produce(final DataStreamChannel dataStreamChannel) throws IOException {
137 SeekableByteChannel seekableByteChannel = channelRef.get();
138 if (seekableByteChannel == null) {
139 seekableByteChannel = Files.newByteChannel(file, openOptions);
140 Asserts.check(channelRef.getAndSet(seekableByteChannel) == null, "Illegal producer state");
141 }
142 if (!eof) {
143 final int bytesRead = seekableByteChannel.read(byteBuffer);
144 if (bytesRead < 0) {
145 eof = true;
146 }
147 }
148 if (byteBuffer.position() > 0) {
149 byteBuffer.flip();
150 dataStreamChannel.write(byteBuffer);
151 byteBuffer.compact();
152 }
153 if (eof && byteBuffer.position() == 0) {
154 dataStreamChannel.endStream();
155 releaseResources();
156 }
157 }
158
159 @Override
160 public void releaseResources() {
161 eof = false;
162 Closer.closeQuietly(channelRef.getAndSet(null));
163 }
164
165 }