001 /* VMChannel.java -- Native interface suppling channel operations.
002 Copyright (C) 2006 Free Software Foundation, Inc.
003
004 The original of this file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037 package gnu.java.nio;
038
039 import gnu.classpath.Configuration;
040
041 import java.io.IOException;
042 import java.net.Inet4Address;
043 import java.net.Inet6Address;
044 import java.net.InetAddress;
045 import java.net.InetSocketAddress;
046 import java.net.SocketAddress;
047 import java.net.SocketException;
048 import java.nio.ByteBuffer;
049 import java.nio.MappedByteBuffer;
050
051 import org.jikesrvm.VM;
052 import org.jikesrvm.mm.mminterface.MemoryManager;
053 import org.jikesrvm.runtime.FileSystem;
054 import org.vmmagic.pragma.NonMovingAllocation;
055
056
057 /**
058 * Native interface to support configuring of channel to run in a non-blocking
059 * manner and support scatter/gather io operations.
060 *
061 * JikesRVM-specific implementation by Robin Garner and Filip Pizlo.
062 */
063 public final class VMChannel
064 {
065 /**
066 * Our reference implementation uses an integer to store the native
067 * file descriptor.
068 */
069 private final State nfd;
070
071 private Kind kind;
072
073 public VMChannel()
074 {
075 // XXX consider adding security check here, so only Classpath
076 // code may create instances.
077 this.nfd = new State();
078 kind = Kind.OTHER;
079 }
080
081 /**
082 * This constructor is used by the POSIX reference implementation;
083 * other virtual machines need not support it.
084 *
085 * <strong>Important:</strong> do not call this in library code that is
086 * not specific to Classpath's reference implementation.
087 *
088 * @param native_fd The native file descriptor integer.
089 * @throws IOException
090 */
091 VMChannel(final int native_fd) throws IOException
092 {
093 this();
094 this.nfd.setNativeFD(native_fd);
095 }
096
097 public State getState()
098 {
099 return nfd;
100 }
101
102 /**
103 * Don't do fast I/O for the standard file descriptors - these are used
104 * for throwing exceptions, so may not be able to allocate.
105 *
106 * Cache the max of the IDs to avoid gratuitous native calls.
107 */
108 private static int MAX_STANDARD_FD;
109
110 private static int max(int[] values) {
111 int result = values[0];
112 for (int i : values)
113 if (i > result) result = i;
114 return result;
115 }
116
117 static
118 {
119 // load the shared library needed for native methods.
120 if (Configuration.INIT_LOAD_LIBRARY)
121 {
122 System.loadLibrary ("javanio");
123 }
124 initIDs();
125 MAX_STANDARD_FD = max(new int[] {stdin_fd(),stdin_fd(),stderr_fd()});
126 }
127
128 public static VMChannel getStdin() throws IOException
129 {
130 return new VMChannel(stdin_fd());
131 }
132
133 public static VMChannel getStdout() throws IOException
134 {
135 return new VMChannel(stdout_fd());
136 }
137
138 public static VMChannel getStderr() throws IOException
139 {
140 return new VMChannel(stderr_fd());
141 }
142
143 private static native int stdin_fd();
144 private static native int stdout_fd();
145 private static native int stderr_fd();
146
147
148 /**
149 * Set the file descriptor to have the required blocking
150 * setting.
151 *
152 * @param blocking The blocking flag to set.
153 */
154 public void setBlocking(boolean blocking) throws IOException
155 {
156 setBlocking(nfd.getNativeFD(), blocking);
157 }
158
159 private static native void setBlocking(int fd, boolean blocking)
160 throws IOException;
161
162 public int available() throws IOException
163 {
164 return available(nfd.getNativeFD());
165 }
166
167 private static native int available(int native_fd) throws IOException;
168
169 /**
170 * A thread-local store of non-moving buffers. Used to perform IO to
171 * in cases where the actual user buffer is in a moving space.
172 */
173 private static class LocalByteArray extends ThreadLocal<byte[]> {
174 private static final int INITIAL_BUFFER_SIZE = 8192;
175 @Override
176 @NonMovingAllocation
177 protected byte[] initialValue() {
178 return new byte[INITIAL_BUFFER_SIZE];
179 }
180
181 /**
182 * Get a buffer, ensuring it is at least 'len' in size
183 * @param len Minimum length of the buffer
184 * @return a new or recycled buffer
185 */
186 @NonMovingAllocation
187 public byte[] get(int len) {
188 byte[] buf = get();
189 if (buf.length < len) {
190 /* Allocate a new buffer by successive doubling of capacity */
191 int newCapacity = buf.length << 1;
192 while (newCapacity < len)
193 newCapacity <<= 1;
194 buf = new byte[newCapacity];
195 set(buf);
196 }
197 return buf;
198 }
199 }
200
201 /**
202 * Thread-local buffer for VM-side buffering of write calls
203 */
204 private static final LocalByteArray localByteArray = new LocalByteArray() ;
205
206 /**
207 * Read the specified byte buffer.
208 *
209 * @param dst
210 * @return the number of bytes actually read
211 * @throws IOException
212 */
213 public int read(ByteBuffer dst) throws IOException {
214 return read(dst,dst.position(),dst.limit()-dst.position());
215 }
216
217 /**
218 * Read a byte buffer, given a starting position and length.
219 * Looks at the type of buffer and decides which is the fastest way
220 * to perform the write. If the buffer is backed by a byte array, use
221 * the internal method, otherwise push it out to classpath's native function
222 * (the slow way).
223 *
224 * @param dst
225 * @param pos
226 * @param len
227 * @return the number of bytes actually read
228 * @throws IOException
229 */
230 private int read(ByteBuffer dst, int pos, int len) throws IOException {
231 int bytes;
232 if (len == 1) {
233 int b = FileSystem.readByte(nfd.getNativeFD());
234 if (b >= 0) {
235 dst.put((byte)(b & 0xFF));
236 dst.position(pos+1);
237 return 1;
238 } else
239 bytes = b;
240 } else if (dst.hasArray()) {
241 bytes = read(dst.array(),pos,len);
242 } else {
243 return read(nfd.getNativeFD(), dst);
244 }
245 if (bytes > 0)
246 dst.position(pos+bytes);
247 return bytes;
248 }
249
250 /**
251 * Reads a byte array directly. Performs optimal buffering.
252 *
253 * If the target buffer is pinned, use it directly. Otherwise
254 * allocate one of the thread-local buffers, perform the IO to
255 * that, and copy the result to the target array.
256 *
257 * @param dst Byte array to read to
258 * @return Number of bytes read.
259 * @throws IOException If an error occurs or dst is not a direct buffers.
260 */
261 private int read(byte[] dst, int pos, int len) throws IOException {
262 if (MemoryManager.willNeverMove(dst)) {
263 return read(nfd.getNativeFD(),dst,pos,len);
264 } else {
265 byte[] buffer;
266 // Rebuffer the IO in a thread-local byte array
267 buffer = localByteArray.get(len);
268
269 /* perform the read */
270 int bytes = read(nfd.getNativeFD(),buffer,0,len);
271 if (bytes > 0)
272 System.arraycopy(buffer,0,dst,pos,bytes);
273 return bytes;
274 }
275 }
276
277 /**
278 * Use JikesRVM's internal read function - the fast way.
279 *
280 * @param fd File descriptor
281 * @param dst Destination buffer
282 * @param position Starting offset in the buffer
283 * @param len Number of bytes to read
284 * @return Number of bytes read, or -1 for end of file.
285 * @throws IOException
286 */
287 private static int read(int fd, byte[] dst, int position, int len) throws IOException {
288 if (VM.VerifyAssertions) VM._assert(MemoryManager.willNeverMove(dst));
289 int bytes = FileSystem.readBytes(fd,dst,position,len);
290 if (bytes < 0) {
291 throw new IOException("Error code "+Integer.toString(bytes));
292 }
293 if (bytes == 0) {
294 bytes = -1;
295 }
296 return bytes;
297 }
298
299 /**
300 * Classpath's native read method. Slow, due to the amount of JNI processing.
301 *
302 * @param fd
303 * @param dst
304 * @return the number of bytes actually read
305 * @throws IOException
306 */
307 private static native int read(int fd, ByteBuffer dst) throws IOException;
308
309 /**
310 * Read a single byte.
311 *
312 * @return The byte read, or -1 on end of file.
313 * @throws IOException
314 */
315 public int read() throws IOException
316 {
317 //return read(nfd.getNativeFD());
318 int result = FileSystem.readByte(nfd.getNativeFD());
319 if (result < -1) {
320 throw new IOException("Error code "+Integer.toString(result));
321 }
322 return result;
323 }
324
325 private static native int read(int fd) throws IOException;
326
327 /**
328 * Reads into byte buffers directly using the supplied file descriptor.
329 * Assumes that the buffer list contains DirectBuffers. Will perform a
330 * scattering read.
331 *
332 * @param dsts An array direct byte buffers.
333 * @param offset Index of the first buffer to read to.
334 * @param length The number of buffers to read to.
335 * @return Number of bytes read.
336 * @throws IOException If an error occurs or the dsts are not direct buffers.
337 */
338 public long readScattering(ByteBuffer[] dsts, int offset, int length)
339 throws IOException
340 {
341 if (offset + length > dsts.length)
342 throw new IndexOutOfBoundsException("offset + length > dsts.length");
343
344 return readScattering(nfd.getNativeFD(), dsts, offset, length);
345 }
346
347 private static native long readScattering(int fd, ByteBuffer[] dsts,
348 int offset, int length)
349 throws IOException;
350
351 /**
352 * Receive a datagram on this channel, returning the host address
353 * that sent the datagram.
354 *
355 * @param dst Where to store the datagram.
356 * @return The host address that sent the datagram.
357 * @throws IOException
358 */
359 public SocketAddress receive(ByteBuffer dst) throws IOException
360 {
361 if (kind != Kind.SOCK_DGRAM)
362 throw new SocketException("not a datagram socket");
363 ByteBuffer hostPort = ByteBuffer.allocateDirect(18);
364 int hostlen = receive(nfd.getNativeFD(), dst, hostPort);
365 if (hostlen == 0)
366 return null;
367 if (hostlen == 4) // IPv4
368 {
369 byte[] addr = new byte[4];
370 hostPort.get(addr);
371 int port = hostPort.getShort() & 0xFFFF;
372 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
373 }
374 if (hostlen == 16) // IPv6
375 {
376 byte[] addr = new byte[16];
377 hostPort.get(addr);
378 int port = hostPort.getShort() & 0xFFFF;
379 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
380 }
381
382 throw new SocketException("host address received with invalid length: "
383 + hostlen);
384 }
385
386 private static native int receive (int fd, ByteBuffer dst, ByteBuffer address)
387 throws IOException;
388
389 /**
390 * Writes from a byte array using the supplied file descriptor.
391 *
392 * @param src The source buffer.
393 * @return Number of bytes written.
394 * @throws IOException
395 */
396 public int write(byte[] src, int pos, int len) throws IOException {
397 if (MemoryManager.willNeverMove(src)) {
398 return write(nfd.getNativeFD(), src, pos, len);
399 } else {
400 byte[] buffer;
401 // Rebuffer the IO in a thread-local DirectBuffer
402 buffer = localByteArray.get(len);
403 if (VM.VerifyAssertions) VM._assert(MemoryManager.willNeverMove(buffer));
404 System.arraycopy(src, pos, buffer,0,len);
405 return write(nfd.getNativeFD(),buffer,0,len);
406 }
407 }
408
409 public int write(ByteBuffer src, int pos, int len) throws IOException {
410 int bytes;
411 if (len == 1) {
412 int ok = FileSystem.writeByte(nfd.getNativeFD(),src.get(pos));
413 if(ok == 0){
414 bytes = 1;
415 }else{
416 throw new IOException("Error code " + Integer.toString(ok));
417 }
418 } else if (src.hasArray()) {
419 bytes = write(src.array(),pos,len);
420 } else {
421 // Use classpath version, which does buffer housekeeping
422 return write(nfd.getNativeFD(), src);
423 }
424 if (bytes > 0)
425 src.position(src.position()+bytes);
426 return bytes;
427 }
428
429 public int write(ByteBuffer src) throws IOException {
430 if (nfd.getNativeFD() > MAX_STANDARD_FD)
431 return write(src,src.position(),src.limit()-src.position());
432 else
433 return write(nfd.getNativeFD(),src);
434 }
435
436 /**
437 * Use JikesRVM's internal read function - the fast way.
438 *
439 * @param fd File descriptor
440 * @param src SOurce buffer
441 * @param pos Starting offset in the buffer
442 * @param len Number of bytes to write
443 * @return Number of bytes written.
444 * @throws IOException
445 */
446 private static int write(int fd, byte[] src, int pos, int len) throws IOException {
447 int bytes = FileSystem.writeBytes(fd,src,pos,len);
448 if (bytes < 0)
449 throw new IOException("Error code "+Integer.toString(bytes));
450 return bytes;
451 }
452
453 /**
454 * Classpath's native write method. Slow, due to the amount of JNI processing.
455 *
456 * @param fd
457 * @param src
458 * @return Number of bytes written
459 * @throws IOException
460 */
461 private static native int write(int fd, ByteBuffer src) throws IOException;
462
463 /**
464 * Writes from byte buffers directly using the supplied file descriptor.
465 * Assumes the that buffer list contains DirectBuffers. Will perform
466 * as gathering write.
467 *
468 * @param srcs
469 * @param offset
470 * @param length
471 * @return Number of bytes written.
472 * @throws IOException
473 */
474 public long writeGathering(ByteBuffer[] srcs, int offset, int length)
475 throws IOException
476 {
477 if (offset + length > srcs.length)
478 throw new IndexOutOfBoundsException("offset + length > srcs.length");
479
480 // A gathering write is limited to 16 buffers; when writing, ensure
481 // that we have at least one buffer with something in it in the 16
482 // buffer window starting at offset.
483 while (!srcs[offset].hasRemaining() && offset < srcs.length)
484 offset++;
485
486 // There are no buffers with anything to write.
487 if (offset == srcs.length)
488 return 0;
489
490 // If we advanced `offset' so far that we don't have `length'
491 // buffers left, reset length to only the remaining buffers.
492 if (length > srcs.length - offset)
493 length = srcs.length - offset;
494
495 return writeGathering(nfd.getNativeFD(), srcs, offset, length);
496 }
497
498 private native long writeGathering(int fd, ByteBuffer[] srcs,
499 int offset, int length)
500 throws IOException;
501
502 /**
503 * Send a datagram to the given address.
504 *
505 * @param src The source buffer.
506 * @param dst The destination address.
507 * @return The number of bytes written.
508 * @throws IOException
509 */
510 public int send(ByteBuffer src, InetSocketAddress dst)
511 throws IOException
512 {
513 InetAddress addr = dst.getAddress();
514 if (addr == null)
515 throw new NullPointerException();
516 if (addr instanceof Inet4Address)
517 return send(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
518 else if (addr instanceof Inet6Address)
519 return send6(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
520 else
521 throw new SocketException("unrecognized inet address type");
522 }
523
524 // Send to an IPv4 address.
525 private static native int send(int fd, ByteBuffer src, byte[] addr, int port)
526 throws IOException;
527
528 // Send to an IPv6 address.
529 private static native int send6(int fd, ByteBuffer src, byte[] addr, int port)
530 throws IOException;
531
532 /**
533 * Write a single byte.
534 *
535 * @param b The byte to write.
536 * @throws IOException
537 */
538 public void write(int b) throws IOException
539 {
540 //write(nfd.getNativeFD(), b);
541 int result = FileSystem.writeByte(nfd.getNativeFD(), b);
542 if (result < 0) {
543 throw new IOException("Error code "+Integer.toString(result));
544 }
545 }
546
547 private static native void write(int fd, int b) throws IOException;
548
549 private native static void initIDs();
550
551 // Network (socket) specific methods.
552
553 /**
554 * Create a new socket. This method will initialize the native file
555 * descriptor state of this instance.
556 *
557 * @param stream Whether or not to create a streaming socket, or a datagram
558 * socket.
559 * @throws IOException If creating a new socket fails, or if this
560 * channel already has its native descriptor initialized.
561 */
562 public void initSocket(boolean stream) throws IOException
563 {
564 if (nfd.isValid())
565 throw new IOException("native FD already initialized");
566 if (stream)
567 kind = Kind.SOCK_STREAM;
568 else
569 kind = Kind.SOCK_DGRAM;
570 nfd.setNativeFD(socket(stream));
571 }
572
573 /**
574 * Create a new socket, returning the native file descriptor.
575 *
576 * @param stream Set to true for streaming sockets; false for datagrams.
577 * @return The native file descriptor.
578 * @throws IOException If creating the socket fails.
579 */
580 private static native int socket(boolean stream) throws IOException;
581
582 /**
583 * Connect the underlying socket file descriptor to the remote host.
584 *
585 * @param saddr The address to connect to.
586 * @param timeout The connect timeout to use for blocking connects.
587 * @return True if the connection succeeded; false if the file descriptor
588 * is in non-blocking mode and the connection did not immediately
589 * succeed.
590 * @throws IOException If an error occurs while connecting.
591 */
592 public boolean connect(InetSocketAddress saddr, int timeout)
593 throws SocketException
594 {
595 int fd;
596
597 InetAddress addr = saddr.getAddress();
598
599 // Translates an IOException into a SocketException to conform
600 // to the throws clause.
601 try
602 {
603 fd = nfd.getNativeFD();
604 }
605 catch (IOException ioe)
606 {
607 throw new SocketException(ioe.getMessage());
608 }
609
610 if (addr instanceof Inet4Address)
611 return connect(fd, addr.getAddress(), saddr.getPort(),
612 timeout);
613 if (addr instanceof Inet6Address)
614 return connect6(fd, addr.getAddress(), saddr.getPort(),
615 timeout);
616 throw new SocketException("unsupported internet address");
617 }
618
619 private static native boolean connect(int fd, byte[] addr, int port, int timeout)
620 throws SocketException;
621
622 private static native boolean connect6(int fd, byte[] addr, int port, int timeout)
623 throws SocketException;
624
625 /**
626 * Disconnect this channel, if it is a datagram socket. Disconnecting
627 * a datagram channel will disassociate it from any address, so the
628 * socket will remain open, but can send and receive datagrams from
629 * any address.
630 *
631 * @throws IOException If disconnecting this channel fails, or if this
632 * channel is not a datagram channel.
633 */
634 public void disconnect() throws IOException
635 {
636 if (kind != Kind.SOCK_DGRAM)
637 throw new IOException("can only disconnect datagram channels");
638 disconnect(nfd.getNativeFD());
639 }
640
641 private static native void disconnect(int fd) throws IOException;
642
643 public InetSocketAddress getLocalAddress() throws IOException
644 {
645 if (!nfd.isValid())
646 return null;
647 ByteBuffer name = ByteBuffer.allocateDirect(18);
648 int namelen = getsockname(nfd.getNativeFD(), name);
649 if (namelen == 0) // not bound
650 return null; // XXX return some wildcard?
651 if (namelen == 4)
652 {
653 byte[] addr = new byte[4];
654 name.get(addr);
655 int port = name.getShort() & 0xFFFF;
656 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
657 }
658 if (namelen == 16)
659 {
660 byte[] addr = new byte[16];
661 name.get(addr);
662 int port = name.getShort() & 0xFFFF;
663 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
664 }
665 throw new SocketException("invalid address length");
666 }
667
668 private static native int getsockname(int fd, ByteBuffer name)
669 throws IOException;
670
671 /**
672 * Returns the socket address of the remote peer this channel is connected
673 * to, or null if this channel is not yet connected.
674 *
675 * @return The peer address.
676 * @throws IOException
677 */
678 public InetSocketAddress getPeerAddress() throws IOException
679 {
680 if (!nfd.isValid())
681 return null;
682 ByteBuffer name = ByteBuffer.allocateDirect(18);
683 int namelen = getpeername (nfd.getNativeFD(), name);
684 if (namelen == 0) // not connected yet
685 return null;
686 if (namelen == 4) // IPv4
687 {
688 byte[] addr = new byte[4];
689 name.get(addr);
690 int port = name.getShort() & 0xFFFF;
691 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
692 }
693 else if (namelen == 16) // IPv6
694 {
695 byte[] addr = new byte[16];
696 name.get(addr);
697 int port = name.getShort() & 0xFFFF;
698 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
699 }
700 throw new SocketException("invalid address length");
701 }
702
703 /*
704 * The format here is the peer address, followed by the port number.
705 * The returned value is the length of the peer address; thus, there
706 * will be LEN + 2 valid bytes put into NAME.
707 */
708 private static native int getpeername(int fd, ByteBuffer name)
709 throws IOException;
710
711 /**
712 * Accept an incoming connection, returning a new VMChannel, or null
713 * if the channel is nonblocking and no connection is pending.
714 *
715 * @return The accepted connection, or null.
716 * @throws IOException If an IO error occurs.
717 */
718 public VMChannel accept() throws IOException
719 {
720 int new_fd = accept(nfd.getNativeFD());
721 if (new_fd == -1) // non-blocking accept had no pending connection
722 return null;
723 return new VMChannel(new_fd);
724 }
725
726 private static native int accept(int native_fd) throws IOException;
727
728 // File-specific methods.
729
730 /**
731 * Open a file at PATH, initializing the native state to operate on
732 * that open file.
733 *
734 * @param path The absolute file path.
735 * @throws IOException If the file cannot be opened, or if this
736 * channel was previously initialized.
737 */
738 public void openFile(String path, int mode) throws IOException
739 {
740 if (nfd.isValid() || nfd.isClosed())
741 throw new IOException("can't reinitialize this channel");
742 int fd = open(path, mode);
743 nfd.setNativeFD(fd);
744 kind = Kind.FILE;
745 }
746
747 private static native int open(String path, int mode) throws IOException;
748
749 public long position() throws IOException
750 {
751 if (kind != Kind.FILE)
752 throw new IOException("not a file");
753 return position(nfd.getNativeFD());
754 }
755
756 private static native long position(int fd) throws IOException;
757
758 public void seek(long pos) throws IOException
759 {
760 if (kind != Kind.FILE)
761 throw new IOException("not a file");
762 seek(nfd.getNativeFD(), pos);
763 }
764
765 private static native void seek(int fd, long pos) throws IOException;
766
767 public void truncate(long length) throws IOException
768 {
769 if (kind != Kind.FILE)
770 throw new IOException("not a file");
771 truncate(nfd.getNativeFD(), length);
772 }
773
774 private static native void truncate(int fd, long len) throws IOException;
775
776 public boolean lock(long pos, long len, boolean shared, boolean wait)
777 throws IOException
778 {
779 if (kind != Kind.FILE)
780 throw new IOException("not a file");
781 return lock(nfd.getNativeFD(), pos, len, shared, wait);
782 }
783
784 private static native boolean lock(int fd, long pos, long len,
785 boolean shared, boolean wait)
786 throws IOException;
787
788 public void unlock(long pos, long len) throws IOException
789 {
790 if (kind != Kind.FILE)
791 throw new IOException("not a file");
792 unlock(nfd.getNativeFD(), pos, len);
793 }
794
795 private static native void unlock(int fd, long pos, long len) throws IOException;
796
797 public long size() throws IOException
798 {
799 if (kind != Kind.FILE)
800 throw new IOException("not a file");
801 return size(nfd.getNativeFD());
802 }
803
804 private static native long size(int fd) throws IOException;
805
806 public MappedByteBuffer map(char mode, long position, int size)
807 throws IOException
808 {
809 if (kind != Kind.FILE)
810 throw new IOException("not a file");
811 return map(nfd.getNativeFD(), mode, position, size);
812 }
813
814 private static native MappedByteBuffer map(int fd, char mode,
815 long position, int size)
816 throws IOException;
817
818 public boolean flush(boolean metadata) throws IOException
819 {
820 if (kind != Kind.FILE)
821 throw new IOException("not a file");
822 return flush(nfd.getNativeFD(), metadata);
823 }
824
825 private static native boolean flush(int fd, boolean metadata) throws IOException;
826
827 // Close.
828
829 /**
830 * Close this socket. The socket is also automatically closed when this
831 * object is finalized.
832 *
833 * @throws IOException If closing the socket fails, or if this object has
834 * no open socket.
835 */
836 public void close() throws IOException
837 {
838 nfd.close();
839 }
840
841 static native void close(int native_fd) throws IOException;
842
843 /**
844 * <p>Provides a simple mean for the JNI code to find out whether the
845 * current thread was interrupted by a call to Thread.interrupt().</p>
846 *
847 * @return true if the current thread was interrupted, false otherwise
848 */
849 static boolean isThreadInterrupted()
850 {
851 return Thread.currentThread().isInterrupted();
852 }
853
854 // Inner classes.
855
856 /**
857 * A wrapper for a native file descriptor integer. This tracks the state
858 * of an open file descriptor, and ensures that
859 *
860 * This class need not be fully supported by virtual machines; if a
861 * virtual machine does not use integer file descriptors, or does and
862 * wishes to hide that, then the methods of this class may be stubbed out.
863 *
864 * System-specific classes that depend on access to native file descriptor
865 * integers SHOULD declare this fact.
866 */
867 public final class State
868 {
869 private int native_fd;
870 private boolean valid;
871 private boolean closed;
872
873 State()
874 {
875 native_fd = -1;
876 valid = false;
877 closed = false;
878 }
879
880 public boolean isValid()
881 {
882 return valid;
883 }
884
885 public boolean isClosed()
886 {
887 return closed;
888 }
889
890 public int getNativeFD() throws IOException
891 {
892 if (!valid)
893 throw new IOException("invalid file descriptor");
894 return native_fd;
895 }
896
897 void setNativeFD(final int native_fd) throws IOException
898 {
899 if (valid)
900 throw new IOException("file descriptor already initialized");
901 this.native_fd = native_fd;
902 valid = true;
903 }
904
905 public void close() throws IOException
906 {
907 if (!valid)
908 throw new IOException("invalid file descriptor");
909 try
910 {
911 VMChannel.close(native_fd);
912 }
913 finally
914 {
915 valid = false;
916 closed = true;
917 }
918 }
919
920 @Override
921 public String toString()
922 {
923 if (closed)
924 return "<<closed>>";
925 if (!valid)
926 return "<<invalid>>";
927 return String.valueOf(native_fd);
928 }
929
930 @Override
931 protected void finalize() throws Throwable
932 {
933 try
934 {
935 if (valid)
936 close();
937 }
938 finally
939 {
940 super.finalize();
941 }
942 }
943 }
944
945 /**
946 * An enumeration of possible kinds of channel.
947 */
948 static class Kind // XXX enum
949 {
950 /** A streaming (TCP) socket. */
951 static final Kind SOCK_STREAM = new Kind();
952
953 /** A datagram (UDP) socket. */
954 static final Kind SOCK_DGRAM = new Kind();
955
956 /** A file. */
957 static final Kind FILE = new Kind();
958
959 /** Something else; not a socket or file. */
960 static final Kind OTHER = new Kind();
961
962 private Kind() { }
963 }
964 }