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.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36
37
38
39
40
41
42
43 public class PathContentProvider extends AbstractTypedContentProvider
44 {
45 private static final Logger LOG = Log.getLogger(PathContentProvider.class);
46
47 private final Path filePath;
48 private final long fileSize;
49 private final int bufferSize;
50
51 public PathContentProvider(Path filePath) throws IOException
52 {
53 this(filePath, 4096);
54 }
55
56 public PathContentProvider(Path filePath, int bufferSize) throws IOException
57 {
58 this("application/octet-stream", filePath, bufferSize);
59 }
60
61 public PathContentProvider(String contentType, Path filePath) throws IOException
62 {
63 this(contentType, filePath, 4096);
64 }
65
66 public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException
67 {
68 super(contentType);
69 if (!Files.isRegularFile(filePath))
70 throw new NoSuchFileException(filePath.toString());
71 if (!Files.isReadable(filePath))
72 throw new AccessDeniedException(filePath.toString());
73 this.filePath = filePath;
74 this.fileSize = Files.size(filePath);
75 this.bufferSize = bufferSize;
76 }
77
78 @Override
79 public long getLength()
80 {
81 return fileSize;
82 }
83
84 @Override
85 public Iterator<ByteBuffer> iterator()
86 {
87 return new PathIterator();
88 }
89
90 private class PathIterator implements Iterator<ByteBuffer>, Closeable
91 {
92 private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
93 private SeekableByteChannel channel;
94 private long position;
95
96 @Override
97 public boolean hasNext()
98 {
99 return position < getLength();
100 }
101
102 @Override
103 public ByteBuffer next()
104 {
105 try
106 {
107 if (channel == null)
108 {
109 channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
110 if (LOG.isDebugEnabled())
111 LOG.debug("Opened file {}", filePath);
112 }
113
114 buffer.clear();
115 int read = channel.read(buffer);
116 if (read < 0)
117 throw new NoSuchElementException();
118
119 if (LOG.isDebugEnabled())
120 LOG.debug("Read {} bytes from {}", read, filePath);
121
122 position += read;
123
124 if (!hasNext())
125 close();
126
127 buffer.flip();
128 return buffer;
129 }
130 catch (NoSuchElementException x)
131 {
132 close();
133 throw x;
134 }
135 catch (Throwable x)
136 {
137 close();
138 throw (NoSuchElementException)new NoSuchElementException().initCause(x);
139 }
140 }
141
142 @Override
143 public void remove()
144 {
145 throw new UnsupportedOperationException();
146 }
147
148 @Override
149 public void close()
150 {
151 try
152 {
153 if (channel != null)
154 channel.close();
155 }
156 catch (Throwable x)
157 {
158 LOG.ignore(x);
159 }
160 }
161 }
162 }