8232673: (dc) DatagramChannel socket adaptor issues
Reviewed-by: dfuchs, chegar
This commit is contained in:
parent
5dafc279a7
commit
db4909bf99
@ -606,7 +606,6 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* @see #bind(SocketAddress)
|
||||
* @since 1.4
|
||||
*/
|
||||
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
if (isClosed())
|
||||
return null;
|
||||
@ -853,7 +852,7 @@ class DatagramSocket implements java.io.Closeable {
|
||||
public InetAddress getLocalAddress() {
|
||||
if (isClosed())
|
||||
return null;
|
||||
InetAddress in = null;
|
||||
InetAddress in;
|
||||
try {
|
||||
in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
|
||||
if (in.isAnyLocalAddress()) {
|
||||
@ -874,8 +873,8 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* is bound.
|
||||
*
|
||||
* @return the port number on the local host to which this socket is bound,
|
||||
{@code -1} if the socket is closed, or
|
||||
{@code 0} if it is not bound yet.
|
||||
* {@code -1} if the socket is closed, or
|
||||
* {@code 0} if it is not bound yet.
|
||||
*/
|
||||
public int getLocalPort() {
|
||||
if (isClosed())
|
||||
@ -887,15 +886,16 @@ class DatagramSocket implements java.io.Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
/** Enable/disable SO_TIMEOUT with the specified timeout, in
|
||||
* milliseconds. With this option set to a positive timeout value,
|
||||
* a call to receive() for this DatagramSocket
|
||||
* will block for only this amount of time. If the timeout expires,
|
||||
* a <B>java.net.SocketTimeoutException</B> is raised, though the
|
||||
* DatagramSocket is still valid. A timeout of zero is interpreted
|
||||
* as an infinite timeout.
|
||||
* The option <B>must</B> be enabled prior to entering the blocking
|
||||
* operation to have effect.
|
||||
/**
|
||||
* Enable/disable SO_TIMEOUT with the specified timeout, in
|
||||
* milliseconds. With this option set to a positive timeout value,
|
||||
* a call to receive() for this DatagramSocket
|
||||
* will block for only this amount of time. If the timeout expires,
|
||||
* a <B>java.net.SocketTimeoutException</B> is raised, though the
|
||||
* DatagramSocket is still valid. A timeout of zero is interpreted
|
||||
* as an infinite timeout.
|
||||
* The option <B>must</B> be enabled prior to entering the blocking
|
||||
* operation to have effect.
|
||||
*
|
||||
* @param timeout the specified timeout in milliseconds.
|
||||
* @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
|
||||
@ -963,8 +963,7 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* negative.
|
||||
* @see #getSendBufferSize()
|
||||
*/
|
||||
public synchronized void setSendBufferSize(int size)
|
||||
throws SocketException{
|
||||
public synchronized void setSendBufferSize(int size) throws SocketException {
|
||||
if (!(size > 0)) {
|
||||
throw new IllegalArgumentException("negative send size");
|
||||
}
|
||||
@ -1021,8 +1020,7 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* negative.
|
||||
* @see #getReceiveBufferSize()
|
||||
*/
|
||||
public synchronized void setReceiveBufferSize(int size)
|
||||
throws SocketException{
|
||||
public synchronized void setReceiveBufferSize(int size) throws SocketException {
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("invalid receive size");
|
||||
}
|
||||
@ -1039,8 +1037,7 @@ class DatagramSocket implements java.io.Closeable {
|
||||
* @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
|
||||
* @see #setReceiveBufferSize(int)
|
||||
*/
|
||||
public synchronized int getReceiveBufferSize()
|
||||
throws SocketException{
|
||||
public synchronized int getReceiveBufferSize() throws SocketException {
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
int result = 0;
|
||||
|
@ -28,6 +28,8 @@ package sun.nio.ch;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Inet4Address;
|
||||
@ -39,6 +41,7 @@ import java.net.PortUnreachableException;
|
||||
import java.net.ProtocolFamily;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketOption;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -47,6 +50,7 @@ import java.nio.channels.AlreadyConnectedException;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.nio.channels.MembershipKey;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
@ -113,8 +117,17 @@ class DatagramChannelImpl
|
||||
private InetSocketAddress localAddress;
|
||||
private InetSocketAddress remoteAddress;
|
||||
|
||||
// Our socket adaptor, if any
|
||||
private DatagramSocket socket;
|
||||
// Socket adaptor, created lazily
|
||||
private static final VarHandle SOCKET;
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
SOCKET = l.findVarHandle(DatagramChannelImpl.class, "socket", DatagramSocket.class);
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
private volatile DatagramSocket socket;
|
||||
|
||||
// Multicast support
|
||||
private MembershipRegistry registry;
|
||||
@ -199,11 +212,14 @@ class DatagramChannelImpl
|
||||
|
||||
@Override
|
||||
public DatagramSocket socket() {
|
||||
synchronized (stateLock) {
|
||||
if (socket == null)
|
||||
socket = DatagramSocketAdaptor.create(this);
|
||||
return socket;
|
||||
DatagramSocket socket = this.socket;
|
||||
if (socket == null) {
|
||||
socket = DatagramSocketAdaptor.create(this);
|
||||
if (!SOCKET.compareAndSet(this, null, socket)) {
|
||||
socket = this.socket;
|
||||
}
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -408,62 +424,35 @@ class DatagramChannelImpl
|
||||
public SocketAddress receive(ByteBuffer dst) throws IOException {
|
||||
if (dst.isReadOnly())
|
||||
throw new IllegalArgumentException("Read-only buffer");
|
||||
|
||||
readLock.lock();
|
||||
try {
|
||||
boolean blocking = isBlocking();
|
||||
boolean completed = false;
|
||||
int n = 0;
|
||||
ByteBuffer bb = null;
|
||||
try {
|
||||
SocketAddress remote = beginRead(blocking, false);
|
||||
boolean connected = (remote != null);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
|
||||
if (connected || (sm == null)) {
|
||||
// connected or no security manager
|
||||
n = receive(fd, dst, connected);
|
||||
n = receive(dst, connected);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(fd, dst, connected);
|
||||
n = receive(dst, connected);
|
||||
}
|
||||
} else if (n == IOStatus.UNAVAILABLE) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Cannot receive into user's buffer when running with a
|
||||
// security manager and not connected
|
||||
bb = Util.getTemporaryDirectBuffer(dst.remaining());
|
||||
for (;;) {
|
||||
n = receive(fd, bb, connected);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(fd, bb, connected);
|
||||
}
|
||||
} else if (n == IOStatus.UNAVAILABLE) {
|
||||
return null;
|
||||
}
|
||||
InetSocketAddress isa = (InetSocketAddress)sender;
|
||||
try {
|
||||
sm.checkAccept(isa.getAddress().getHostAddress(),
|
||||
isa.getPort());
|
||||
} catch (SecurityException se) {
|
||||
// Ignore packet
|
||||
bb.clear();
|
||||
n = 0;
|
||||
continue;
|
||||
}
|
||||
bb.flip();
|
||||
dst.put(bb);
|
||||
break;
|
||||
}
|
||||
// security manager and unconnected
|
||||
n = untrustedReceive(dst);
|
||||
}
|
||||
assert sender != null;
|
||||
if (n == IOStatus.UNAVAILABLE)
|
||||
return null;
|
||||
completed = (n > 0) || (n == 0 && isOpen());
|
||||
return sender;
|
||||
} finally {
|
||||
if (bb != null)
|
||||
Util.releaseTemporaryDirectBuffer(bb);
|
||||
endRead(blocking, n > 0);
|
||||
endRead(blocking, completed);
|
||||
assert IOStatus.check(n);
|
||||
}
|
||||
} finally {
|
||||
@ -471,15 +460,164 @@ class DatagramChannelImpl
|
||||
}
|
||||
}
|
||||
|
||||
private int receive(FileDescriptor fd, ByteBuffer dst, boolean connected)
|
||||
/**
|
||||
* Receives a datagram into an untrusted buffer. When there is a security
|
||||
* manager set, and the socket is not connected, datagrams have to be received
|
||||
* into a buffer that is not accessible to the user. The datagram is copied
|
||||
* into the user's buffer when the sender address is accepted by the security
|
||||
* manager.
|
||||
*
|
||||
* @return the size of the datagram or IOStatus.UNAVAILABLE
|
||||
*/
|
||||
private int untrustedReceive(ByteBuffer dst) throws IOException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
assert readLock.isHeldByCurrentThread()
|
||||
&& sm != null && remoteAddress == null;
|
||||
|
||||
ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
|
||||
try {
|
||||
boolean blocking = isBlocking();
|
||||
for (;;) {
|
||||
int n = receive(bb, false);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(bb, false);
|
||||
}
|
||||
} else if (n == IOStatus.UNAVAILABLE) {
|
||||
return n;
|
||||
}
|
||||
InetSocketAddress isa = (InetSocketAddress) sender;
|
||||
try {
|
||||
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
|
||||
bb.flip();
|
||||
dst.put(bb);
|
||||
return n;
|
||||
} catch (SecurityException se) {
|
||||
// ignore datagram
|
||||
bb.clear();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Util.releaseTemporaryDirectBuffer(bb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a datagram into the given buffer.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor. The buffer is
|
||||
* assumed to be trusted, meaning it is not accessible to user code.
|
||||
*
|
||||
* @throws IllegalBlockingModeException if the channel is non-blocking
|
||||
* @throws SocketTimeoutException if the timeout elapses
|
||||
*/
|
||||
SocketAddress blockingReceive(ByteBuffer dst, long nanos) throws IOException {
|
||||
readLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
boolean connected = isConnected();
|
||||
SocketAddress sender;
|
||||
do {
|
||||
if (nanos > 0) {
|
||||
sender = trustedBlockingReceive(dst, nanos);
|
||||
} else {
|
||||
sender = trustedBlockingReceive(dst);
|
||||
}
|
||||
// check sender when security manager set and not connected
|
||||
if (sm != null && !connected) {
|
||||
InetSocketAddress isa = (InetSocketAddress) sender;
|
||||
try {
|
||||
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
|
||||
} catch (SecurityException e) {
|
||||
sender = null;
|
||||
}
|
||||
}
|
||||
} while (sender == null);
|
||||
return sender;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a datagram into given buffer. This method is used to support
|
||||
* the socket adaptor. The buffer is assumed to be trusted.
|
||||
* @throws SocketTimeoutException if the timeout elapses
|
||||
*/
|
||||
private SocketAddress trustedBlockingReceive(ByteBuffer dst)
|
||||
throws IOException
|
||||
{
|
||||
assert readLock.isHeldByCurrentThread() && isBlocking();
|
||||
boolean completed = false;
|
||||
int n = 0;
|
||||
try {
|
||||
SocketAddress remote = beginRead(true, false);
|
||||
boolean connected = (remote != null);
|
||||
n = receive(dst, connected);
|
||||
while (n == IOStatus.UNAVAILABLE && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(dst, connected);
|
||||
}
|
||||
completed = (n > 0) || (n == 0 && isOpen());
|
||||
return sender;
|
||||
} finally {
|
||||
endRead(true, completed);
|
||||
assert IOStatus.check(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a datagram into given buffer with a timeout. This method is
|
||||
* used to support the socket adaptor. The buffer is assumed to be trusted.
|
||||
* @throws SocketTimeoutException if the timeout elapses
|
||||
*/
|
||||
private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos)
|
||||
throws IOException
|
||||
{
|
||||
assert readLock.isHeldByCurrentThread() && isBlocking();
|
||||
boolean completed = false;
|
||||
int n = 0;
|
||||
try {
|
||||
SocketAddress remote = beginRead(true, false);
|
||||
boolean connected = (remote != null);
|
||||
|
||||
// change socket to non-blocking
|
||||
lockedConfigureBlocking(false);
|
||||
try {
|
||||
long startNanos = System.nanoTime();
|
||||
n = receive(dst, connected);
|
||||
while (n == IOStatus.UNAVAILABLE && isOpen()) {
|
||||
long remainingNanos = nanos - (System.nanoTime() - startNanos);
|
||||
if (remainingNanos <= 0) {
|
||||
throw new SocketTimeoutException("Receive timed out");
|
||||
}
|
||||
park(Net.POLLIN, remainingNanos);
|
||||
n = receive(dst, connected);
|
||||
}
|
||||
completed = (n > 0) || (n == 0 && isOpen());
|
||||
return sender;
|
||||
} finally {
|
||||
// restore socket to blocking mode (if channel is open)
|
||||
tryLockedConfigureBlocking(true);
|
||||
}
|
||||
|
||||
} finally {
|
||||
endRead(true, completed);
|
||||
assert IOStatus.check(n);
|
||||
}
|
||||
}
|
||||
|
||||
private int receive(ByteBuffer dst, boolean connected) throws IOException {
|
||||
int pos = dst.position();
|
||||
int lim = dst.limit();
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
if (dst instanceof DirectBuffer && rem > 0)
|
||||
return receiveIntoNativeBuffer(fd, dst, rem, pos, connected);
|
||||
return receiveIntoNativeBuffer(dst, rem, pos, connected);
|
||||
|
||||
// Substitute a native buffer. If the supplied buffer is empty
|
||||
// we must instead use a nonempty buffer, otherwise the call
|
||||
@ -487,7 +625,7 @@ class DatagramChannelImpl
|
||||
int newSize = Math.max(rem, 1);
|
||||
ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
|
||||
try {
|
||||
int n = receiveIntoNativeBuffer(fd, bb, newSize, 0, connected);
|
||||
int n = receiveIntoNativeBuffer(bb, newSize, 0, connected);
|
||||
bb.flip();
|
||||
if (n > 0 && rem > 0)
|
||||
dst.put(bb);
|
||||
@ -497,8 +635,8 @@ class DatagramChannelImpl
|
||||
}
|
||||
}
|
||||
|
||||
private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
|
||||
int rem, int pos, boolean connected)
|
||||
private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos,
|
||||
boolean connected)
|
||||
throws IOException
|
||||
{
|
||||
int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected);
|
||||
@ -563,6 +701,25 @@ class DatagramChannelImpl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a datagram from the bytes in given buffer.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor.
|
||||
*
|
||||
* @throws IllegalBlockingModeException if the channel is non-blocking
|
||||
*/
|
||||
void blockingSend(ByteBuffer src, SocketAddress target) throws IOException {
|
||||
writeLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
send(src, target);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target)
|
||||
throws IOException
|
||||
{
|
||||
@ -785,10 +942,7 @@ class DatagramChannelImpl
|
||||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
}
|
||||
lockedConfigureBlocking(block);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
@ -797,6 +951,36 @@ class DatagramChannelImpl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the blocking mode. readLock or writeLock must already be held.
|
||||
*/
|
||||
private void lockedConfigureBlocking(boolean block) throws IOException {
|
||||
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
|
||||
synchronized (stateLock) {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the blocking mode if the channel is open. readLock or writeLock
|
||||
* must already be held.
|
||||
*
|
||||
* @return {@code true} if the blocking mode was adjusted, {@code false} if
|
||||
* the blocking mode was not adjusted because the channel is closed
|
||||
*/
|
||||
private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
|
||||
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
|
||||
synchronized (stateLock) {
|
||||
if (isOpen()) {
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress localAddress() {
|
||||
synchronized (stateLock) {
|
||||
return localAddress;
|
||||
@ -861,6 +1045,16 @@ class DatagramChannelImpl
|
||||
|
||||
@Override
|
||||
public DatagramChannel connect(SocketAddress sa) throws IOException {
|
||||
return connect(sa, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the channel's socket.
|
||||
*
|
||||
* @param sa the remote address to which this channel is to be connected
|
||||
* @param check true to check if the channel is already connected.
|
||||
*/
|
||||
DatagramChannel connect(SocketAddress sa, boolean check) throws IOException {
|
||||
InetSocketAddress isa = Net.checkAddress(sa, family);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
@ -879,7 +1073,7 @@ class DatagramChannelImpl
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
ensureOpen();
|
||||
if (state == ST_CONNECTED)
|
||||
if (check && state == ST_CONNECTED)
|
||||
throw new AlreadyConnectedException();
|
||||
|
||||
// ensure that the socket is bound
|
||||
@ -908,7 +1102,7 @@ class DatagramChannelImpl
|
||||
}
|
||||
try {
|
||||
ByteBuffer buf = ByteBuffer.allocate(100);
|
||||
while (receive(fd, buf, false) > 0) {
|
||||
while (receive(buf, false) >= 0) {
|
||||
buf.clear();
|
||||
}
|
||||
} finally {
|
||||
@ -1331,30 +1525,6 @@ class DatagramChannelImpl
|
||||
return translateReadyOps(ops, 0, ski);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll this channel's socket for reading up to the given timeout.
|
||||
* @return {@code true} if the socket is polled
|
||||
*/
|
||||
boolean pollRead(long timeout) throws IOException {
|
||||
boolean blocking = isBlocking();
|
||||
assert Thread.holdsLock(blockingLock()) && blocking;
|
||||
|
||||
readLock.lock();
|
||||
try {
|
||||
boolean polled = false;
|
||||
try {
|
||||
beginRead(blocking, false);
|
||||
int events = Net.poll(fd, Net.POLLIN, timeout);
|
||||
polled = (events != 0);
|
||||
} finally {
|
||||
endRead(blocking, polled);
|
||||
}
|
||||
return polled;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates an interest operation set into a native poll event set
|
||||
*/
|
||||
|
@ -26,6 +26,9 @@
|
||||
package sun.nio.ch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.DatagramSocketImpl;
|
||||
@ -35,15 +38,16 @@ import java.net.NetworkInterface;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketOption;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AlreadyConnectedException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.util.Objects;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
// Make a datagram-socket channel look like a datagram socket.
|
||||
//
|
||||
@ -61,13 +65,9 @@ class DatagramSocketAdaptor
|
||||
// Timeout "option" value for receives
|
||||
private volatile int timeout;
|
||||
|
||||
// ## super will create a useless impl
|
||||
// create DatagramSocket with useless impl
|
||||
private DatagramSocketAdaptor(DatagramChannelImpl dc) {
|
||||
// Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
|
||||
// passing a dummy DatagramSocketImpl object to avoid any native
|
||||
// resource allocation in super class and invoking our bind method
|
||||
// before the dc field is initialized.
|
||||
super(dummyDatagramSocket);
|
||||
super(new DummyDatagramSocketImpl());
|
||||
this.dc = dc;
|
||||
}
|
||||
|
||||
@ -75,17 +75,9 @@ class DatagramSocketAdaptor
|
||||
return new DatagramSocketAdaptor(dc);
|
||||
}
|
||||
|
||||
private void connectInternal(SocketAddress remote)
|
||||
throws SocketException
|
||||
{
|
||||
InetSocketAddress isa = Net.asInetSocketAddress(remote);
|
||||
int port = isa.getPort();
|
||||
if (port < 0 || port > 0xFFFF)
|
||||
throw new IllegalArgumentException("connect: " + port);
|
||||
if (remote == null)
|
||||
throw new IllegalArgumentException("connect: null address");
|
||||
private void connectInternal(SocketAddress remote) throws SocketException {
|
||||
try {
|
||||
dc.connect(remote);
|
||||
dc.connect(remote, false); // skips check for already connected
|
||||
} catch (ClosedChannelException e) {
|
||||
// ignore
|
||||
} catch (Exception x) {
|
||||
@ -95,9 +87,12 @@ class DatagramSocketAdaptor
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local) throws SocketException {
|
||||
if (local != null) {
|
||||
local = Net.asInetSocketAddress(local);
|
||||
} else {
|
||||
local = new InetSocketAddress(0);
|
||||
}
|
||||
try {
|
||||
if (local == null)
|
||||
local = new InetSocketAddress(0);
|
||||
dc.bind(local);
|
||||
} catch (Exception x) {
|
||||
Net.translateToSocketException(x);
|
||||
@ -106,17 +101,20 @@ class DatagramSocketAdaptor
|
||||
|
||||
@Override
|
||||
public void connect(InetAddress address, int port) {
|
||||
if (address == null)
|
||||
throw new IllegalArgumentException("Address can't be null");
|
||||
try {
|
||||
connectInternal(new InetSocketAddress(address, port));
|
||||
} catch (SocketException x) {
|
||||
// Yes, j.n.DatagramSocket really does this
|
||||
throw new Error(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remote) throws SocketException {
|
||||
Objects.requireNonNull(remote, "Address can't be null");
|
||||
connectInternal(remote);
|
||||
if (remote == null)
|
||||
throw new IllegalArgumentException("Address can't be null");
|
||||
connectInternal(Net.asInetSocketAddress(remote));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,80 +155,84 @@ class DatagramSocketAdaptor
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
return dc.localAddress();
|
||||
try {
|
||||
return dc.getLocalAddress();
|
||||
} catch (ClosedChannelException e) {
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
throw new Error(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(DatagramPacket p) throws IOException {
|
||||
synchronized (dc.blockingLock()) {
|
||||
if (!dc.isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
try {
|
||||
synchronized (p) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(p.getData(),
|
||||
p.getOffset(),
|
||||
p.getLength());
|
||||
if (dc.isConnected()) {
|
||||
if (p.getAddress() == null) {
|
||||
// Legacy DatagramSocket will send in this case
|
||||
// and set address and port of the packet
|
||||
InetSocketAddress isa = dc.remoteAddress();
|
||||
p.setPort(isa.getPort());
|
||||
p.setAddress(isa.getAddress());
|
||||
dc.write(bb);
|
||||
} else {
|
||||
// Target address may not match connected address
|
||||
dc.send(bb, p.getSocketAddress());
|
||||
}
|
||||
} else {
|
||||
// Not connected so address must be valid or throw
|
||||
dc.send(bb, p.getSocketAddress());
|
||||
ByteBuffer bb = null;
|
||||
try {
|
||||
InetSocketAddress target;
|
||||
synchronized (p) {
|
||||
// copy bytes to temporary direct buffer
|
||||
int len = p.getLength();
|
||||
bb = Util.getTemporaryDirectBuffer(len);
|
||||
bb.put(p.getData(), p.getOffset(), len);
|
||||
bb.flip();
|
||||
|
||||
// target address
|
||||
if (p.getAddress() == null) {
|
||||
InetSocketAddress remote = dc.remoteAddress();
|
||||
if (remote == null) {
|
||||
// not specified by DatagramSocket
|
||||
throw new IllegalArgumentException("Address not set");
|
||||
}
|
||||
// set address/port to maintain compatibility with DatagramSocket
|
||||
p.setAddress(remote.getAddress());
|
||||
p.setPort(remote.getPort());
|
||||
target = remote;
|
||||
} else {
|
||||
// throws IllegalArgumentException if port not set
|
||||
target = (InetSocketAddress) p.getSocketAddress();
|
||||
}
|
||||
} catch (IOException x) {
|
||||
Net.translateException(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SocketAddress receive(ByteBuffer bb) throws IOException {
|
||||
assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking();
|
||||
|
||||
long to = this.timeout;
|
||||
if (to == 0) {
|
||||
return dc.receive(bb);
|
||||
} else {
|
||||
for (;;) {
|
||||
if (!dc.isOpen())
|
||||
throw new ClosedChannelException();
|
||||
long st = System.currentTimeMillis();
|
||||
if (dc.pollRead(to)) {
|
||||
return dc.receive(bb);
|
||||
}
|
||||
to -= System.currentTimeMillis() - st;
|
||||
if (to <= 0)
|
||||
throw new SocketTimeoutException();
|
||||
// send datagram
|
||||
try {
|
||||
dc.blockingSend(bb, target);
|
||||
} catch (AlreadyConnectedException e) {
|
||||
throw new IllegalArgumentException("Connected and packet address differ");
|
||||
} catch (ClosedChannelException e) {
|
||||
var exc = new SocketException("Socket closed");
|
||||
exc.initCause(e);
|
||||
throw exc;
|
||||
}
|
||||
} finally {
|
||||
if (bb != null) {
|
||||
Util.offerFirstTemporaryDirectBuffer(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(DatagramPacket p) throws IOException {
|
||||
synchronized (dc.blockingLock()) {
|
||||
if (!dc.isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
try {
|
||||
synchronized (p) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(p.getData(),
|
||||
p.getOffset(),
|
||||
p.getLength());
|
||||
SocketAddress sender = receive(bb);
|
||||
p.setSocketAddress(sender);
|
||||
p.setLength(bb.position() - p.getOffset());
|
||||
}
|
||||
} catch (IOException x) {
|
||||
Net.translateException(x);
|
||||
// get temporary direct buffer with a capacity of p.bufLength
|
||||
int bufLength = DatagramPackets.getBufLength(p);
|
||||
ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength);
|
||||
try {
|
||||
long nanos = MILLISECONDS.toNanos(timeout);
|
||||
SocketAddress sender = dc.blockingReceive(bb, nanos);
|
||||
bb.flip();
|
||||
synchronized (p) {
|
||||
// copy bytes to the DatagramPacket and set length
|
||||
int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p));
|
||||
bb.get(p.getData(), p.getOffset(), len);
|
||||
DatagramPackets.setLength(p, len);
|
||||
|
||||
// sender address
|
||||
p.setSocketAddress(sender);
|
||||
}
|
||||
} catch (ClosedChannelException e) {
|
||||
var exc = new SocketException("Socket closed");
|
||||
exc.initCause(e);
|
||||
throw exc;
|
||||
} finally {
|
||||
Util.offerFirstTemporaryDirectBuffer(bb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,19 +259,16 @@ class DatagramSocketAdaptor
|
||||
public int getLocalPort() {
|
||||
if (isClosed())
|
||||
return -1;
|
||||
try {
|
||||
InetSocketAddress local = dc.localAddress();
|
||||
if (local != null) {
|
||||
return local.getPort();
|
||||
}
|
||||
} catch (Exception x) {
|
||||
InetSocketAddress local = dc.localAddress();
|
||||
if (local != null) {
|
||||
return local.getPort();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoTimeout(int timeout) throws SocketException {
|
||||
if (!dc.isOpen())
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
@ -278,7 +277,7 @@ class DatagramSocketAdaptor
|
||||
|
||||
@Override
|
||||
public int getSoTimeout() throws SocketException {
|
||||
if (!dc.isOpen())
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
return timeout;
|
||||
}
|
||||
@ -353,7 +352,6 @@ class DatagramSocketAdaptor
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -411,50 +409,157 @@ class DatagramSocketAdaptor
|
||||
return dc.supportedOptions();
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy implementation of DatagramSocketImpl that can be passed to the
|
||||
* DatagramSocket constructor so that no native resources are allocated in
|
||||
* super class.
|
||||
*/
|
||||
private static final DatagramSocketImpl dummyDatagramSocket
|
||||
= new DatagramSocketImpl()
|
||||
{
|
||||
protected void create() throws SocketException {}
|
||||
|
||||
protected void bind(int lport, InetAddress laddr) throws SocketException {}
|
||||
/**
|
||||
* DatagramSocketImpl implementation where all methods throw an error.
|
||||
*/
|
||||
private static class DummyDatagramSocketImpl extends DatagramSocketImpl {
|
||||
private static <T> T shouldNotGetHere() {
|
||||
throw new InternalError("Should not get here");
|
||||
}
|
||||
|
||||
protected void send(DatagramPacket p) throws IOException {}
|
||||
@Override
|
||||
protected void create() {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected int peek(InetAddress i) throws IOException { return 0; }
|
||||
@Override
|
||||
protected void bind(int lport, InetAddress laddr) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected int peekData(DatagramPacket p) throws IOException { return 0; }
|
||||
@Override
|
||||
protected void send(DatagramPacket p) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void receive(DatagramPacket p) throws IOException {}
|
||||
@Override
|
||||
protected int peek(InetAddress address) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setTTL(byte ttl) throws IOException {}
|
||||
@Override
|
||||
protected int peekData(DatagramPacket p) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected byte getTTL() throws IOException { return 0; }
|
||||
@Override
|
||||
protected void receive(DatagramPacket p) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void setTimeToLive(int ttl) throws IOException {}
|
||||
@Deprecated
|
||||
protected void setTTL(byte ttl) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected int getTimeToLive() throws IOException { return 0;}
|
||||
@Deprecated
|
||||
protected byte getTTL() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void join(InetAddress inetaddr) throws IOException {}
|
||||
@Override
|
||||
protected void setTimeToLive(int ttl) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void leave(InetAddress inetaddr) throws IOException {}
|
||||
@Override
|
||||
protected int getTimeToLive() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void joinGroup(SocketAddress mcastaddr,
|
||||
NetworkInterface netIf) throws IOException {}
|
||||
@Override
|
||||
protected void join(InetAddress group) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void leaveGroup(SocketAddress mcastaddr,
|
||||
NetworkInterface netIf) throws IOException {}
|
||||
@Override
|
||||
protected void leave(InetAddress inetaddr) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
protected void close() {}
|
||||
@Override
|
||||
protected void joinGroup(SocketAddress group, NetworkInterface netIf) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
public Object getOption(int optID) throws SocketException { return null;}
|
||||
@Override
|
||||
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
public void setOption(int optID, Object value) throws SocketException {}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
protected void close() {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOption(int optID) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOption(int optID, Object value) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void setOption(SocketOption<T> name, T value) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> T getOption(SocketOption<T> name) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<SocketOption<?>> supportedOptions() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines static methods to get/set DatagramPacket fields and workaround
|
||||
* DatagramPacket deficiencies.
|
||||
*/
|
||||
private static class DatagramPackets {
|
||||
private static final VarHandle LENGTH;
|
||||
private static final VarHandle BUF_LENGTH;
|
||||
static {
|
||||
try {
|
||||
PrivilegedAction<Lookup> pa = () -> {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup());
|
||||
} catch (Exception e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
};
|
||||
MethodHandles.Lookup l = AccessController.doPrivileged(pa);
|
||||
LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class);
|
||||
BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class);
|
||||
} catch (Exception e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be
|
||||
* used at this time because it sets both the length and bufLength fields.
|
||||
*/
|
||||
static void setLength(DatagramPacket p, int value) {
|
||||
synchronized (p) {
|
||||
LENGTH.set(p, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the DatagramPacket.bufLength field.
|
||||
*/
|
||||
static int getBufLength(DatagramPacket p) {
|
||||
synchronized (p) {
|
||||
return (int) BUF_LENGTH.get(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,20 +22,22 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 4313882 4981129 8143610
|
||||
* @bug 4313882 4981129 8143610 8232673
|
||||
* @summary Unit test for datagram-socket-channel adaptors
|
||||
* @modules java.base/java.net:+open
|
||||
* @library .. /test/lib
|
||||
* @build jdk.test.lib.Utils TestServers
|
||||
* @run main AdaptDatagramSocket
|
||||
* @run main AdaptorBasic
|
||||
* @key randomness
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
|
||||
public class AdaptDatagramSocket {
|
||||
public class AdaptorBasic {
|
||||
|
||||
static java.io.PrintStream out = System.out;
|
||||
static Random rand = new Random();
|
||||
@ -46,13 +48,19 @@ public class AdaptDatagramSocket {
|
||||
+ "]");
|
||||
}
|
||||
|
||||
static void test(DatagramSocket ds, InetSocketAddress dst,
|
||||
boolean shouldTimeout)
|
||||
static int getBufLength(DatagramPacket p) throws Exception {
|
||||
Field f = DatagramPacket.class.getDeclaredField("bufLength");
|
||||
f.setAccessible(true);
|
||||
return (int) f.get(p);
|
||||
}
|
||||
|
||||
static void test(DatagramSocket ds, InetSocketAddress dst, boolean shouldTimeout)
|
||||
throws Exception
|
||||
{
|
||||
DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
|
||||
rand.nextBytes(op.getData());
|
||||
DatagramPacket ip = new DatagramPacket(new byte[100], 19, 100 - 19);
|
||||
int bufLength = 100 - 19;
|
||||
DatagramPacket ip = new DatagramPacket(new byte[100], 19, bufLength);
|
||||
out.println("pre op: " + toString(op) + " ip: " + toString(ip));
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
@ -61,10 +69,6 @@ public class AdaptDatagramSocket {
|
||||
for (;;) {
|
||||
try {
|
||||
ds.receive(ip);
|
||||
if (ip.getLength() == 0) { // ## Not sure why this happens
|
||||
ip.setLength(100 - 19);
|
||||
continue;
|
||||
}
|
||||
} catch (SocketTimeoutException x) {
|
||||
if (shouldTimeout) {
|
||||
out.println("Receive timed out, as expected");
|
||||
@ -88,6 +92,10 @@ public class AdaptDatagramSocket {
|
||||
throw new Exception("Incorrect sender address, expected: " + dst
|
||||
+ " actual: " + ip.getSocketAddress());
|
||||
}
|
||||
|
||||
if (getBufLength(ip) != bufLength) {
|
||||
throw new Exception("DatagramPacket bufLength changed by receive!!!");
|
||||
}
|
||||
}
|
||||
|
||||
static void test(InetSocketAddress dst,
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8232673
|
||||
* @summary Test DatagramChannel socket adaptor with concurrent send/receive
|
||||
*/
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class AdaptorConcurrentIO {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testConcurrentSendReceive(0);
|
||||
testConcurrentSendReceive(60_000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a task that blocks in the adaptor's receive method, then invokes
|
||||
* the adaptor's send method to send a datagram. If the adaptor were using
|
||||
* the channel's blockingLock then send without be blocked waiting for
|
||||
* the receive to complete.
|
||||
*/
|
||||
static void testConcurrentSendReceive(int timeout) throws Exception {
|
||||
try (DatagramChannel dc = DatagramChannel.open()) {
|
||||
InetAddress lb = InetAddress.getLoopbackAddress();
|
||||
dc.bind(new InetSocketAddress(lb, 0));
|
||||
DatagramSocket s = dc.socket();
|
||||
s.setSoTimeout(timeout);
|
||||
|
||||
ExecutorService pool = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
Future<String> result = pool.submit(() -> {
|
||||
byte[] data = new byte[100];
|
||||
DatagramPacket p = new DatagramPacket(data, 0, data.length);
|
||||
s.receive(p);
|
||||
return new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8");
|
||||
});
|
||||
|
||||
Thread.sleep(200); // give chance for thread to block
|
||||
|
||||
byte[] data = "hello".getBytes("UTF-8");
|
||||
DatagramPacket p = new DatagramPacket(data, 0, data.length);
|
||||
p.setSocketAddress(s.getLocalSocketAddress());
|
||||
s.send(p);
|
||||
|
||||
String msg = result.get();
|
||||
if (!msg.equals("hello"))
|
||||
throw new RuntimeException("Unexpected message: " + msg);
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
117
test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java
Normal file
117
test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8232673
|
||||
* @summary Test DatagramChannel socket adaptor connect method with illegal args
|
||||
* @run testng AdaptorConnect
|
||||
*/
|
||||
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import static java.net.InetAddress.getLoopbackAddress;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class AdaptorConnect {
|
||||
|
||||
/**
|
||||
* Invoke the given socket's connect method with illegal arguments.
|
||||
*/
|
||||
private void testConnectWithIllegalArguments(DatagramSocket s) {
|
||||
assertThrows(IllegalArgumentException.class, () -> s.connect(null));
|
||||
assertThrows(IllegalArgumentException.class, () -> s.connect(null, 7000));
|
||||
assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), -1));
|
||||
assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), 100_000));
|
||||
|
||||
SocketAddress sillyAddress = new SocketAddress() { };
|
||||
assertThrows(IllegalArgumentException.class, () -> s.connect(sillyAddress));
|
||||
|
||||
SocketAddress unresolved = InetSocketAddress.createUnresolved("foo", 7777);
|
||||
assertThrows(SocketException.class, () -> s.connect(unresolved));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connect method with an open socket.
|
||||
*/
|
||||
public void testOpenSocket() throws Exception {
|
||||
try (DatagramChannel dc = DatagramChannel.open()) {
|
||||
DatagramSocket s = dc.socket();
|
||||
|
||||
testConnectWithIllegalArguments(s);
|
||||
|
||||
// should not be bound or connected
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
assertTrue(s.getRemoteSocketAddress() == null);
|
||||
|
||||
// connect(SocketAddress)
|
||||
var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001);
|
||||
s.connect(remote1);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote1);
|
||||
testConnectWithIllegalArguments(s);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote1);
|
||||
|
||||
// connect(SocketAddress)
|
||||
var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002);
|
||||
s.connect(remote2);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote2);
|
||||
testConnectWithIllegalArguments(s);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote2);
|
||||
|
||||
// connect(InetAddress, int)
|
||||
var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003);
|
||||
s.connect(remote3.getAddress(), remote3.getPort());
|
||||
assertEquals(s.getRemoteSocketAddress(), remote3);
|
||||
testConnectWithIllegalArguments(s);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote3);
|
||||
|
||||
// connect(InetAddress, int)
|
||||
var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004);
|
||||
s.connect(remote4.getAddress(), remote4.getPort());
|
||||
assertEquals(s.getRemoteSocketAddress(), remote4);
|
||||
testConnectWithIllegalArguments(s);
|
||||
assertEquals(s.getRemoteSocketAddress(), remote4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connect method with a closed socket.
|
||||
*/
|
||||
public void testClosedSocket() throws Exception {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
DatagramSocket s = dc.socket();
|
||||
dc.close();
|
||||
|
||||
testConnectWithIllegalArguments(s);
|
||||
|
||||
// connect does not throw an exception when closed
|
||||
var remote = new InetSocketAddress(getLoopbackAddress(), 7001);
|
||||
s.connect(remote);
|
||||
s.connect(remote.getAddress(), remote.getPort());
|
||||
}
|
||||
}
|
223
test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java
Normal file
223
test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8232673
|
||||
* @summary Test the DatagramChannel socket adaptor getter methods
|
||||
* @run testng AdaptorGetters
|
||||
*/
|
||||
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class AdaptorGetters {
|
||||
|
||||
/**
|
||||
* Test getters on unbound socket, before and after it is closed.
|
||||
*/
|
||||
public void testUnboundSocket() throws Exception {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
DatagramSocket s = dc.socket();
|
||||
try {
|
||||
|
||||
// state
|
||||
assertFalse(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertFalse(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertTrue(s.getLocalAddress().isAnyLocalAddress());
|
||||
assertTrue(s.getLocalPort() == 0);
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
|
||||
} finally {
|
||||
dc.close();
|
||||
}
|
||||
|
||||
// state
|
||||
assertFalse(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertTrue(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertTrue(s.getLocalAddress() == null);
|
||||
assertTrue(s.getLocalPort() == -1);
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
assertTrue((s.getRemoteSocketAddress() == null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getters on bound socket, before and after it is closed.
|
||||
*/
|
||||
public void testBoundSocket() throws Exception {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
DatagramSocket s = dc.socket();
|
||||
try {
|
||||
dc.bind(new InetSocketAddress(0));
|
||||
var localAddress = (InetSocketAddress) dc.getLocalAddress();
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertFalse(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertEquals(s.getLocalAddress(), localAddress.getAddress());
|
||||
assertTrue(s.getLocalPort() == localAddress.getPort());
|
||||
assertEquals(s.getLocalSocketAddress(), localAddress);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
assertTrue((s.getRemoteSocketAddress() == null));
|
||||
|
||||
} finally {
|
||||
dc.close();
|
||||
}
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertTrue(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertTrue(s.getLocalAddress() == null);
|
||||
assertTrue(s.getLocalPort() == -1);
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
assertTrue((s.getRemoteSocketAddress() == null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getters on connected socket, before and after it is closed.
|
||||
*/
|
||||
public void testConnectedSocket() throws Exception {
|
||||
var loopback = InetAddress.getLoopbackAddress();
|
||||
var remoteAddress = new InetSocketAddress(loopback, 7777);
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
DatagramSocket s = dc.socket();
|
||||
try {
|
||||
dc.connect(remoteAddress);
|
||||
var localAddress = (InetSocketAddress) dc.getLocalAddress();
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertTrue(s.isConnected());
|
||||
assertFalse(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertEquals(s.getLocalAddress(), localAddress.getAddress());
|
||||
assertTrue(s.getLocalPort() == localAddress.getPort());
|
||||
assertEquals(s.getLocalSocketAddress(), localAddress);
|
||||
|
||||
// remote address
|
||||
assertEquals(s.getInetAddress(), remoteAddress.getAddress());
|
||||
assertTrue(s.getPort() == remoteAddress.getPort());
|
||||
assertEquals(s.getRemoteSocketAddress(), remoteAddress);
|
||||
|
||||
} finally {
|
||||
dc.close();
|
||||
}
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertTrue(s.isConnected());
|
||||
assertTrue(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertTrue(s.getLocalAddress() == null);
|
||||
assertTrue(s.getLocalPort() == -1);
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
|
||||
// remote address
|
||||
assertEquals(s.getInetAddress(), remoteAddress.getAddress());
|
||||
assertTrue(s.getPort() == remoteAddress.getPort());
|
||||
assertEquals(s.getRemoteSocketAddress(), remoteAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getters on disconnected socket, before and after it is closed.
|
||||
*/
|
||||
public void testDisconnectedSocket() throws Exception {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
DatagramSocket s = dc.socket();
|
||||
try {
|
||||
var loopback = InetAddress.getLoopbackAddress();
|
||||
dc.connect(new InetSocketAddress(loopback, 7777));
|
||||
dc.disconnect();
|
||||
|
||||
var localAddress = (InetSocketAddress) dc.getLocalAddress();
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertFalse(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertEquals(s.getLocalAddress(), localAddress.getAddress());
|
||||
assertTrue(s.getLocalPort() == localAddress.getPort());
|
||||
assertEquals(s.getLocalSocketAddress(), localAddress);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
assertTrue((s.getRemoteSocketAddress() == null));
|
||||
|
||||
|
||||
} finally {
|
||||
dc.close();
|
||||
}
|
||||
|
||||
// state
|
||||
assertTrue(s.isBound());
|
||||
assertFalse(s.isConnected());
|
||||
assertTrue(s.isClosed());
|
||||
|
||||
// local address
|
||||
assertTrue(s.getLocalAddress() == null);
|
||||
assertTrue(s.getLocalPort() == -1);
|
||||
assertTrue(s.getLocalSocketAddress() == null);
|
||||
|
||||
// remote address
|
||||
assertTrue(s.getInetAddress() == null);
|
||||
assertTrue(s.getPort() == -1);
|
||||
assertTrue((s.getRemoteSocketAddress() == null));
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 7184932
|
||||
* @bug 7184932 8232673
|
||||
* @summary Test asynchronous close and interrupt of timed socket adapter methods
|
||||
* @key randomness intermittent
|
||||
*/
|
||||
@ -78,8 +78,10 @@ public class AdaptorCloseAndInterrupt {
|
||||
|
||||
try (DatagramChannel peer = DatagramChannel.open()) {
|
||||
peer.socket().bind(null);
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose();
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt();
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(0);
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(30_000);
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(0);
|
||||
new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(30_000);
|
||||
}
|
||||
|
||||
new AdaptorCloseAndInterrupt().ssAcceptAsyncClose();
|
||||
@ -138,11 +140,10 @@ public class AdaptorCloseAndInterrupt {
|
||||
}
|
||||
}
|
||||
|
||||
void dcReceiveAsyncClose() throws IOException {
|
||||
void dcReceiveAsyncClose(int timeout) throws IOException {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
dc.connect(new InetSocketAddress(
|
||||
InetAddress.getLoopbackAddress(), port));
|
||||
dc.socket().setSoTimeout(30*1000);
|
||||
dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
|
||||
dc.socket().setSoTimeout(timeout);
|
||||
|
||||
doAsyncClose(dc);
|
||||
|
||||
@ -150,24 +151,23 @@ public class AdaptorCloseAndInterrupt {
|
||||
dc.socket().receive(new DatagramPacket(new byte[100], 100));
|
||||
System.err.format("close() was invoked: %s%n", isClosed.get());
|
||||
throw new RuntimeException("receive should not have completed");
|
||||
} catch (ClosedChannelException expected) {}
|
||||
} catch (SocketException expected) { }
|
||||
|
||||
if (!dc.socket().isClosed())
|
||||
throw new RuntimeException("socket is not closed");
|
||||
}
|
||||
|
||||
void dcReceiveAsyncInterrupt() throws IOException {
|
||||
void dcReceiveAsyncInterrupt(int timeout) throws IOException {
|
||||
DatagramChannel dc = DatagramChannel.open();
|
||||
dc.connect(new InetSocketAddress(
|
||||
InetAddress.getLoopbackAddress(), port));
|
||||
dc.socket().setSoTimeout(30*1000);
|
||||
dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
|
||||
dc.socket().setSoTimeout(timeout);
|
||||
|
||||
doAsyncInterrupt();
|
||||
|
||||
try {
|
||||
dc.socket().receive(new DatagramPacket(new byte[100], 100));
|
||||
throw new RuntimeException("receive should not have completed");
|
||||
} catch (ClosedByInterruptException expected) {
|
||||
} catch (SocketException expected) {
|
||||
System.out.format("interrupt() was invoked: %s%n",
|
||||
isInterrupted.get());
|
||||
System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n",
|
||||
|
Loading…
x
Reference in New Issue
Block a user