--- /dev/null 2020-04-01 14:15:03.000000000 +0100 +++ new/test/jdk/java/net/SocketOption/CachedImplOptions.java 2020-04-01 14:15:02.000000000 +0100 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2020, 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 8241988 + * @summary Checks that the caching of options does not affect other impls + * @run testng/othervm CachedImplOptions + * @run testng/othervm -Djava.net.preferIPv4Stack=true CachedImplOptions + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.DatagramSocketImpl; +import java.net.MulticastSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.util.Set; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class CachedImplOptions { + + @Test + public void testDatagramSocket() throws IOException { + try (var impl = new DatagramSocket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_SNDBUF)); + } + try (var impl = new DatagramSocket(new FooDatagramSocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(FooDatagramSocketImpl.FOO_OPTION)); + } + try (var impl = new DatagramSocket(new BarDatagramSocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(BarDatagramSocketImpl.BAR_OPTION)); + } + try (var impl = new DatagramSocket(new BazDatagramSocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(BazDatagramSocketImpl.BAZ_OPTION)); + } + try (var impl = new DatagramSocket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_SNDBUF)); + } + } + + @Test + public void testMulticastSocket() throws IOException { + try (var impl = new MulticastSocket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_SNDBUF)); + } + + // Use the factory to inject an alternative impl + DatagramSocket.setDatagramSocketImplFactory(() -> new FooDatagramSocketImpl()); + + try (var impl = new MulticastSocket()) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(FooDatagramSocketImpl.FOO_OPTION)); + } + } + + static class FooDatagramSocketImpl extends AbstractDatagramSocketImpl { + public static final SocketOption FOO_OPTION = new SocketOption<>() { + @Override public String name() { return "FOO_OPTION"; } + @Override public Class type() { return Boolean.class; } + }; + @Override public Set> supportedOptions() { return Set.of(FOO_OPTION); } + } + + static class BarDatagramSocketImpl extends AbstractDatagramSocketImpl { + public static final SocketOption BAR_OPTION = new SocketOption<>() { + @Override public String name() { return "BAR_OPTION"; } + @Override public Class type() { return Integer.class; } + }; + @Override public Set> supportedOptions() { return Set.of(BAR_OPTION); } + } + + static class BazDatagramSocketImpl extends AbstractDatagramSocketImpl { + public static final SocketOption BAZ_OPTION = new SocketOption<>() { + @Override public String name() { return "BAZ_OPTION"; } + @Override public Class type() { return Long.class; } + }; + @Override public Set> supportedOptions() { return Set.of(BAZ_OPTION); } + } + + static abstract class AbstractDatagramSocketImpl extends DatagramSocketImpl { + + @Override public Set> supportedOptions() { return null; } + @Override protected void create() throws SocketException { } + @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 x, NetworkInterface y) { } + @Override protected void leaveGroup(SocketAddress x, NetworkInterface y) { } + @Override protected void close() { } + @Override public void setOption(int optID, Object value) { } + @Override public Object getOption(int optID) { return null; } + } + + // -- socket + + @Test + public void testSocket() throws IOException { + try (var impl = new Socket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_SNDBUF)); + } + try (var impl = new Socket(new LarrySocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(LarrySocketImpl.LARRY_OPTION)); + } + try (var impl = new Socket(new CurlySocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(CurlySocketImpl.CURLY_OPTION)); + } + try (var impl = new Socket(new MoeSocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(MoeSocketImpl.MOE_OPTION)); + } + try (var impl = new Socket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_SNDBUF)); + } + } + + @Test + public void testServerSocket() throws IOException { + try (var impl = new ServerSocket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_RCVBUF)); + } + try (var impl = new ServerSocket(new LarrySocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(LarrySocketImpl.LARRY_OPTION)); + } + try (var impl = new ServerSocket(new CurlySocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(CurlySocketImpl.CURLY_OPTION)); + } + try (var impl = new ServerSocket(new MoeSocketImpl()) {}) { + Set> options = impl.supportedOptions(); + assertEquals(options, Set.of(MoeSocketImpl.MOE_OPTION)); + } + try (var impl = new ServerSocket()) { + Set> options = impl.supportedOptions(); + assertTrue(options.contains(StandardSocketOptions.SO_RCVBUF)); + } + } + + static class LarrySocketImpl extends AbstractSocketImpl { + public static final SocketOption LARRY_OPTION = new SocketOption<>() { + @Override public String name() { return "LARRY_OPTION"; } + @Override public Class type() { return Boolean.class; } + }; + @Override public Set> supportedOptions() { return Set.of(LARRY_OPTION); } + } + + static class CurlySocketImpl extends AbstractSocketImpl { + public static final SocketOption CURLY_OPTION = new SocketOption<>() { + @Override public String name() { return "CURLY_OPTION"; } + @Override public Class type() { return Integer.class; } + }; + @Override public Set> supportedOptions() { return Set.of(CURLY_OPTION); } + } + + static class MoeSocketImpl extends AbstractSocketImpl { + public static final SocketOption MOE_OPTION = new SocketOption<>() { + @Override public String name() { return "MOE_OPTION"; } + @Override public Class type() { return Long.class; } + }; + @Override public Set> supportedOptions() { return Set.of(MOE_OPTION); } + } + + static abstract class AbstractSocketImpl extends SocketImpl { + + @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; } + } +}