summaryrefslogtreecommitdiff
path: root/vm/reference/gnu/java/nio/VMChannel.java
diff options
context:
space:
mode:
Diffstat (limited to 'vm/reference/gnu/java/nio/VMChannel.java')
-rw-r--r--vm/reference/gnu/java/nio/VMChannel.java627
1 files changed, 578 insertions, 49 deletions
diff --git a/vm/reference/gnu/java/nio/VMChannel.java b/vm/reference/gnu/java/nio/VMChannel.java
index fdea8ff62..688a94ac4 100644
--- a/vm/reference/gnu/java/nio/VMChannel.java
+++ b/vm/reference/gnu/java/nio/VMChannel.java
@@ -39,13 +39,17 @@ exception statement from your version. */
package gnu.java.nio;
import gnu.classpath.Configuration;
-import gnu.java.net.PlainSocketImpl;
-import gnu.java.nio.PipeImpl.SinkChannelImpl;
-import gnu.java.nio.PipeImpl.SourceChannelImpl;
-import gnu.java.nio.channels.FileChannelImpl;
+import gnu.classpath.jdwp.exception.NotImplementedException;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
/**
* Native interface to support configuring of channel to run in a non-blocking
@@ -54,33 +58,43 @@ import java.nio.ByteBuffer;
* @author Michael Barker <mike@middlesoft.co.uk>
*
*/
-public class VMChannel
+public final class VMChannel
{
- private final int fd;
-
- private VMChannel(int fd)
- {
- this.fd = fd;
- }
+ /**
+ * Our reference implementation uses an integer to store the native
+ * file descriptor. Implementations without such support
+ */
+ private final State nfd;
- public static VMChannel getVMChannel(PlainSocketImpl socket)
- {
- return new VMChannel(socket.getNativeFD());
- }
+ private Kind kind;
- public static VMChannel getVMChannel(SourceChannelImpl source)
+ public VMChannel()
{
- return new VMChannel(source.getNativeFD());
+ // XXX consider adding security check here, so only Classpath
+ // code may create instances.
+ this.nfd = new State();
+ kind = Kind.OTHER;
}
- public static VMChannel getVMChannel(SinkChannelImpl sink)
+ /**
+ * This constructor is used by the POSIX reference implementation;
+ * other virtual machines need not support it.
+ *
+ * <strong>Important:</strong> do not call this in library code that is
+ * not specific to Classpath's reference implementation.
+ *
+ * @param native_fd The native file descriptor integer.
+ * @throws IOException
+ */
+ VMChannel(final int native_fd) throws IOException
{
- return new VMChannel(sink.getNativeFD());
+ this();
+ this.nfd.setNativeFD(native_fd);
}
- public static VMChannel getVMChannel(FileChannelImpl file)
+ public State getState()
{
- return new VMChannel(file.getNativeFD());
+ return nfd;
}
static
@@ -93,81 +107,151 @@ public class VMChannel
initIDs();
}
+ public static VMChannel getStdin() throws IOException
+ {
+ return new VMChannel(stdin_fd());
+ }
+
+ public static VMChannel getStdout() throws IOException
+ {
+ return new VMChannel(stdout_fd());
+ }
+
+ public static VMChannel getStderr() throws IOException
+ {
+ return new VMChannel(stderr_fd());
+ }
+
+ private static native int stdin_fd();
+ private static native int stdout_fd();
+ private static native int stderr_fd();
+
/**
* Set the file descriptor to have the required blocking
* setting.
*
- * @param fd
- * @param blocking
+ * @param blocking The blocking flag to set.
*/
- public native void setBlocking(int fd, boolean blocking);
+ public void setBlocking(boolean blocking) throws IOException
+ {
+ setBlocking(nfd.getNativeFD(), blocking);
+ }
+
+ private static native void setBlocking(int fd, boolean blocking)
+ throws IOException;
- public void setBlocking(boolean blocking)
+ public int available() throws IOException
{
- setBlocking(fd, blocking);
+ return available(nfd.getNativeFD());
}
+ private static native int available(int native_fd) throws IOException;
/**
* Reads a byte buffer directly using the supplied file descriptor.
- * Assumes that the buffer is a DirectBuffer.
*
- * @param fd Native file descriptor to read from.
* @param dst Direct Byte Buffer to read to.
* @return Number of bytes read.
* @throws IOException If an error occurs or dst is not a direct buffers.
*/
- native int read(int fd, ByteBuffer dst)
- throws IOException;
-
public int read(ByteBuffer dst)
throws IOException
{
- return read(fd, dst);
+ return read(nfd.getNativeFD(), dst);
}
+ private static native int read(int fd, ByteBuffer dst) throws IOException;
+
+ /**
+ * Read a single byte.
+ *
+ * @return The byte read, or -1 on end of file.
+ * @throws IOException
+ */
+ public int read() throws IOException
+ {
+ return read(nfd.getNativeFD());
+ }
+
+ private static native int read(int fd) throws IOException;
+
/**
* Reads into byte buffers directly using the supplied file descriptor.
* Assumes that the buffer list contains DirectBuffers. Will perform a
* scattering read.
*
- * @param fd Native file descriptor to read from.
* @param dsts An array direct byte buffers.
* @param offset Index of the first buffer to read to.
* @param length The number of buffers to read to.
* @return Number of bytes read.
* @throws IOException If an error occurs or the dsts are not direct buffers.
*/
- native long readScattering(int fd, ByteBuffer[] dsts, int offset, int length)
- throws IOException;
-
public long readScattering(ByteBuffer[] dsts, int offset, int length)
throws IOException
{
if (offset + length > dsts.length)
throw new IndexOutOfBoundsException("offset + length > dsts.length");
+
+ return readScattering(nfd.getNativeFD(), dsts, offset, length);
+ }
+
+ private static native long readScattering(int fd, ByteBuffer[] dsts,
+ int offset, int length)
+ throws IOException;
+
+ /**
+ * Receive a datagram on this channel, returning the host address
+ * that sent the datagram.
+ *
+ * @param dst Where to store the datagram.
+ * @return The host address that sent the datagram.
+ * @throws IOException
+ */
+ public SocketAddress receive(ByteBuffer dst) throws IOException
+ {
+ if (kind != Kind.SOCK_DGRAM)
+ throw new SocketException("not a datagram socket");
+ ByteBuffer hostPort = ByteBuffer.allocateDirect(18);
+ int hostlen = receive(nfd.getNativeFD(), dst, hostPort);
+ if (hostlen == 0)
+ return null;
+ if (hostlen == 4) // IPv4
+ {
+ byte[] addr = new byte[4];
+ hostPort.get(addr);
+ int port = hostPort.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
+ }
+ if (hostlen == 16) // IPv6
+ {
+ byte[] addr = new byte[16];
+ hostPort.get(addr);
+ int port = hostPort.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
+ }
- return readScattering(fd, dsts, offset, length);
+ throw new SocketException("host address received with invalid length: "
+ + hostlen);
}
+ private static native int receive (int fd, ByteBuffer dst, ByteBuffer address)
+ throws IOException;
+
/**
* Writes from a direct byte bufer using the supplied file descriptor.
* Assumes the buffer is a DirectBuffer.
*
- * @param fd
- * @param src
+ * @param src The source buffer.
* @return Number of bytes written.
* @throws IOException
*/
- native int write(int fd, ByteBuffer src)
- throws IOException;
-
- public int write(ByteBuffer src)
- throws IOException
+ public int write(ByteBuffer src) throws IOException
{
- return write(fd, src);
+ return write(nfd.getNativeFD(), src);
}
+ private native int write(int fd, ByteBuffer src) throws IOException;
+
/**
* Writes from byte buffers directly using the supplied file descriptor.
* Assumes the that buffer list constains DirectBuffers. Will perform
@@ -180,18 +264,463 @@ public class VMChannel
* @return Number of bytes written.
* @throws IOException
*/
- native long writeGathering(int fd, ByteBuffer[] srcs, int offset, int length)
- throws IOException;
-
public long writeGathering(ByteBuffer[] srcs, int offset, int length)
throws IOException
{
if (offset + length > srcs.length)
throw new IndexOutOfBoundsException("offset + length > srcs.length");
- return writeGathering(fd, srcs, offset, length);
+ // A gathering write is limited to 16 buffers; when writing, ensure
+ // that we have at least one buffer with something in it in the 16
+ // buffer window starting at offset.
+ while (!srcs[offset].hasRemaining() && offset < srcs.length)
+ offset++;
+
+ // There are no buffers with anything to write.
+ if (offset == srcs.length)
+ return 0;
+
+ // If we advanced `offset' so far that we don't have `length'
+ // buffers left, reset length to only the remaining buffers.
+ if (length > srcs.length - offset)
+ length = srcs.length - offset;
+
+ return writeGathering(nfd.getNativeFD(), srcs, offset, length);
}
+ private native long writeGathering(int fd, ByteBuffer[] srcs,
+ int offset, int length)
+ throws IOException;
+
+ /**
+ * Send a datagram to the given address.
+ *
+ * @param src The source buffer.
+ * @param dst The destination address.
+ * @return The number of bytes written.
+ * @throws IOException
+ */
+ public int send(ByteBuffer src, InetSocketAddress dst)
+ throws IOException
+ {
+ InetAddress addr = dst.getAddress();
+ if (addr == null)
+ throw new NullPointerException();
+ if (addr instanceof Inet4Address)
+ return send(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
+ else if (addr instanceof Inet6Address)
+ return send6(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
+ else
+ throw new SocketException("unrecognized inet address type");
+ }
+
+ // Send to an IPv4 address.
+ private static native int send(int fd, ByteBuffer src, byte[] addr, int port)
+ throws IOException;
+
+ // Send to an IPv6 address.
+ private static native int send6(int fd, ByteBuffer src, byte[] addr, int port)
+ throws IOException;
+
+ /**
+ * Write a single byte.
+ *
+ * @param b The byte to write.
+ * @throws IOException
+ */
+ public void write(int b) throws IOException
+ {
+ write(nfd.getNativeFD(), b);
+ }
+
+ private static native void write(int fd, int b) throws IOException;
+
private native static void initIDs();
+ // Network (socket) specific methods.
+
+ /**
+ * Create a new socket. This method will initialize the native file
+ * descriptor state of this instance.
+ *
+ * @param stream Whether or not to create a streaming socket, or a datagram
+ * socket.
+ * @throws IOException If creating a new socket fails, or if this
+ * channel already has its native descriptor initialized.
+ */
+ public void initSocket(boolean stream) throws IOException
+ {
+ if (nfd.isValid())
+ throw new IOException("native FD already initialized");
+ if (stream)
+ kind = Kind.SOCK_STREAM;
+ else
+ kind = Kind.SOCK_DGRAM;
+ nfd.setNativeFD(socket(stream));
+ }
+
+ /**
+ * Create a new socket, returning the native file descriptor.
+ *
+ * @param stream Set to true for streaming sockets; false for datagrams.
+ * @return The native file descriptor.
+ * @throws IOException If creating the socket fails.
+ */
+ private static native int socket(boolean stream) throws IOException;
+
+ /**
+ * Connect the underlying socket file descriptor to the remote host.
+ *
+ * @param saddr The address to connect to.
+ * @param timeout The connect timeout to use for blocking connects.
+ * @return True if the connection succeeded; false if the file descriptor
+ * is in non-blocking mode and the connection did not immediately
+ * succeed.
+ * @throws IOException If an error occurs while connecting.
+ */
+ public boolean connect(InetSocketAddress saddr, int timeout)
+ throws IOException
+ {
+ InetAddress addr = saddr.getAddress();
+ if (addr instanceof Inet4Address)
+ return connect(nfd.getNativeFD(), addr.getAddress(), saddr.getPort(),
+ timeout);
+ if (addr instanceof Inet6Address)
+ return connect6(nfd.getNativeFD(), addr.getAddress(), saddr.getPort(),
+ timeout);
+ throw new IOException("unsupported internet address");
+ }
+
+ private static native boolean connect(int fd, byte[] addr, int port, int timeout)
+ throws IOException;
+
+ private static native boolean connect6(int fd, byte[] addr, int port, int timeout)
+ throws IOException;
+
+ /**
+ * Disconnect this channel, if it is a datagram socket. Disconnecting
+ * a datagram channel will disassociate it from any address, so the
+ * socket will remain open, but can send and receive datagrams from
+ * any address.
+ *
+ * @throws IOException If disconnecting this channel fails, or if this
+ * channel is not a datagram channel.
+ */
+ public void disconnect() throws IOException
+ {
+ if (kind != Kind.SOCK_DGRAM)
+ throw new IOException("can only disconnect datagram channels");
+ disconnect(nfd.getNativeFD());
+ }
+
+ private static native void disconnect(int fd) throws IOException;
+
+ public InetSocketAddress getLocalAddress() throws IOException
+ {
+ if (!nfd.isValid())
+ return null;
+ ByteBuffer name = ByteBuffer.allocateDirect(18);
+ int namelen = getsockname(nfd.getNativeFD(), name);
+ if (namelen == 0) // not bound
+ return null; // XXX return some wildcard?
+ if (namelen == 4)
+ {
+ byte[] addr = new byte[4];
+ name.get(addr);
+ int port = name.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
+ }
+ if (namelen == 16)
+ {
+ byte[] addr = new byte[16];
+ name.get(addr);
+ int port = name.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
+ }
+ throw new SocketException("invalid address length");
+ }
+
+ private static native int getsockname(int fd, ByteBuffer name)
+ throws IOException;
+
+ /**
+ * Returns the socket address of the remote peer this channel is connected
+ * to, or null if this channel is not yet connected.
+ *
+ * @return The peer address.
+ * @throws IOException
+ */
+ public InetSocketAddress getPeerAddress() throws IOException
+ {
+ if (!nfd.isValid())
+ return null;
+ ByteBuffer name = ByteBuffer.allocateDirect(18);
+ int namelen = getpeername (nfd.getNativeFD(), name);
+ if (namelen == 0) // not connected yet
+ return null;
+ if (namelen == 4) // IPv4
+ {
+ byte[] addr = new byte[4];
+ name.get(addr);
+ int port = name.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
+ }
+ else if (namelen == 16) // IPv6
+ {
+ byte[] addr = new byte[16];
+ name.get(addr);
+ int port = name.getShort() & 0xFFFF;
+ return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
+ }
+ throw new SocketException("invalid address length");
+ }
+
+ /*
+ * The format here is the peer address, followed by the port number.
+ * The returned value is the length of the peer address; thus, there
+ * will be LEN + 2 valid bytes put into NAME.
+ */
+ private static native int getpeername(int fd, ByteBuffer name)
+ throws IOException;
+
+ /**
+ * Accept an incoming connection, returning a new VMChannel, or null
+ * if the channel is nonblocking and no connection is pending.
+ *
+ * @return The accepted connection, or null.
+ * @throws IOException If an IO error occurs.
+ */
+ public VMChannel accept() throws IOException
+ {
+ int new_fd = accept(nfd.getNativeFD());
+ if (new_fd == -1) // non-blocking accept had no pending connection
+ return null;
+ return new VMChannel(new_fd);
+ }
+
+ private static native int accept(int native_fd) throws IOException;
+
+ // File-specific methods.
+
+ /**
+ * Open a file at PATH, initializing the native state to operate on
+ * that open file.
+ *
+ * @param path The absolute file path.
+ * @throws IOException If the file cannot be opened, or if this
+ * channel was previously initialized.
+ */
+ public void openFile(String path, int mode) throws IOException
+ {
+ if (nfd.isValid() || nfd.isClosed())
+ throw new IOException("can't reinitialize this channel");
+ int fd = open(path, mode);
+ nfd.setNativeFD(fd);
+ kind = Kind.FILE;
+ }
+
+ private static native int open(String path, int mode) throws IOException;
+
+ public long position() throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ return position(nfd.getNativeFD());
+ }
+
+ private static native long position(int fd) throws IOException;
+
+ public void seek(long pos) throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ seek(nfd.getNativeFD(), pos);
+ }
+
+ private static native void seek(int fd, long pos) throws IOException;
+
+ public void truncate(long length) throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ truncate(nfd.getNativeFD(), length);
+ }
+
+ private static native void truncate(int fd, long len) throws IOException;
+
+ public boolean lock(long pos, long len, boolean shared, boolean wait)
+ throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ return lock(nfd.getNativeFD(), pos, len, shared, wait);
+ }
+
+ private static native boolean lock(int fd, long pos, long len,
+ boolean shared, boolean wait)
+ throws IOException;
+
+ public void unlock(long pos, long len) throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ unlock(nfd.getNativeFD(), pos, len);
+ }
+
+ private static native void unlock(int fd, long pos, long len) throws IOException;
+
+ public long size() throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ return size(nfd.getNativeFD());
+ }
+
+ private static native long size(int fd) throws IOException;
+
+ public MappedByteBuffer map(char mode, long position, int size)
+ throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ return map(nfd.getNativeFD(), mode, position, size);
+ }
+
+ private static native MappedByteBuffer map(int fd, char mode,
+ long position, int size)
+ throws IOException;
+
+ public boolean flush(boolean metadata) throws IOException
+ {
+ if (kind != Kind.FILE)
+ throw new IOException("not a file");
+ return flush(nfd.getNativeFD(), metadata);
+ }
+
+ private static native boolean flush(int fd, boolean metadata) throws IOException;
+
+ // Close.
+
+ /**
+ * Close this socket. The socket is also automatically closed when this
+ * object is finalized.
+ *
+ * @throws IOException If closing the socket fails, or if this object has
+ * no open socket.
+ */
+ public void close() throws IOException
+ {
+ nfd.close();
+ }
+
+ static native void close(int native_fd) throws IOException;
+
+ // Inner classes.
+
+ /**
+ * A wrapper for a native file descriptor integer. This tracks the state
+ * of an open file descriptor, and ensures that
+ *
+ * This class need not be fully supported by virtual machines; if a
+ * virtual machine does not use integer file descriptors, or does and
+ * wishes to hide that, then the methods of this class may be stubbed out.
+ *
+ * System-specific classes that depend on access to native file descriptor
+ * integers SHOULD declare this fact.
+ */
+ public final class State
+ {
+ private int native_fd;
+ private boolean valid;
+ private boolean closed;
+
+ State()
+ {
+ native_fd = -1;
+ valid = false;
+ closed = false;
+ }
+
+ public boolean isValid()
+ {
+ return valid;
+ }
+
+ public boolean isClosed()
+ {
+ return closed;
+ }
+
+ public int getNativeFD() throws IOException
+ {
+ if (!valid)
+ throw new IOException("invalid file descriptor");
+ return native_fd;
+ }
+
+ void setNativeFD(final int native_fd) throws IOException
+ {
+ if (valid)
+ throw new IOException("file descriptor already initialized");
+ this.native_fd = native_fd;
+ valid = true;
+ }
+
+ public void close() throws IOException
+ {
+ if (!valid)
+ throw new IOException("invalid file descriptor");
+ try
+ {
+ VMChannel.close(native_fd);
+ }
+ finally
+ {
+ valid = false;
+ closed = true;
+ }
+ }
+
+ public String toString()
+ {
+ if (closed)
+ return "<<closed>>";
+ if (!valid)
+ return "<<invalid>>";
+ return String.valueOf(native_fd);
+ }
+
+ protected void finalize() throws Throwable
+ {
+ try
+ {
+ if (valid)
+ close();
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
+ }
+
+ /**
+ * An enumeration of possible kinds of channel.
+ */
+ static class Kind // XXX enum
+ {
+ /** A streaming (TCP) socket. */
+ static final Kind SOCK_STREAM = new Kind();
+
+ /** A datagram (UDP) socket. */
+ static final Kind SOCK_DGRAM = new Kind();
+
+ /** A file. */
+ static final Kind FILE = new Kind();
+
+ /** Something else; not a socket or file. */
+ static final Kind OTHER = new Kind();
+
+ private Kind() { }
+ }
}