8224477: java.net socket types new-style socket option methods - spec and impl mismatch

Reviewed-by: alanb
This commit is contained in:
Chris Hegarty 2019-05-29 13:58:05 +01:00
parent cf48689855
commit bc24d17e80
18 changed files with 1289 additions and 300 deletions

View file

@ -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.FileDescriptor;
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;
/**
@ -87,26 +89,6 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
return isReusePortAvailable;
}
/**
* 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
*/
@ -400,6 +382,125 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
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)

View file

@ -35,12 +35,14 @@ import java.security.PrivilegedActionException;
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 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
*/
protected boolean stream;
/* whether this is a server or not */
final boolean isServer;
/**
* Load net library into runtime.
*/
@ -112,27 +117,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
}
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 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
}
}
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,

View file

@ -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.io.IOException;
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 @@ class DatagramSocket implements java.io.Closeable {
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 @@ class DatagramSocket implements java.io.Closeable {
* @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);
}

View file

@ -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 @@ package java.net;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;
/**
@ -265,123 +266,69 @@ public abstract class DatagramSocketImpl implements SocketOptions {
/**
* 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();
}
}

View file

@ -1025,6 +1025,9 @@ class ServerSocket implements java.io.Closeable {
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 @@ class ServerSocket implements java.io.Closeable {
* @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);
}

View file

@ -33,6 +33,7 @@ import java.lang.invoke.VarHandle;
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 @@ class Socket implements java.io.Closeable {
* @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 @@ class Socket implements java.io.Closeable {
*/
@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);
}

View file

@ -29,8 +29,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileDescriptor;
import java.util.Objects;
import java.util.Set;
import sun.net.PlatformSocketImpl;
/**
@ -74,22 +74,10 @@ public abstract class SocketImpl implements SocketOptions {
*/
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 @@ public abstract class SocketImpl implements SocketOptions {
/**
* 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 @@ public abstract class SocketImpl implements SocketOptions {
} 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();
}
}