Handle deprecated APIs (#5550)

* Handle deprecated APIs

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
This commit is contained in:
Holger Friedrich
2026-05-17 00:19:33 +02:00
committed by GitHub
parent 1f74dde4ed
commit d7470afc23
6 changed files with 43 additions and 145 deletions
@@ -26,6 +26,8 @@ import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.LongSupplier; import java.util.function.LongSupplier;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -309,10 +311,21 @@ public class IntegrationTestSupport extends JavaTest {
public class TCPSlaveConnectionFactoryImpl implements TCPSlaveConnectionFactory { public class TCPSlaveConnectionFactoryImpl implements TCPSlaveConnectionFactory {
private final Queue<Socket> acceptedSockets = new ConcurrentLinkedQueue<>();
@Override @Override
public TCPSlaveConnection create(@NonNullByDefault({}) Socket socket) { public TCPSlaveConnection create(@NonNullByDefault({}) Socket socket) {
acceptedSockets.add(socket);
return new TCPSlaveConnection(socket, new SpyingModbusTCPTransportFactory()); return new TCPSlaveConnection(socket, new SpyingModbusTCPTransportFactory());
} }
public long countOpenSockets() {
return acceptedSockets.stream().filter(s -> !s.isClosed()).count();
}
public void clearAcceptedSockets() {
acceptedSockets.clear();
}
} }
public class UDPSlaveTerminalFactoryImpl implements UDPSlaveTerminalFactory { public class UDPSlaveTerminalFactoryImpl implements UDPSlaveTerminalFactory {
@@ -17,21 +17,8 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.*; import static org.junit.jupiter.api.Assumptions.*;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.util.BitSet; import java.util.BitSet;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -74,14 +61,6 @@ public class SmokeTest extends IntegrationTestSupport {
private static final int DISCRETE_EVERY_N_TRUE = 3; private static final int DISCRETE_EVERY_N_TRUE = 3;
private static final int HOLDING_REGISTER_MULTIPLIER = 1; private static final int HOLDING_REGISTER_MULTIPLIER = 1;
private static final int INPUT_REGISTER_MULTIPLIER = 10; private static final int INPUT_REGISTER_MULTIPLIER = 10;
private static final SpyingSocketFactory SOCKET_SPY = new SpyingSocketFactory();
static {
try {
Socket.setSocketImplFactory(SOCKET_SPY);
} catch (IOException e) {
fail("Could not install socket spy in SmokeTest");
}
}
/** /**
* Whether tests are run in Continuous Integration environment, i.e. Jenkins or Travis CI * Whether tests are run in Continuous Integration environment, i.e. Jenkins or Travis CI
@@ -138,8 +117,8 @@ public class SmokeTest extends IntegrationTestSupport {
} }
@BeforeEach @BeforeEach
public void setUpSocketSpy() throws IOException { public void setUpSocketSpy() {
SOCKET_SPY.sockets.clear(); ((TCPSlaveConnectionFactoryImpl) tcpConnectionFactory).clearAcceptedSockets();
} }
/** /**
@@ -844,7 +823,7 @@ public class SmokeTest extends IntegrationTestSupport {
config.setReconnectAfterMillis(9_000_000); config.setReconnectAfterMillis(9_000_000);
// 1. capture open connections at this point // 1. capture open connections at this point
long openSocketsBefore = getNumberOfOpenClients(SOCKET_SPY); long openSocketsBefore = getNumberOfOpenClients();
assertThat(openSocketsBefore, is(equalTo(0L))); assertThat(openSocketsBefore, is(equalTo(0L)));
// 2. make poll, binding opens the tcp connection // 2. make poll, binding opens the tcp connection
@@ -861,7 +840,7 @@ public class SmokeTest extends IntegrationTestSupport {
} }
waitForAssert(() -> { waitForAssert(() -> {
// 3. ensure one open connection // 3. ensure one open connection
long openSocketsAfter = getNumberOfOpenClients(SOCKET_SPY); long openSocketsAfter = getNumberOfOpenClients();
assertThat(openSocketsAfter, is(equalTo(1L))); assertThat(openSocketsAfter, is(equalTo(1L)));
}); });
try (ModbusCommunicationInterface ignored = modbusManager.newModbusCommunicationInterface(endpoint, try (ModbusCommunicationInterface ignored = modbusManager.newModbusCommunicationInterface(endpoint,
@@ -876,20 +855,20 @@ public class SmokeTest extends IntegrationTestSupport {
}); });
assertTrue(latch.await(60, TimeUnit.SECONDS)); assertTrue(latch.await(60, TimeUnit.SECONDS));
} }
assertThat(getNumberOfOpenClients(SOCKET_SPY), is(equalTo(1L))); assertThat(getNumberOfOpenClients(), is(equalTo(1L)));
// wait for moment (to check that no connections are closed) // wait for moment (to check that no connections are closed)
Thread.sleep(1000); Thread.sleep(1000);
// no more than 1 connection, even though requests are going through // no more than 1 connection, even though requests are going through
assertThat(getNumberOfOpenClients(SOCKET_SPY), is(equalTo(1L))); assertThat(getNumberOfOpenClients(), is(equalTo(1L)));
} }
Thread.sleep(1000); Thread.sleep(1000);
// Still one connection open even after closing second connection // Still one connection open even after closing second connection
assertThat(getNumberOfOpenClients(SOCKET_SPY), is(equalTo(1L))); assertThat(getNumberOfOpenClients(), is(equalTo(1L)));
} // 4. close (the last) comms } // 4. close (the last) comms
// ensure that open connections are closed // ensure that open connections are closed
// (despite huge "reconnect after millis") // (despite huge "reconnect after millis")
waitForAssert(() -> { waitForAssert(() -> {
long openSocketsAfterClose = getNumberOfOpenClients(SOCKET_SPY); long openSocketsAfterClose = getNumberOfOpenClients();
assertThat(openSocketsAfterClose, is(equalTo(0L))); assertThat(openSocketsAfterClose, is(equalTo(0L)));
}); });
} }
@@ -908,7 +887,7 @@ public class SmokeTest extends IntegrationTestSupport {
config.setReconnectAfterMillis(2_000); config.setReconnectAfterMillis(2_000);
// 1. capture open connections at this point // 1. capture open connections at this point
long openSocketsBefore = getNumberOfOpenClients(SOCKET_SPY); long openSocketsBefore = getNumberOfOpenClients();
assertThat(openSocketsBefore, is(equalTo(0L))); assertThat(openSocketsBefore, is(equalTo(0L)));
// 2. make poll, binding opens the tcp connection // 2. make poll, binding opens the tcp connection
@@ -926,119 +905,20 @@ public class SmokeTest extends IntegrationTestSupport {
// Right after the poll we should have one connection open // Right after the poll we should have one connection open
waitForAssert(() -> { waitForAssert(() -> {
// 3. ensure one open connection // 3. ensure one open connection
long openSocketsAfter = getNumberOfOpenClients(SOCKET_SPY); long openSocketsAfter = getNumberOfOpenClients();
assertThat(openSocketsAfter, is(equalTo(1L))); assertThat(openSocketsAfter, is(equalTo(1L)));
}); });
// 4. Connection should close itself by the commons pool eviction policy (checking for old idle connection // 4. Connection should close itself by the commons pool eviction policy (checking for old idle connection
// every now and then) // every now and then)
waitForAssert(() -> { waitForAssert(() -> {
// 3. ensure one open connection // 3. ensure one open connection
long openSocketsAfter = getNumberOfOpenClients(SOCKET_SPY); long openSocketsAfter = getNumberOfOpenClients();
assertThat(openSocketsAfter, is(equalTo(0L))); assertThat(openSocketsAfter, is(equalTo(0L)));
}, 60_000, 50); }, 60_000, 50);
} }
} }
private long getNumberOfOpenClients(SpyingSocketFactory socketSpy) { private long getNumberOfOpenClients() {
localAddress(); return ((TCPSlaveConnectionFactoryImpl) tcpConnectionFactory).countOpenSockets();
return socketSpy.sockets.stream().filter(this::isConnectedToTestServer).count();
}
/**
* Spy all sockets that are created
*
* @author Sami Salonen
*
*/
private static class SpyingSocketFactory implements SocketImplFactory {
Queue<SocketImpl> sockets = new ConcurrentLinkedQueue<>();
@Override
public SocketImpl createSocketImpl() {
SocketImpl socket = newSocksSocketImpl();
sockets.add(socket);
return socket;
}
}
private static SocketImpl newSocksSocketImpl() {
try {
Class<?> socksSocketImplClass = Class.forName("java.net.SocksSocketImpl");
Class<?> socketImplClass = SocketImpl.class;
// // For Debugging
// for (Method method : socketImplClass.getDeclaredMethods()) {
// LoggerFactory.getLogger("foobar")
// .error("SocketImpl." + method.getName() + Arrays.toString(method.getParameters()));
// }
// for (Constructor constructor : socketImplClass.getDeclaredConstructors()) {
// LoggerFactory.getLogger("foobar")
// .error("SocketImpl." + constructor.getName() + Arrays.toString(constructor.getParameters()));
// }
// for (Method method : socksSocketImplClass.getDeclaredMethods()) {
// LoggerFactory.getLogger("foobar")
// .error("SocksSocketImpl." + method.getName() + Arrays.toString(method.getParameters()));
// }
// for (Constructor constructor : socksSocketImplClass.getDeclaredConstructors()) {
// LoggerFactory.getLogger("foobar").error(
// "SocksSocketImpl." + constructor.getName() + Arrays.toString(constructor.getParameters()));
// }
try {
Constructor<?> constructor = socksSocketImplClass.getDeclaredConstructor();
constructor.setAccessible(true);
return (SocketImpl) Objects.requireNonNull(constructor.newInstance());
} catch (NoSuchMethodException e) {
// Newer Javas (Java 14->) do not have default constructor 'SocksSocketImpl()'
// Instead we use "static SocketImpl.createPlatformSocketImpl" and "SocksSocketImpl(SocketImpl)
Method socketImplCreateMethod = socketImplClass.getDeclaredMethod("createPlatformSocketImpl",
boolean.class);
socketImplCreateMethod.setAccessible(true);
Object socketImpl = socketImplCreateMethod
.invoke(/* null since we deal with static method */ giveNull(), /* server */false);
Constructor<?> socksSocketImplConstructor = socksSocketImplClass
.getDeclaredConstructor(socketImplClass);
socksSocketImplConstructor.setAccessible(true);
return (SocketImpl) Objects.requireNonNull(socksSocketImplConstructor.newInstance(socketImpl));
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private boolean isConnectedToTestServer(SocketImpl impl) {
final InetAddress testServerAddress = localAddress();
final int port;
boolean connected = true;
final InetAddress address;
try {
Method getPort = SocketImpl.class.getDeclaredMethod("getPort");
getPort.setAccessible(true);
port = (int) getPort.invoke(impl);
Method getInetAddressMethod = SocketImpl.class.getDeclaredMethod("getInetAddress");
getInetAddressMethod.setAccessible(true);
address = (InetAddress) getInetAddressMethod.invoke(impl);
// hacky (but java8-14 compatible) way to know if socket is open
// SocketImpl.getOption throws IOException when socket is closed
Method getOption = SocketImpl.class.getDeclaredMethod("getOption", SocketOption.class);
getOption.setAccessible(true);
try {
getOption.invoke(impl, StandardSocketOptions.SO_KEEPALIVE);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof IOException) {
connected = false;
} else {
throw e;
}
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return port == tcpModbusPort && connected && address.equals(testServerAddress);
} }
} }
@@ -143,7 +143,7 @@ public class YamlModelRepositoryImpl implements WatchService.WatchEventListener,
objectMapper.findAndRegisterModules(); objectMapper.findAndRegisterModules();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.setDefaultPropertyInclusion(Include.NON_NULL);
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
this.watchService = watchService; this.watchService = watchService;
@@ -201,7 +201,7 @@ public class LRUMediaCacheEntryTest {
} }
}).toList(); }).toList();
IOException possibleException = exceptionCatched.getValue(); IOException possibleException = exceptionCatched.get();
if (possibleException != null) { if (possibleException != null) {
throw possibleException; throw possibleException;
} }
@@ -12,6 +12,7 @@
*/ */
package org.openhab.core.tools; package org.openhab.core.tools;
import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
@@ -20,10 +21,10 @@ import java.util.Set;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.help.HelpFormatter;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.storage.json.internal.JsonStorage; import org.openhab.core.storage.json.internal.JsonStorage;
@@ -69,15 +70,15 @@ public class UpgradeTool {
options.addOption(Option.builder().longOpt(OPT_USERDATA_DIR).desc( options.addOption(Option.builder().longOpt(OPT_USERDATA_DIR).desc(
"USERDATA directory to process. Enclose it in double quotes to ensure that any backslashes are not ignored by your command shell.") "USERDATA directory to process. Enclose it in double quotes to ensure that any backslashes are not ignored by your command shell.")
.numberOfArgs(1).build()); .numberOfArgs(1).get());
options.addOption(Option.builder().longOpt(OPT_CONF_DIR).desc( options.addOption(Option.builder().longOpt(OPT_CONF_DIR).desc(
"CONF directory to process. Enclose it in double quotes to ensure that any backslashes are not ignored by your command shell.") "CONF directory to process. Enclose it in double quotes to ensure that any backslashes are not ignored by your command shell.")
.numberOfArgs(1).build()); .numberOfArgs(1).get());
options.addOption(Option.builder().longOpt(OPT_COMMAND).numberOfArgs(1) options.addOption(Option.builder().longOpt(OPT_COMMAND).numberOfArgs(1)
.desc("command to execute (executes all if omitted)").build()); .desc("command to execute (executes all if omitted)").get());
options.addOption(Option.builder().longOpt(OPT_LIST_COMMANDS).desc("list available commands").build()); options.addOption(Option.builder().longOpt(OPT_LIST_COMMANDS).desc("list available commands").get());
options.addOption(Option.builder().longOpt(OPT_LOG).numberOfArgs(1).desc("log verbosity").build()); options.addOption(Option.builder().longOpt(OPT_LOG).numberOfArgs(1).desc("log verbosity").get());
options.addOption(Option.builder().longOpt(OPT_FORCE).desc("force execution (even if already done)").build()); options.addOption(Option.builder().longOpt(OPT_FORCE).desc("force execution (even if already done)").get());
return options; return options;
} }
@@ -143,8 +144,12 @@ public class UpgradeTool {
} }
}); });
} catch (ParseException e) { } catch (ParseException e) {
HelpFormatter formatter = new HelpFormatter(); HelpFormatter formatter = HelpFormatter.builder().get();
formatter.printHelp("upgradetool", "", options, "", true); try {
formatter.printHelp("upgradetool", "", options, "", true);
} catch (IOException ioException) {
LOGGER.error("Error printing help: {}", ioException.getMessage());
}
} }
System.exit(0); System.exit(0);
@@ -82,7 +82,7 @@ public class YamlConfigurationV1TagsUpgrader implements Upgrader {
objectMapper.findAndRegisterModules(); objectMapper.findAndRegisterModules();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.setSerializationInclusion(Include.NON_NULL); objectMapper.setDefaultPropertyInclusion(Include.NON_NULL);
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
} }