| /* | |
| * Copyright (c) 2019, 2021, 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. | |
| */ | |
| import java.io.IOException; | |
| 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.NetworkChannel; | |
| import java.nio.channels.ServerSocketChannel; | |
| import java.nio.channels.SocketChannel; | |
| import java.util.Set; | |
| import java.util.stream.Stream; | |
| import org.testng.annotations.Test; | |
| import org.testng.annotations.DataProvider; | |
| import static java.net.StandardSocketOptions.*; | |
| /* | |
| * @test | |
| * @bug 8235141 | |
| * @summary verifies that our implementation supports the set | |
| * of SocketOptions that are required by the API documentation. | |
| * @run testng/othervm RequiredOptions | |
| */ | |
| public class RequiredOptions { | |
| static final Set<SocketOption<?>> DATAGRAM_OPTIONS = | |
| Set.of(SO_BROADCAST, SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, IP_TOS); | |
| static final Set<SocketOption<?>> MULTICAST_OPTIONS = | |
| concat(DATAGRAM_OPTIONS, Set.of(IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL)); | |
| static final Set<SocketOption<?>> SOCKET_OPTIONS = | |
| Set.of(SO_KEEPALIVE, SO_LINGER, SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, TCP_NODELAY); | |
| static final Set<SocketOption<?>> SERVER_OPTIONS = | |
| Set.of(SO_RCVBUF, SO_REUSEADDR); | |
| static Set<SocketOption<?>> concat(Set<SocketOption<?>> ...options) { | |
| return Set.of(Stream.of(options).flatMap(Set::stream).distinct().toArray(SocketOption[]::new)); | |
| } | |
| @DataProvider(name = "sockets") | |
| static Object[][] provider() throws IOException { | |
| return new Object[][] { | |
| // UDP | |
| { Configurable.of(new DatagramSocket(null)), DATAGRAM_OPTIONS }, | |
| { Configurable.of(new MulticastSocket(null)), MULTICAST_OPTIONS }, | |
| // TCP | |
| { Configurable.of(new Socket()), SOCKET_OPTIONS }, | |
| { Configurable.of(new ServerSocket()), SERVER_OPTIONS }, | |
| // Adaptors | |
| { Configurable.of(DatagramChannel.open().socket()), MULTICAST_OPTIONS }, | |
| { Configurable.of(SocketChannel.open().socket()), SOCKET_OPTIONS }, | |
| { Configurable.of(ServerSocketChannel.open().socket()), SERVER_OPTIONS }, | |
| }; | |
| } | |
| @Test(dataProvider = "sockets") | |
| public <R, E extends Exception> | |
| void test(Configurable<R,E> socket, Set<SocketOption<?>> options) throws E { | |
| try (var s = socket) { | |
| var impl = socket.socket().getClass(); | |
| System.out.println("Testing " + impl + " with " + options); | |
| Set<SocketOption<?>> supported = socket.supportedOptions(); | |
| if (!supported.containsAll(options)) { | |
| for (var option : options) { | |
| if (!supported.contains(option)) { | |
| System.err.println("Option " + option + " not supported by " + impl); | |
| } | |
| } | |
| throw new AssertionError("Not all documented options are supported by " + impl); | |
| } | |
| } | |
| } | |
| static interface Configurable<R, E extends Exception> extends AutoCloseable { | |
| <T> R setOption(SocketOption<T> name, T value) throws E; | |
| <T> T getOption(SocketOption<T> name) throws E; | |
| Set<SocketOption<?>> supportedOptions() throws E; | |
| R socket(); | |
| void close() throws E; | |
| static Configurable<DatagramSocket, IOException> of(DatagramSocket socket) { | |
| return new ConfigurableImpl<>(socket, socket::setOption, | |
| socket::getOption, socket::supportedOptions, socket::close); | |
| } | |
| static Configurable<Socket, IOException> of(Socket socket) { | |
| return new ConfigurableImpl<>(socket, socket::setOption, | |
| socket::getOption, socket::supportedOptions, socket::close); | |
| } | |
| static Configurable<ServerSocket, IOException> of(ServerSocket socket) { | |
| return new ConfigurableImpl<>(socket, socket::setOption, | |
| socket::getOption, socket::supportedOptions, socket::close); | |
| } | |
| } | |
| static final class ConfigurableImpl<R, E extends Exception> implements Configurable<R, E> { | |
| @FunctionalInterface | |
| interface SetOption<R, E extends Exception> { | |
| <T> R setOption(SocketOption<T> name, T value) throws E; | |
| } | |
| @FunctionalInterface | |
| interface GetOption<E extends Exception> { | |
| <T> T getOption(SocketOption<T> name) throws E; | |
| } | |
| @FunctionalInterface | |
| interface SupportedOption<E extends Exception> { | |
| Set<SocketOption<?>> supportedOptions() throws E; | |
| } | |
| @FunctionalInterface | |
| interface Closer<E extends Exception> { | |
| void close() throws E; | |
| } | |
| private final R socket; | |
| private final SetOption<R, E> setter; | |
| private final GetOption<E> getter; | |
| private final SupportedOption<E> support; | |
| private final Closer<E> closer; | |
| public ConfigurableImpl(R socket, SetOption<R, E> setter, GetOption<E> getter, | |
| SupportedOption<E> support, Closer<E> closer) { | |
| this.socket = socket; | |
| this.setter = setter; | |
| this.getter = getter; | |
| this.support = support; | |
| this.closer = closer; | |
| } | |
| @Override | |
| public <T> R setOption(SocketOption<T> name, T value) throws E { | |
| return setter.setOption(name, value); | |
| } | |
| @Override | |
| public <T> T getOption(SocketOption<T> name) throws E { | |
| return getter.getOption(name); | |
| } | |
| @Override | |
| public Set<SocketOption<?>> supportedOptions() throws E { | |
| return support.supportedOptions(); | |
| } | |
| @Override | |
| public R socket() { | |
| return socket; | |
| } | |
| @Override | |
| public void close() throws E { | |
| closer.close(); | |
| } | |
| } | |
| } |