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