--- a/src/java.base/share/classes/java/net/SocketImpl.java Mon May 06 09:43:48 2019 +0100
+++ b/src/java.base/share/classes/java/net/SocketImpl.java Thu May 09 17:49:28 2019 +0100
@@ -43,21 +43,21 @@
* create both client and server sockets.
*
* @implNote Client and server sockets created with the {@code Socket} and
- * {@code SocketServer} public constructors create a system/platform default
+ * {@code SocketServer} public constructors create a system-default
* {@code SocketImpl}. The JDK historically used a {@code SocketImpl}
* implementation type named "PlainSocketImpl" that has since been replaced by a
* newer implementation. The JDK continues to ship with the older implementation
* to allow code to run that depends on unspecified behavior that differs between
* the old and new implementations. The old implementation will be used if the
* Java virtual machine is started with the system property {@systemProperty
- * jdk.net.usePlainSocketImpl} set on the command line. It may also be set in
- * the JDK's network configuration file, located in {@code
- * ${java.home}/conf/net.properties}, for cases where it needs to be set without
- * changing the command line. The value of the system property is the string
+ * jdk.net.usePlainSocketImpl} set to use the old implementation. It may also be
+ * set in the JDK's network configuration file, located in {@code
+ * ${java.home}/conf/net.properties}. The value of the property is the string
* representation of a boolean. If set without a value then it defaults to {@code
* true}, hence running with {@code -Djdk.net.usePlainSocketImpl} or {@code
* -Djdk.net.usePlainSocketImpl=true} will configure the Java virtual machine
- * to use the old implementation.
+ * to use the old implementation. The property and old implementation will be
+ * removed in a future version.
*
* @author unascribed
* @since 1.0
--- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Mon May 06 09:43:48 2019 +0100
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java Thu May 09 17:49:28 2019 +0100
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.InetAddress;
@@ -107,7 +108,7 @@
private boolean stream;
private FileDescriptorCloser closer;
- // set by configureNonBlockingForever when the socket changed to non-blocking
+ // set to true when the socket is in non-blocking mode
private volatile boolean nonBlocking;
// used by connect/read/write/accept, protected by stateLock
@@ -191,29 +192,28 @@
}
/**
- * Configures the socket's blocking mode except when socket has been
- * configured non-blocking by {@code configureNonBlockingForever}.
+ * Configures the socket to blocking mode. This method is a no-op if the
+ * socket is already in blocking mode.
* @throws IOException if closed or there is an I/O error changing the mode
*/
- private void configureBlocking(FileDescriptor fd, boolean block) throws IOException {
- assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
- if (!nonBlocking) {
+ private void configureBlocking(FileDescriptor fd) throws IOException {
+ assert readLock.isHeldByCurrentThread();
+ if (nonBlocking) {
synchronized (stateLock) {
- if (!nonBlocking) {
- ensureOpen();
- IOUtil.configureBlocking(fd, block);
- }
+ ensureOpen();
+ IOUtil.configureBlocking(fd, true);
+ nonBlocking = false;
}
}
}
/**
- * Configures the socket to be non-blocking. Once configured to non-blocking
- * by this method then the blocking mode cannot be changed back to blocking.
+ * Configures the socket to non-blocking mode. This method is a no-op if the
+ * socket is already in non-blocking mode.
* @throws IOException if closed or there is an I/O error changing the mode
*/
- private void configureNonBlockingForever(FileDescriptor fd) throws IOException {
- assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+ private void configureNonBlocking(FileDescriptor fd) throws IOException {
+ assert readLock.isHeldByCurrentThread();
if (!nonBlocking) {
synchronized (stateLock) {
ensureOpen();
@@ -244,7 +244,7 @@
readerThread = 0;
int state = this.state;
if (state == ST_CLOSING)
- tryClose();
+ tryFinishClose();
if (!completed && state >= ST_CLOSING)
throw new SocketException("Socket closed");
}
@@ -306,7 +306,7 @@
int timeout = this.timeout;
if (timeout > 0) {
// read with timeout
- configureNonBlockingForever(fd);
+ configureNonBlocking(fd);
n = timedRead(fd, b, off, len, MILLISECONDS.toNanos(timeout));
} else {
// read, no timeout
@@ -379,7 +379,7 @@
writerThread = 0;
int state = this.state;
if (state == ST_CLOSING)
- tryClose();
+ tryFinishClose();
if (!completed && state >= ST_CLOSING)
throw new SocketException("Socket closed");
}
@@ -405,7 +405,7 @@
/**
* Writes a sequence of bytes to the socket from the given byte array.
* @return the number of bytes written
- * @throws SocketException if the socket is closed or an socket I/O error occurs
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
*/
private int implWrite(byte[] b, int off, int len) throws IOException {
int n = 0;
@@ -426,7 +426,7 @@
/**
* Writes a sequence of bytes to the socket from the given byte array.
- * @throws SocketException if the socket is closed or an socket I/O error occurs
+ * @throws SocketException if the socket is closed or a socket I/O error occurs
*/
private void write(byte[] b, int off, int len) throws IOException {
Objects.checkFromIndexSize(off, len, b.length);
@@ -523,7 +523,7 @@
readerThread = 0;
int state = this.state;
if (state == ST_CLOSING)
- tryClose();
+ tryFinishClose();
if (completed && state == ST_CONNECTING) {
this.state = ST_CONNECTED;
localport = Net.localAddress(fd).getPort();
@@ -582,7 +582,7 @@
// configure socket to non-blocking mode when there is a timeout
if (millis > 0) {
- configureBlocking(fd, false);
+ configureNonBlocking(fd);
}
int n = Net.connect(fd, address, port);
@@ -608,7 +608,7 @@
// restore socket to blocking mode
if (connected && millis > 0) {
- configureBlocking(fd, true);
+ configureBlocking(fd);
}
} finally {
@@ -684,7 +684,7 @@
int state = this.state;
readerThread = 0;
if (state == ST_CLOSING)
- tryClose();
+ tryFinishClose();
if (!completed && state >= ST_CLOSING)
throw new SocketException("Socket closed");
}
@@ -748,7 +748,7 @@
try {
if (remainingNanos > 0) {
// accept with timeout
- configureNonBlockingForever(fd);
+ configureNonBlocking(fd);
n = timedAccept(fd, newfd, isaa, remainingNanos);
} else {
// accept, no timeout
@@ -844,14 +844,15 @@
}
/**
- * Closes the socket, and returns true, if there are no I/O operations in
- * progress.
+ * Closes the socket if there are no I/O operations in progress.
*/
- private boolean tryClose() {
+ private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
if (readerThread == 0 && writerThread == 0) {
try {
closer.run();
+ } catch (UncheckedIOException ioe) {
+ throw ioe.getCause();
} finally {
state = ST_CLOSED;
}
@@ -862,6 +863,17 @@
}
/**
+ * Invokes tryClose to attempt to close the socket.
+ *
+ * This method is used for deferred closing by I/O operations.
+ */
+ private void tryFinishClose() {
+ try {
+ tryClose();
+ } catch (IOException ignore) { }
+ }
+
+ /**
* Closes the socket. If there are I/O operations in progress then the
* socket is pre-closed and the threads are signalled. The socket will be
* closed when the last I/O operation aborts.
@@ -1218,7 +1230,7 @@
try {
nd.close(fd);
} catch (IOException ioe) {
- throw new RuntimeException(ioe);
+ throw new UncheckedIOException(ioe);
} finally {
if (!stream) {
// decrement
--- a/test/jdk/java/net/Socket/Timeouts.java Mon May 06 09:43:48 2019 +0100
+++ b/test/jdk/java/net/Socket/Timeouts.java Thu May 09 17:49:28 2019 +0100
@@ -96,10 +96,8 @@
public void testTimedConnect4() throws IOException {
try (ServerSocket ss = boundServerSocket()) {
try (Socket s = new Socket()) {
- try {
- s.connect(ss.getLocalSocketAddress(), -1);
- assertTrue(false);
- } catch (IllegalArgumentException expected) { }
+ expectThrows(IllegalArgumentException.class,
+ () -> s.connect(ss.getLocalSocketAddress(), -1));
}
}
}
@@ -135,13 +133,9 @@
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
long startMillis = millisTime();
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException expected) {
- int timeout = s2.getSoTimeout();
- checkDuration(startMillis, timeout-100, timeout+2000);
- }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
+ int timeout = s2.getSoTimeout();
+ checkDuration(startMillis, timeout-100, timeout+2000);
});
}
@@ -151,10 +145,7 @@
public void testTimedRead4() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s1.getOutputStream().write(99);
int b = s2.getInputStream().read();
assertTrue(b == 99);
@@ -168,10 +159,7 @@
public void testTimedRead5() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s2.setSoTimeout(30*3000);
scheduleWrite(s1.getOutputStream(), 99, 2000);
int b = s2.getInputStream().read();
@@ -185,10 +173,7 @@
public void testTimedRead6() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
s1.getOutputStream().write(99);
s2.setSoTimeout(0);
int b = s2.getInputStream().read();
@@ -203,10 +188,7 @@
public void testTimedRead7() throws IOException {
withConnection((s1, s2) -> {
s2.setSoTimeout(2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketTimeoutException e) { }
+ expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
scheduleWrite(s1.getOutputStream(), 99, 2000);
s2.setSoTimeout(0);
int b = s2.getInputStream().read();
@@ -221,10 +203,7 @@
withConnection((s1, s2) -> {
s2.setSoTimeout(30*1000);
scheduleClose(s2, 2000);
- try {
- s2.getInputStream().read();
- assertTrue(false);
- } catch (SocketException expected) { }
+ expectThrows(SocketException.class, () -> s2.getInputStream().read());
});
}
@@ -323,7 +302,7 @@
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) {
int timeout = ss.getSoTimeout();
checkDuration(startMillis, timeout-100, timeout+2000);
@@ -341,7 +320,7 @@
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
try (Socket s1 = new Socket()) {
s1.connect(ss.getLocalSocketAddress());
@@ -361,7 +340,7 @@
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
ss.setSoTimeout(0);
try (Socket s1 = new Socket()) {
@@ -382,7 +361,7 @@
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) { }
ss.setSoTimeout(0);
scheduleConnect(ss.getLocalSocketAddress(), 2000);
@@ -402,7 +381,7 @@
long startMillis = millisTime();
try {
ss.accept().close();
- assertTrue(false);
+ fail();
} catch (SocketException expected) {
checkDuration(startMillis, delay-100, delay+2000);
}
@@ -420,7 +399,7 @@
try {
Socket s = ss.accept();
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) {
// accept should have blocked for 2 seconds
int timeout = ss.getSoTimeout();
@@ -444,7 +423,7 @@
try {
Socket s = ss.accept(); // should block for 4 seconds
s.close();
- assertTrue(false);
+ fail();
} catch (SocketTimeoutException expected) {
// accept should have blocked for 4 seconds
int timeout = ss.getSoTimeout();
--- a/test/jdk/java/net/Socket/UdpSocket.java Mon May 06 09:43:48 2019 +0100
+++ b/test/jdk/java/net/Socket/UdpSocket.java Thu May 09 17:49:28 2019 +0100
@@ -77,7 +77,8 @@
byte[] array2 = new byte[100];
int n = s.getInputStream().read(array2);
assertTrue(n == MESSAGE.length(), "Unexpected size");
- assertTrue(Arrays.equals(array1, 0, n, array2, 0, n), "Unexpected contents");
+ assertEquals(Arrays.copyOf(array1, n), Arrays.copyOf(array2, n),
+ "Unexpected contents");
}
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/CompareSocketOptions.java Thu May 09 17:49:28 2019 +0100
@@ -0,0 +1,81 @@
+/*
+ * 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 8221481
+ * @modules java.base/java.net:+open java.base/sun.nio.ch:+open
+ * @run testng CompareSocketOptions
+ * @summary Compare the set of socket options supported by the old and new SocketImpls
+ */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketImpl;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class CompareSocketOptions {
+
+ /**
+ * Test that the old and new platform SocketImpl support the same set of
+ * client socket options.
+ */
+ public void testClientSocketSupportedOptions() throws IOException {
+ Socket s1 = new Socket(createOldSocketImpl(false)) { };
+ Socket s2 = new Socket(createNewSocketImpl(false)) { };
+ assertEquals(s1.supportedOptions(), s2.supportedOptions());
+ }
+
+ /**
+ * Test that the old and new platform SocketImpl support the same set of
+ * server socket options.
+ */
+ public void testServerSocketSupportedOptions() throws IOException {
+ ServerSocket ss1 = new ServerSocket(createOldSocketImpl(true)) { };
+ ServerSocket ss2 = new ServerSocket(createNewSocketImpl(true)) { };
+ assertEquals(ss1.supportedOptions(), ss2.supportedOptions());
+ }
+
+ private static SocketImpl createOldSocketImpl(boolean server) {
+ return newPlatformSocketImpl("java.net.PlainSocketImpl", server);
+ }
+
+ private static SocketImpl createNewSocketImpl(boolean server) {
+ return newPlatformSocketImpl("sun.nio.ch.NioSocketImpl", server);
+ }
+
+ private static SocketImpl newPlatformSocketImpl(String name, boolean server) {
+ try {
+ var ctor = Class.forName(name).getDeclaredConstructor(boolean.class);
+ ctor.setAccessible(true);
+ return (SocketImpl) ctor.newInstance(server);
+ } catch (Exception e) {
+ fail("Should not get here", e);
+ return null;
+ }
+ }
+}
\ No newline at end of file