[denonmarantz] Run the Telnet socket in a dedicated thread (#9511)

* Run the Telnet socket in a dedicated thread, not from the shared thread pool.
Fixes #9494
Signed-off-by: Jan-Willem Veldhuis <jwveldhuis@gmail.com>
This commit is contained in:
jwveldhuis 2020-12-26 20:21:21 +01:00 committed by GitHub
parent 9d36c31d76
commit d014ac6350
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 31 additions and 27 deletions

View File

@ -29,9 +29,9 @@ import org.openhab.binding.denonmarantz.internal.connector.telnet.DenonMarantzTe
public class DenonMarantzConnectorFactory { public class DenonMarantzConnectorFactory {
public DenonMarantzConnector getConnector(DenonMarantzConfiguration config, DenonMarantzState state, public DenonMarantzConnector getConnector(DenonMarantzConfiguration config, DenonMarantzState state,
ScheduledExecutorService scheduler, HttpClient httpClient) { ScheduledExecutorService scheduler, HttpClient httpClient, String thingUID) {
if (config.isTelnet()) { if (config.isTelnet()) {
return new DenonMarantzTelnetConnector(config, state, scheduler); return new DenonMarantzTelnetConnector(config, state, scheduler, thingUID);
} else { } else {
return new DenonMarantzHttpConnector(config, state, scheduler, httpClient); return new DenonMarantzHttpConnector(config, state, scheduler, httpClient);
} }

View File

@ -31,9 +31,9 @@ import org.slf4j.LoggerFactory;
* @author Jeroen Idserda - Initial contribution (1.x Binding) * @author Jeroen Idserda - Initial contribution (1.x Binding)
* @author Jan-Willem Veldhuis - Refactored for 2.x * @author Jan-Willem Veldhuis - Refactored for 2.x
*/ */
public class DenonMarantzTelnetClient implements Runnable { public class DenonMarantzTelnetClientThread extends Thread {
private Logger logger = LoggerFactory.getLogger(DenonMarantzTelnetClient.class); private Logger logger = LoggerFactory.getLogger(DenonMarantzTelnetClientThread.class);
private static final Integer RECONNECT_DELAY = 60000; // 1 minute private static final Integer RECONNECT_DELAY = 60000; // 1 minute
@ -43,8 +43,6 @@ public class DenonMarantzTelnetClient implements Runnable {
private DenonMarantzTelnetListener listener; private DenonMarantzTelnetListener listener;
private boolean running = true;
private boolean connected = false; private boolean connected = false;
private Socket socket; private Socket socket;
@ -53,7 +51,7 @@ public class DenonMarantzTelnetClient implements Runnable {
private BufferedReader in; private BufferedReader in;
public DenonMarantzTelnetClient(DenonMarantzConfiguration config, DenonMarantzTelnetListener listener) { public DenonMarantzTelnetClientThread(DenonMarantzConfiguration config, DenonMarantzTelnetListener listener) {
logger.debug("Denon listener created"); logger.debug("Denon listener created");
this.config = config; this.config = config;
this.listener = listener; this.listener = listener;
@ -61,7 +59,7 @@ public class DenonMarantzTelnetClient implements Runnable {
@Override @Override
public void run() { public void run() {
while (running) { while (!isInterrupted()) {
if (!connected) { if (!connected) {
connectTelnetSocket(); connectTelnetSocket();
} }
@ -90,20 +88,21 @@ public class DenonMarantzTelnetClient implements Runnable {
connected = false; connected = false;
} }
} catch (IOException e) { } catch (IOException e) {
if (!Thread.currentThread().isInterrupted()) { if (!isInterrupted()) {
// only log if we don't stop this on purpose causing a SocketClosed // only log if we don't stop this on purpose causing a SocketClosed
logger.debug("Error in telnet connection ", e); logger.debug("Error in telnet connection ", e);
} }
connected = false; connected = false;
listener.telnetClientConnected(false); listener.telnetClientConnected(false);
} }
} while (running && connected); } while (!isInterrupted() && connected);
} }
disconnect();
logger.debug("Stopped client thread");
} }
public void sendCommand(String command) { public void sendCommand(String command) {
if (out != null) { if (out != null) {
logger.debug("Sending command {}", command);
try { try {
out.write(command + '\r'); out.write(command + '\r');
out.flush(); out.flush();
@ -116,7 +115,6 @@ public class DenonMarantzTelnetClient implements Runnable {
} }
public void shutdown() { public void shutdown() {
this.running = false;
disconnect(); disconnect();
} }
@ -124,13 +122,14 @@ public class DenonMarantzTelnetClient implements Runnable {
disconnect(); disconnect();
int delay = 0; int delay = 0;
while (this.running && (socket == null || !socket.isConnected())) { while (!isInterrupted() && (socket == null || !socket.isConnected())) {
try { try {
Thread.sleep(delay); Thread.sleep(delay);
logger.debug("Connecting to {}", config.getHost()); logger.debug("Connecting to {}", config.getHost());
// Use raw socket instead of TelnetClient here because TelnetClient sends an extra newline char // Use raw socket instead of TelnetClient here because TelnetClient sends an
// after each write which causes the connection to become unresponsive. // extra newline char after each write which causes the connection to become
// unresponsive.
socket = new Socket(); socket = new Socket();
socket.connect(new InetSocketAddress(config.getHost(), config.getTelnetPort()), TIMEOUT); socket.connect(new InetSocketAddress(config.getHost(), config.getTelnetPort()), TIMEOUT);
socket.setKeepAlive(true); socket.setKeepAlive(true);
@ -141,6 +140,7 @@ public class DenonMarantzTelnetClient implements Runnable {
connected = true; connected = true;
listener.telnetClientConnected(true); listener.telnetClientConnected(true);
logger.debug("Denon telnet client connected to {}", config.getHost());
} catch (IOException e) { } catch (IOException e) {
logger.debug("Cannot connect to {}", config.getHost(), e); logger.debug("Cannot connect to {}", config.getHost(), e);
listener.telnetClientConnected(false); listener.telnetClientConnected(false);
@ -149,8 +149,6 @@ public class DenonMarantzTelnetClient implements Runnable {
} }
delay = RECONNECT_DELAY; delay = RECONNECT_DELAY;
} }
logger.debug("Denon telnet client connected to {}", config.getHost());
} }
public boolean isConnected() { public boolean isConnected() {

View File

@ -46,7 +46,7 @@ public class DenonMarantzTelnetConnector extends DenonMarantzConnector implement
private static final BigDecimal NINETYNINE = new BigDecimal("99"); private static final BigDecimal NINETYNINE = new BigDecimal("99");
private DenonMarantzTelnetClient telnetClient; private DenonMarantzTelnetClientThread telnetClientThread;
private boolean displayNowplaying = false; private boolean displayNowplaying = false;
@ -54,13 +54,14 @@ public class DenonMarantzTelnetConnector extends DenonMarantzConnector implement
private Future<?> telnetStateRequest; private Future<?> telnetStateRequest;
private Future<?> telnetRunnable; private String thingUID;
public DenonMarantzTelnetConnector(DenonMarantzConfiguration config, DenonMarantzState state, public DenonMarantzTelnetConnector(DenonMarantzConfiguration config, DenonMarantzState state,
ScheduledExecutorService scheduler) { ScheduledExecutorService scheduler, String thingUID) {
this.config = config; this.config = config;
this.scheduler = scheduler; this.scheduler = scheduler;
this.state = state; this.state = state;
this.thingUID = thingUID;
} }
/** /**
@ -68,8 +69,9 @@ public class DenonMarantzTelnetConnector extends DenonMarantzConnector implement
*/ */
@Override @Override
public void connect() { public void connect() {
telnetClient = new DenonMarantzTelnetClient(config, this); telnetClientThread = new DenonMarantzTelnetClientThread(config, this);
telnetRunnable = scheduler.submit(telnetClient); telnetClientThread.setName("OH-binding-" + thingUID);
telnetClientThread.start();
} }
@Override @Override
@ -98,9 +100,12 @@ public class DenonMarantzTelnetConnector extends DenonMarantzConnector implement
telnetStateRequest = null; telnetStateRequest = null;
} }
if (telnetClient != null && !telnetRunnable.isDone()) { if (telnetClientThread != null) {
telnetRunnable.cancel(true); telnetClientThread.interrupt();
telnetClient.shutdown(); // Invoke a shutdown after interrupting the thread to close the socket immediately,
// otherwise the client keeps running until a line was received from the telnet connection
telnetClientThread.shutdown();
telnetClientThread = null;
} }
} }
@ -262,7 +267,7 @@ public class DenonMarantzTelnetConnector extends DenonMarantzConnector implement
logger.warn("Trying to send empty command"); logger.warn("Trying to send empty command");
return; return;
} }
telnetClient.sendCommand(command); telnetClientThread.sendCommand(command);
} }
/** /**

View File

@ -314,7 +314,8 @@ public class DenonMarantzHandler extends BaseThingHandler implements DenonMarant
if (connector != null) { if (connector != null) {
connector.dispose(); connector.dispose();
} }
connector = connectorFactory.getConnector(config, denonMarantzState, scheduler, httpClient); connector = connectorFactory.getConnector(config, denonMarantzState, scheduler, httpClient,
this.getThing().getUID().getAsString());
connector.connect(); connector.connect();
} }