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