| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.nio.internal; |
| |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.ConnectException; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.Socket; |
| import java.net.SocketAddress; |
| import java.net.SocketException; |
| import java.net.SocketImpl; |
| import java.net.SocketOptions; |
| import java.net.UnknownHostException; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.AlreadyConnectedException; |
| import java.nio.channels.ClosedChannelException; |
| import java.nio.channels.ConnectionPendingException; |
| import java.nio.channels.IllegalBlockingModeException; |
| import java.nio.channels.NoConnectionPendingException; |
| import java.nio.channels.NotYetConnectedException; |
| import java.nio.channels.SocketChannel; |
| import java.nio.channels.UnresolvedAddressException; |
| import java.nio.channels.UnsupportedAddressTypeException; |
| import java.nio.channels.spi.SelectorProvider; |
| |
| import org.apache.harmony.luni.net.PlainSocketImpl; |
| import org.apache.harmony.luni.platform.FileDescriptorHandler; |
| import org.apache.harmony.luni.platform.INetworkSystem; |
| import org.apache.harmony.luni.platform.Platform; |
| import org.apache.harmony.luni.util.ErrorCodeException; |
| import org.apache.harmony.luni.util.Msg; |
| import org.apache.harmony.nio.AddressUtil; |
| import org.apache.harmony.nio.internal.nls.Messages; |
| |
| /* |
| * The default implementation class of java.nio.channels.SocketChannel. |
| */ |
| class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { |
| |
| private static final int EOF = -1; |
| |
| private static final int ERRCODE_SOCKET_NONBLOCKING_WOULD_BLOCK = -211; |
| |
| // The singleton to do the native network operation. |
| static final INetworkSystem networkSystem = Platform.getNetworkSystem(); |
| |
| // Status un-init, not initialized. |
| static final int SOCKET_STATUS_UNINIT = EOF; |
| |
| // Status before connect. |
| static final int SOCKET_STATUS_UNCONNECTED = 0; |
| |
| // Status connection pending. |
| static final int SOCKET_STATUS_PENDING = 1; |
| |
| // Status after connection success. |
| static final int SOCKET_STATUS_CONNECTED = 2; |
| |
| // Status closed. |
| static final int SOCKET_STATUS_CLOSED = 3; |
| |
| // Timeout used for non-block mode. |
| private static final int TIMEOUT_NONBLOCK = 0; |
| |
| // Timeout used for block mode. |
| private static final int TIMEOUT_BLOCK = EOF; |
| |
| // Step used for connect. |
| private static final int HY_SOCK_STEP_START = 0; |
| |
| // Step used for finishConnect. |
| private static final int HY_PORT_SOCKET_STEP_CHECK = 1; |
| |
| // Connect success. |
| private static final int CONNECT_SUCCESS = 0; |
| |
| // The descriptor to interact with native code. |
| FileDescriptor fd; |
| |
| // Our internal Socket. |
| private Socket socket = null; |
| |
| // The address to be connected. |
| InetSocketAddress connectAddress = null; |
| |
| // Local address of the this socket (package private for adapter). |
| InetAddress localAddress = null; |
| |
| // Local port number. |
| int localPort; |
| |
| // At first, uninitialized. |
| int status = SOCKET_STATUS_UNINIT; |
| |
| // Whether the socket is bound. |
| volatile boolean isBound = false; |
| |
| private static class ReadLock {} |
| private final Object readLock = new ReadLock(); |
| |
| private static class WriteLock {} |
| private final Object writeLock = new WriteLock(); |
| |
| // This content is a point used to set in connect_withtimeout() in pending |
| // mode. |
| // Must be a new instance of Long (i.e. not valueOf) as it's value may |
| // be modified by native code. |
| private Long connectContext = new Long(0L); |
| |
| // Used to store the trafficClass value which is simply returned |
| // as the value that was set. We also need it to pass it to methods |
| // that specify an address packets are going to be sent to. |
| private int trafficClass = 0; |
| |
| /* |
| * Constructor for creating a connected socket channel. |
| */ |
| public SocketChannelImpl(SelectorProvider selectorProvider) |
| throws IOException { |
| this(selectorProvider, true); |
| } |
| |
| /* |
| * Constructor for creating an optionally connected socket channel. |
| */ |
| public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) |
| throws IOException { |
| super(selectorProvider); |
| fd = new FileDescriptor(); |
| status = SOCKET_STATUS_UNCONNECTED; |
| if (connect) { |
| networkSystem.createStreamSocket(fd, true); |
| } |
| } |
| |
| /* |
| * For native call. |
| */ |
| @SuppressWarnings("unused") |
| private SocketChannelImpl() { |
| super(SelectorProvider.provider()); |
| fd = new FileDescriptor(); |
| connectAddress = new InetSocketAddress(0); |
| status = SOCKET_STATUS_CONNECTED; |
| } |
| |
| // Keep this to see if need next version |
| // SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor fd, |
| // SocketImpl si) { |
| // super(selectorProvider); |
| // fd = fd; |
| // networkSystem = OSNetworkSystem.getOSNetworkSystem(); |
| // status = SOCKET_STATUS_UNCONNECTED; |
| // networkSystem.createSocket(fd, true); |
| // } |
| |
| /* |
| * Package private constructor. |
| */ |
| SocketChannelImpl(Socket aSocket, FileDescriptor aFd) { |
| super(SelectorProvider.provider()); |
| socket = aSocket; |
| fd = aFd; |
| status = SOCKET_STATUS_UNCONNECTED; |
| } |
| |
| /* |
| * Getting the internal Socket If we have not the socket, we create a new |
| * one. |
| */ |
| @Override |
| synchronized public Socket socket() { |
| if (null == socket) { |
| try { |
| InetAddress addr = null; |
| int port = 0; |
| if (connectAddress != null) { |
| addr = connectAddress.getAddress(); |
| port = connectAddress.getPort(); |
| } |
| socket = new SocketAdapter( |
| new PlainSocketImpl(fd, localPort, addr, port), this); |
| } catch (SocketException e) { |
| return null; |
| } |
| } |
| return socket; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#isConnected() |
| */ |
| @Override |
| synchronized public boolean isConnected() { |
| return status == SOCKET_STATUS_CONNECTED; |
| } |
| |
| /* |
| * Status setting used by other class. |
| */ |
| synchronized void setConnected() { |
| status = SOCKET_STATUS_CONNECTED; |
| } |
| |
| void setBound(boolean flag) { |
| isBound = flag; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#isConnectionPending() |
| */ |
| @Override |
| synchronized public boolean isConnectionPending() { |
| return status == SOCKET_STATUS_PENDING; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#connect(java.net.SocketAddress) |
| */ |
| @Override |
| public boolean connect(SocketAddress socketAddress) throws IOException { |
| // status must be open and unconnected |
| checkUnconnected(); |
| |
| // check the address |
| InetSocketAddress inetSocketAddress = validateAddress(socketAddress); |
| InetAddress normalAddr = inetSocketAddress.getAddress(); |
| |
| // When connecting, map ANY address to Localhost |
| if (normalAddr.isAnyLocalAddress()) { |
| normalAddr = InetAddress.getLocalHost(); |
| } |
| |
| int port = inetSocketAddress.getPort(); |
| String hostName = normalAddr.getHostName(); |
| // security check |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkConnect(hostName, port); |
| } |
| |
| // connect result |
| int result = EOF; |
| boolean finished = false; |
| |
| try { |
| if (isBlocking()) { |
| begin(); |
| result = networkSystem.connect(fd, trafficClass, normalAddr, port); |
| } else { |
| result = networkSystem.connectWithTimeout(fd, 0, trafficClass, |
| normalAddr, port, HY_SOCK_STEP_START, connectContext); |
| // set back to nonblocking to work around with a bug in portlib |
| if (!this.isBlocking()) { |
| networkSystem.setNonBlocking(fd, true); |
| } |
| } |
| finished = (CONNECT_SUCCESS == result); |
| isBound = finished; |
| } catch (IOException e) { |
| if (isOpen()) { |
| close(); |
| finished = true; |
| } |
| throw e; |
| } finally { |
| if (isBlocking()) { |
| end(finished); |
| } |
| } |
| |
| // set local port |
| localPort = networkSystem.getSocketLocalPort(fd, false); |
| localAddress = networkSystem.getSocketLocalAddress(fd, false); |
| |
| // set the connected address. |
| connectAddress = inetSocketAddress; |
| synchronized (this) { |
| if (finished) { |
| status = SOCKET_STATUS_CONNECTED; |
| } else { |
| status = isBlocking() ? SOCKET_STATUS_UNCONNECTED |
| : SOCKET_STATUS_PENDING; |
| } |
| } |
| return finished; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#finishConnect() |
| */ |
| @Override |
| public boolean finishConnect() throws IOException { |
| // status check |
| synchronized (this) { |
| if (!isOpen()) { |
| throw new ClosedChannelException(); |
| } |
| if (status == SOCKET_STATUS_CONNECTED) { |
| return true; |
| } |
| if (status != SOCKET_STATUS_PENDING) { |
| throw new NoConnectionPendingException(); |
| } |
| } |
| |
| // finish result |
| int result = EOF; |
| boolean finished = false; |
| |
| try { |
| begin(); |
| result = networkSystem.connectWithTimeout(fd, |
| isBlocking() ? -1 : 0, trafficClass, connectAddress |
| .getAddress(), connectAddress.getPort(), |
| HY_PORT_SOCKET_STEP_CHECK, connectContext); |
| finished = (result == CONNECT_SUCCESS); |
| isBound = finished; |
| localAddress = networkSystem.getSocketLocalAddress(fd, false); |
| } catch (ConnectException e) { |
| if (isOpen()) { |
| close(); |
| finished = true; |
| } |
| throw e; |
| } finally { |
| end(finished); |
| } |
| |
| synchronized (this) { |
| status = (finished ? SOCKET_STATUS_CONNECTED : status); |
| isBound = finished; |
| // TPE: Workaround for bug that turns socket back to blocking |
| if (!isBlocking()) implConfigureBlocking(false); |
| } |
| return finished; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#read(java.nio.ByteBuffer) |
| */ |
| @Override |
| public int read(ByteBuffer target) throws IOException { |
| if (null == target) { |
| throw new NullPointerException(); |
| } |
| checkOpenConnected(); |
| if (!target.hasRemaining()) { |
| return 0; |
| } |
| |
| int readCount; |
| if (target.isDirect() || target.hasArray()) { |
| readCount = readImpl(target); |
| if (readCount > 0) { |
| target.position(target.position() + readCount); |
| } |
| } else { |
| ByteBuffer readBuffer = null; |
| byte[] readArray = null; |
| readArray = new byte[target.remaining()]; |
| readBuffer = ByteBuffer.wrap(readArray); |
| readCount = readImpl(readBuffer); |
| if (readCount > 0) { |
| target.put(readArray, 0, readCount); |
| } |
| } |
| return readCount; |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#read(java.nio.ByteBuffer[], int, |
| * int) |
| */ |
| @Override |
| public long read(ByteBuffer[] targets, int offset, int length) |
| throws IOException { |
| if (!isIndexValid(targets, offset, length)) { |
| throw new IndexOutOfBoundsException(); |
| } |
| |
| checkOpenConnected(); |
| int totalCount = calculateByteBufferArray(targets, offset, length); |
| if (0 == totalCount) { |
| return 0; |
| } |
| byte[] readArray = new byte[totalCount]; |
| ByteBuffer readBuffer = ByteBuffer.wrap(readArray); |
| int readCount; |
| // read data to readBuffer, and then transfer data from readBuffer to |
| // targets. |
| readCount = readImpl(readBuffer); |
| if (readCount > 0) { |
| int left = readCount; |
| int index = offset; |
| // transfer data from readArray to targets |
| while (left > 0) { |
| int putLength = Math.min(targets[index].remaining(), left); |
| targets[index].put(readArray, readCount - left, putLength); |
| index++; |
| left -= putLength; |
| } |
| } |
| return readCount; |
| } |
| |
| private boolean isIndexValid(ByteBuffer[] targets, int offset, int length) { |
| return (length >= 0) && (offset >= 0) |
| && ((long) length + (long) offset <= targets.length); |
| } |
| |
| /** |
| * Read from channel, and store the result in the target. |
| * |
| * @param target |
| * output parameter |
| */ |
| private int readImpl(ByteBuffer target) throws IOException { |
| synchronized (readLock) { |
| int readCount = 0; |
| try { |
| if (isBlocking()) { |
| begin(); |
| } |
| int offset = target.position(); |
| int length = target.remaining(); |
| if (target.isDirect()) { |
| long address = AddressUtil.getDirectBufferAddress(target); |
| readCount = networkSystem.readDirect(fd, address + offset, |
| length, (isBlocking() ? TIMEOUT_BLOCK |
| : TIMEOUT_NONBLOCK)); |
| } else { |
| // target is assured to have array. |
| byte[] array = target.array(); |
| offset += target.arrayOffset(); |
| readCount = networkSystem.read(fd, array, offset, length, |
| (isBlocking() ? TIMEOUT_BLOCK : TIMEOUT_NONBLOCK)); |
| } |
| return readCount; |
| } finally { |
| if (isBlocking()) { |
| end(readCount > 0); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#write(java.nio.ByteBuffer) |
| */ |
| @Override |
| public int write(ByteBuffer source) throws IOException { |
| if (null == source) { |
| throw new NullPointerException(); |
| } |
| checkOpenConnected(); |
| if (!source.hasRemaining()) { |
| return 0; |
| } |
| return writeImpl(source); |
| } |
| |
| /** |
| * @see java.nio.channels.SocketChannel#write(java.nio.ByteBuffer[], int, |
| * int) |
| */ |
| @Override |
| public long write(ByteBuffer[] sources, int offset, int length) |
| throws IOException { |
| if (!isIndexValid(sources, offset, length)) { |
| throw new IndexOutOfBoundsException(); |
| } |
| |
| checkOpenConnected(); |
| |
| Object[] src = new Object[length]; |
| int[] offsets = new int[length]; |
| int[] counts = new int[length]; |
| for (int i = 0; i < length; ++i) { |
| ByteBuffer buffer = sources[i + offset]; |
| if (!buffer.isDirect()) { |
| if (buffer.hasArray()) { |
| src[i] = buffer.array(); |
| counts[i] = buffer.remaining(); |
| offsets[i] = buffer.position(); |
| } else { |
| ByteBuffer db = ByteBuffer.allocateDirect(buffer.remaining()); |
| int oldPosition = buffer.position(); |
| db.put(buffer); |
| buffer.position(oldPosition); |
| db.flip(); |
| src[i] = db; |
| counts[i] = buffer.remaining(); |
| offsets[i] = 0; |
| } |
| } else { |
| src[i] = buffer; |
| counts[i] = buffer.remaining(); |
| offsets[i] = buffer.position(); |
| } |
| } |
| |
| if (length == 0) { |
| return 0; |
| } |
| |
| int result = writevImpl(src, offsets, counts); |
| int val = offset; |
| int written = result; |
| while (result > 0) { |
| ByteBuffer source = sources[val]; |
| int gap = Math.min(result, source.remaining()); |
| source.position(source.position() + gap); |
| val++; |
| result -= gap; |
| } |
| return written; |
| } |
| |
| /* |
| * Write the source. return the count of bytes written. |
| */ |
| private int writevImpl(Object[] sources, int[] offsets, int[] counts) throws IOException { |
| int writeCount = 0; |
| try { |
| if (isBlocking()) { |
| begin(); |
| } |
| |
| synchronized (writeLock) { |
| writeCount = networkSystem.writev(fd, sources, offsets, counts, sources.length); |
| } |
| } catch (SocketException e) { |
| if (e.getCause() instanceof ErrorCodeException) { |
| if (ERRCODE_SOCKET_NONBLOCKING_WOULD_BLOCK == ((ErrorCodeException) e |
| .getCause()).getErrorCode()) { |
| return writeCount; |
| } |
| } |
| throw e; |
| } finally { |
| if (isBlocking()) { |
| end(writeCount >= 0); |
| } |
| } |
| return writeCount; |
| } |
| |
| private int calculateByteBufferArray(ByteBuffer[] sources, int offset, |
| int length) { |
| int sum = 0; |
| for (int val = offset; val < offset + length; val++) { |
| sum = sum + sources[val].remaining(); |
| } |
| return sum; |
| } |
| |
| /* |
| * Write the source. return the count of bytes written. |
| */ |
| private int writeImpl(ByteBuffer source) throws IOException { |
| synchronized (writeLock) { |
| if (!source.hasRemaining()) { |
| return 0; |
| } |
| int writeCount = 0; |
| try { |
| int pos = source.position(); |
| int length = source.remaining(); |
| if (isBlocking()) { |
| begin(); |
| } |
| if (source.isDirect()) { |
| long address = AddressUtil.getDirectBufferAddress(source); |
| writeCount = networkSystem.writeDirect(fd, address + pos, |
| length); |
| } else if (source.hasArray()) { |
| pos += source.arrayOffset(); |
| writeCount = networkSystem.write(fd, source.array(), pos, |
| length); |
| } else { |
| byte[] array = new byte[length]; |
| source.get(array); |
| writeCount = networkSystem.write(fd, array, 0, length); |
| } |
| source.position(pos + writeCount); |
| } catch (SocketException e) { |
| if (e.getCause() instanceof ErrorCodeException) { |
| if (ERRCODE_SOCKET_NONBLOCKING_WOULD_BLOCK == ((ErrorCodeException) e |
| .getCause()).getErrorCode()) { |
| return writeCount; |
| } |
| } |
| throw e; |
| } finally { |
| if (isBlocking()) { |
| end(writeCount >= 0); |
| } |
| } |
| return writeCount; |
| } |
| } |
| |
| /* |
| * Status check, open and "connected", when read and write. |
| */ |
| synchronized private void checkOpenConnected() |
| throws ClosedChannelException { |
| if (!isOpen()) { |
| throw new ClosedChannelException(); |
| } |
| if (!isConnected()) { |
| throw new NotYetConnectedException(); |
| } |
| } |
| |
| /* |
| * Status check, open and "unconnected", before connection. |
| */ |
| synchronized private void checkUnconnected() throws IOException { |
| if (!isOpen()) { |
| throw new ClosedChannelException(); |
| } |
| if (status == SOCKET_STATUS_CONNECTED) { |
| throw new AlreadyConnectedException(); |
| } |
| if (status == SOCKET_STATUS_PENDING) { |
| throw new ConnectionPendingException(); |
| } |
| } |
| |
| /* |
| * Shared by this class and DatagramChannelImpl, to do the address transfer |
| * and check. |
| */ |
| static InetSocketAddress validateAddress(SocketAddress socketAddress) { |
| if (null == socketAddress) { |
| throw new IllegalArgumentException(); |
| } |
| if (!(socketAddress instanceof InetSocketAddress)) { |
| throw new UnsupportedAddressTypeException(); |
| } |
| InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; |
| if (inetSocketAddress.isUnresolved()) { |
| throw new UnresolvedAddressException(); |
| } |
| return inetSocketAddress; |
| } |
| |
| /* |
| * Get local address. |
| */ |
| public InetAddress getLocalAddress() throws UnknownHostException { |
| byte[] any_bytes = { 0, 0, 0, 0 }; |
| if (!isBound) { |
| return InetAddress.getByAddress(any_bytes); |
| } |
| return localAddress; |
| } |
| |
| /* |
| * Do really closing action here. |
| */ |
| @Override |
| synchronized protected void implCloseSelectableChannel() throws IOException { |
| if (SOCKET_STATUS_CLOSED != status) { |
| status = SOCKET_STATUS_CLOSED; |
| if (null != socket && !socket.isClosed()) { |
| socket.close(); |
| } else { |
| networkSystem.socketClose(fd); |
| } |
| } |
| } |
| |
| /** |
| * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking(boolean) |
| */ |
| @Override |
| protected void implConfigureBlocking(boolean blockMode) throws IOException { |
| synchronized (blockingLock()) { |
| networkSystem.setNonBlocking(fd, !blockMode); |
| } |
| } |
| |
| /* |
| * Get the fd. |
| */ |
| public FileDescriptor getFD() { |
| return fd; |
| } |
| |
| /* |
| * Adapter classes for internal socket. |
| */ |
| private static class SocketAdapter extends Socket { |
| |
| SocketChannelImpl channel; |
| |
| SocketImpl socketImpl; |
| |
| SocketAdapter(SocketImpl socketimpl, SocketChannelImpl channel) |
| throws SocketException { |
| super(socketimpl); |
| socketImpl = socketimpl; |
| this.channel = channel; |
| } |
| |
| /** |
| * @see java.net.Socket#getChannel() |
| */ |
| @Override |
| public SocketChannel getChannel() { |
| return channel; |
| } |
| |
| /** |
| * @see java.net.Socket#isBound() |
| */ |
| @Override |
| public boolean isBound() { |
| return channel.isBound; |
| } |
| |
| /** |
| * @see java.net.Socket#isConnected() |
| */ |
| @Override |
| public boolean isConnected() { |
| return channel.isConnected(); |
| } |
| |
| /** |
| * @see java.net.Socket#getLocalAddress() |
| */ |
| @Override |
| public InetAddress getLocalAddress() { |
| try { |
| return channel.getLocalAddress(); |
| } catch (UnknownHostException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * @see java.net.Socket#connect(java.net.SocketAddress, int) |
| */ |
| @Override |
| public void connect(SocketAddress remoteAddr, int timeout) |
| throws IOException { |
| if (!channel.isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| if (isConnected()) { |
| throw new AlreadyConnectedException(); |
| } |
| super.connect(remoteAddr, timeout); |
| channel.localAddress = networkSystem.getSocketLocalAddress( |
| channel.fd, false); |
| if (super.isConnected()) { |
| channel.setConnected(); |
| channel.isBound = super.isBound(); |
| } |
| } |
| |
| /** |
| * @see java.net.Socket#bind(java.net.SocketAddress) |
| */ |
| @Override |
| public void bind(SocketAddress localAddr) throws IOException { |
| if (channel.isConnected()) { |
| throw new AlreadyConnectedException(); |
| } |
| if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) { |
| throw new ConnectionPendingException(); |
| } |
| super.bind(localAddr); |
| // keep here to see if need next version |
| // channel.Address = getLocalSocketAddress(); |
| // channel.localport = getLocalPort(); |
| channel.isBound = true; |
| |
| } |
| |
| /** |
| * @see java.net.Socket#close() |
| */ |
| @Override |
| public void close() throws IOException { |
| synchronized (channel) { |
| if (channel.isOpen()) { |
| channel.close(); |
| } else { |
| super.close(); |
| } |
| channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED; |
| } |
| } |
| |
| @Override |
| public boolean getReuseAddress() throws SocketException { |
| checkOpen(); |
| return ((Boolean) socketImpl.getOption(SocketOptions.SO_REUSEADDR)) |
| .booleanValue(); |
| } |
| |
| @Override |
| public synchronized int getReceiveBufferSize() throws SocketException { |
| checkOpen(); |
| return ((Integer) socketImpl.getOption(SocketOptions.SO_RCVBUF)) |
| .intValue(); |
| } |
| |
| @Override |
| public synchronized int getSendBufferSize() throws SocketException { |
| checkOpen(); |
| return ((Integer) socketImpl.getOption(SocketOptions.SO_SNDBUF)) |
| .intValue(); |
| } |
| |
| @Override |
| public synchronized int getSoTimeout() throws SocketException { |
| checkOpen(); |
| return ((Integer) socketImpl.getOption(SocketOptions.SO_TIMEOUT)) |
| .intValue(); |
| } |
| |
| @Override |
| public int getTrafficClass() throws SocketException { |
| checkOpen(); |
| return ((Number) socketImpl.getOption(SocketOptions.IP_TOS)) |
| .intValue(); |
| } |
| |
| /** |
| * @see java.net.Socket#getKeepAlive() |
| */ |
| @Override |
| public boolean getKeepAlive() throws SocketException { |
| checkOpen(); |
| return ((Boolean) socketImpl.getOption(SocketOptions.SO_KEEPALIVE)) |
| .booleanValue(); |
| } |
| |
| /** |
| * @see java.net.Socket#getOOBInline() |
| */ |
| @Override |
| public boolean getOOBInline() throws SocketException { |
| checkOpen(); |
| return ((Boolean) socketImpl.getOption(SocketOptions.SO_OOBINLINE)) |
| .booleanValue(); |
| } |
| |
| /** |
| * @see java.net.Socket#getSoLinger() |
| */ |
| @Override |
| public int getSoLinger() throws SocketException { |
| checkOpen(); |
| return ((Integer) socketImpl.getOption(SocketOptions.SO_LINGER)) |
| .intValue(); |
| } |
| |
| /** |
| * @see java.net.Socket#getTcpNoDelay() |
| */ |
| @Override |
| public boolean getTcpNoDelay() throws SocketException { |
| checkOpen(); |
| return ((Boolean) socketImpl.getOption(SocketOptions.TCP_NODELAY)) |
| .booleanValue(); |
| } |
| |
| @Override |
| public void setKeepAlive(boolean value) throws SocketException { |
| checkOpen(); |
| socketImpl.setOption(SocketOptions.SO_KEEPALIVE, value ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| |
| @Override |
| public void setOOBInline(boolean oobinline) throws SocketException { |
| checkOpen(); |
| socketImpl.setOption(SocketOptions.SO_OOBINLINE, oobinline ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| |
| @Override |
| public synchronized void setReceiveBufferSize(int size) |
| throws SocketException { |
| checkOpen(); |
| if (size < 1) { |
| throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ |
| } |
| socketImpl |
| .setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); |
| } |
| |
| @Override |
| public void setReuseAddress(boolean reuse) throws SocketException { |
| checkOpen(); |
| socketImpl.setOption(SocketOptions.SO_REUSEADDR, reuse ? Boolean.TRUE |
| : Boolean.FALSE); |
| } |
| |
| @Override |
| public synchronized void setSendBufferSize(int size) throws SocketException { |
| checkOpen(); |
| if (size < 1) { |
| throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ |
| } |
| socketImpl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); |
| } |
| |
| @Override |
| public void setSoLinger(boolean on, int timeout) throws SocketException { |
| checkOpen(); |
| if (on && timeout < 0) { |
| throw new IllegalArgumentException(Msg.getString("K0045")); //$NON-NLS-1$ |
| } |
| int val = on ? (65535 < timeout ? 65535 : timeout) : -1; |
| socketImpl.setOption(SocketOptions.SO_LINGER, Integer.valueOf(val)); |
| } |
| |
| @Override |
| public synchronized void setSoTimeout(int timeout) throws SocketException { |
| checkOpen(); |
| if (timeout < 0) { |
| throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$ |
| } |
| socketImpl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); |
| } |
| |
| @Override |
| public void setTcpNoDelay(boolean on) throws SocketException { |
| checkOpen(); |
| socketImpl.setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); |
| } |
| |
| @Override |
| public void setTrafficClass(int value) throws SocketException { |
| checkOpen(); |
| if (value < 0 || value > 255) { |
| throw new IllegalArgumentException(); |
| } |
| socketImpl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); |
| } |
| |
| /** |
| * @see java.net.Socket#getOutputStream() |
| */ |
| @Override |
| public OutputStream getOutputStream() throws IOException { |
| if (!channel.isOpen()) { |
| // nio.00=Socket is closed |
| throw new SocketException(Messages.getString("nio.00")); //$NON-NLS-1$ |
| } |
| if (!channel.isConnected()) { |
| // nio.01=Socket is not connected |
| throw new SocketException(Messages.getString("nio.01")); //$NON-NLS-1$ |
| } |
| if (isOutputShutdown()) { |
| // nio.02=Socket output is shutdown |
| throw new SocketException(Messages.getString("nio.02")); //$NON-NLS-1$ |
| } |
| return new SocketChannelOutputStream(channel); |
| } |
| |
| /** |
| * @see java.net.Socket#getInputStream() |
| */ |
| @Override |
| public InputStream getInputStream() throws IOException { |
| if (!channel.isOpen()) { |
| // nio.00=Socket is closed |
| throw new SocketException(Messages.getString("nio.00")); //$NON-NLS-1$ |
| } |
| if (!channel.isConnected()) { |
| // nio.01=Socket is not connected |
| throw new SocketException(Messages.getString("nio.01")); //$NON-NLS-1$ |
| } |
| if (isInputShutdown()) { |
| // nio.03=Socket input is shutdown |
| throw new SocketException(Messages.getString("nio.03")); //$NON-NLS-1$ |
| } |
| return new SocketChannelInputStream(channel); |
| } |
| |
| /* |
| * Checks whether the channel is open. |
| */ |
| private void checkOpen() throws SocketException { |
| if (isClosed()) { |
| // nio.00=Socket is closed |
| throw new SocketException(Messages.getString("nio.00")); //$NON-NLS-1$ |
| } |
| } |
| |
| /* |
| * Used for net and nio exchange. |
| */ |
| public SocketImpl getImpl() { |
| return socketImpl; |
| } |
| } |
| |
| /* |
| * This output stream delegates all operations to the associated channel. |
| * Throws an IllegalBlockingModeException if the channel is in non-blocking |
| * mode when performing write operations. |
| */ |
| private static class SocketChannelOutputStream extends OutputStream { |
| SocketChannel channel; |
| |
| public SocketChannelOutputStream(SocketChannel channel) { |
| this.channel = channel; |
| } |
| |
| /* |
| * Closes this stream and channel. |
| * |
| * @exception IOException thrown if an error occurs during the close |
| */ |
| @Override |
| public void close() throws IOException { |
| channel.close(); |
| } |
| |
| /** |
| * @see java.io.OutputStream#write(byte[], int, int) |
| */ |
| @Override |
| public void write(byte[] buffer, int offset, int count) |
| throws IOException { |
| if (0 > offset || 0 > count || count + offset > buffer.length) { |
| throw new IndexOutOfBoundsException(); |
| } |
| ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count); |
| if (!channel.isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| channel.write(buf); |
| } |
| |
| /** |
| * @see java.io.OutputStream#write(int) |
| */ |
| @Override |
| public void write(int oneByte) throws IOException { |
| if (!channel.isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| ByteBuffer buffer = ByteBuffer.allocate(1); |
| buffer.put(0, (byte) (oneByte & 0xFF)); |
| channel.write(buffer); |
| } |
| } |
| |
| /* |
| * This input stream delegates all operations to the associated channel. |
| * Throws an IllegalBlockingModeException if the channel is in non-blocking |
| * mode when performing read operations. |
| */ |
| private static class SocketChannelInputStream extends InputStream { |
| SocketChannel channel; |
| |
| public SocketChannelInputStream(SocketChannel channel) { |
| this.channel = channel; |
| } |
| |
| /* |
| * Closes this stream and channel. |
| */ |
| @Override |
| public void close() throws IOException { |
| channel.close(); |
| } |
| |
| /** |
| * @see java.io.InputStream#read() |
| */ |
| @Override |
| public int read() throws IOException { |
| if (!channel.isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| ByteBuffer buf = ByteBuffer.allocate(1); |
| int result = channel.read(buf); |
| return (-1 == result) ? result : buf.get() & 0xFF; |
| } |
| |
| /** |
| * @see java.io.InputStream#read(byte[], int, int) |
| */ |
| @Override |
| public int read(byte[] buffer, int offset, int count) |
| throws IOException { |
| if (0 > offset || 0 > count || count + offset > buffer.length) { |
| throw new IndexOutOfBoundsException(); |
| } |
| if (!channel.isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count); |
| return channel.read(buf); |
| } |
| } |
| } |