1 /* FileChannelImpl.java --
2    Copyright (C) 2002, 2004, 2005, 2006  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;
40 
41 import gnu.classpath.Configuration;
42 import gnu.java.nio.FileLockImpl;
43 import gnu.java.nio.VMChannel;
44 
45 import java.io.File;
46 import java.io.FileNotFoundException;
47 import java.io.IOException;
48 import java.nio.ByteBuffer;
49 import java.nio.MappedByteBuffer;
50 import java.nio.channels.ClosedChannelException;
51 import java.nio.channels.FileChannel;
52 import java.nio.channels.FileLock;
53 import java.nio.channels.NonReadableChannelException;
54 import java.nio.channels.NonWritableChannelException;
55 import java.nio.channels.ReadableByteChannel;
56 import java.nio.channels.WritableByteChannel;
57 
58 /**
59  * This file is not user visible !
60  * But alas, Java does not have a concept of friendly packages
61  * so this class is public.
62  * Instances of this class are created by invoking getChannel
63  * Upon a Input/Output/RandomAccessFile object.
64  */
65 public final class FileChannelImpl extends FileChannel
66 {
67   // These are mode values for open().
68   public static final int READ   = 1;
69   public static final int WRITE  = 2;
70   public static final int APPEND = 4;
71 
72   // EXCL is used only when making a temp file.
73   public static final int EXCL   = 8;
74   public static final int SYNC   = 16;
75   public static final int DSYNC  = 32;
76 
77   public static final FileChannelImpl in;
78   public static final FileChannelImpl out;
79   public static final FileChannelImpl err;
80 
81   //private static native void init();
82 
83   static
84   {
85     if (Configuration.INIT_LOAD_LIBRARY)
86       {
87         System.loadLibrary("javanio");
88       }
89 
90     //init();
91 
92     FileChannelImpl ch = null;
93     try
94       {
95         ch = new FileChannelImpl(VMChannel.getStdin(), READ);
96       }
97     catch (IOException ioe)
98       {
99         throw new Error(ioe);
100       }
101     in = ch;
102 
103     ch = null;
104     try
105       {
106         ch = new FileChannelImpl(VMChannel.getStdout(), WRITE);
107       }
108     catch (IOException ioe)
109       {
110         throw new Error(ioe);
111       }
112     out = ch;
113 
114     ch = null;
115     try
116       {
117         ch = new FileChannelImpl(VMChannel.getStderr(), WRITE);
118       }
119     catch (IOException ioe)
120       {
121         throw new Error(ioe);
122       }
123     err = ch;
124   }
125 
126   /**
127    * This is the actual native file descriptor value
128    */
129   private VMChannel ch;
130 
131   private int mode;
132 
133   final String description;
134 
135   /* Open a file.  MODE is a combination of the above mode flags. */
136   /* This is a static factory method, so that VM implementors can decide
137    * substitute subclasses of FileChannelImpl. */
create(File file, int mode)138   public static FileChannelImpl create(File file, int mode)
139     throws IOException
140   {
141     return new FileChannelImpl(file, mode);
142   }
143 
FileChannelImpl(File file, int mode)144   private FileChannelImpl(File file, int mode)
145     throws IOException
146   {
147     String path = file.getPath();
148     description = path;
149     this.mode = mode;
150     this.ch = new VMChannel();
151     ch.openFile(path, mode);
152 
153     // First open the file and then check if it is a a directory
154     // to avoid race condition.
155     if (file.isDirectory())
156       {
157         try
158           {
159             close();
160           }
161         catch (IOException e)
162           {
163             /* ignore it */
164           }
165 
166         throw new FileNotFoundException(description + " is a directory");
167       }
168   }
169 
170   /**
171    * Constructor for default channels in, out and err.
172    *
173    * Used by init() (native code).
174    *
175    * @param fd the file descriptor (0, 1, 2 for stdin, stdout, stderr).
176    *
177    * @param mode READ or WRITE
178    */
FileChannelImpl(VMChannel ch, int mode)179   FileChannelImpl (VMChannel ch, int mode)
180   {
181     this.mode = mode;
182     this.description = "descriptor(" + ch.getState() + ")";
183     this.ch = ch;
184   }
185 
available()186   public int available() throws IOException
187   {
188     return ch.available();
189   }
190 
implPosition()191   private long implPosition() throws IOException
192   {
193     return ch.position();
194   }
195 
seek(long newPosition)196   private void seek(long newPosition) throws IOException
197   {
198     ch.seek(newPosition);
199   }
200 
implTruncate(long size)201   private void implTruncate(long size) throws IOException
202   {
203     ch.truncate(size);
204   }
205 
unlock(long pos, long len)206   public void unlock(long pos, long len) throws IOException
207   {
208     ch.unlock(pos, len);
209   }
210 
size()211   public long size () throws IOException
212   {
213     return ch.size();
214   }
215 
implCloseChannel()216   protected void implCloseChannel() throws IOException
217   {
218     ch.close();
219   }
220 
221   /**
222    * Makes sure the Channel is properly closed.
223    */
finalize()224   protected void finalize() throws IOException
225   {
226     if (ch.getState().isValid())
227       close();
228   }
229 
read(ByteBuffer dst)230   public int read (ByteBuffer dst) throws IOException
231   {
232     return ch.read(dst);
233   }
234 
read(ByteBuffer dst, long position)235   public int read (ByteBuffer dst, long position)
236     throws IOException
237   {
238     if (position < 0)
239       throw new IllegalArgumentException ("position: " + position);
240     long oldPosition = implPosition ();
241     position (position);
242     int result = read(dst);
243     position (oldPosition);
244 
245     return result;
246   }
247 
read()248   public int read() throws IOException
249   {
250     return ch.read();
251   }
252 
read(ByteBuffer[] dsts, int offset, int length)253   public long read (ByteBuffer[] dsts, int offset, int length)
254     throws IOException
255   {
256     return ch.readScattering(dsts, offset, length);
257   }
258 
write(ByteBuffer src)259   public int write (ByteBuffer src) throws IOException
260   {
261     return ch.write(src);
262   }
263 
write(ByteBuffer src, long position)264   public int write (ByteBuffer src, long position)
265     throws IOException
266   {
267     if (position < 0)
268       throw new IllegalArgumentException ("position: " + position);
269 
270     if (!isOpen ())
271       throw new ClosedChannelException ();
272 
273     if ((mode & WRITE) == 0)
274        throw new NonWritableChannelException ();
275 
276     int result;
277     long oldPosition;
278 
279     oldPosition = implPosition ();
280     seek (position);
281     result = write(src);
282     seek (oldPosition);
283 
284     return result;
285   }
286 
write(int b)287   public void write (int b) throws IOException
288   {
289     ch.write(b);
290   }
291 
write(ByteBuffer[] srcs, int offset, int length)292   public long write(ByteBuffer[] srcs, int offset, int length)
293     throws IOException
294   {
295     return ch.writeGathering(srcs, offset, length);
296   }
297 
map(FileChannel.MapMode mode, long position, long size)298   public MappedByteBuffer map (FileChannel.MapMode mode,
299                                long position, long size)
300     throws IOException
301   {
302     char nmode = 0;
303     if (mode == MapMode.READ_ONLY)
304       {
305         nmode = 'r';
306         if ((this.mode & READ) == 0)
307           throw new NonReadableChannelException();
308       }
309     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
310       {
311         nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
312         if ((this.mode & WRITE) != WRITE)
313           throw new NonWritableChannelException();
314         if ((this.mode & READ) != READ)
315           throw new NonReadableChannelException();
316       }
317     else
318       throw new IllegalArgumentException ("mode: " + mode);
319 
320     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
321       throw new IllegalArgumentException ("position: " + position
322                                           + ", size: " + size);
323     return ch.map(nmode, position, (int) size);
324   }
325 
326   /**
327    * msync with the disk
328    */
force(boolean metaData)329   public void force (boolean metaData) throws IOException
330   {
331     if (!isOpen ())
332       throw new ClosedChannelException ();
333 
334     ch.flush(metaData);
335   }
336 
337   // like transferTo, but with a count of less than 2Gbytes
smallTransferTo(long position, int count, WritableByteChannel target)338   private int smallTransferTo (long position, int count,
339                                WritableByteChannel target)
340     throws IOException
341   {
342     ByteBuffer buffer;
343     try
344       {
345         // Try to use a mapped buffer if we can.  If this fails for
346         // any reason we'll fall back to using a ByteBuffer.
347         buffer = map (MapMode.READ_ONLY, position, count);
348       }
349     catch (IOException e)
350       {
351         buffer = ByteBuffer.allocate (count);
352         read (buffer, position);
353         buffer.flip();
354       }
355 
356     return target.write (buffer);
357   }
358 
transferTo(long position, long count, WritableByteChannel target)359   public long transferTo (long position, long count,
360                           WritableByteChannel target)
361     throws IOException
362   {
363     if (position < 0
364         || count < 0)
365       throw new IllegalArgumentException ("position: " + position
366                                           + ", count: " + count);
367 
368     if (!isOpen ())
369       throw new ClosedChannelException ();
370 
371     if ((mode & READ) == 0)
372        throw new NonReadableChannelException ();
373 
374     final int pageSize = 65536;
375     long total = 0;
376 
377     while (count > 0)
378       {
379         int transferred
380           = smallTransferTo (position, (int)Math.min (count, pageSize),
381                              target);
382         if (transferred < 0)
383           break;
384         total += transferred;
385         position += transferred;
386         count -= transferred;
387       }
388 
389     return total;
390   }
391 
392   // like transferFrom, but with a count of less than 2Gbytes
smallTransferFrom(ReadableByteChannel src, long position, int count)393   private int smallTransferFrom (ReadableByteChannel src, long position,
394                                  int count)
395     throws IOException
396   {
397     ByteBuffer buffer = null;
398 
399     if (src instanceof FileChannel)
400       {
401         try
402           {
403             // Try to use a mapped buffer if we can.  If this fails
404             // for any reason we'll fall back to using a ByteBuffer.
405             buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position,
406                                              count);
407           }
408         catch (IOException e)
409           {
410           }
411       }
412 
413     if (buffer == null)
414       {
415         buffer = ByteBuffer.allocate (count);
416         src.read (buffer);
417         buffer.flip();
418       }
419 
420     return write (buffer, position);
421   }
422 
transferFrom(ReadableByteChannel src, long position, long count)423   public long transferFrom (ReadableByteChannel src, long position,
424                             long count)
425     throws IOException
426   {
427     if (position < 0
428         || count < 0)
429       throw new IllegalArgumentException ("position: " + position
430                                           + ", count: " + count);
431 
432     if (!isOpen ())
433       throw new ClosedChannelException ();
434 
435     if ((mode & WRITE) == 0)
436        throw new NonWritableChannelException ();
437 
438     final int pageSize = 65536;
439     long total = 0;
440 
441     while (count > 0)
442       {
443         int transferred = smallTransferFrom (src, position,
444                                              (int)Math.min (count, pageSize));
445         if (transferred < 0)
446           break;
447         total += transferred;
448         position += transferred;
449         count -= transferred;
450       }
451 
452     return total;
453   }
454 
455   // Shared sanity checks between lock and tryLock methods.
lockCheck(long position, long size, boolean shared)456   private void lockCheck(long position, long size, boolean shared)
457     throws IOException
458   {
459     if (position < 0
460         || size < 0)
461       throw new IllegalArgumentException ("position: " + position
462                                           + ", size: " + size);
463 
464     if (!isOpen ())
465       throw new ClosedChannelException();
466 
467     if (shared && ((mode & READ) == 0))
468       throw new NonReadableChannelException();
469 
470     if (!shared && ((mode & WRITE) == 0))
471       throw new NonWritableChannelException();
472   }
473 
tryLock(long position, long size, boolean shared)474   public FileLock tryLock (long position, long size, boolean shared)
475     throws IOException
476   {
477     lockCheck(position, size, shared);
478 
479     boolean completed = false;
480     try
481       {
482         begin();
483         boolean lockable = ch.lock(position, size, shared, false);
484         completed = true;
485         return (lockable
486                 ? new FileLockImpl(this, position, size, shared)
487                 : null);
488       }
489     finally
490       {
491         end(completed);
492       }
493   }
494 
lock(long position, long size, boolean shared)495   public FileLock lock (long position, long size, boolean shared)
496     throws IOException
497   {
498     lockCheck(position, size, shared);
499 
500     boolean completed = false;
501     try
502       {
503         boolean lockable = ch.lock(position, size, shared, true);
504         completed = true;
505         return (lockable
506                 ? new FileLockImpl(this, position, size, shared)
507                 : null);
508       }
509     finally
510       {
511         end(completed);
512       }
513   }
514 
position()515   public long position ()
516     throws IOException
517   {
518     if (!isOpen ())
519       throw new ClosedChannelException ();
520 
521     return implPosition ();
522   }
523 
position(long newPosition)524   public FileChannel position (long newPosition)
525     throws IOException
526   {
527     if (newPosition < 0)
528       throw new IllegalArgumentException ("newPosition: " + newPosition);
529 
530     if (!isOpen ())
531       throw new ClosedChannelException ();
532 
533     // FIXME note semantics if seeking beyond eof.
534     // We should seek lazily - only on a write.
535     seek (newPosition);
536     return this;
537   }
538 
truncate(long size)539   public FileChannel truncate (long size)
540     throws IOException
541   {
542     if (size < 0)
543       throw new IllegalArgumentException ("size: " + size);
544 
545     if (!isOpen ())
546       throw new ClosedChannelException ();
547 
548     if ((mode & WRITE) == 0)
549        throw new NonWritableChannelException ();
550 
551     if (size < size ())
552       implTruncate (size);
553 
554     return this;
555   }
556 
toString()557   public String toString()
558   {
559     return (super.toString()
560             + "[ fd: " + ch.getState()
561             + "; mode: " + Integer.toOctalString(mode)
562             + "; " + description + " ]");
563   }
564 
565   /**
566    * @return The native file descriptor.
567    * /
568   public int getNativeFD()
569   {
570     return fd;
571   }*/
572 }
573