6621689: (dc spec) DatagramChannel.receive when channel is not bound is not specified
authoralanb
Tue, 24 Mar 2009 14:10:38 +0000
changeset 2427 f35f516befc3
parent 2426 a528c0830862
child 2428 e63d91602813
child 2433 9f0ab7f726b8
6621689: (dc spec) DatagramChannel.receive when channel is not bound is not specified Reviewed-by: sherman
jdk/src/share/classes/java/nio/channels/DatagramChannel.java
jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java
jdk/test/java/nio/channels/DatagramChannel/NotBound.java
--- a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java	Tue Mar 24 14:08:37 2009 +0000
+++ b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java	Tue Mar 24 14:10:38 2009 +0000
@@ -261,7 +261,10 @@
      *
      * <p> This method may be invoked at any time.  It will not have any effect
      * on read or write operations that are already in progress at the moment
-     * that it is invoked.  </p>
+     * that it is invoked. If this channel's socket is not bound then this method
+     * will first cause the socket to be bound to an address that is assigned
+     * automatically, as if invoking the {@link #bind bind} method with a
+     * parameter of {@code null}. </p>
      *
      * @param  remote
      *         The remote address to which this channel is to be connected
@@ -356,7 +359,10 @@
      * <p> This method may be invoked at any time.  If another thread has
      * already initiated a read operation upon this channel, however, then an
      * invocation of this method will block until the first operation is
-     * complete. </p>
+     * complete. If this channel's socket is not bound then this method will
+     * first cause the socket to be bound to an address that is assigned
+     * automatically, as if invoking the {@link #bind bind} method with a
+     * parameter of {@code null}. </p>
      *
      * @param  dst
      *         The buffer into which the datagram is to be transferred
@@ -413,7 +419,10 @@
      * <p> This method may be invoked at any time.  If another thread has
      * already initiated a write operation upon this channel, however, then an
      * invocation of this method will block until the first operation is
-     * complete. </p>
+     * complete. If this channel's socket is not bound then this method will
+     * first cause the socket to be bound to an address that is assigned
+     * automatically, as if by invoking the {@link #bind bind) method with a
+     * parameter of {@code null}. </p>
      *
      * @param  src
      *         The buffer containing the datagram to be sent
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Tue Mar 24 14:08:37 2009 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Tue Mar 24 14:10:38 2009 +0000
@@ -313,11 +313,9 @@
             throw new NullPointerException();
         synchronized (readLock) {
             ensureOpen();
-            // If socket is not bound then behave as if nothing received
-            // Will be fixed by 6621699
-            if (localAddress() == null) {
-                return null;
-            }
+            // Socket was not bound before attempting receive
+            if (localAddress() == null)
+                bind(null);
             int n = 0;
             ByteBuffer bb = null;
             try {
--- a/jdk/test/java/nio/channels/DatagramChannel/NotBound.java	Tue Mar 24 14:08:37 2009 +0000
+++ b/jdk/test/java/nio/channels/DatagramChannel/NotBound.java	Tue Mar 24 14:10:38 2009 +0000
@@ -22,27 +22,110 @@
  */
 
 /* @test
- * @bug 4512723
- * @summary Unit test for datagram-socket-channel adaptors
+ * @bug 4512723 6621689
+ * @summary Test that connect/send/receive with unbound DatagramChannel causes
+ *     the channel's socket to be bound to a local address
  */
 
 import java.net.*;
-import java.nio.*;
-import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.io.IOException;
+
+public class NotBound {
+
+    static void checkBound(DatagramChannel dc) throws IOException {
+        if (dc.getLocalAddress() == null)
+            throw new RuntimeException("Not bound??");
+    }
 
-class NotBound {
-    public static void main(String[] args) throws Exception {
-        test1(false);
-        test1(true);
+    // starts a thread to send a datagram to the given channel once the channel
+    // is bound to a local address
+    static void wakeupWhenBound(final DatagramChannel dc) {
+        Runnable wakeupTask = new Runnable() {
+            public void run() {
+                try {
+                    // poll for local address
+                    InetSocketAddress local;
+                    do {
+                        Thread.sleep(50);
+                        local = (InetSocketAddress)dc.getLocalAddress();
+                    } while (local == null);
+
+                    // send message to channel to wakeup receiver
+                    DatagramChannel sender = DatagramChannel.open();
+                    try {
+                        ByteBuffer bb = ByteBuffer.wrap("hello".getBytes());
+                        InetAddress lh = InetAddress.getLocalHost();
+                        SocketAddress target =
+                            new InetSocketAddress(lh, local.getPort());
+                        sender.send(bb, target);
+                    } finally {
+                        sender.close();
+                    }
+
+                } catch (Exception x) {
+                    x.printStackTrace();
+                }
+            }};
+        new Thread(wakeupTask).start();
     }
 
-    static void test1(boolean blocking) throws Exception {
-        ByteBuffer bb = ByteBuffer.allocateDirect(256);
-        DatagramChannel dc1 = DatagramChannel.open();
-        dc1.configureBlocking(false);
-        SocketAddress isa = dc1.receive(bb);
-        if (isa != null)
-            throw new Exception("Unbound dc returned non-null");
-        dc1.close();
+    public static void main(String[] args) throws IOException {
+        DatagramChannel dc;
+
+        // connect
+        dc = DatagramChannel.open();
+        try {
+            DatagramChannel peer = DatagramChannel.open()
+                .bind(new InetSocketAddress(0));
+            int peerPort = ((InetSocketAddress)(peer.getLocalAddress())).getPort();
+            try {
+                dc.connect(new InetSocketAddress(InetAddress.getLocalHost(), peerPort));
+                checkBound(dc);
+            } finally {
+                peer.close();
+            }
+        } finally {
+            dc.close();
+        }
+
+        // send
+        dc = DatagramChannel.open();
+        try {
+            ByteBuffer bb = ByteBuffer.wrap("ignore this".getBytes());
+            SocketAddress target =
+                new InetSocketAddress(InetAddress.getLocalHost(), 5000);
+            dc.send(bb, target);
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
+
+        // receive (blocking)
+        dc = DatagramChannel.open();
+        try {
+            ByteBuffer bb = ByteBuffer.allocateDirect(128);
+            wakeupWhenBound(dc);
+            SocketAddress sender = dc.receive(bb);
+            if (sender == null)
+                throw new RuntimeException("Sender should not be null");
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
+
+        // receive (non-blocking)
+        dc = DatagramChannel.open();
+        try {
+            dc.configureBlocking(false);
+            ByteBuffer bb = ByteBuffer.allocateDirect(128);
+            SocketAddress sender = dc.receive(bb);
+            if (sender != null)
+                throw new RuntimeException("Sender should be null");
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
     }
 }