1818
1919import java .io .File ;
2020import java .io .IOException ;
21+ import java .io .RandomAccessFile ;
2122import java .nio .ByteBuffer ;
2223import java .nio .channels .ClosedByInterruptException ;
2324import java .nio .channels .ClosedChannelException ;
@@ -39,22 +40,22 @@ class FileDataBlock implements CloseableDataBlock {
3940
4041 private static final DebugLogger debug = DebugLogger .get (FileDataBlock .class );
4142
42- static Tracker tracker ;
43+ static Tracker tracker = Tracker . NONE ;
4344
44- private final ManagedFileChannel channel ;
45+ private final FileAccess fileAccess ;
4546
4647 private final long offset ;
4748
4849 private final long size ;
4950
5051 FileDataBlock (Path path ) throws IOException {
51- this .channel = new ManagedFileChannel (path );
52+ this .fileAccess = new FileAccess (path );
5253 this .offset = 0 ;
5354 this .size = Files .size (path );
5455 }
5556
56- FileDataBlock (ManagedFileChannel channel , long offset , long size ) {
57- this .channel = channel ;
57+ FileDataBlock (FileAccess fileAccess , long offset , long size ) {
58+ this .fileAccess = fileAccess ;
5859 this .offset = offset ;
5960 this .size = size ;
6061 }
@@ -79,7 +80,7 @@ public int read(ByteBuffer dst, long pos) throws IOException {
7980 originalDestinationLimit = dst .limit ();
8081 dst .limit (dst .position () + remaining );
8182 }
82- int result = this .channel .read (dst , this .offset + pos );
83+ int result = this .fileAccess .read (dst , this .offset + pos );
8384 if (originalDestinationLimit != -1 ) {
8485 dst .limit (originalDestinationLimit );
8586 }
@@ -92,7 +93,7 @@ public int read(ByteBuffer dst, long pos) throws IOException {
9293 * @throws IOException on I/O error
9394 */
9495 void open () throws IOException {
95- this .channel .open ();
96+ this .fileAccess .open ();
9697 }
9798
9899 /**
@@ -102,7 +103,7 @@ void open() throws IOException {
102103 */
103104 @ Override
104105 public void close () throws IOException {
105- this .channel .close ();
106+ this .fileAccess .close ();
106107 }
107108
108109 /**
@@ -112,7 +113,7 @@ public void close() throws IOException {
112113 * @throws E if the channel is closed
113114 */
114115 <E extends Exception > void ensureOpen (Supplier <E > exceptionSupplier ) throws E {
115- this .channel .ensureOpen (exceptionSupplier );
116+ this .fileAccess .ensureOpen (exceptionSupplier );
116117 }
117118
118119 /**
@@ -145,14 +146,14 @@ FileDataBlock slice(long offset, long size) {
145146 if (size < 0 || offset + size > this .size ) {
146147 throw new IllegalArgumentException ("Size must not be negative and must be within bounds" );
147148 }
148- debug .log ("Slicing %s at %s with size %s" , this .channel , offset , size );
149- return new FileDataBlock (this .channel , this .offset + offset , size );
149+ debug .log ("Slicing %s at %s with size %s" , this .fileAccess , offset , size );
150+ return new FileDataBlock (this .fileAccess , this .offset + offset , size );
150151 }
151152
152153 /**
153154 * Manages access to underlying {@link FileChannel}.
154155 */
155- static class ManagedFileChannel {
156+ static class FileAccess {
156157
157158 static final int BUFFER_SIZE = 1024 * 10 ;
158159
@@ -162,6 +163,10 @@ static class ManagedFileChannel {
162163
163164 private FileChannel fileChannel ;
164165
166+ private boolean fileChannelInterrupted ;
167+
168+ private RandomAccessFile randomAccessFile ;
169+
165170 private ByteBuffer buffer ;
166171
167172 private long bufferPosition = -1 ;
@@ -170,7 +175,7 @@ static class ManagedFileChannel {
170175
171176 private final Object lock = new Object ();
172177
173- ManagedFileChannel (Path path ) {
178+ FileAccess (Path path ) {
174179 if (!Files .isRegularFile (path )) {
175180 throw new IllegalArgumentException (path + " must be a regular file" );
176181 }
@@ -194,34 +199,45 @@ int read(ByteBuffer dst, long position) throws IOException {
194199 }
195200
196201 private void fillBuffer (long position ) throws IOException {
197- for (int i = 0 ; i < 10 ; i ++) {
198- boolean interrupted = (i != 0 ) ? Thread .interrupted () : false ;
199- try {
200- this .buffer .clear ();
201- this .bufferSize = this .fileChannel .read (this .buffer , position );
202- this .bufferPosition = position ;
203- return ;
204- }
205- catch (ClosedByInterruptException ex ) {
202+ if (Thread .currentThread ().isInterrupted ()) {
203+ fillBufferUsingRandomAccessFile (position );
204+ return ;
205+ }
206+ try {
207+ if (this .fileChannelInterrupted ) {
206208 repairFileChannel ();
209+ this .fileChannelInterrupted = false ;
207210 }
208- finally {
209- if (interrupted ) {
210- Thread .currentThread ().interrupt ();
211- }
212- }
211+ this .buffer .clear ();
212+ this .bufferSize = this .fileChannel .read (this .buffer , position );
213+ this .bufferPosition = position ;
214+ }
215+ catch (ClosedByInterruptException ex ) {
216+ this .fileChannelInterrupted = true ;
217+ fillBufferUsingRandomAccessFile (position );
213218 }
214- throw new ClosedByInterruptException ();
215219 }
216220
217- private void repairFileChannel () throws IOException {
218- if (tracker != null ) {
219- tracker .closedFileChannel (this .path , this .fileChannel );
221+ private void fillBufferUsingRandomAccessFile (long position ) throws IOException {
222+ if (this .randomAccessFile == null ) {
223+ this .randomAccessFile = new RandomAccessFile (this .path .toFile (), "r" );
224+ tracker .openedFileChannel (this .path );
220225 }
221- this .fileChannel = FileChannel .open (this .path , StandardOpenOption .READ );
222- if (tracker != null ) {
223- tracker .openedFileChannel (this .path , this .fileChannel );
226+ byte [] bytes = new byte [BUFFER_SIZE ];
227+ this .randomAccessFile .seek (position );
228+ int len = this .randomAccessFile .read (bytes );
229+ this .buffer .clear ();
230+ if (len > 0 ) {
231+ this .buffer .put (bytes , 0 , len );
224232 }
233+ this .bufferSize = len ;
234+ this .bufferPosition = position ;
235+ }
236+
237+ private void repairFileChannel () throws IOException {
238+ tracker .closedFileChannel (this .path );
239+ this .fileChannel = FileChannel .open (this .path , StandardOpenOption .READ );
240+ tracker .openedFileChannel (this .path );
225241 }
226242
227243 void open () throws IOException {
@@ -230,9 +246,7 @@ void open() throws IOException {
230246 debug .log ("Opening '%s'" , this .path );
231247 this .fileChannel = FileChannel .open (this .path , StandardOpenOption .READ );
232248 this .buffer = ByteBuffer .allocateDirect (BUFFER_SIZE );
233- if (tracker != null ) {
234- tracker .openedFileChannel (this .path , this .fileChannel );
235- }
249+ tracker .openedFileChannel (this .path );
236250 }
237251 this .referenceCount ++;
238252 debug .log ("Reference count for '%s' incremented to %s" , this .path , this .referenceCount );
@@ -251,18 +265,21 @@ void close() throws IOException {
251265 this .bufferPosition = -1 ;
252266 this .bufferSize = 0 ;
253267 this .fileChannel .close ();
254- if (tracker != null ) {
255- tracker .closedFileChannel (this .path , this .fileChannel );
256- }
268+ tracker .closedFileChannel (this .path );
257269 this .fileChannel = null ;
270+ if (this .randomAccessFile != null ) {
271+ this .randomAccessFile .close ();
272+ tracker .closedFileChannel (this .path );
273+ this .randomAccessFile = null ;
274+ }
258275 }
259276 debug .log ("Reference count for '%s' decremented to %s" , this .path , this .referenceCount );
260277 }
261278 }
262279
263280 <E extends Exception > void ensureOpen (Supplier <E > exceptionSupplier ) throws E {
264281 synchronized (this .lock ) {
265- if (this .referenceCount == 0 || ! this . fileChannel . isOpen () ) {
282+ if (this .referenceCount == 0 ) {
266283 throw exceptionSupplier .get ();
267284 }
268285 }
@@ -280,9 +297,21 @@ public String toString() {
280297 */
281298 interface Tracker {
282299
283- void openedFileChannel (Path path , FileChannel fileChannel );
300+ Tracker NONE = new Tracker () {
301+
302+ @ Override
303+ public void openedFileChannel (Path path ) {
304+ }
305+
306+ @ Override
307+ public void closedFileChannel (Path path ) {
308+ }
309+
310+ };
311+
312+ void openedFileChannel (Path path );
284313
285- void closedFileChannel (Path path , FileChannel fileChannel );
314+ void closedFileChannel (Path path );
286315
287316 }
288317
0 commit comments