libjava/
[official-gcc.git] / libjava / classpath / vm / reference / gnu / java / nio / VMChannel.java
bloba9d1452b72d7c82dccc5228f9ce83945e37527e5
1 /* VMChannel.java -- Native interface suppling channel operations.
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
39 package gnu.java.nio;
41 import gnu.classpath.Configuration;
43 import java.io.IOException;
44 import java.net.Inet4Address;
45 import java.net.Inet6Address;
46 import java.net.InetAddress;
47 import java.net.InetSocketAddress;
48 import java.net.SocketAddress;
49 import java.net.SocketException;
50 import java.nio.ByteBuffer;
51 import java.nio.MappedByteBuffer;
53 /**
54 * Native interface to support configuring of channel to run in a non-blocking
55 * manner and support scatter/gather io operations.
57 * @author Michael Barker <mike@middlesoft.co.uk>
60 public final class VMChannel
62 /**
63 * Our reference implementation uses an integer to store the native
64 * file descriptor. Implementations without such support
66 private final State nfd;
68 private Kind kind;
70 public VMChannel()
72 // XXX consider adding security check here, so only Classpath
73 // code may create instances.
74 this.nfd = new State();
75 kind = Kind.OTHER;
78 /**
79 * This constructor is used by the POSIX reference implementation;
80 * other virtual machines need not support it.
82 * <strong>Important:</strong> do not call this in library code that is
83 * not specific to Classpath's reference implementation.
85 * @param native_fd The native file descriptor integer.
86 * @throws IOException
88 VMChannel(final int native_fd) throws IOException
90 this();
91 this.nfd.setNativeFD(native_fd);
94 public State getState()
96 return nfd;
99 static
101 // load the shared library needed for native methods.
102 if (Configuration.INIT_LOAD_LIBRARY)
104 System.loadLibrary ("javanio");
106 initIDs();
109 public static VMChannel getStdin() throws IOException
111 return new VMChannel(stdin_fd());
114 public static VMChannel getStdout() throws IOException
116 return new VMChannel(stdout_fd());
119 public static VMChannel getStderr() throws IOException
121 return new VMChannel(stderr_fd());
124 private static native int stdin_fd();
125 private static native int stdout_fd();
126 private static native int stderr_fd();
129 * Set the file descriptor to have the required blocking
130 * setting.
132 * @param blocking The blocking flag to set.
134 public void setBlocking(boolean blocking) throws IOException
136 setBlocking(nfd.getNativeFD(), blocking);
139 private static native void setBlocking(int fd, boolean blocking)
140 throws IOException;
142 public int available() throws IOException
144 return available(nfd.getNativeFD());
147 private static native int available(int native_fd) throws IOException;
150 * Reads a byte buffer directly using the supplied file descriptor.
152 * @param dst Direct Byte Buffer to read to.
153 * @return Number of bytes read.
154 * @throws IOException If an error occurs or dst is not a direct buffers.
156 public int read(ByteBuffer dst)
157 throws IOException
159 return read(nfd.getNativeFD(), dst);
162 private static native int read(int fd, ByteBuffer dst) throws IOException;
165 * Read a single byte.
167 * @return The byte read, or -1 on end of file.
168 * @throws IOException
170 public int read() throws IOException
172 return read(nfd.getNativeFD());
175 private static native int read(int fd) throws IOException;
178 * Reads into byte buffers directly using the supplied file descriptor.
179 * Assumes that the buffer list contains DirectBuffers. Will perform a
180 * scattering read.
182 * @param dsts An array direct byte buffers.
183 * @param offset Index of the first buffer to read to.
184 * @param length The number of buffers to read to.
185 * @return Number of bytes read.
186 * @throws IOException If an error occurs or the dsts are not direct buffers.
188 public long readScattering(ByteBuffer[] dsts, int offset, int length)
189 throws IOException
191 if (offset + length > dsts.length)
192 throw new IndexOutOfBoundsException("offset + length > dsts.length");
194 return readScattering(nfd.getNativeFD(), dsts, offset, length);
197 private static native long readScattering(int fd, ByteBuffer[] dsts,
198 int offset, int length)
199 throws IOException;
202 * Receive a datagram on this channel, returning the host address
203 * that sent the datagram.
205 * @param dst Where to store the datagram.
206 * @return The host address that sent the datagram.
207 * @throws IOException
209 public SocketAddress receive(ByteBuffer dst) throws IOException
211 if (kind != Kind.SOCK_DGRAM)
212 throw new SocketException("not a datagram socket");
213 ByteBuffer hostPort = ByteBuffer.allocateDirect(18);
214 int hostlen = receive(nfd.getNativeFD(), dst, hostPort);
215 if (hostlen == 0)
216 return null;
217 if (hostlen == 4) // IPv4
219 byte[] addr = new byte[4];
220 hostPort.get(addr);
221 int port = hostPort.getShort() & 0xFFFF;
222 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
224 if (hostlen == 16) // IPv6
226 byte[] addr = new byte[16];
227 hostPort.get(addr);
228 int port = hostPort.getShort() & 0xFFFF;
229 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
232 throw new SocketException("host address received with invalid length: "
233 + hostlen);
236 private static native int receive (int fd, ByteBuffer dst, ByteBuffer address)
237 throws IOException;
240 * Writes from a direct byte bufer using the supplied file descriptor.
241 * Assumes the buffer is a DirectBuffer.
243 * @param src The source buffer.
244 * @return Number of bytes written.
245 * @throws IOException
247 public int write(ByteBuffer src) throws IOException
249 return write(nfd.getNativeFD(), src);
252 private native int write(int fd, ByteBuffer src) throws IOException;
255 * Writes from byte buffers directly using the supplied file descriptor.
256 * Assumes the that buffer list constains DirectBuffers. Will perform
257 * as gathering write.
259 * @param srcs
260 * @param offset
261 * @param length
262 * @return Number of bytes written.
263 * @throws IOException
265 public long writeGathering(ByteBuffer[] srcs, int offset, int length)
266 throws IOException
268 if (offset + length > srcs.length)
269 throw new IndexOutOfBoundsException("offset + length > srcs.length");
271 // A gathering write is limited to 16 buffers; when writing, ensure
272 // that we have at least one buffer with something in it in the 16
273 // buffer window starting at offset.
274 while (!srcs[offset].hasRemaining() && offset < srcs.length)
275 offset++;
277 // There are no buffers with anything to write.
278 if (offset == srcs.length)
279 return 0;
281 // If we advanced `offset' so far that we don't have `length'
282 // buffers left, reset length to only the remaining buffers.
283 if (length > srcs.length - offset)
284 length = srcs.length - offset;
286 return writeGathering(nfd.getNativeFD(), srcs, offset, length);
289 private native long writeGathering(int fd, ByteBuffer[] srcs,
290 int offset, int length)
291 throws IOException;
294 * Send a datagram to the given address.
296 * @param src The source buffer.
297 * @param dst The destination address.
298 * @return The number of bytes written.
299 * @throws IOException
301 public int send(ByteBuffer src, InetSocketAddress dst)
302 throws IOException
304 InetAddress addr = dst.getAddress();
305 if (addr == null)
306 throw new NullPointerException();
307 if (addr instanceof Inet4Address)
308 return send(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
309 else if (addr instanceof Inet6Address)
310 return send6(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
311 else
312 throw new SocketException("unrecognized inet address type");
315 // Send to an IPv4 address.
316 private static native int send(int fd, ByteBuffer src, byte[] addr, int port)
317 throws IOException;
319 // Send to an IPv6 address.
320 private static native int send6(int fd, ByteBuffer src, byte[] addr, int port)
321 throws IOException;
324 * Write a single byte.
326 * @param b The byte to write.
327 * @throws IOException
329 public void write(int b) throws IOException
331 write(nfd.getNativeFD(), b);
334 private static native void write(int fd, int b) throws IOException;
336 private native static void initIDs();
338 // Network (socket) specific methods.
341 * Create a new socket. This method will initialize the native file
342 * descriptor state of this instance.
344 * @param stream Whether or not to create a streaming socket, or a datagram
345 * socket.
346 * @throws IOException If creating a new socket fails, or if this
347 * channel already has its native descriptor initialized.
349 public void initSocket(boolean stream) throws IOException
351 if (nfd.isValid())
352 throw new IOException("native FD already initialized");
353 if (stream)
354 kind = Kind.SOCK_STREAM;
355 else
356 kind = Kind.SOCK_DGRAM;
357 nfd.setNativeFD(socket(stream));
361 * Create a new socket, returning the native file descriptor.
363 * @param stream Set to true for streaming sockets; false for datagrams.
364 * @return The native file descriptor.
365 * @throws IOException If creating the socket fails.
367 private static native int socket(boolean stream) throws IOException;
370 * Connect the underlying socket file descriptor to the remote host.
372 * @param saddr The address to connect to.
373 * @param timeout The connect timeout to use for blocking connects.
374 * @return True if the connection succeeded; false if the file descriptor
375 * is in non-blocking mode and the connection did not immediately
376 * succeed.
377 * @throws IOException If an error occurs while connecting.
379 public boolean connect(InetSocketAddress saddr, int timeout)
380 throws SocketException
382 int fd;
384 InetAddress addr = saddr.getAddress();
386 // Translates an IOException into a SocketException to conform
387 // to the throws clause.
390 fd = nfd.getNativeFD();
392 catch (IOException ioe)
394 throw new SocketException(ioe.getMessage());
397 if (addr instanceof Inet4Address)
398 return connect(fd, addr.getAddress(), saddr.getPort(),
399 timeout);
400 if (addr instanceof Inet6Address)
401 return connect6(fd, addr.getAddress(), saddr.getPort(),
402 timeout);
403 throw new SocketException("unsupported internet address");
406 private static native boolean connect(int fd, byte[] addr, int port, int timeout)
407 throws SocketException;
409 private static native boolean connect6(int fd, byte[] addr, int port, int timeout)
410 throws SocketException;
413 * Disconnect this channel, if it is a datagram socket. Disconnecting
414 * a datagram channel will disassociate it from any address, so the
415 * socket will remain open, but can send and receive datagrams from
416 * any address.
418 * @throws IOException If disconnecting this channel fails, or if this
419 * channel is not a datagram channel.
421 public void disconnect() throws IOException
423 if (kind != Kind.SOCK_DGRAM)
424 throw new IOException("can only disconnect datagram channels");
425 disconnect(nfd.getNativeFD());
428 private static native void disconnect(int fd) throws IOException;
430 public InetSocketAddress getLocalAddress() throws IOException
432 if (!nfd.isValid())
433 return null;
434 ByteBuffer name = ByteBuffer.allocateDirect(18);
435 int namelen = getsockname(nfd.getNativeFD(), name);
436 if (namelen == 0) // not bound
437 return null; // XXX return some wildcard?
438 if (namelen == 4)
440 byte[] addr = new byte[4];
441 name.get(addr);
442 int port = name.getShort() & 0xFFFF;
443 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
445 if (namelen == 16)
447 byte[] addr = new byte[16];
448 name.get(addr);
449 int port = name.getShort() & 0xFFFF;
450 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
452 throw new SocketException("invalid address length");
455 private static native int getsockname(int fd, ByteBuffer name)
456 throws IOException;
459 * Returns the socket address of the remote peer this channel is connected
460 * to, or null if this channel is not yet connected.
462 * @return The peer address.
463 * @throws IOException
465 public InetSocketAddress getPeerAddress() throws IOException
467 if (!nfd.isValid())
468 return null;
469 ByteBuffer name = ByteBuffer.allocateDirect(18);
470 int namelen = getpeername (nfd.getNativeFD(), name);
471 if (namelen == 0) // not connected yet
472 return null;
473 if (namelen == 4) // IPv4
475 byte[] addr = new byte[4];
476 name.get(addr);
477 int port = name.getShort() & 0xFFFF;
478 return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
480 else if (namelen == 16) // IPv6
482 byte[] addr = new byte[16];
483 name.get(addr);
484 int port = name.getShort() & 0xFFFF;
485 return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
487 throw new SocketException("invalid address length");
491 * The format here is the peer address, followed by the port number.
492 * The returned value is the length of the peer address; thus, there
493 * will be LEN + 2 valid bytes put into NAME.
495 private static native int getpeername(int fd, ByteBuffer name)
496 throws IOException;
499 * Accept an incoming connection, returning a new VMChannel, or null
500 * if the channel is nonblocking and no connection is pending.
502 * @return The accepted connection, or null.
503 * @throws IOException If an IO error occurs.
505 public VMChannel accept() throws IOException
507 int new_fd = accept(nfd.getNativeFD());
508 if (new_fd == -1) // non-blocking accept had no pending connection
509 return null;
510 return new VMChannel(new_fd);
513 private static native int accept(int native_fd) throws IOException;
515 // File-specific methods.
518 * Open a file at PATH, initializing the native state to operate on
519 * that open file.
521 * @param path The absolute file path.
522 * @throws IOException If the file cannot be opened, or if this
523 * channel was previously initialized.
525 public void openFile(String path, int mode) throws IOException
527 if (nfd.isValid() || nfd.isClosed())
528 throw new IOException("can't reinitialize this channel");
529 int fd = open(path, mode);
530 nfd.setNativeFD(fd);
531 kind = Kind.FILE;
534 private static native int open(String path, int mode) throws IOException;
536 public long position() throws IOException
538 if (kind != Kind.FILE)
539 throw new IOException("not a file");
540 return position(nfd.getNativeFD());
543 private static native long position(int fd) throws IOException;
545 public void seek(long pos) throws IOException
547 if (kind != Kind.FILE)
548 throw new IOException("not a file");
549 seek(nfd.getNativeFD(), pos);
552 private static native void seek(int fd, long pos) throws IOException;
554 public void truncate(long length) throws IOException
556 if (kind != Kind.FILE)
557 throw new IOException("not a file");
558 truncate(nfd.getNativeFD(), length);
561 private static native void truncate(int fd, long len) throws IOException;
563 public boolean lock(long pos, long len, boolean shared, boolean wait)
564 throws IOException
566 if (kind != Kind.FILE)
567 throw new IOException("not a file");
568 return lock(nfd.getNativeFD(), pos, len, shared, wait);
571 private static native boolean lock(int fd, long pos, long len,
572 boolean shared, boolean wait)
573 throws IOException;
575 public void unlock(long pos, long len) throws IOException
577 if (kind != Kind.FILE)
578 throw new IOException("not a file");
579 unlock(nfd.getNativeFD(), pos, len);
582 private static native void unlock(int fd, long pos, long len) throws IOException;
584 public long size() throws IOException
586 if (kind != Kind.FILE)
587 throw new IOException("not a file");
588 return size(nfd.getNativeFD());
591 private static native long size(int fd) throws IOException;
593 public MappedByteBuffer map(char mode, long position, int size)
594 throws IOException
596 if (kind != Kind.FILE)
597 throw new IOException("not a file");
598 return map(nfd.getNativeFD(), mode, position, size);
601 private static native MappedByteBuffer map(int fd, char mode,
602 long position, int size)
603 throws IOException;
605 public boolean flush(boolean metadata) throws IOException
607 if (kind != Kind.FILE)
608 throw new IOException("not a file");
609 return flush(nfd.getNativeFD(), metadata);
612 private static native boolean flush(int fd, boolean metadata) throws IOException;
614 // Close.
617 * Close this socket. The socket is also automatically closed when this
618 * object is finalized.
620 * @throws IOException If closing the socket fails, or if this object has
621 * no open socket.
623 public void close() throws IOException
625 nfd.close();
628 static native void close(int native_fd) throws IOException;
631 * <p>Provides a simple mean for the JNI code to find out whether the
632 * current thread was interrupted by a call to Thread.interrupt().</p>
634 * @return
636 static boolean isThreadInterrupted()
638 return Thread.currentThread().isInterrupted();
641 // Inner classes.
644 * A wrapper for a native file descriptor integer. This tracks the state
645 * of an open file descriptor, and ensures that
647 * This class need not be fully supported by virtual machines; if a
648 * virtual machine does not use integer file descriptors, or does and
649 * wishes to hide that, then the methods of this class may be stubbed out.
651 * System-specific classes that depend on access to native file descriptor
652 * integers SHOULD declare this fact.
654 public final class State
656 private int native_fd;
657 private boolean valid;
658 private boolean closed;
660 State()
662 native_fd = -1;
663 valid = false;
664 closed = false;
667 public boolean isValid()
669 return valid;
672 public boolean isClosed()
674 return closed;
677 public int getNativeFD() throws IOException
679 if (!valid)
680 throw new IOException("invalid file descriptor");
681 return native_fd;
684 void setNativeFD(final int native_fd) throws IOException
686 if (valid)
687 throw new IOException("file descriptor already initialized");
688 this.native_fd = native_fd;
689 valid = true;
692 public void close() throws IOException
694 if (!valid)
695 throw new IOException("invalid file descriptor");
698 VMChannel.close(native_fd);
700 finally
702 valid = false;
703 closed = true;
707 public String toString()
709 if (closed)
710 return "<<closed>>";
711 if (!valid)
712 return "<<invalid>>";
713 return String.valueOf(native_fd);
716 protected void finalize() throws Throwable
720 if (valid)
721 close();
723 finally
725 super.finalize();
731 * An enumeration of possible kinds of channel.
733 static class Kind // XXX enum
735 /** A streaming (TCP) socket. */
736 static final Kind SOCK_STREAM = new Kind();
738 /** A datagram (UDP) socket. */
739 static final Kind SOCK_DGRAM = new Kind();
741 /** A file. */
742 static final Kind FILE = new Kind();
744 /** Something else; not a socket or file. */
745 static final Kind OTHER = new Kind();
747 private Kind() { }