1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client.util;
20
21 import java.io.Closeable;
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.SeekableByteChannel;
25 import java.nio.file.AccessDeniedException;
26 import java.nio.file.Files;
27 import java.nio.file.NoSuchFileException;
28 import java.nio.file.Path;
29 import java.nio.file.StandardOpenOption;
30 import java.util.Iterator;
31 import java.util.NoSuchElementException;
32
33 import org.eclipse.jetty.client.api.ContentProvider;
34 import org.eclipse.jetty.io.ByteBufferPool;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37
38
39
40
41
42
43
44
45
46 public class PathContentProvider extends AbstractTypedContentProvider
47 {
48 private static final Logger LOG = Log.getLogger(PathContentProvider.class);
49
50 private final Path filePath;
51 private final long fileSize;
52 private final int bufferSize;
53 private ByteBufferPool bufferPool;
54
55 public PathContentProvider(Path filePath) throws IOException
56 {
57 this(filePath, 4096);
58 }
59
60 public PathContentProvider(Path filePath, int bufferSize) throws IOException
61 {
62 this("application/octet-stream", filePath, bufferSize);
63 }
64
65 public PathContentProvider(String contentType, Path filePath) throws IOException
66 {
67 this(contentType, filePath, 4096);
68 }
69
70 public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException
71 {
72 super(contentType);
73 if (!Files.isRegularFile(filePath))
74 throw new NoSuchFileException(filePath.toString());
75 if (!Files.isReadable(filePath))
76 throw new AccessDeniedException(filePath.toString());
77 this.filePath = filePath;
78 this.fileSize = Files.size(filePath);
79 this.bufferSize = bufferSize;
80 }
81
82 @Override
83 public long getLength()
84 {
85 return fileSize;
86 }
87
88 public ByteBufferPool getByteBufferPool()
89 {
90 return bufferPool;
91 }
92
93 public void setByteBufferPool(ByteBufferPool byteBufferPool)
94 {
95 this.bufferPool = byteBufferPool;
96 }
97
98 @Override
99 public Iterator<ByteBuffer> iterator()
100 {
101 return new PathIterator();
102 }
103
104 private class PathIterator implements Iterator<ByteBuffer>, Closeable
105 {
106 private ByteBuffer buffer;
107 private SeekableByteChannel channel;
108 private long position;
109
110 @Override
111 public boolean hasNext()
112 {
113 return position < getLength();
114 }
115
116 @Override
117 public ByteBuffer next()
118 {
119 try
120 {
121 if (channel == null)
122 {
123 buffer = bufferPool == null ?
124 ByteBuffer.allocateDirect(bufferSize) :
125 bufferPool.acquire(bufferSize, true);
126 channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
127 if (LOG.isDebugEnabled())
128 LOG.debug("Opened file {}", filePath);
129 }
130
131 buffer.clear();
132 int read = channel.read(buffer);
133 if (read < 0)
134 throw new NoSuchElementException();
135
136 if (LOG.isDebugEnabled())
137 LOG.debug("Read {} bytes from {}", read, filePath);
138
139 position += read;
140
141 buffer.flip();
142 return buffer;
143 }
144 catch (NoSuchElementException x)
145 {
146 close();
147 throw x;
148 }
149 catch (Throwable x)
150 {
151 close();
152 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
153 }
154 }
155
156 @Override
157 public void close()
158 {
159 try
160 {
161 if (bufferPool != null && buffer != null)
162 bufferPool.release(buffer);
163 if (channel != null)
164 channel.close();
165 }
166 catch (Throwable x)
167 {
168 LOG.ignore(x);
169 }
170 }
171 }
172 }