Summary
Update the default implementation of the new-style socket options
methods of java.net.SocketImpl and java.net.DatagramSocketImpl. As
well as the throwing specification and behaviour of the new-style
socket option methods of the four java.net socket types.
Problem
The java.net TCP-based socket types, ServerSocket and Socket, have
their implementations backed by java.net.SocketImpl. The java.net
UDP-based socket types, DatagramSocket and MulticastSocket, have
their implementations backed by java.net.DatagramSocketImpl. The
backing socket impl allows for different implementations to be plugged
into the socket APIs, which amounts to an SPI-like mechanism ( dating
back to the Java 1.0 era ).
When the three new-style methods for socket options, namely getOption,
setOption, and supportedOptions, were added in Java 9, to the four
java.net socket types, the supporting SocketImpl and
DatagramSocketImpl were also retrofitted with similar methods. Since
SocketImpl and DatagramSocketImpl are abstract classes, a default
implementation of these three new methods was added ( to allow backward
compatibility with pre-existing implementations ).
The default implementation of SocketImpl::supportedOptions returns a
set of socket options that represent that of what a typical
ServerSocket implementation may choose to support. The default
implementation of SocketImpl::getOption and SocketImpl::setOption
do some basic forwarding of the new-style options to the old-style
options, otherwise throw (the specified) UnsupportedOperationException, indicating that the SocketImpl does not support the option. Given
that the default supportedOptions support only the small set of
three server-like socket options ( SO_RCVBUF, SO_REUSEADDR, and
IP_TOS), the getOption/setOption methods only forward these three
socket options. This default implementation is neither specified nor
documented.
Similarly, the default implementation of
DatagramSocketImpl::supportedOptions returns a set of socket options
that represent that of what a typical DatagramSocket implementation
may choose to support. The default implementation of
DatagramSocketImpl::getOption and DatagramSocketImpl::setOption do
some basic forwarding of the new-style options to the old-style options,
otherwise throw (the specified) UnsupportedOperationException -
indicating that the DatagramSocketImpl does not support the option.
Given that the default supportedOptions support only the small set
of socket options ( SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, and
IP_TOS), the getOption/setOption methods only forward these four
socket options. This default implementation is neither specified nor
documented.
Given the above limited default implementations, it is reasonable to
assume that any concrete implementation of SocketImpl or
DatagramSocketImpl, will have to override these three new-style socket
option methods, to provide their own implementation tailored to the
specific socket options that they support. With that in mind, it would
be simpler and more straight forward to reduce the default
implementation and add a note suggesting that subclasses provide their
own implementation. Additionally, in some cases an effective non-empty
( no-op ) specification-compliant default implementation is not
practicable, e.g. it is not possible to for getOption to throw an
IOExcepton after the socket impl is closed, since the socket impl has
no specified state.
The second part of this CSR is concerted with the behavioural ( and minor specification ) aspects of the exceptions thrown when "bad" values are given to the following methods:
Socket::setOption(SocketOption name, T value)
Socket::getOption(SocketOption name)
ServerSocket::setOption(SocketOption name, T value)
ServerSocket::getOption(SocketOption name)
DatagramSocket::setOption(SocketOption name, T value)
DatagramSocket::getOption(SocketOption name)
MulticastSocket::setOption(SocketOption name, T value)
MulticastSocket::getOption(SocketOption name)
SocketImpl::setOption(SocketOption name, T value)
SocketImpl::getOption(SocketOption name)
DatagramSocketImpl::setOption(SocketOption name, T value)
DatagramSocketImpl::getOption(SocketOption name)
The aforementioned methods are specified to throw:
-
NullPointerExceptionif given anullsocket optionname( they currently may throwUnsupportedOperationException) -
IllegalArgumentExceptionif given a "bad" socket optionvalue( they currently may throwSocketException) -
IOExceptionif invoked after the socket has been closed ( they currently continue to operate, or may throwSocketException)
Additionally, there are some inconsistencies in their respective
specifications across socket and impl types, when it comes to the
documentation of particular exceptions, e.g. SocketImpl::setValue is
missing an @throws IllegalArgumentException, while Socket::setValue
documents it ( Socket::setValue cannot actually validate the option
value, validation is performed by the impl ).
DatagramSocketImpl::setValue has a similar issue. Also, there are some
@throws NullPointerException missing.
Solution
-
Update ( simplify ) and specify the default implementation of the three new-style socket option methods of
SocketImplandDatagramSocketImpl. More specifically, change the default implementation ofsupportedOptionsto return an empty set - indicating that no socket options are supported. The default implementation ofgetOption/setOptioncan then be updated to always throwUnsupportedOperationException, as logically no socket options are supported. -
Update the implementation of the four
java.netsocket types, and the two impl types, to match their specification. That is, to throw the specified exception when "bad" input is given. -
Make the specification of all three new-style socket option methods consistent, with respect to their specified exceptions, across all four socket types and two socket impls.
Specification
java.net.SocketImpl
/**
* 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()
/**
* 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 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
/**
* 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
+ * @throws NullPointerException if name is {@code null}
*
* @since 9
*/
protected <T> T getOption(SocketOption<T> name) throws IOException
java.net.DatagramSocketImpl
/**
* 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()
/**
* 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 problem occurs while attempting to set 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
/**
* 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 socket option
*
* @throws UnsupportedOperationException if the DatagramSocketImpl does not
* support the option
- * @throws IOException if an I/O problem occurs while attempting to set 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> T getOption(SocketOption<T> name) throws IOException
- csr of
-
JDK-8224477 java.net socket types new-style socket option methods - spec and impl mismatch
-
- Resolved
-