Source code: gnu/java/nio/channels/FileChannelImpl.java
1 /* FileChannelImpl.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.java.nio.channels;
40
41 import gnu.classpath.Configuration;
42 import gnu.java.nio.FileLockImpl;
43
44 import java.io.File;
45 import java.io.FileNotFoundException;
46 import java.io.IOException;
47 import java.nio.ByteBuffer;
48 import java.nio.MappedByteBuffer;
49 import java.nio.channels.ClosedChannelException;
50 import java.nio.channels.FileChannel;
51 import java.nio.channels.FileLock;
52 import java.nio.channels.NonReadableChannelException;
53 import java.nio.channels.NonWritableChannelException;
54 import java.nio.channels.ReadableByteChannel;
55 import java.nio.channels.WritableByteChannel;
56
57 /**
58 * This file is not user visible !
59 * But alas, Java does not have a concept of friendly packages
60 * so this class is public.
61 * Instances of this class are created by invoking getChannel
62 * Upon a Input/Output/RandomAccessFile object.
63 */
64 public final class FileChannelImpl extends FileChannel
65 {
66 // These are mode values for open().
67 public static final int READ = 1;
68 public static final int WRITE = 2;
69 public static final int APPEND = 4;
70
71 // EXCL is used only when making a temp file.
72 public static final int EXCL = 8;
73 public static final int SYNC = 16;
74 public static final int DSYNC = 32;
75
76 public static FileChannelImpl in;
77 public static FileChannelImpl out;
78 public static FileChannelImpl err;
79
80 private static native void init();
81
82 static
83 {
84 if (Configuration.INIT_LOAD_LIBRARY)
85 {
86 System.loadLibrary("javanio");
87 }
88
89 init();
90
91 in = new FileChannelImpl(0, READ);
92 out = new FileChannelImpl(1, WRITE);
93 err = new FileChannelImpl(2, WRITE);
94 }
95
96 /**
97 * This is the actual native file descriptor value
98 */
99 // System's notion of file descriptor. It might seem redundant to
100 // initialize this given that it is reassigned in the constructors.
101 // However, this is necessary because if open() throws an exception
102 // we want to make sure this has the value -1. This is the most
103 // efficient way to accomplish that.
104 private int fd = -1;
105
106 private int mode;
107
108 final String description;
109
110 /* Open a file. MODE is a combination of the above mode flags. */
111 /* This is a static factory method, so that VM implementors can decide
112 * substitute subclasses of FileChannelImpl. */
113 public static FileChannelImpl create(File file, int mode)
114 throws FileNotFoundException
115 {
116 return new FileChannelImpl(file, mode);
117 }
118
119 private FileChannelImpl(File file, int mode)
120 throws FileNotFoundException
121 {
122 String path = file.getPath();
123 description = path;
124 fd = open (path, mode);
125 this.mode = mode;
126
127 // First open the file and then check if it is a a directory
128 // to avoid race condition.
129 if (file.isDirectory())
130 {
131 try
132 {
133 close();
134 }
135 catch (IOException e)
136 {
137 /* ignore it */
138 }
139
140 throw new FileNotFoundException(description + " is a directory");
141 }
142 }
143
144 /**
145 * Constructor for default channels in, out and err.
146 *
147 * Used by init() (native code).
148 *
149 * @param fd the file descriptor (0, 1, 2 for stdin, stdout, stderr).
150 *
151 * @param mode READ or WRITE
152 */
153 FileChannelImpl (int fd, int mode)
154 {
155 this.fd = fd;
156 this.mode = mode;
157 this.description = "descriptor(" + fd + ")";
158 }
159
160 private native int open (String path, int mode) throws FileNotFoundException;
161
162 public native int available () throws IOException;
163 private native long implPosition () throws IOException;
164 private native void seek (long newPosition) throws IOException;
165 private native void implTruncate (long size) throws IOException;
166
167 public native void unlock (long pos, long len) throws IOException;
168
169 public native long size () throws IOException;
170
171 protected native void implCloseChannel() throws IOException;
172
173 /**
174 * Makes sure the Channel is properly closed.
175 */
176 protected void finalize() throws IOException
177 {
178 this.close();
179 }
180
181 public int read (ByteBuffer dst) throws IOException
182 {
183 int result;
184 byte[] buffer = new byte [dst.remaining ()];
185
186 result = read (buffer, 0, buffer.length);
187
188 if (result > 0)
189 dst.put (buffer, 0, result);
190
191 return result;
192 }
193
194 public int read (ByteBuffer dst, long position)
195 throws IOException
196 {
197 if (position < 0)
198 throw new IllegalArgumentException ("position: " + position);
199 long oldPosition = implPosition ();
200 position (position);
201 int result = read(dst);
202 position (oldPosition);
203
204 return result;
205 }
206
207 public native int read ()
208 throws IOException;
209
210 public native int read (byte[] buffer, int offset, int length)
211 throws IOException;
212
213 public long read (ByteBuffer[] dsts, int offset, int length)
214 throws IOException
215 {
216 long result = 0;
217
218 for (int i = offset; i < offset + length; i++)
219 {
220 result += read (dsts [i]);
221 }
222
223 return result;
224 }
225
226 public int write (ByteBuffer src) throws IOException
227 {
228 int len = src.remaining ();
229 if (src.hasArray())
230 {
231 byte[] buffer = src.array();
232 write(buffer, src.arrayOffset() + src.position(), len);
233 src.position(src.position() + len);
234 }
235 else
236 {
237 // Use a more efficient native method! FIXME!
238 byte[] buffer = new byte [len];
239 src.get (buffer, 0, len);
240 write (buffer, 0, len);
241 }
242 return len;
243 }
244
245 public int write (ByteBuffer src, long position)
246 throws IOException
247 {
248 if (position < 0)
249 throw new IllegalArgumentException ("position: " + position);
250
251 if (!isOpen ())
252 throw new ClosedChannelException ();
253
254 if ((mode & WRITE) == 0)
255 throw new NonWritableChannelException ();
256
257 int result;
258 long oldPosition;
259
260 oldPosition = implPosition ();
261 seek (position);
262 result = write(src);
263 seek (oldPosition);
264
265 return result;
266 }
267
268 public native void write (byte[] buffer, int offset, int length)
269 throws IOException;
270
271 public native void write (int b) throws IOException;
272
273 public long write(ByteBuffer[] srcs, int offset, int length)
274 throws IOException
275 {
276 long result = 0;
277
278 for (int i = offset;i < offset + length;i++)
279 {
280 result += write (srcs[i]);
281 }
282
283 return result;
284 }
285
286 public native MappedByteBuffer mapImpl (char mode, long position, int size)
287 throws IOException;
288
289 public MappedByteBuffer map (FileChannel.MapMode mode,
290 long position, long size)
291 throws IOException
292 {
293 char nmode = 0;
294 if (mode == MapMode.READ_ONLY)
295 {
296 nmode = 'r';
297 if ((this.mode & READ) == 0)
298 throw new NonReadableChannelException();
299 }
300 else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
301 {
302 nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
303 if ((this.mode & (READ|WRITE)) != (READ|WRITE))
304 throw new NonWritableChannelException();
305 }
306 else
307 throw new IllegalArgumentException ("mode: " + mode);
308
309 if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
310 throw new IllegalArgumentException ("position: " + position
311 + ", size: " + size);
312 return mapImpl(nmode, position, (int) size);
313 }
314
315 /**
316 * msync with the disk
317 */
318 public void force (boolean metaData) throws IOException
319 {
320 if (!isOpen ())
321 throw new ClosedChannelException ();
322
323 force ();
324 }
325
326 private native void force ();
327
328 // like transferTo, but with a count of less than 2Gbytes
329 private int smallTransferTo (long position, int count,
330 WritableByteChannel target)
331 throws IOException
332 {
333 ByteBuffer buffer;
334 try
335 {
336 // Try to use a mapped buffer if we can. If this fails for
337 // any reason we'll fall back to using a ByteBuffer.
338 buffer = map (MapMode.READ_ONLY, position, count);
339 }
340 catch (IOException e)
341 {
342 buffer = ByteBuffer.allocate (count);
343 read (buffer, position);
344 buffer.flip();
345 }
346
347 return target.write (buffer);
348 }
349
350 public long transferTo (long position, long count,
351 WritableByteChannel target)
352 throws IOException
353 {
354 if (position < 0
355 || count < 0)
356 throw new IllegalArgumentException ("position: " + position
357 + ", count: " + count);
358
359 if (!isOpen ())
360 throw new ClosedChannelException ();
361
362 if ((mode & READ) == 0)
363 throw new NonReadableChannelException ();
364
365 final int pageSize = 65536;
366 long total = 0;
367
368 while (count > 0)
369 {
370 int transferred
371 = smallTransferTo (position, (int)Math.min (count, pageSize),
372 target);
373 if (transferred < 0)
374 break;
375 total += transferred;
376 position += transferred;
377 count -= transferred;
378 }
379
380 return total;
381 }
382
383 // like transferFrom, but with a count of less than 2Gbytes
384 private int smallTransferFrom (ReadableByteChannel src, long position,
385 int count)
386 throws IOException
387 {
388 ByteBuffer buffer = null;
389
390 if (src instanceof FileChannel)
391 {
392 try
393 {
394 // Try to use a mapped buffer if we can. If this fails
395 // for any reason we'll fall back to using a ByteBuffer.
396 buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position,
397 count);
398 }
399 catch (IOException e)
400 {
401 }
402 }
403
404 if (buffer == null)
405 {
406 buffer = ByteBuffer.allocate ((int) count);
407 src.read (buffer);
408 buffer.flip();
409 }
410
411 return write (buffer, position);
412 }
413
414 public long transferFrom (ReadableByteChannel src, long position,
415 long count)
416 throws IOException
417 {
418 if (position < 0
419 || count < 0)
420 throw new IllegalArgumentException ("position: " + position
421 + ", count: " + count);
422
423 if (!isOpen ())
424 throw new ClosedChannelException ();
425
426 if ((mode & WRITE) == 0)
427 throw new NonWritableChannelException ();
428
429 final int pageSize = 65536;
430 long total = 0;
431
432 while (count > 0)
433 {
434 int transferred = smallTransferFrom (src, position,
435 (int)Math.min (count, pageSize));
436 if (transferred < 0)
437 break;
438 total += transferred;
439 position += transferred;
440 count -= transferred;
441 }
442
443 return total;
444 }
445
446 // Shared sanity checks between lock and tryLock methods.
447 private void lockCheck(long position, long size, boolean shared)
448 throws IOException
449 {
450 if (position < 0
451 || size < 0)
452 throw new IllegalArgumentException ("position: " + position
453 + ", size: " + size);
454
455 if (!isOpen ())
456 throw new ClosedChannelException();
457
458 if (shared && ((mode & READ) == 0))
459 throw new NonReadableChannelException();
460
461 if (!shared && ((mode & WRITE) == 0))
462 throw new NonWritableChannelException();
463 }
464
465 public FileLock tryLock (long position, long size, boolean shared)
466 throws IOException
467 {
468 lockCheck(position, size, shared);
469
470 boolean completed = false;
471 try
472 {
473 begin();
474 boolean lockable = lock(position, size, shared, false);
475 completed = true;
476 return (lockable
477 ? new FileLockImpl(this, position, size, shared)
478 : null);
479 }
480 finally
481 {
482 end(completed);
483 }
484 }
485
486 /** Try to acquire a lock at the given position and size.
487 * On success return true.
488 * If wait as specified, block until we can get it.
489 * Otherwise return false.
490 */
491 private native boolean lock(long position, long size,
492 boolean shared, boolean wait) throws IOException;
493
494 public FileLock lock (long position, long size, boolean shared)
495 throws IOException
496 {
497 lockCheck(position, size, shared);
498
499 boolean completed = false;
500 try
501 {
502 boolean lockable = lock(position, size, shared, true);
503 completed = true;
504 return (lockable
505 ? new FileLockImpl(this, position, size, shared)
506 : null);
507 }
508 finally
509 {
510 end(completed);
511 }
512 }
513
514 public long position ()
515 throws IOException
516 {
517 if (!isOpen ())
518 throw new ClosedChannelException ();
519
520 return implPosition ();
521 }
522
523 public FileChannel position (long newPosition)
524 throws IOException
525 {
526 if (newPosition < 0)
527 throw new IllegalArgumentException ("newPostition: " + newPosition);
528
529 if (!isOpen ())
530 throw new ClosedChannelException ();
531
532 // FIXME note semantics if seeking beyond eof.
533 // We should seek lazily - only on a write.
534 seek (newPosition);
535 return this;
536 }
537
538 public FileChannel truncate (long size)
539 throws IOException
540 {
541 if (size < 0)
542 throw new IllegalArgumentException ("size: " + size);
543
544 if (!isOpen ())
545 throw new ClosedChannelException ();
546
547 if ((mode & WRITE) == 0)
548 throw new NonWritableChannelException ();
549
550 if (size < size ())
551 implTruncate (size);
552
553 return this;
554 }
555
556 public String toString()
557 {
558 return (this.getClass()
559 + "[fd=" + fd
560 + ",mode=" + mode + ","
561 + description + "]");
562 }
563 }