summaryrefslogtreecommitdiff
path: root/vm/reference/gnu/java
diff options
context:
space:
mode:
Diffstat (limited to 'vm/reference/gnu/java')
-rw-r--r--vm/reference/gnu/java/net/VMPlainSocketImpl.java505
-rw-r--r--vm/reference/gnu/java/nio/VMChannel.java627
-rw-r--r--vm/reference/gnu/java/nio/VMPipe.java24
3 files changed, 896 insertions, 260 deletions
diff --git a/vm/reference/gnu/java/net/VMPlainSocketImpl.java b/vm/reference/gnu/java/net/VMPlainSocketImpl.java
index 76be558dc..126294c7f 100644
--- a/vm/reference/gnu/java/net/VMPlainSocketImpl.java
+++ b/vm/reference/gnu/java/net/VMPlainSocketImpl.java
@@ -38,8 +38,11 @@ exception statement from your version. */
package gnu.java.net;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
@@ -47,6 +50,8 @@ import java.net.SocketOptions;
import java.net.UnknownHostException;
import gnu.classpath.Configuration;
+import gnu.java.nio.VMChannel;
+import gnu.java.nio.VMChannel.State;
/**
* The VM interface for {@link gnu.java.net.PlainSocketImpl}.
@@ -56,6 +61,8 @@ import gnu.classpath.Configuration;
*/
public final class VMPlainSocketImpl
{
+ private final State nfd;
+
/**
* Static initializer to load native library.
*/
@@ -66,249 +73,333 @@ public final class VMPlainSocketImpl
System.loadLibrary("javanet");
}
}
+
+ public VMPlainSocketImpl()
+ {
+ // XXX consider adding security check here.
+ nfd = new State();
+ }
+
+ public VMPlainSocketImpl(VMChannel channel) throws IOException
+ {
+ this();
+ nfd.setChannelFD(channel.getState());
+ }
+
+ public State getState()
+ {
+ return nfd;
+ }
- /**
- * Sets the specified option on a socket to the passed in object.
- * The optionId parameter is one of the defined constants in
- * the SocketImpl interface.
- *
- * @param socket the socket object
- * @param optionId the identifier of the option
- * @param value the value to set the option to
- *
- * @throws SocketException if an error occurs
- */
- static native void setOption(PlainSocketImpl socket, int optionId, Object value)
- throws SocketException;
-
- /**
- * Returns the current setting of the specified option. The optionId
- * is one of the defined constants in this interface.
- *
- * @param socket the socket object
- * @param optionId the option identifier
- *
- * @return the current value of the option
- *
- * @throws SocketException ff an error occurs
- */
- static native Object getOption(PlainSocketImpl socket, int optionId)
+ public void setOption(int optionId, Object optionValue)
+ throws SocketException
+ {
+ int value;
+ if (optionValue instanceof Integer)
+ value = ((Integer) optionValue).intValue();
+ else if (optionValue instanceof Boolean)
+ value = ((Boolean) optionValue).booleanValue() ? 1 : 0;
+ else
+ throw new IllegalArgumentException("option value type "
+ + optionValue.getClass().getName());
+
+ try
+ {
+ setOption(nfd.getNativeFD(), optionId, value);
+ }
+ catch (IOException ioe)
+ {
+ SocketException se = new SocketException();
+ se.initCause(ioe);
+ throw se;
+ }
+ }
+
+ private static native void setOption(int fd, int id, int value)
throws SocketException;
/**
- * Creates a new socket that is not bound to any local address/port and
- * is not connected to any remote address/port.
- *
- * @param socket the socket object
- *
- * @throws IOException if something goes wrong while creating the socket
+ * Get a socket option. This implementation is only required to support
+ * socket options that are boolean values, which include:
+ *
+ * SocketOptions.IP_MULTICAST_LOOP
+ * SocketOptions.SO_BROADCAST
+ * SocketOptions.SO_KEEPALIVE
+ * SocketOptions.SO_OOBINLINE
+ * SocketOptions.TCP_NODELAY
+ *
+ * and socket options that are integer values, which include:
+ *
+ * SocketOptions.IP_TOS
+ * SocketOptions.SO_LINGER
+ * SocketOptions.SO_RCVBUF
+ * SocketOptions.SO_SNDBUF
+ * SocketOptions.SO_TIMEOUT
+ *
+ * @param optionId The option ID to fetch.
+ * @return A {@link Boolean} or {@link Integer} containing the socket
+ * option.
+ * @throws SocketException
*/
- static native void create(PlainSocketImpl socket) throws IOException;
-
+ public Object getOption(int optionId) throws SocketException
+ {
+ int value;
+ try
+ {
+ value = getOption(nfd.getNativeFD(), optionId);
+ }
+ catch (IOException ioe)
+ {
+ SocketException se = new SocketException();
+ se.initCause(ioe);
+ throw se;
+ }
+
+ switch (optionId)
+ {
+ case SocketOptions.IP_MULTICAST_LOOP:
+ case SocketOptions.SO_BROADCAST:
+ case SocketOptions.SO_KEEPALIVE:
+ case SocketOptions.SO_OOBINLINE:
+ case SocketOptions.TCP_NODELAY:
+ return Boolean.valueOf(value != 0);
+
+ case SocketOptions.IP_TOS:
+ case SocketOptions.SO_LINGER:
+ case SocketOptions.SO_RCVBUF:
+ case SocketOptions.SO_SNDBUF:
+ case SocketOptions.SO_TIMEOUT:
+ return new Integer(value);
+
+ default:
+ throw new SocketException("getting option " + optionId +
+ " not supported here");
+ }
+ }
+
+ private static native int getOption(int fd, int id) throws SocketException;
+
/**
- * Connects to the remote address and port specified as arguments.
+ * Binds this socket to the given local address and port.
*
- * @param socket the socket object
- * @param addr the remote address to connect to
- * @param port the remote port to connect to
- *
- * @throws IOException if an error occurs
+ * @param address The address to bind to; the InetAddress is either
+ * an IPv4 or IPv6 address.
+ * @throws IOException If binding fails; for example, if the port
+ * in the given InetSocketAddress is privileged, and the current
+ * process has insufficient privileges.
*/
- static native void connect(PlainSocketImpl socket, InetAddress addr,
- int port) throws IOException;
-
+ public void bind(InetSocketAddress address) throws IOException
+ {
+ InetAddress addr = address.getAddress();
+ if (addr instanceof Inet4Address)
+ {
+ bind (nfd.getNativeFD(), addr.getAddress(), address.getPort());
+ }
+ else if (addr instanceof Inet6Address)
+ bind6 (nfd.getNativeFD(), addr.getAddress(), address.getPort());
+ else
+ throw new SocketException ("unsupported address type");
+ }
+
/**
- * Binds to the specified port on the specified addr. Note that this addr
- * must represent a local IP address. **** How bind to INADDR_ANY? ****
- *
- * @param socket the socket object
- * @param addr the address to bind to
- * @param port the port number to bind to
+ * Native bind function for IPv4 addresses. The addr array must be
+ * exactly four bytes long.
+ *
+ * VMs without native support need not implement this.
*
- * @exception IOException If an error occurs
+ * @param fd The native file descriptor integer.
+ * @param addr The IPv4 address, in network byte order.
+ * @param port The port to bind to.
+ * @throws IOException
*/
- static native void bind(PlainSocketImpl socket, InetAddress addr, int port)
+ private static native void bind(int fd, byte[] addr, int port)
throws IOException;
-
+
/**
- * Starts listening for connections on a socket. The queueLen parameter
- * is how many pending connections will queue up waiting to be serviced
- * before being accepted. If the queue of pending requests exceeds this
- * number, additional connections will be refused.
- *
- * @param socket the socket object
- * @param queueLen the length of the pending connection queue
+ * Native bind function for IPv6 addresses. The addr array must be
+ * exactly sixteen bytes long.
*
- * @exception IOException if an error occurs
- */
- static native void listen(PlainSocketImpl socket, int queueLen)
- throws IOException;
-
- /**
- * Accepts a new connection on this socket.
+ * VMs without native support need not implement this.
*
- * @param socket the socket object
- * @param impl the socket object to accept this connection.
+ * @param fd The native file descriptor integer.
+ * @param addr The IPv6 address, in network byte order.
+ * @param port The port to bind to.
+ * @throws IOException
*/
- static native void accept(PlainSocketImpl socket, SocketImpl impl)
+ private static native void bind6(int fd, byte[] addr, int port)
throws IOException;
/**
- * Returns the number of bytes that the caller can read from this socket
- * without blocking.
- *
- * @param socket the socket object
- *
- * @return the number of readable bytes before blocking
- *
- * @throws IOException If an error occurs
- */
- static native int available(PlainSocketImpl socket) throws IOException;
-
- /**
- * Closes the socket. This will cause any InputStream or OutputStream
- * objects for this Socket to be closed as well.
- *
- * <p>
- * Note that if the SO_LINGER option is set on this socket, then the
- * operation could block.
- * </p>
- *
- * @param socket the socket object
+ * Listen on this socket for incoming connections.
*
- * @throws IOException if an error occurs
+ * @param backlog The backlog of connections.
+ * @throws IOException If listening fails.
+ * @see gnu.java.nio.VMChannel#accept()
*/
- static native void close(PlainSocketImpl socket) throws IOException;
-
+ public void listen(int backlog) throws IOException
+ {
+ listen(nfd.getNativeFD(), backlog);
+ }
+
/**
- * Internal method used by SocketInputStream for reading data from
- * the connection. Reads up to len bytes of data into the buffer
- * buf starting at offset bytes into the buffer.
- *
- * @param socket the socket object
+ * Native listen function. VMs without native support need not implement
+ * this.
*
- * @return the actual number of bytes read or -1 if end of stream.
- *
- * @throws IOException if an error occurs
+ * @param fd The file descriptor integer.
+ * @param backlog The listen backlog size.
+ * @throws IOException
*/
- static native int read(PlainSocketImpl socket, byte[] buf, int offset,
- int len) throws IOException;
+ private static native void listen(int fd, int backlog) throws IOException;
- /**
- * Internal method used by SocketInputStream for reading data from
- * the connection. Reads and returns one byte of data.
- *
- * @param socket the socket object
- *
- * @return read byte or -1 if end of stream.
- *
- * @throws IOException if an error occurs
- */
- static int read(PlainSocketImpl socket) throws IOException
+ public void join(InetAddress group) throws IOException
{
- byte[] buf = new byte[1];
- if (read(socket, buf, 0, 1) > 0)
- return buf[0] & 0xFF;
+ if (group instanceof Inet4Address)
+ join(nfd.getNativeFD(), group.getAddress());
+ else if (group instanceof Inet6Address)
+ join6(nfd.getNativeFD(), group.getAddress());
else
- return -1;
+ throw new IllegalArgumentException("unknown address type");
}
+
+ private static native void join(int fd, byte[] addr) throws IOException;
+
+ private static native void join6(int fd, byte[] addr) throws IOException;
+
+ public void leave(InetAddress group) throws IOException
+ {
+ if (group instanceof Inet4Address)
+ leave(nfd.getNativeFD(), group.getAddress());
+ else if (group instanceof Inet6Address)
+ leave6(nfd.getNativeFD(), group.getAddress());
+ else
+ throw new IllegalArgumentException("unknown address type");
+ }
+
+ private static native void leave(int fd, byte[] addr) throws IOException;
+
+ private static native void leave6(int fd, byte[] addr) throws IOException;
- /**
- * Internal method used by SocketOuputStream for writing data to
- * the connection. Writes up to len bytes of data from the buffer
- * <code>buf</code> starting at <cod>offset</code> bytes into the buffer.
- *
- * @param socket the socket object
- * @param buf the buffer to write to the stream
- * @param offset the start offset in the buffer
- * @param len the number of bytes to write
- *
- * @throws IOException if an error occurs
- */
- static native void write(PlainSocketImpl socket, byte[] buf, int offset,
- int len) throws IOException;
-
- /**
- * Internal method used by SocketOuputStream for writing data to
- * the connection. Writes exactly one byte to the socket.
- *
- * @param socket the socket object
- * @param data the byte to write to the socket
- *
- * @throws IOException if an error occurs
- */
- static void write(PlainSocketImpl socket, int data)
+ public void joinGroup(InetSocketAddress addr, NetworkInterface netif)
throws IOException
{
- write(socket, new byte[]{ (byte) data }, 0, 1);
+ InetAddress address = addr.getAddress();
+
+ if (address instanceof Inet4Address)
+ joinGroup(nfd.getNativeFD(), address.getAddress(),
+ netif != null ? netif.getName() : null);
+ else if (address instanceof Inet6Address)
+ joinGroup6(nfd.getNativeFD(), address.getAddress(),
+ netif != null ? netif.getName() : null);
+ else
+ throw new IllegalArgumentException("unknown address type");
}
-
- /**
- * Sets the input stream for this socket to the end of the stream. Any
- * attempts to read more bytes from the stream will return an EOF.
- *
- * @param socket the socket object
- *
- * @throws IOException if I/O errors occur
- */
- static native void shutdownInput(PlainSocketImpl socket) throws IOException;
-
- /**
- * Disables the output stream for this socket. Any attempt to write more
- * data to the socket will throw an IOException.
- *
- * @param socket the socket object
- *
- * @throws IOException if I/O errors occur
- */
- static native void shutdownOutput(PlainSocketImpl socket) throws IOException;
-
- /**
- * Connects to the remote socket address with a specified timeout.
- *
- * @param socket the socket object
- * @param address the remote address to connect to
- * @param timeout the timeout to use for this connect, 0 means infinite.
- *
- * @throws IOException if an error occurs
- */
- static synchronized void connect(PlainSocketImpl socket,
- SocketAddress address, int timeout)
+
+ private static native void joinGroup(int fd, byte[] addr, String ifname)
+ throws IOException;
+
+ private static native void joinGroup6(int fd, byte[] addr, String ifname)
+ throws IOException;
+
+ public void leaveGroup(InetSocketAddress addr, NetworkInterface netif)
throws IOException
{
- InetSocketAddress sockAddr = (InetSocketAddress) address;
- InetAddress addr = sockAddr.getAddress();
-
- if (addr == null)
- throw new UnknownHostException(sockAddr.getHostName());
-
- int port = sockAddr.getPort();
-
- if (timeout < 0)
- throw new IllegalArgumentException("negative timeout");
-
- Object oldTimeoutObj = null;
- try
- {
- oldTimeoutObj = getOption(socket, SocketOptions.SO_TIMEOUT);
- setOption(socket, SocketOptions.SO_TIMEOUT, new Integer(timeout));
- connect(socket, addr, port);
- }
- finally
- {
- if (oldTimeoutObj != null)
- setOption(socket, SocketOptions.SO_TIMEOUT, oldTimeoutObj);
- }
+ InetAddress address = addr.getAddress();
+ if (address instanceof Inet4Address)
+ leaveGroup(nfd.getNativeFD(), address.getAddress(),
+ netif != null ? netif.getName() : null);
+ else if (address instanceof Inet6Address)
+ leaveGroup6(nfd.getNativeFD(), address.getAddress(),
+ netif != null ? netif.getName() : null);
+ else
+ throw new IllegalArgumentException("unknown address type");
}
-
+
+ private static native void leaveGroup(int fd, byte[] addr, String ifname)
+ throws IOException;
+
+ private static native void leaveGroup6(int fd, byte[] addr, String ifname)
+ throws IOException;
+
+
+ public void shutdownInput() throws IOException
+ {
+ shutdownInput(nfd.getNativeFD());
+ }
+
+ private static native void shutdownInput(int native_fd) throws IOException;
+
+ public void shutdownOutput() throws IOException
+ {
+ shutdownOutput(nfd.getNativeFD());
+ }
+
+ private static native void shutdownOutput(int native_fd) throws IOException;
+
+ public void sendUrgentData(int data) throws IOException
+ {
+ sendUrgentData(nfd.getNativeFD(), data);
+ }
+
+ private static native void sendUrgentData(int natfive_fd, int data) throws IOException;
+
+ public void close() throws IOException
+ {
+ nfd.close();
+ }
+
+ // Inner classes.
+
/**
- * Send one byte of urgent data over the socket.
- *
- * @param socket the socket object
- * @param data the byte to send
+ * Our wrapper for the native file descriptor. In this implementation,
+ * it is a simple wrapper around {@link VMChannel.State}, to simplify
+ * management of the native state.
*/
- static void sendUrgendData(PlainSocketImpl socket, int data)
+ public final class State
{
- throw new InternalError ("PlainSocketImpl::sendUrgentData not implemented");
+ private VMChannel.State channelFd;
+
+ State()
+ {
+ channelFd = null;
+ }
+
+ public boolean isValid()
+ {
+ if (channelFd != null)
+ return channelFd.isValid();
+ return false;
+ }
+
+ public int getNativeFD() throws IOException
+ {
+ return channelFd.getNativeFD();
+ }
+
+ public void setChannelFD(final VMChannel.State nfd) throws IOException
+ {
+ if (this.channelFd != null && this.channelFd.isValid())
+ throw new IOException("file descriptor already initialized");
+ this.channelFd = nfd;
+ }
+
+ public void close() throws IOException
+ {
+ if (channelFd == null)
+ throw new IOException("invalid file descriptor");
+ channelFd.close();
+ }
+
+ protected void finalize() throws Throwable
+ {
+ try
+ {
+ if (isValid())
+ close();
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
}
-}
+} \ No newline at end of file
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() { }
+ }
}
diff --git a/vm/reference/gnu/java/nio/VMPipe.java b/vm/reference/gnu/java/nio/VMPipe.java
index 11dd2aa7b..e5257c1cf 100644
--- a/vm/reference/gnu/java/nio/VMPipe.java
+++ b/vm/reference/gnu/java/nio/VMPipe.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package gnu.java.nio;
import java.io.IOException;
-import java.nio.channels.spi.SelectorProvider;
import gnu.classpath.Configuration;
/**
@@ -58,7 +57,24 @@ final class VMPipe
System.loadLibrary ("javanio");
}
}
-
- static native void init(PipeImpl self, SelectorProvider provider)
- throws IOException;
+
+ /**
+ * Create a pipe, consisting of a readable VMChannel and a writable
+ * VMChannel. The readable channel is returned is the first element
+ * of the array, and the writable in the second.
+ *
+ * @return A pair of VMChannels; the first readable, the second
+ * writable.
+ * @throws IOException If the pipe cannot be created.
+ */
+ static VMChannel[] pipe() throws IOException
+ {
+ VMChannel[] pipe = new VMChannel[2];
+ int[] fds = pipe0();
+ pipe[0] = new VMChannel(fds[0]);
+ pipe[1] = new VMChannel(fds[1]);
+ return pipe;
+ }
+
+ private static native int[] pipe0() throws IOException;
}