8224477: java.net socket types new-style socket option methods - spec and impl mismatch
authorchegar
Wed, 29 May 2019 13:58:05 +0100
changeset 55081 dd321e3596c0
parent 55080 ef713640430e
child 55082 335f474becde
8224477: java.net socket types new-style socket option methods - spec and impl mismatch Reviewed-by: alanb
src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java
src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
src/java.base/share/classes/java/net/DatagramSocket.java
src/java.base/share/classes/java/net/DatagramSocketImpl.java
src/java.base/share/classes/java/net/ServerSocket.java
src/java.base/share/classes/java/net/Socket.java
src/java.base/share/classes/java/net/SocketImpl.java
src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java
src/java.base/unix/classes/java/net/PlainSocketImpl.java
test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java
test/jdk/java/net/SocketImpl/TestDefaultBehavior.java
test/jdk/java/net/SocketOption/AfterClose.java
test/jdk/java/net/SocketOption/NullsAndBadValues.java
test/jdk/java/net/SocketOption/OptionsTest.java
test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java
--- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -28,9 +28,11 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 
 import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -88,26 +90,6 @@
     }
 
     /**
-     * Returns a set of SocketOptions supported by this impl and by this impl's
-     * socket (Socket or ServerSocket)
-     *
-     * @return a Set of SocketOptions
-     */
-    @Override
-    protected Set<SocketOption<?>> supportedOptions() {
-        Set<SocketOption<?>> options;
-        if (isReusePortAvailable()) {
-            options = new HashSet<>();
-            options.addAll(super.supportedOptions());
-            options.add(StandardSocketOptions.SO_REUSEPORT);
-            options = Collections.unmodifiableSet(options);
-        } else {
-            options = super.supportedOptions();
-        }
-        return options;
-    }
-
-    /**
      * Creates a datagram socket
      */
     protected synchronized void create() throws SocketException {
@@ -400,6 +382,125 @@
         return result;
     }
 
+    static final ExtendedSocketOptions extendedOptions =
+            ExtendedSocketOptions.getInstance();
+
+    private static final Set<SocketOption<?>> datagramSocketOptions = datagramSocketOptions();
+    private static final Set<SocketOption<?>> multicastSocketOptions = multicastSocketOptions();
+
+    private static Set<SocketOption<?>> datagramSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_SNDBUF);
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.IP_TOS);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.datagramSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    private static Set<SocketOption<?>> multicastSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_SNDBUF);
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.IP_TOS);
+        options.add(StandardSocketOptions.IP_MULTICAST_IF);
+        options.add(StandardSocketOptions.IP_MULTICAST_TTL);
+        options.add(StandardSocketOptions.IP_MULTICAST_LOOP);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.datagramSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        if (getDatagramSocket() instanceof MulticastSocket)
+            return multicastSocketOptions;
+        else
+            return datagramSocketOptions;
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
+
+        if (isClosed())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_SNDBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid send buffer size:" + value);
+            setOption(SocketOptions.SO_SNDBUF, value);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid recv buffer size:" + value);
+            setOption(SocketOptions.SO_RCVBUF, value);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            setOption(SocketOptions.SO_REUSEADDR, value);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            setOption(SocketOptions.SO_REUSEPORT, value);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            int i = ((Integer)value).intValue();
+            if (i < 0 || i > 255)
+                throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
+            setOption(SocketOptions.IP_TOS, value);
+        } else if (name == StandardSocketOptions.IP_MULTICAST_IF ) {
+            setOption(SocketOptions.IP_MULTICAST_IF2, value);
+        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
+            int i = ((Integer)value).intValue();
+            if (i < 0 || i > 255)
+                throw new IllegalArgumentException("Invalid TTL/hop value: " + value);
+            setTimeToLive((Integer)value);
+        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
+            setOption(SocketOptions.IP_MULTICAST_LOOP, value);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            extendedOptions.setOption(fd, name, value);
+        } else {
+            throw new AssertionError("unknown option :" + name);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (isClosed())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_SNDBUF) {
+            return (T) getOption(SocketOptions.SO_SNDBUF);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            return (T) getOption(SocketOptions.SO_RCVBUF);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            return (T) getOption(SocketOptions.SO_REUSEADDR);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            return (T) getOption(SocketOptions.SO_REUSEPORT);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            return (T) getOption(SocketOptions.IP_TOS);
+        } else if (name == StandardSocketOptions.IP_MULTICAST_IF) {
+            return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
+        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL) {
+            return (T) ((Integer) getTimeToLive());
+        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP) {
+            return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            return (T) extendedOptions.getOption(fd, name);
+        } else {
+            throw new AssertionError("unknown option: " + name);
+        }
+    }
+
     protected abstract void datagramSocketCreate() throws SocketException;
     protected abstract void datagramSocketClose();
     protected abstract void socketSetOption(int opt, Object val)
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -35,12 +35,14 @@
 import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 
 import sun.net.ConnectionResetException;
 import sun.net.NetHooks;
 import sun.net.PlatformSocketImpl;
 import sun.net.ResourceManager;
+import sun.net.ext.ExtendedSocketOptions;
 import sun.net.util.SocketExceptions;
 
 /**
@@ -84,6 +86,9 @@
     */
     protected boolean stream;
 
+    /* whether this is a server or not */
+    final boolean isServer;
+
     /**
      * Load net library into runtime.
      */
@@ -112,27 +117,7 @@
     }
 
     AbstractPlainSocketImpl(boolean isServer) {
-        super(isServer);
-    }
-
-    /**
-     * Returns a set of SocketOptions supported by this impl and by this impl's
-     * socket (Socket or ServerSocket)
-     *
-     * @return a Set of SocketOptions
-     */
-    @Override
-    protected Set<SocketOption<?>> supportedOptions() {
-        Set<SocketOption<?>> options;
-        if (isReusePortAvailable()) {
-            options = new HashSet<>();
-            options.addAll(super.supportedOptions());
-            options.add(StandardSocketOptions.SO_REUSEPORT);
-            options = Collections.unmodifiableSet(options);
-        } else {
-            options = super.supportedOptions();
-        }
-        return options;
+        this.isServer = isServer;
     }
 
     /**
@@ -394,6 +379,121 @@
         }
     }
 
+    static final ExtendedSocketOptions extendedOptions =
+            ExtendedSocketOptions.getInstance();
+
+    private static final Set<SocketOption<?>> clientSocketOptions = clientSocketOptions();
+    private static final Set<SocketOption<?>> serverSocketOptions = serverSocketOptions();
+
+    private static Set<SocketOption<?>> clientSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_KEEPALIVE);
+        options.add(StandardSocketOptions.SO_SNDBUF);
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.SO_LINGER);
+        options.add(StandardSocketOptions.IP_TOS);
+        options.add(StandardSocketOptions.TCP_NODELAY);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.clientSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    private static Set<SocketOption<?>> serverSocketOptions() {
+        HashSet<SocketOption<?>> options = new HashSet<>();
+        options.add(StandardSocketOptions.SO_RCVBUF);
+        options.add(StandardSocketOptions.SO_REUSEADDR);
+        options.add(StandardSocketOptions.IP_TOS);
+        if (isReusePortAvailable())
+            options.add(StandardSocketOptions.SO_REUSEPORT);
+        options.addAll(ExtendedSocketOptions.serverSocketOptions());
+        return Collections.unmodifiableSet(options);
+    }
+
+    @Override
+    protected Set<SocketOption<?>> supportedOptions() {
+        if (isServer)
+            return serverSocketOptions;
+        else
+            return clientSocketOptions;
+    }
+
+    @Override
+    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
+
+        if (isClosedOrPending())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_KEEPALIVE) {
+            setOption(SocketOptions.SO_KEEPALIVE, value);
+        } else if (name == StandardSocketOptions.SO_SNDBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid send buffer size:" + value);
+            setOption(SocketOptions.SO_SNDBUF, value);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            if (((Integer)value).intValue() < 0)
+                throw new IllegalArgumentException("Invalid recv buffer size:" + value);
+            setOption(SocketOptions.SO_RCVBUF, value);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            setOption(SocketOptions.SO_REUSEADDR, value);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            setOption(SocketOptions.SO_REUSEPORT, value);
+        } else if (name == StandardSocketOptions.SO_LINGER ) {
+            setOption(SocketOptions.SO_LINGER, value);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            int i = ((Integer)value).intValue();
+            if (i < 0 || i > 255)
+                throw new IllegalArgumentException("Invalid IP_TOS value: " + value);
+            setOption(SocketOptions.IP_TOS, value);
+        } else if (name == StandardSocketOptions.TCP_NODELAY) {
+            setOption(SocketOptions.TCP_NODELAY, value);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            extendedOptions.setOption(fd, name, value);
+        } else {
+            throw new AssertionError("unknown option: " + name);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (!supportedOptions().contains(name))
+            throw new UnsupportedOperationException("'" + name + "' not supported");
+
+        if (isClosedOrPending())
+            throw new SocketException("Socket closed");
+
+        if (name == StandardSocketOptions.SO_KEEPALIVE) {
+            return (T)getOption(SocketOptions.SO_KEEPALIVE);
+        } else if (name == StandardSocketOptions.SO_SNDBUF) {
+            return (T)getOption(SocketOptions.SO_SNDBUF);
+        } else if (name == StandardSocketOptions.SO_RCVBUF) {
+            return (T)getOption(SocketOptions.SO_RCVBUF);
+        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
+            return (T)getOption(SocketOptions.SO_REUSEADDR);
+        } else if (name == StandardSocketOptions.SO_REUSEPORT) {
+            return (T)getOption(SocketOptions.SO_REUSEPORT);
+        } else if (name == StandardSocketOptions.SO_LINGER) {
+            return (T)getOption(SocketOptions.SO_LINGER);
+        } else if (name == StandardSocketOptions.IP_TOS) {
+            return (T)getOption(SocketOptions.IP_TOS);
+        } else if (name == StandardSocketOptions.TCP_NODELAY) {
+            return (T)getOption(SocketOptions.TCP_NODELAY);
+        } else if (extendedOptions.isOptionSupported(name)) {
+            return (T) extendedOptions.getOption(fd, name);
+        } else {
+            throw new AssertionError("unknown option: " + name);
+        }
+    }
+
     /**
      * The workhorse of the connection operation.  Tries several times to
      * establish a connection to the given <host, port>.  If unsuccessful,
--- a/src/java.base/share/classes/java/net/DatagramSocket.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java	Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -29,6 +29,7 @@
 import java.nio.channels.DatagramChannel;
 import java.security.AccessController;
 import java.security.PrivilegedExceptionAction;
+import java.util.Objects;
 import java.util.Set;
 import java.util.Collections;
 
@@ -1343,6 +1344,9 @@
     public <T> DatagramSocket setOption(SocketOption<T> name, T value)
         throws IOException
     {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         getImpl().setOption(name, value);
         return this;
     }
@@ -1371,6 +1375,9 @@
      * @since 9
      */
     public <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         return getImpl().getOption(name);
     }
 
--- a/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -27,6 +27,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -265,123 +266,69 @@
     /**
      * Called to set a socket option.
      *
+     * @implSpec
+     * The default implementation of this method first checks that the given
+     * socket option {code name} is not null, then throws {@code
+     * UnsupportedOperationException}. Subclasses should override this method
+     * with an appropriate implementation.
+     *
      * @param <T> The type of the socket option value
      * @param name The socket option
-     *
      * @param value The value of the socket option. A value of {@code null}
      *              may be valid for some options.
      *
      * @throws UnsupportedOperationException if the DatagramSocketImpl does not
      *         support the option
-     *
+     * @throws IllegalArgumentException if the value is not valid for
+     *         the option
+     * @throws IOException if an I/O error occurs, or if the socket is closed
      * @throws NullPointerException if name is {@code null}
-     * @throws IOException if an I/O problem occurs while attempting to set the option
+     *
      * @since 9
      */
     protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
-        if (name == StandardSocketOptions.SO_SNDBUF) {
-            setOption(SocketOptions.SO_SNDBUF, value);
-        } else if (name == StandardSocketOptions.SO_RCVBUF) {
-            setOption(SocketOptions.SO_RCVBUF, value);
-        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
-            setOption(SocketOptions.SO_REUSEADDR, value);
-        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
-            supportedOptions().contains(name)) {
-            setOption(SocketOptions.SO_REUSEPORT, value);
-        } else if (name == StandardSocketOptions.IP_TOS) {
-            setOption(SocketOptions.IP_TOS, value);
-        } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            setOption(SocketOptions.IP_MULTICAST_IF2, value);
-        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            if (! (value instanceof Integer)) {
-                throw new IllegalArgumentException("not an integer");
-            }
-            setTimeToLive((Integer)value);
-        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            setOption(SocketOptions.IP_MULTICAST_LOOP, value);
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
+        Objects.requireNonNull(name);
+        throw new UnsupportedOperationException("'" + name + "' not supported");
     }
 
     /**
      * Called to get a socket option.
      *
-     * @return the socket option
+     * @implSpec
+     * The default implementation of this method first checks that the given
+     * socket option {code name} is not null, then throws {@code
+     * UnsupportedOperationException}. Subclasses should override this method
+     * with an appropriate implementation.
+     *
      * @param <T> The type of the socket option value
      * @param name The socket option
+     * @return the socket option
      *
      * @throws UnsupportedOperationException if the DatagramSocketImpl does not
      *         support the option
-     *
+     * @throws IOException if an I/O error occurs, or if the socket is closed
      * @throws NullPointerException if name is {@code null}
-     * @throws IOException if an I/O problem occurs while attempting to set the option
      *
      * @since 9
      */
-    @SuppressWarnings("unchecked")
     protected <T> T getOption(SocketOption<T> name) throws IOException {
-        if (name == StandardSocketOptions.SO_SNDBUF) {
-            return (T) getOption(SocketOptions.SO_SNDBUF);
-        } else if (name == StandardSocketOptions.SO_RCVBUF) {
-            return (T) getOption(SocketOptions.SO_RCVBUF);
-        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
-            return (T) getOption(SocketOptions.SO_REUSEADDR);
-        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
-            supportedOptions().contains(name)) {
-            return (T) getOption(SocketOptions.SO_REUSEPORT);
-        } else if (name == StandardSocketOptions.IP_TOS) {
-            return (T) getOption(SocketOptions.IP_TOS);
-        } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
-        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            Integer ttl = getTimeToLive();
-            return (T)ttl;
-        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
-            (getDatagramSocket() instanceof MulticastSocket)) {
-            return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
-    }
-
-    private static final Set<SocketOption<?>> dgSocketOptions;
-
-    private static final Set<SocketOption<?>> mcSocketOptions;
-
-    static {
-        dgSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
-                                 StandardSocketOptions.SO_RCVBUF,
-                                 StandardSocketOptions.SO_REUSEADDR,
-                                 StandardSocketOptions.IP_TOS);
-
-        mcSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
-                                 StandardSocketOptions.SO_RCVBUF,
-                                 StandardSocketOptions.SO_REUSEADDR,
-                                 StandardSocketOptions.IP_TOS,
-                                 StandardSocketOptions.IP_MULTICAST_IF,
-                                 StandardSocketOptions.IP_MULTICAST_TTL,
-                                 StandardSocketOptions.IP_MULTICAST_LOOP);
+        Objects.requireNonNull(name);
+        throw new UnsupportedOperationException("'" + name + "' not supported");
     }
 
     /**
      * Returns a set of SocketOptions supported by this impl
      * and by this impl's socket (DatagramSocket or MulticastSocket)
      *
+     * @implSpec
+     * The default implementation of this method returns an empty set.
+     * Subclasses should override this method with an appropriate implementation.
+     *
      * @return a Set of SocketOptions
      *
      * @since 9
      */
     protected Set<SocketOption<?>> supportedOptions() {
-        if (getDatagramSocket() instanceof MulticastSocket) {
-            return mcSocketOptions;
-        } else {
-            return dgSocketOptions;
-        }
+        return Set.of();
     }
 }
--- a/src/java.base/share/classes/java/net/ServerSocket.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/ServerSocket.java	Wed May 29 13:58:05 2019 +0100
@@ -1025,6 +1025,9 @@
     public <T> ServerSocket setOption(SocketOption<T> name, T value)
         throws IOException
     {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         getImpl().setOption(name, value);
         return this;
     }
@@ -1053,6 +1056,9 @@
      * @since 9
      */
     public <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         return getImpl().getOption(name);
     }
 
--- a/src/java.base/share/classes/java/net/Socket.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/Socket.java	Wed May 29 13:58:05 2019 +0100
@@ -33,6 +33,7 @@
 import java.nio.channels.SocketChannel;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.Objects;
 import java.util.Set;
 import java.util.Collections;
 
@@ -1786,6 +1787,9 @@
      * @since 9
      */
     public <T> Socket setOption(SocketOption<T> name, T value) throws IOException {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         getImpl().setOption(name, value);
         return this;
     }
@@ -1815,6 +1819,9 @@
      */
     @SuppressWarnings("unchecked")
     public <T> T getOption(SocketOption<T> name) throws IOException {
+        Objects.requireNonNull(name);
+        if (isClosed())
+            throw new SocketException("Socket is closed");
         return getImpl().getOption(name);
     }
 
--- a/src/java.base/share/classes/java/net/SocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/java/net/SocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -29,8 +29,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.FileDescriptor;
+import java.util.Objects;
 import java.util.Set;
-
 import sun.net.PlatformSocketImpl;
 
 /**
@@ -75,21 +75,9 @@
     protected int localport;
 
     /**
-     * Whether this is a server or not.
-     */
-    final boolean isServer;
-
-
-    SocketImpl(boolean isServer) {
-        this.isServer = isServer;
-    }
-
-    /**
      * Initialize a new instance of this class
      */
-    public SocketImpl() {
-        this.isServer = false;
-    }
+    public SocketImpl() { }
 
     /**
      * Creates either a stream or a datagram socket.
@@ -376,79 +364,54 @@
     /**
      * Called to set a socket option.
      *
+     * @implSpec
+     * The default implementation of this method first checks that the given
+     * socket option {code name} is not null, then throws {@code
+     * UnsupportedOperationException}. Subclasses should override this method
+     * with an appropriate implementation.
+     *
      * @param <T> The type of the socket option value
      * @param name The socket option
-     *
      * @param value The value of the socket option. A value of {@code null}
      *              may be valid for some options.
      *
      * @throws UnsupportedOperationException if the SocketImpl does not
      *         support the option
-     *
-     * @throws IOException if an I/O error occurs, or if the socket is closed.
+     * @throws IllegalArgumentException if the value is not valid for
+     *         the option
+     * @throws IOException if an I/O error occurs, or if the socket is closed
+     * @throws NullPointerException if name is {@code null}
      *
      * @since 9
      */
     protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
-        if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
-            setOption(SocketOptions.SO_KEEPALIVE, value);
-        } else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
-            setOption(SocketOptions.SO_SNDBUF, value);
-        } else if (name == StandardSocketOptions.SO_RCVBUF) {
-            setOption(SocketOptions.SO_RCVBUF, value);
-        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
-            setOption(SocketOptions.SO_REUSEADDR, value);
-        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
-            supportedOptions().contains(name)) {
-            setOption(SocketOptions.SO_REUSEPORT, value);
-        } else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
-            setOption(SocketOptions.SO_LINGER, value);
-        } else if (name == StandardSocketOptions.IP_TOS) {
-            setOption(SocketOptions.IP_TOS, value);
-        } else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
-            setOption(SocketOptions.TCP_NODELAY, value);
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
+        Objects.requireNonNull(name);
+        throw new UnsupportedOperationException("'" + name + "' not supported");
     }
 
     /**
      * Called to get a socket option.
      *
+     * @implSpec
+     * The default implementation of this method first checks that the given
+     * socket option {code name} is not null, then throws {@code
+     * UnsupportedOperationException}. Subclasses should override this method
+     * with an appropriate implementation.
+     *
      * @param <T> The type of the socket option value
      * @param name The socket option
-     *
      * @return the value of the named option
      *
      * @throws UnsupportedOperationException if the SocketImpl does not
-     *         support the option.
-     *
-     * @throws IOException if an I/O error occurs, or if the socket is closed.
+     *         support the option
+     * @throws IOException if an I/O error occurs, or if the socket is closed
+     * @throws NullPointerException if name is {@code null}
      *
      * @since 9
      */
-    @SuppressWarnings("unchecked")
     protected <T> T getOption(SocketOption<T> name) throws IOException {
-        if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
-            return (T)getOption(SocketOptions.SO_KEEPALIVE);
-        } else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
-            return (T)getOption(SocketOptions.SO_SNDBUF);
-        } else if (name == StandardSocketOptions.SO_RCVBUF) {
-            return (T)getOption(SocketOptions.SO_RCVBUF);
-        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
-            return (T)getOption(SocketOptions.SO_REUSEADDR);
-        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
-            supportedOptions().contains(name)) {
-            return (T)getOption(SocketOptions.SO_REUSEPORT);
-        } else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
-            return (T)getOption(SocketOptions.SO_LINGER);
-        } else if (name == StandardSocketOptions.IP_TOS) {
-            return (T)getOption(SocketOptions.IP_TOS);
-        } else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
-            return (T)getOption(SocketOptions.TCP_NODELAY);
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
+        Objects.requireNonNull(name);
+        throw new UnsupportedOperationException("'" + name + "' not supported");
     }
 
     /**
@@ -464,37 +427,19 @@
         } catch (IOException ignore) { }
     }
 
-    private static final Set<SocketOption<?>> socketOptions;
-
-    private static final Set<SocketOption<?>> serverSocketOptions;
-
-    static {
-        socketOptions = Set.of(StandardSocketOptions.SO_KEEPALIVE,
-                               StandardSocketOptions.SO_SNDBUF,
-                               StandardSocketOptions.SO_RCVBUF,
-                               StandardSocketOptions.SO_REUSEADDR,
-                               StandardSocketOptions.SO_LINGER,
-                               StandardSocketOptions.IP_TOS,
-                               StandardSocketOptions.TCP_NODELAY);
-
-        serverSocketOptions = Set.of(StandardSocketOptions.SO_RCVBUF,
-                                     StandardSocketOptions.SO_REUSEADDR,
-                                     StandardSocketOptions.IP_TOS);
-    }
-
     /**
      * Returns a set of SocketOptions supported by this impl
      * and by this impl's socket (Socket or ServerSocket)
      *
+     * @implSpec
+     * The default implementation of this method returns an empty set.
+     * Subclasses should override this method with an appropriate implementation.
+     *
      * @return a Set of SocketOptions
      *
      * @since 9
      */
     protected Set<SocketOption<?>> supportedOptions() {
-        if (!isServer) {
-            return socketOptions;
-        } else {
-            return serverSocketOptions;
-        }
+        return Set.of();
     }
 }
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -222,6 +222,8 @@
         Objects.requireNonNull(name);
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
 
         synchronized (stateLock) {
             ensureOpen();
@@ -236,8 +238,6 @@
             }
 
             if (name == StandardSocketOptions.IP_MULTICAST_IF) {
-                if (value == null)
-                    throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
                 NetworkInterface interf = (NetworkInterface)value;
                 if (family == StandardProtocolFamily.INET6) {
                     int index = interf.getIndex();
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -147,6 +147,9 @@
         Objects.requireNonNull(name);
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
+
         synchronized (stateLock) {
             ensureOpen();
 
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -218,6 +218,8 @@
         Objects.requireNonNull(name);
         if (!supportedOptions().contains(name))
             throw new UnsupportedOperationException("'" + name + "' not supported");
+        if (!name.type().isInstance(value))
+            throw new IllegalArgumentException("Invalid value '" + value + "'");
 
         synchronized (stateLock) {
             ensureOpen();
--- a/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -41,46 +41,6 @@
         init();
     }
 
-    static final ExtendedSocketOptions extendedOptions =
-            ExtendedSocketOptions.getInstance();
-
-    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
-        if (isClosed()) {
-            throw new SocketException("Socket closed");
-        }
-        if (supportedOptions().contains(name)) {
-            if (extendedOptions.isOptionSupported(name)) {
-                extendedOptions.setOption(fd, name, value);
-            } else {
-                super.setOption(name, value);
-            }
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected <T> T getOption(SocketOption<T> name) throws IOException {
-        if (isClosed()) {
-            throw new SocketException("Socket closed");
-        }
-        if (supportedOptions().contains(name)) {
-            if (extendedOptions.isOptionSupported(name)) {
-                return (T) extendedOptions.getOption(fd, name);
-            } else {
-                return super.getOption(name);
-            }
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
-    }
-
-    protected Set<SocketOption<?>> supportedOptions() {
-        HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
-        options.addAll(ExtendedSocketOptions.datagramSocketOptions());
-        return options;
-    }
-
     protected void socketSetOption(int opt, Object val) throws SocketException {
         if (opt == SocketOptions.SO_REUSEPORT &&
             !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
--- a/src/java.base/unix/classes/java/net/PlainSocketImpl.java	Wed May 29 08:21:33 2019 -0400
+++ b/src/java.base/unix/classes/java/net/PlainSocketImpl.java	Wed May 29 13:58:05 2019 +0100
@@ -25,7 +25,6 @@
 package java.net;
 
 import java.io.IOException;
-import java.io.FileDescriptor;
 import java.util.Set;
 import java.util.HashSet;
 import sun.net.ext.ExtendedSocketOptions;
@@ -49,50 +48,6 @@
         super(isServer);
     }
 
-    static final ExtendedSocketOptions extendedOptions =
-            ExtendedSocketOptions.getInstance();
-
-    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
-        if (isClosedOrPending()) {
-            throw new SocketException("Socket closed");
-        }
-        if (supportedOptions().contains(name)) {
-            if (extendedOptions.isOptionSupported(name)) {
-                extendedOptions.setOption(fd, name, value);
-            } else {
-                super.setOption(name, value);
-            }
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected <T> T getOption(SocketOption<T> name) throws IOException {
-        if (isClosedOrPending()) {
-            throw new SocketException("Socket closed");
-        }
-        if (supportedOptions().contains(name)) {
-            if (extendedOptions.isOptionSupported(name)) {
-                return (T) extendedOptions.getOption(fd, name);
-            } else {
-                return super.getOption(name);
-            }
-        } else {
-            throw new UnsupportedOperationException("unsupported option");
-        }
-    }
-
-    protected Set<SocketOption<?>> supportedOptions() {
-        HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
-        if (isServer) {
-            options.addAll(ExtendedSocketOptions.serverSocketOptions());
-        } else {
-            options.addAll(ExtendedSocketOptions.clientSocketOptions());
-        }
-        return options;
-    }
-
     protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
         if (opt == SocketOptions.SO_REUSEPORT &&
             !supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java	Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,114 @@
+/*
+ * 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 8224477
+ * @summary Basic test for java.net.DatagramSocketImpl default behavior
+ * @run testng TestDefaultBehavior
+ */
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.util.Set;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
+
+public class TestDefaultBehavior {
+
+    static final Class<NullPointerException> NPE = NullPointerException.class;
+    static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+    @Test
+    public void datagramSocketImpl() {
+        CustomDatagramSocketImpl dsi = new CustomDatagramSocketImpl();
+
+        assertEquals(dsi.supportedOptions().size(), 0);
+
+        expectThrows(NPE, () -> dsi.setOption(null, null));
+        expectThrows(NPE, () -> dsi.setOption(null, 1));
+        expectThrows(UOE, () -> dsi.setOption(SO_RCVBUF, 100));
+        expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE));
+        expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, FALSE));
+        expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, TRUE));
+        expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, FALSE));
+        expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE));
+
+        expectThrows(NPE, () -> dsi.getOption(null));
+        expectThrows(UOE, () -> dsi.getOption(SO_RCVBUF));
+        expectThrows(UOE, () -> dsi.getOption(SO_KEEPALIVE));
+        expectThrows(UOE, () -> dsi.getOption(FAKE_SOCK_OPT));
+    }
+
+    static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+        @Override public String name() { return "FAKE_SOCK_OPT"; }
+        @Override public Class<Boolean> type() { return Boolean.class; }
+    };
+
+    // A DatagramSocketImpl that delegates the three new-style socket option
+    // methods to the default java.net.DatagramSocketImpl implementation.
+    static class CustomDatagramSocketImpl extends DatagramSocketImpl {
+
+        @Override
+        public <T> void setOption(SocketOption<T> name, T value) throws IOException {
+            super.setOption(name, value);
+        }
+
+        @Override
+        public Set<SocketOption<?>> supportedOptions() {
+            return super.supportedOptions();
+        }
+
+        @Override
+        public <T> T getOption(SocketOption<T> name) throws IOException {
+            return super.getOption(name);
+        }
+
+        // --
+        @Override protected void create() { }
+        @Override protected void bind(int lport, InetAddress laddr) { }
+        @Override protected void send(DatagramPacket p) { }
+        @Override protected int peek(InetAddress i) { return 0; }
+        @Override protected int peekData(DatagramPacket p) { return 0; }
+        @Override protected void receive(DatagramPacket p) { }
+        @Override protected void setTTL(byte ttl) { }
+        @Override protected byte getTTL() { return 0; }
+        @Override protected void setTimeToLive(int ttl) { }
+        @Override protected int getTimeToLive() { return 0; }
+        @Override protected void join(InetAddress inetaddr) { }
+        @Override protected void leave(InetAddress inetaddr)  { }
+        @Override protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) { }
+        @Override protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) { }
+        @Override protected void close() { }
+        @Override public void setOption(int optID, Object value) { }
+        @Override public Object getOption(int optID) { return null; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java	Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,112 @@
+/*
+ * 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 8224477
+ * @summary Basic test for java.net.SocketImpl default behavior
+ * @run testng TestDefaultBehavior
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.SocketImpl;
+import java.net.SocketOption;
+import java.util.Set;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
+
+public class TestDefaultBehavior {
+
+    static final Class<NullPointerException> NPE = NullPointerException.class;
+    static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+    @Test
+    public void socketImpl() {
+        CustomSocketImpl csi = new CustomSocketImpl();
+
+        assertEquals(csi.supportedOptions().size(), 0);
+
+        expectThrows(NPE, () -> csi.setOption(null, null));
+        expectThrows(NPE, () -> csi.setOption(null, 1));
+        expectThrows(UOE, () -> csi.setOption(SO_RCVBUF, 100));
+        expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE));
+        expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, FALSE));
+        expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, TRUE));
+        expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, FALSE));
+        expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE));
+
+        expectThrows(NPE, () -> csi.getOption(null));
+        expectThrows(UOE, () -> csi.getOption(SO_RCVBUF));
+        expectThrows(UOE, () -> csi.getOption(SO_KEEPALIVE));
+        expectThrows(UOE, () -> csi.getOption(FAKE_SOCK_OPT));
+    }
+
+    static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+        @Override public String name() { return "FAKE_SOCK_OPT"; }
+        @Override public Class<Boolean> type() { return Boolean.class; }
+    };
+
+    // A SocketImpl that delegates the three new-style socket option
+    // methods to the default java.net.SocketImpl implementation.
+    static class CustomSocketImpl extends SocketImpl {
+
+        @Override
+        public <T> void setOption(SocketOption<T> name, T value) throws IOException {
+            super.setOption(name, value);
+        }
+
+        @Override
+        public Set<SocketOption<?>> supportedOptions() {
+            return super.supportedOptions();
+        }
+
+        @Override
+        public <T> T getOption(SocketOption<T> name) throws IOException {
+            return super.getOption(name);
+        }
+
+        // --
+
+        @Override protected void create(boolean stream) { }
+        @Override protected void connect(String host, int port) { }
+        @Override protected void connect(InetAddress address, int port) { }
+        @Override protected void connect(SocketAddress address, int timeout)  { }
+        @Override protected void bind(InetAddress host, int port) { }
+        @Override protected void listen(int backlog) { }
+        @Override protected void accept(SocketImpl s)  { }
+        @Override protected InputStream getInputStream() { return null; }
+        @Override protected OutputStream getOutputStream() { return null; }
+        @Override protected int available() { return 0; }
+        @Override protected void close() { }
+        @Override protected void sendUrgentData(int data)  { }
+        @Override public void setOption(int optID, Object value) { }
+        @Override public Object getOption(int optID) { return null; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketOption/AfterClose.java	Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,384 @@
+/*
+ * 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 8224477
+ * @summary Ensures that IOException is thrown after the socket is closed
+ * @run testng AfterClose
+ */
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.DatagramSocket;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketOption;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.expectThrows;
+
+public class AfterClose {
+
+    static final Class<IOException> IOE = IOException.class;
+
+    static Map<SocketOption<?>,List<Object>> OPTION_VALUES_MAP = optionValueMap();
+
+    static Map<SocketOption<?>,List<Object>> optionValueMap() {
+        Map<SocketOption<?>,List<Object>> map = new HashMap<>();
+        map.put(IP_MULTICAST_IF,   listOf(TRUE, FALSE) );
+        map.put(IP_MULTICAST_LOOP, listOf(TRUE, FALSE) );
+        map.put(IP_MULTICAST_TTL,  listOf(0, 100, 255) );
+        map.put(IP_TOS,            listOf(0, 101, 255) );
+        map.put(SO_BROADCAST,      listOf(TRUE, FALSE) );
+        map.put(SO_KEEPALIVE,      listOf(TRUE, FALSE) );
+        map.put(SO_LINGER,         listOf(0, 5, 15)    );
+        map.put(SO_RCVBUF,         listOf(1, 100, 1000));
+        map.put(SO_REUSEADDR,      listOf(TRUE, FALSE) );
+        map.put(SO_REUSEPORT,      listOf(TRUE, FALSE) );
+        map.put(SO_SNDBUF,         listOf(1, 100, 1000));
+        map.put(TCP_NODELAY,       listOf(TRUE, FALSE) );
+        // extended options
+        try {
+            Class<?> c = Class.forName("jdk.net.ExtendedSocketOptions");
+            Field field = c.getField("SO_FLOW_SLA");
+            map.put((SocketOption<?>)field.get(null), listOf(createSocketFlow()));
+            field = c.getField("TCP_QUICKACK");
+            map.put((SocketOption<?>)field.get(null), listOf(TRUE, FALSE));
+            field = c.getField("TCP_KEEPIDLE");
+            map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+            field = c.getField("TCP_KEEPINTERVAL");
+            map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+            field = c.getField("TCP_KEEPCOUNT");
+            map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+        } catch (ClassNotFoundException e) {
+            // ignore, jdk.net module not present
+        } catch (ReflectiveOperationException e) {
+            throw new AssertionError(e);
+        }
+        return map;
+    }
+
+    // -- Socket
+
+    @DataProvider(name = "socketOptionValues")
+    public Object[][] socketOptionValues() throws Exception {
+        try (Socket s = new Socket()) {
+            return s.supportedOptions().stream()
+                    .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+                    .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "socketOptionValues")
+    public <T> void closedSocketImplUncreated(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        Socket socket = createClosedSocketImplUncreated();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> socket.setOption(option, value));
+                expectThrows(IOE, () -> socket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "socketOptionValues")
+    public <T> void closedSocketImplCreated(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        Socket socket = createClosedSocketImplCreated();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> socket.setOption(option, value));
+                expectThrows(IOE, () -> socket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "socketOptionValues")
+    public <T> void closedSocketAdapter(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        Socket socket = createClosedSocketFromAdapter();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> socket.setOption(option, value));
+                expectThrows(IOE, () -> socket.getOption(option));
+            }
+        }
+    }
+
+    // -- ServerSocket
+
+    @DataProvider(name = "serverSocketOptionValues")
+    public Object[][] serverSocketOptionValues() throws Exception {
+        try (ServerSocket ss = new ServerSocket()) {
+            return ss.supportedOptions().stream()
+                     .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "serverSocketOptionValues")
+    public <T> void closedServerSocketImplUncreated(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        ServerSocket serverSocket = createClosedServerSocketImplUncreated();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> serverSocket.setOption(option, value));
+                expectThrows(IOE, () -> serverSocket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "serverSocketOptionValues")
+    public <T> void closedServerSocketImplCreated(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        ServerSocket serverSocket = createClosedServerSocketImplCreated();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> serverSocket.setOption(option, value));
+                expectThrows(IOE, () -> serverSocket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "serverSocketOptionValues")
+    public <T> void closedServerSocketAdapter(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        if (option == IP_TOS)
+            return;  // SSC does not support IP_TOS
+
+        ServerSocket serverSocket = createClosedServerSocketFromAdapter();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> serverSocket.setOption(option, value));
+                expectThrows(IOE, () -> serverSocket.getOption(option));
+            }
+        }
+    }
+
+    // -- DatagramSocket
+
+    @DataProvider(name = "datagramSocketOptionValues")
+    public Object[][] datagramSocketOptionValues() throws Exception {
+        try (DatagramSocket ds = new DatagramSocket()) {
+            return ds.supportedOptions().stream()
+                     .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "datagramSocketOptionValues")
+    public <T> void closedUnboundDatagramSocket(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        DatagramSocket datagramSocket = createClosedUnboundDatagramSocket();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+                expectThrows(IOE, () -> datagramSocket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "datagramSocketOptionValues")
+    public <T> void closedBoundDatagramSocket(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        DatagramSocket datagramSocket = createClosedBoundDatagramSocket();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+                expectThrows(IOE, () -> datagramSocket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "datagramSocketOptionValues")
+    public <T> void closedDatagramAdapter(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        DatagramSocket datagramSocket = createClosedBoundDatagramSocket();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> datagramSocket.setOption(option, value));
+                expectThrows(IOE, () -> datagramSocket.getOption(option));
+            }
+        }
+    }
+
+    // -- MulticastSocket
+
+    @DataProvider(name = "multicastSocketOptionValues")
+    public Object[][] multicastSocketOptionValues() throws Exception {
+        try (MulticastSocket ms = new MulticastSocket()) {
+            return ms.supportedOptions().stream()
+                     .map(so -> new Object[] {so, OPTION_VALUES_MAP.get(so)})
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "multicastSocketOptionValues")
+    public <T> void closedUnboundMulticastSocket(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        MulticastSocket multicastSocket = createClosedUnboundMulticastSocket();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> multicastSocket.setOption(option, value));
+                expectThrows(IOE, () -> multicastSocket.getOption(option));
+            }
+        }
+    }
+
+    @Test(dataProvider = "multicastSocketOptionValues")
+    public <T> void closedBoundMulticastSocket(SocketOption<T> option, List<T> values)
+        throws IOException
+    {
+        MulticastSocket multicastSocket = createClosedBoundMulticastSocket();
+        for (int i=0; i<3; i++); {
+            for (T value : values) {
+                expectThrows(IOE, () -> multicastSocket.setOption(option, value));
+                expectThrows(IOE, () -> multicastSocket.getOption(option));
+            }
+        }
+    }
+
+    // --
+
+    static List<Object> listOf(Object... objs) {
+        List<Object> l = new ArrayList<>();
+        Arrays.stream(objs).forEachOrdered(l::add);
+        return l;
+    }
+
+    // Returns a closed Socket that has an impl whose `create` method has NOT been invoked.
+    static Socket createClosedSocketImplUncreated() throws IOException {
+        Socket s = new Socket();
+        s.close();
+        return s;
+    }
+
+    // Returns a closed Socket that has an impl whose `create` method has been invoked.
+    static Socket createClosedSocketImplCreated() throws IOException {
+        Socket s = new Socket();
+        s.bind(null);  // binding causes impl::create to be invoked
+        s.close();
+        return s;
+    }
+
+    // Returns a closed Socket created from a SocketChannel's adapter.
+    static Socket createClosedSocketFromAdapter() throws IOException {
+        SocketChannel sc = SocketChannel.open();
+        sc.close();
+        return sc.socket();
+    }
+
+    // Returns a closed ServerSocket that has an impl whose `create` method has NOT been invoked.
+    static ServerSocket createClosedServerSocketImplUncreated() throws IOException {
+        ServerSocket ss = new ServerSocket();
+        ss.close();
+        return ss;
+    }
+
+    // Returns a closed ServerSocket that has an impl whose `create` method has been invoked.
+    static ServerSocket createClosedServerSocketImplCreated() throws IOException {
+        ServerSocket ss = new ServerSocket();
+        ss.bind(null);  // binding causes impl::create to be invoked
+        ss.close();
+        return ss;
+    }
+
+    // Returns a closed ServerSocket created from a ServerSocketChannel's adapter.
+    static ServerSocket createClosedServerSocketFromAdapter() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.close();
+        return ssc.socket();
+    }
+
+    // Returns a closed unbound DatagramSocket.
+    static DatagramSocket createClosedUnboundDatagramSocket() throws IOException {
+        DatagramSocket ds = new DatagramSocket(null);
+        assert ds.isBound() == false;
+        ds.close();
+        return ds;
+    }
+
+    // Returns a closed bound DatagramSocket.
+    static DatagramSocket createClosedBoundDatagramSocket() throws IOException {
+        DatagramSocket ds = new DatagramSocket();
+        assert ds.isBound() == true;
+        ds.close();
+        return ds;
+    }
+
+    // Returns a closed DatagramSocket that created from a DatagramChannel's adapter.
+    static DatagramSocket createClosedDatagramSocketFromAdapter() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        dc.close();
+        return dc.socket();
+    }
+
+    // Returns a closed unbound MulticastSocket.
+    static MulticastSocket createClosedUnboundMulticastSocket() throws IOException {
+        MulticastSocket ms = new MulticastSocket(null);
+        assert ms.isBound() == false;
+        ms.close();
+        return ms;
+    }
+
+    // Returns a closed bound MulticastSocket.
+    static MulticastSocket createClosedBoundMulticastSocket() throws IOException {
+        MulticastSocket ms = new MulticastSocket();
+        assert ms.isBound() == true;
+        ms.close();
+        return ms;
+    }
+
+    static Object createSocketFlow() {
+        try {
+            Class<?> c = Class.forName("jdk.net.SocketFlow");
+            Method method = c.getDeclaredMethod("create");
+            return method.invoke(null);
+        } catch (ReflectiveOperationException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/SocketOption/NullsAndBadValues.java	Wed May 29 13:58:05 2019 +0100
@@ -0,0 +1,331 @@
+/*
+ * 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 8224477
+ * @summary Basic test for NPE, UOE, and IAE for get/setOption
+ * @run testng NullsAndBadValues
+ * @run testng/othervm -Dsun.net.useExclusiveBind=false NullsAndBadValues
+ */
+
+import java.net.DatagramSocket;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketOption;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Boolean.*;
+import static java.net.StandardSocketOptions.*;
+import static org.testng.Assert.expectThrows;
+
+public class NullsAndBadValues {
+
+    static final Class<NullPointerException> NPE = NullPointerException.class;
+    static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
+    static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+    @Test
+    public void nulls() throws Exception {
+        try (Socket s = new Socket()) {
+            expectThrows(NPE, () -> s.setOption(null, null));
+            expectThrows(NPE, () -> s.setOption(null, ""));
+            expectThrows(NPE, () -> s.setOption(null, 1));
+            expectThrows(NPE, () -> s.getOption(null));
+        }
+        try (ServerSocket ss = new ServerSocket()) {
+            expectThrows(NPE, () -> ss.setOption(null, null));
+            expectThrows(NPE, () -> ss.setOption(null, ""));
+            expectThrows(NPE, () -> ss.setOption(null, 1));
+            expectThrows(NPE, () -> ss.getOption(null));
+        }
+        try (DatagramSocket ds = new DatagramSocket()) {
+            expectThrows(NPE, () -> ds.setOption(null, null));
+            expectThrows(NPE, () -> ds.setOption(null, ""));
+            expectThrows(NPE, () -> ds.setOption(null, 1));
+            expectThrows(NPE, () -> ds.getOption(null));
+        }
+        try (MulticastSocket ms = new MulticastSocket()) {
+            expectThrows(NPE, () -> ms.setOption(null, null));
+            expectThrows(NPE, () -> ms.setOption(null, ""));
+            expectThrows(NPE, () -> ms.setOption(null, 1));
+            expectThrows(NPE, () -> ms.getOption(null));
+        }
+        try (Socket sa = SocketChannel.open().socket()) {
+            expectThrows(NPE, () -> sa.setOption(null, null));
+            expectThrows(NPE, () -> sa.setOption(null, ""));
+            expectThrows(NPE, () -> sa.setOption(null, 1));
+            expectThrows(NPE, () -> sa.getOption(null));
+        }
+        try (ServerSocket ssa = ServerSocketChannel.open().socket()) {
+            expectThrows(NPE, () -> ssa.setOption(null, null));
+            expectThrows(NPE, () -> ssa.setOption(null, ""));
+            expectThrows(NPE, () -> ssa.setOption(null, 1));
+            expectThrows(NPE, () -> ssa.getOption(null));
+        }
+        try (DatagramSocket dsa = DatagramChannel.open().socket()) {
+            expectThrows(NPE, () -> dsa.setOption(null, null));
+            expectThrows(NPE, () -> dsa.setOption(null, ""));
+            expectThrows(NPE, () -> dsa.setOption(null, 1));
+            expectThrows(NPE, () -> dsa.getOption(null));
+        }
+    }
+
+    static final SocketOption<Boolean> FAKE_SOCK_OPT = new SocketOption<>() {
+        @Override public String name() { return "FAKE_SOCK_OPT"; }
+        @Override public Class<Boolean> type() { return Boolean.class; }
+    };
+
+    static final SocketOption RAW_SOCK_OPT = new SocketOption() {
+        @Override public String name() { return "RAW_SOCK_OPT"; }
+        @Override public Class type()  { return Boolean.class;  }
+    };
+
+    @Test
+    public void uoe() throws Exception {
+        try (Socket s = new Socket()) {
+            expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> s.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> s.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> s.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> s.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> s.getOption(RAW_SOCK_OPT));
+        }
+        try (ServerSocket ss = new ServerSocket()) {
+            expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> ss.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> ss.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> ss.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> ss.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> ss.getOption(RAW_SOCK_OPT));
+        }
+        try (DatagramSocket ds = new DatagramSocket()) {
+            expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> ds.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> ds.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> ds.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> ds.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> ds.getOption(RAW_SOCK_OPT));
+        }
+        try (MulticastSocket ms = new MulticastSocket()) {
+            expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> ms.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> ms.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> ms.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> ms.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> ms.getOption(RAW_SOCK_OPT));
+        }
+        try (Socket sa = SocketChannel.open().socket()) {
+            expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> sa.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> sa.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> sa.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> sa.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> sa.getOption(RAW_SOCK_OPT));
+        }
+        try (ServerSocket ssa = ServerSocketChannel.open().socket()) {
+            expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> ssa.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> ssa.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> ssa.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> ssa.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> ssa.getOption(RAW_SOCK_OPT));
+        }
+        try (DatagramSocket dsa = DatagramChannel.open().socket()) {
+            expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, null));
+            expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, TRUE));
+            expectThrows(UOE, () -> dsa.setOption(FAKE_SOCK_OPT, FALSE));
+            expectThrows(UOE, () -> dsa.setOption(RAW_SOCK_OPT, ""));
+            expectThrows(UOE, () -> dsa.setOption(RAW_SOCK_OPT, 1));
+            expectThrows(UOE, () -> dsa.getOption(FAKE_SOCK_OPT));
+            expectThrows(UOE, () -> dsa.getOption(RAW_SOCK_OPT));
+        }
+    }
+
+    static Map<SocketOption<?>,List<Object>> BAD_OPTION_VALUES = badOptionValues();
+
+    static Map<SocketOption<?>,List<Object>> badOptionValues() {
+        Map<SocketOption<?>,List<Object>> map = new HashMap<>();
+        map.put(IP_MULTICAST_IF,   listOf(null)         );
+        map.put(IP_MULTICAST_LOOP, listOf(null)         );
+        map.put(IP_MULTICAST_TTL,  listOf(null, -1, 256));
+        map.put(IP_TOS,            listOf(null, -1, 256));
+        map.put(SO_BROADCAST,      listOf(null)         );
+        map.put(SO_KEEPALIVE,      listOf(null)         );
+        map.put(SO_LINGER,         listOf(null)         );
+        map.put(SO_RCVBUF,         listOf(null, -1)     );
+        map.put(SO_REUSEADDR,      listOf(null)         );
+        map.put(SO_REUSEPORT,      listOf(null)         );
+        map.put(SO_SNDBUF,         listOf(null, -1)     );
+        map.put(TCP_NODELAY,       listOf(null)         );
+        // extended options, not in the map, will get a null value
+        return map;
+    }
+
+    // -- Socket
+
+    @DataProvider(name = "socketBadOptionValues")
+    public Object[][] socketBadOptionValues() throws Exception {
+        try (Socket s = new Socket()) {
+            return s.supportedOptions().stream()
+                    .flatMap(NullsAndBadValues::socketOptionToBadValues)
+                    .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "socketBadOptionValues")
+    public <T> void socket(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (Socket s = new Socket()) {
+            expectThrows(IAE, () -> s.setOption(option, value));
+        }
+    }
+
+    @Test(dataProvider = "socketBadOptionValues")
+    public <T> void socketAdapter(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (Socket s = SocketChannel.open().socket()) {
+            expectThrows(IAE, () -> s.setOption(option, value));
+        }
+    }
+
+    // -- ServerSocket
+
+    @DataProvider(name = "serverSocketBadOptionValues")
+    public Object[][] serverSocketBadOptionValues() throws Exception {
+        try (ServerSocket ss = new ServerSocket()) {
+            return ss.supportedOptions().stream()
+                     .flatMap(NullsAndBadValues::socketOptionToBadValues)
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "serverSocketBadOptionValues")
+    public <T> void serverSocket(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (ServerSocket ss = new ServerSocket()) {
+            expectThrows(IAE, () -> ss.setOption(option, value));
+        }
+    }
+
+    @Test(dataProvider = "serverSocketBadOptionValues")
+    public <T> void serverSocketAdapter(SocketOption<T> option, T value)
+        throws Exception
+    {
+        if (option == IP_TOS)
+            return;  // SSC does not support IP_TOS
+
+        try (ServerSocket ss = ServerSocketChannel.open().socket()) {
+            expectThrows(IAE, () -> ss.setOption(option, value));
+        }
+    }
+
+    // -- DatagramSocket
+
+    @DataProvider(name = "datagramSocketBadOptionValues")
+    public Object[][] datagramSocketBadOptionValues() throws Exception {
+        try (DatagramSocket ds = new DatagramSocket()) {
+            return ds.supportedOptions().stream()
+                     .flatMap(NullsAndBadValues::socketOptionToBadValues)
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "datagramSocketBadOptionValues")
+    public <T> void datagramSocket(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (DatagramSocket ds = new DatagramSocket()) {
+            expectThrows(IAE, () -> ds.setOption(option, value));
+        }
+    }
+
+    @Test(dataProvider = "datagramSocketBadOptionValues")
+    public <T> void datagramSocketAdapter(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (DatagramSocket ds = DatagramChannel.open().socket()) {
+            expectThrows(IAE, () -> ds.setOption(option, value));
+        }
+    }
+
+    // -- MulticastSocket
+
+    @DataProvider(name = "multicastSocketBadOptionValues")
+    public Object[][] multicastSocketBadOptionValues() throws Exception {
+        try (MulticastSocket ms = new MulticastSocket()) {
+            return ms.supportedOptions().stream()
+                     .flatMap(NullsAndBadValues::socketOptionToBadValues)
+                     .toArray(Object[][]::new);
+        }
+    }
+
+    @Test(dataProvider = "multicastSocketBadOptionValues")
+    public <T> void multicastSocket(SocketOption<T> option, T value)
+        throws Exception
+    {
+        try (MulticastSocket ms = new MulticastSocket()) {
+            expectThrows(IAE, () -> ms.setOption(option, value));
+        }
+    }
+
+    // --
+
+    static List<Object> listOf(Object... objs) {
+        List<Object> l = new ArrayList<>();
+        if (objs == null)
+            l.add(null);
+        else
+            Arrays.stream(objs).forEachOrdered(l::add);
+        return l;
+    }
+
+    static Stream<Object[]> socketOptionToBadValues(SocketOption<?> socketOption) {
+        List<Object> values = BAD_OPTION_VALUES.get(socketOption);
+        if (values == null) {
+            Object[][] a = new Object[][] { new Object[] { socketOption, null } };
+            return Stream.of(a);
+        }
+        return values.stream()
+                .flatMap(v -> Stream.of(new Object[][] { new Object[] { socketOption, v } }) );
+    }
+}
--- a/test/jdk/java/net/SocketOption/OptionsTest.java	Wed May 29 08:21:33 2019 -0400
+++ b/test/jdk/java/net/SocketOption/OptionsTest.java	Wed May 29 13:58:05 2019 +0100
@@ -61,14 +61,18 @@
         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
         Test.create(StandardSocketOptions.SO_LINGER, Integer.valueOf(80)),
-        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
     };
 
     static Test[] serverSocketTests = new Test[] {
         Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
-        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
     };
 
     static Test[] dgSocketTests = new Test[] {
@@ -76,12 +80,16 @@
         Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
         Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
         Test.create(StandardSocketOptions.SO_REUSEPORT, Boolean.FALSE),
-        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(0)),  // lower-bound
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100)),
+        Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(255))  //upper-bound
     };
 
     static Test[] mcSocketTests = new Test[] {
         Test.create(StandardSocketOptions.IP_MULTICAST_IF, getNetworkInterface()),
+        Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(0)),   // lower-bound
         Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(10)),
+        Test.create(StandardSocketOptions.IP_MULTICAST_TTL, Integer.valueOf(255)), //upper-bound
         Test.create(StandardSocketOptions.IP_MULTICAST_LOOP, Boolean.TRUE)
     };
 
--- a/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java	Wed May 29 08:21:33 2019 -0400
+++ b/test/jdk/java/net/SocketOption/UnsupportedOptionsTest.java	Wed May 29 13:58:05 2019 +0100
@@ -64,6 +64,13 @@
             socketOptions.add((SocketOption<?>)field.get(null));
             field = c.getField("TCP_QUICKACK");
             socketOptions.add((SocketOption<?>)field.get(null));
+            field = c.getField("TCP_KEEPIDLE");
+            socketOptions.add((SocketOption<?>)field.get(null));
+            field = c.getField("TCP_KEEPINTERVAL");
+            socketOptions.add((SocketOption<?>)field.get(null));
+            field = c.getField("TCP_KEEPCOUNT");
+            socketOptions.add((SocketOption<?>)field.get(null));
+
         } catch (ClassNotFoundException e) {
             // ignore, jdk.net module not present
         } catch (ReflectiveOperationException e) {