8260428: Drop support for pre JDK 1.4 DatagramSocketImpl implementations

Reviewed-by: alanb, dfuchs, vtewari
This commit is contained in:
Patrick Concannon 2021-11-01 17:11:20 +00:00
parent e265f83858
commit 977154400b
3 changed files with 212 additions and 93 deletions

View file

@ -27,6 +27,7 @@ package java.net;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -99,19 +100,30 @@ public abstract class DatagramSocketImpl implements SocketOptions {
* packet has been received for that address, then a subsequent call to * packet has been received for that address, then a subsequent call to
* send or receive may throw a PortUnreachableException. * send or receive may throw a PortUnreachableException.
* Note, there is no guarantee that the exception will be thrown. * Note, there is no guarantee that the exception will be thrown.
*
* @implSpec The default implementation of this method throws {@code SocketException}.
*
* @param address the remote InetAddress to connect to * @param address the remote InetAddress to connect to
* @param port the remote port number * @param port the remote port number
* @throws SocketException may be thrown if the socket cannot be * @throws SocketException may be thrown if the socket cannot be
* connected to the remote destination * connected to the remote destination
* @since 1.4 * @since 1.4
*/ */
protected void connect(InetAddress address, int port) throws SocketException {} protected void connect(InetAddress address, int port) throws SocketException {
throw new SocketException("connect not implemented");
}
/** /**
* Disconnects a datagram socket from its remote destination. * Disconnects a datagram socket from its remote destination.
*
* @implSpec The default implementation of this method throws {@code UncheckedIOException}.
*
* @throws UncheckedIOException if disconnect fails or no implementation is provided
* @since 1.4 * @since 1.4
*/ */
protected void disconnect() {} protected void disconnect() {
throw new UncheckedIOException(new SocketException("disconnect not implemented"));
}
/** /**
* Peek at the packet to see who it is from. Updates the specified {@code InetAddress} * Peek at the packet to see who it is from. Updates the specified {@code InetAddress}

View file

@ -56,11 +56,6 @@ final class NetMulticastSocket extends MulticastSocket {
*/ */
private final DatagramSocketImpl impl; private final DatagramSocketImpl impl;
/**
* Are we using an older DatagramSocketImpl?
*/
private final boolean oldImpl;
/** /**
* Set when a socket is ST_CONNECTED until we are certain * Set when a socket is ST_CONNECTED until we are certain
* that any packets which might have been received prior * that any packets which might have been received prior
@ -76,11 +71,9 @@ final class NetMulticastSocket extends MulticastSocket {
* Connection state: * Connection state:
* ST_NOT_CONNECTED = socket not connected * ST_NOT_CONNECTED = socket not connected
* ST_CONNECTED = socket connected * ST_CONNECTED = socket connected
* ST_CONNECTED_NO_IMPL = socket connected but not at impl level
*/ */
static final int ST_NOT_CONNECTED = 0; static final int ST_NOT_CONNECTED = 0;
static final int ST_CONNECTED = 1; static final int ST_CONNECTED = 1;
static final int ST_CONNECTED_NO_IMPL = 2;
int connectState = ST_NOT_CONNECTED; int connectState = ST_NOT_CONNECTED;
@ -97,7 +90,6 @@ final class NetMulticastSocket extends MulticastSocket {
NetMulticastSocket(DatagramSocketImpl impl) { NetMulticastSocket(DatagramSocketImpl impl) {
super((MulticastSocket) null); super((MulticastSocket) null);
this.impl = Objects.requireNonNull(impl); this.impl = Objects.requireNonNull(impl);
this.oldImpl = checkOldImpl(impl);
} }
/** /**
@ -135,59 +127,24 @@ final class NetMulticastSocket extends MulticastSocket {
if (!isBound()) if (!isBound())
bind(new InetSocketAddress(0)); bind(new InetSocketAddress(0));
// old impls do not support connect/disconnect getImpl().connect(address, port);
if (oldImpl) {
connectState = ST_CONNECTED_NO_IMPL;
} else {
try {
getImpl().connect(address, port);
// socket is now connected by the impl // socket is now connected by the impl
connectState = ST_CONNECTED; connectState = ST_CONNECTED;
// Do we need to filter some packets? // Do we need to filter some packets?
int avail = getImpl().dataAvailable(); int avail = getImpl().dataAvailable();
if (avail == -1) { if (avail == -1) {
throw new SocketException(); throw new SocketException();
} }
explicitFilter = avail > 0; explicitFilter = avail > 0;
if (explicitFilter) { if (explicitFilter) {
bytesLeftToFilter = getReceiveBufferSize(); bytesLeftToFilter = getReceiveBufferSize();
}
} catch (SocketException se) {
// connection will be emulated by DatagramSocket
connectState = ST_CONNECTED_NO_IMPL;
}
} }
connectedAddress = address; connectedAddress = address;
connectedPort = port; connectedPort = port;
} }
/**
* Return true if the given DatagramSocketImpl is an "old" impl. An old impl
* is one that doesn't implement the abstract methods added in Java SE 1.4.
*/
@SuppressWarnings("removal")
private static boolean checkOldImpl(DatagramSocketImpl impl) {
// DatagramSocketImpl.peekData() is a protected method, therefore we need to use
// getDeclaredMethod, therefore we need permission to access the member
try {
AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public Void run() throws NoSuchMethodException {
Class<?>[] cl = new Class<?>[1];
cl[0] = DatagramPacket.class;
impl.getClass().getDeclaredMethod("peekData", cl);
return null;
}
});
return false;
} catch (java.security.PrivilegedActionException e) {
return true;
}
}
/** /**
* Return the {@code DatagramSocketImpl} attached to this socket, * Return the {@code DatagramSocketImpl} attached to this socket,
* creating the socket if not already created. * creating the socket if not already created.
@ -382,19 +339,11 @@ final class NetMulticastSocket extends MulticastSocket {
SecurityManager security = System.getSecurityManager(); SecurityManager security = System.getSecurityManager();
if (security != null) { if (security != null) {
while (true) { while (true) {
String peekAd = null;
int peekPort = 0; int peekPort = 0;
// peek at the packet to see who it is from. // peek at the packet to see who it is from.
if (!oldImpl) { DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
// We can use the new peekData() API peekPort = getImpl().peekData(peekPacket);
DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1); String peekAd = peekPacket.getAddress().getHostAddress();
peekPort = getImpl().peekData(peekPacket);
peekAd = peekPacket.getAddress().getHostAddress();
} else {
InetAddress adr = new InetAddress();
peekPort = getImpl().peek(adr);
peekAd = adr.getHostAddress();
}
try { try {
security.checkAccept(peekAd, peekPort); security.checkAccept(peekAd, peekPort);
// security check succeeded - so now break // security check succeeded - so now break
@ -418,7 +367,7 @@ final class NetMulticastSocket extends MulticastSocket {
} }
} }
DatagramPacket tmp = null; DatagramPacket tmp = null;
if ((connectState == ST_CONNECTED_NO_IMPL) || explicitFilter) { if (explicitFilter) {
// We have to do the filtering the old fashioned way since // We have to do the filtering the old fashioned way since
// the native impl doesn't support connect or the connect // the native impl doesn't support connect or the connect
// via the impl failed, or .. "explicitFilter" may be set when // via the impl failed, or .. "explicitFilter" may be set when
@ -426,21 +375,11 @@ final class NetMulticastSocket extends MulticastSocket {
// when packets from other sources might be queued on socket. // when packets from other sources might be queued on socket.
boolean stop = false; boolean stop = false;
while (!stop) { while (!stop) {
InetAddress peekAddress = null;
int peekPort = -1;
// peek at the packet to see who it is from. // peek at the packet to see who it is from.
if (!oldImpl) { DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
// We can use the new peekData() API int peekPort = getImpl().peekData(peekPacket);
DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1); InetAddress peekAddress = peekPacket.getAddress();
peekPort = getImpl().peekData(peekPacket); if ((!connectedAddress.equals(peekAddress)) || (connectedPort != peekPort)) {
peekAddress = peekPacket.getAddress();
} else {
// this api only works for IPv4
peekAddress = new InetAddress();
peekPort = getImpl().peek(peekAddress);
}
if ((!connectedAddress.equals(peekAddress)) ||
(connectedPort != peekPort)) {
// throw the packet away and silently continue // throw the packet away and silently continue
tmp = new DatagramPacket( tmp = new DatagramPacket(
new byte[1024], 1024); new byte[1024], 1024);
@ -578,11 +517,7 @@ final class NetMulticastSocket extends MulticastSocket {
public synchronized void setReuseAddress(boolean on) throws SocketException { public synchronized void setReuseAddress(boolean on) throws SocketException {
if (isClosed()) if (isClosed())
throw new SocketException("Socket is closed"); throw new SocketException("Socket is closed");
// Integer instead of Boolean for compatibility with older DatagramSocketImpl getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
if (oldImpl)
getImpl().setOption(SocketOptions.SO_REUSEADDR, on ? -1 : 0);
else
getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
} }
@Override @Override
@ -809,9 +744,6 @@ final class NetMulticastSocket extends MulticastSocket {
if (!(mcastaddr instanceof InetSocketAddress addr)) if (!(mcastaddr instanceof InetSocketAddress addr))
throw new IllegalArgumentException("Unsupported address type"); throw new IllegalArgumentException("Unsupported address type");
if (oldImpl)
throw new UnsupportedOperationException();
checkAddress(addr.getAddress(), "joinGroup"); checkAddress(addr.getAddress(), "joinGroup");
@SuppressWarnings("removal") @SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager(); SecurityManager security = System.getSecurityManager();
@ -835,9 +767,6 @@ final class NetMulticastSocket extends MulticastSocket {
if (!(mcastaddr instanceof InetSocketAddress addr)) if (!(mcastaddr instanceof InetSocketAddress addr))
throw new IllegalArgumentException("Unsupported address type"); throw new IllegalArgumentException("Unsupported address type");
if (oldImpl)
throw new UnsupportedOperationException();
checkAddress(addr.getAddress(), "leaveGroup"); checkAddress(addr.getAddress(), "leaveGroup");
@SuppressWarnings("removal") @SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager(); SecurityManager security = System.getSecurityManager();

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 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.
*/
/*
* @test
* @bug 8260428
* @summary Drop support for pre JDK 1.4 DatagramSocketImpl implementations
* @run testng/othervm OldDatagramSocketImplTest
*/
import org.testng.annotations.Test;
import java.net.*;
import java.io.*;
import static org.testng.Assert.assertEquals;
public class OldDatagramSocketImplTest {
InetAddress LOOPBACK = InetAddress.getLoopbackAddress();
@Test
public void testOldImplConnect() {
try (var ds = new DatagramSocket(new OldDatagramSocketImpl()) {}) {
ds.connect(new InetSocketAddress(LOOPBACK, 6667));
throw new RuntimeException("ERROR: test failed");
} catch (SocketException ex) {
assertEquals(ex.getMessage(), "connect not implemented");
System.out.println("PASSED: default implementation of connect has thrown as expected");
}
}
@Test
public void testOldImplConnectTwoArgs() {
try (var ds = new DatagramSocket(new OldDatagramSocketImpl()) {}) {
ds.connect(LOOPBACK, 6667);
throw new RuntimeException("ERROR: test failed");
} catch (UncheckedIOException ex) {
assertEquals(ex.getMessage(), "connect failed");
System.out.println("PASSED: default implementation of connect has thrown as expected");
}
}
@Test
public void testOldImplDisconnect() {
try (var ds = new DatagramSocket(new OldDatagramSocketImplWithValidConnect()) { }){
ds.connect(LOOPBACK, 6667);
ds.disconnect();
throw new RuntimeException("ERROR: test failed");
} catch (UncheckedIOException ex) {
var innerException = ex.getCause();
assertEquals(innerException.getClass(), SocketException.class);
assertEquals(innerException.getMessage(), "disconnect not implemented");
System.out.println("PASSED: default implementation of disconnect has thrown as expected");
}
}
@Test
public void testOldImplPublic() {
try (var ds = new PublicOldDatagramSocketImpl()) {
ds.connect(LOOPBACK, 0);
throw new RuntimeException("ERROR: test failed");
} catch (SocketException ex) {
assertEquals(ex.getMessage(), "connect not implemented");
System.out.println("PASSED: default implementation of disconnect has thrown as expected");
}
}
@Test
public void testOldImplPublicDisconnect() {
try (var ds = new PublicOldDatagramSocketImplWithValidConnect()) {
ds.disconnect();
throw new RuntimeException("ERROR: test failed");
} catch (UncheckedIOException ex) {
var innerException = ex.getCause();
assertEquals(innerException.getClass(), SocketException.class);
assertEquals(innerException.getMessage(), "disconnect not implemented");
System.out.println("PASSED: default implementation of disconnect has thrown as expected");
}
}
private class OldDatagramSocketImpl extends DatagramSocketImpl implements AutoCloseable {
@Override
protected void create() throws SocketException { }
@Override
protected void bind(int lport, InetAddress laddr) throws SocketException { }
@Override
protected void send(DatagramPacket p) throws IOException { }
@Override
protected int peek(InetAddress i) throws IOException {
return 0;
}
@Override
protected int peekData(DatagramPacket p) throws IOException {
return 0;
}
@Override
protected void receive(DatagramPacket p) throws IOException { }
@Override
protected void setTTL(byte ttl) throws IOException { }
@Override
protected byte getTTL() throws IOException {
return 0;
}
@Override
protected void setTimeToLive(int ttl) throws IOException { }
@Override
protected int getTimeToLive() throws IOException {
return 0;
}
@Override
protected void join(InetAddress inetaddr) throws IOException { }
@Override
protected void leave(InetAddress inetaddr) throws IOException { }
@Override
protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException { }
@Override
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException { }
@Override
public void close() { }
@Override
public void setOption(int optID, Object value) throws SocketException { }
@Override
public Object getOption(int optID) throws SocketException {
return null;
}
}
private class OldDatagramSocketImplWithValidConnect extends OldDatagramSocketImpl implements AutoCloseable {
@Override
protected void connect(InetAddress address, int port) throws SocketException { }
}
// Overriding connect() to make it public so that it can be called
// directly from the test code
private class PublicOldDatagramSocketImpl extends OldDatagramSocketImpl {
public void connect(InetAddress addr, int port) throws SocketException { super.connect(addr, port); }
}
// Overriding disconnect() to make it public so that it can be called
// directly from the test code
private class PublicOldDatagramSocketImplWithValidConnect extends OldDatagramSocketImplWithValidConnect {
public void disconnect() { super.disconnect(); }
}
}