[insteon] Use shared jetty http client (#17922)

Signed-off-by: jsetton <jeremy.setton@gmail.com>
This commit is contained in:
Jeremy 2024-12-19 17:39:33 -05:00 committed by GitHub
parent 5d40a719f4
commit 2ab9822605
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 90 additions and 87 deletions

View File

@ -20,6 +20,7 @@ import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.discovery.InsteonDiscoveryService;
import org.openhab.binding.insteon.internal.discovery.InsteonLegacyDiscoveryService;
import org.openhab.binding.insteon.internal.handler.InsteonBridgeHandler;
@ -29,6 +30,7 @@ import org.openhab.binding.insteon.internal.handler.InsteonLegacyNetworkHandler;
import org.openhab.binding.insteon.internal.handler.InsteonSceneHandler;
import org.openhab.binding.insteon.internal.handler.X10DeviceHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.storage.StorageService;
import org.openhab.core.thing.Bridge;
@ -56,6 +58,7 @@ import org.osgi.service.component.annotations.Reference;
@Component(configurationPid = "binding.insteon", service = ThingHandlerFactory.class)
public class InsteonHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient;
private final SerialPortManager serialPortManager;
private final InsteonStateDescriptionProvider stateDescriptionProvider;
private final StorageService storageService;
@ -64,10 +67,12 @@ public class InsteonHandlerFactory extends BaseThingHandlerFactory {
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
@Activate
public InsteonHandlerFactory(final @Reference SerialPortManager serialPortManager,
public InsteonHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference SerialPortManager serialPortManager,
final @Reference InsteonStateDescriptionProvider stateDescriptionProvider,
final @Reference StorageService storageService, final @Reference ThingManager thingManager,
final @Reference ThingRegistry thingRegistry) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.serialPortManager = serialPortManager;
this.stateDescriptionProvider = stateDescriptionProvider;
this.storageService = storageService;
@ -86,14 +91,14 @@ public class InsteonHandlerFactory extends BaseThingHandlerFactory {
if (THING_TYPE_HUB1.equals(thingTypeUID) || THING_TYPE_HUB2.equals(thingTypeUID)
|| THING_TYPE_PLM.equals(thingTypeUID)) {
InsteonBridgeHandler handler = new InsteonBridgeHandler((Bridge) thing, serialPortManager, storageService,
thingRegistry);
InsteonBridgeHandler handler = new InsteonBridgeHandler((Bridge) thing, httpClient, serialPortManager,
storageService, thingRegistry);
InsteonDiscoveryService service = new InsteonDiscoveryService(handler);
registerDiscoveryService(handler, service);
return handler;
} else if (THING_TYPE_LEGACY_NETWORK.equals(thingTypeUID)) {
InsteonLegacyNetworkHandler handler = new InsteonLegacyNetworkHandler((Bridge) thing, serialPortManager,
thingManager, thingRegistry);
InsteonLegacyNetworkHandler handler = new InsteonLegacyNetworkHandler((Bridge) thing, httpClient,
serialPortManager, thingManager, thingRegistry);
InsteonLegacyDiscoveryService service = new InsteonLegacyDiscoveryService(handler);
registerDiscoveryService(handler, service);
return handler;

View File

@ -27,6 +27,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonLegacyChannelConfiguration;
import org.openhab.binding.insteon.internal.config.InsteonLegacyNetworkConfiguration;
import org.openhab.binding.insteon.internal.device.DeviceAddress;
@ -119,13 +120,13 @@ public class InsteonLegacyBinding implements LegacyDriverListener, LegacyPortLis
private InsteonLegacyNetworkHandler handler;
public InsteonLegacyBinding(InsteonLegacyNetworkHandler handler, InsteonLegacyNetworkConfiguration config,
SerialPortManager serialPortManager, ScheduledExecutorService scheduler) {
HttpClient httpClient, ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
this.handler = handler;
String port = config.getRedactedPort();
logger.debug("port = '{}'", port);
driver = new LegacyDriver(config, this, serialPortManager, scheduler);
driver = new LegacyDriver(config, this, httpClient, scheduler, serialPortManager);
driver.addPortListener(this);
Integer devicePollIntervalSeconds = config.getDevicePollIntervalSeconds();

View File

@ -21,6 +21,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
import org.openhab.binding.insteon.internal.device.database.DatabaseManager;
import org.openhab.binding.insteon.internal.device.database.ModemDB;
@ -53,10 +54,10 @@ public class InsteonModem extends BaseDevice<InsteonAddress, InsteonBridgeHandle
private boolean initialized = false;
private int msgsReceived = 0;
public InsteonModem(InsteonBridgeConfiguration config, ScheduledExecutorService scheduler,
public InsteonModem(InsteonBridgeConfiguration config, HttpClient httpClient, ScheduledExecutorService scheduler,
SerialPortManager serialPortManager) {
super(InsteonAddress.UNKNOWN);
this.port = new Port(config, scheduler, serialPortManager);
this.port = new Port(config, httpClient, scheduler, serialPortManager);
this.modemDB = new ModemDB(this);
this.dbm = new DatabaseManager(this, scheduler);
this.linker = new LinkManager(this, scheduler);
@ -509,13 +510,14 @@ public class InsteonModem extends BaseDevice<InsteonAddress, InsteonBridgeHandle
*
* @param handler the bridge handler
* @param config the bridge config
* @param httpClient the http client
* @param scheduler the scheduler service
* @param serialPortManager the serial port manager
* @return the newly created InsteonModem
*/
public static InsteonModem makeModem(InsteonBridgeHandler handler, InsteonBridgeConfiguration config,
ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
InsteonModem modem = new InsteonModem(config, scheduler, serialPortManager);
HttpClient httpClient, ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
InsteonModem modem = new InsteonModem(config, httpClient, scheduler, serialPortManager);
modem.setHandler(handler);
return modem;
}

View File

@ -21,6 +21,7 @@ import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
import org.openhab.binding.insteon.internal.config.InsteonHub1Configuration;
import org.openhab.binding.insteon.internal.config.InsteonHub2Configuration;
@ -63,13 +64,15 @@ public class InsteonBridgeHandler extends InsteonBaseThingHandler implements Bri
private @Nullable ScheduledFuture<?> reconnectJob;
private @Nullable ScheduledFuture<?> resetJob;
private @Nullable ScheduledFuture<?> statisticsJob;
private HttpClient httpClient;
private SerialPortManager serialPortManager;
private Storage<DeviceCache> storage;
private ThingRegistry thingRegistry;
public InsteonBridgeHandler(Bridge bridge, SerialPortManager serialPortManager, StorageService storageService,
ThingRegistry thingRegistry) {
public InsteonBridgeHandler(Bridge bridge, HttpClient httpClient, SerialPortManager serialPortManager,
StorageService storageService, ThingRegistry thingRegistry) {
super(bridge);
this.httpClient = httpClient;
this.serialPortManager = serialPortManager;
this.storage = storageService.getStorage(bridge.getUID().toString(), DeviceCache.class.getClassLoader());
this.thingRegistry = thingRegistry;
@ -164,7 +167,7 @@ public class InsteonBridgeHandler extends InsteonBaseThingHandler implements Bri
legacyHandler.disable();
}
InsteonModem modem = InsteonModem.makeModem(this, config, scheduler, serialPortManager);
InsteonModem modem = InsteonModem.makeModem(this, config, httpClient, scheduler, serialPortManager);
this.modem = modem;
if (isInitialized()) {

View File

@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.InsteonLegacyBinding;
import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
import org.openhab.binding.insteon.internal.config.InsteonLegacyNetworkConfiguration;
@ -67,6 +68,7 @@ public class InsteonLegacyNetworkHandler extends BaseBridgeHandler {
private @Nullable ScheduledFuture<?> reconnectJob = null;
private @Nullable ScheduledFuture<?> settleJob = null;
private long lastInsteonDeviceCreatedTimestamp = 0;
private HttpClient httpClient;
private SerialPortManager serialPortManager;
private ThingManager thingManager;
private ThingRegistry thingRegistry;
@ -74,9 +76,10 @@ public class InsteonLegacyNetworkHandler extends BaseBridgeHandler {
private Map<String, String> channelInfo = new ConcurrentHashMap<>();
private Map<ChannelUID, Configuration> channelConfigs = new ConcurrentHashMap<>();
public InsteonLegacyNetworkHandler(Bridge bridge, SerialPortManager serialPortManager, ThingManager thingManager,
ThingRegistry thingRegistry) {
public InsteonLegacyNetworkHandler(Bridge bridge, HttpClient httpClient, SerialPortManager serialPortManager,
ThingManager thingManager, ThingRegistry thingRegistry) {
super(bridge);
this.httpClient = httpClient;
this.serialPortManager = serialPortManager;
this.thingManager = thingManager;
this.thingRegistry = thingRegistry;
@ -105,7 +108,7 @@ public class InsteonLegacyNetworkHandler extends BaseBridgeHandler {
return;
}
insteonBinding = new InsteonLegacyBinding(this, config, serialPortManager, scheduler);
insteonBinding = new InsteonLegacyBinding(this, config, httpClient, scheduler, serialPortManager);
updateStatus(ThingStatus.UNKNOWN);
// hold off on starting to poll until devices that already are defined as things are added.
@ -136,7 +139,7 @@ public class InsteonLegacyNetworkHandler extends BaseBridgeHandler {
this.driverInitializedJob = null;
}
} else {
logger.debug("driver is not initialized yet");
logger.trace("driver is not initialized yet");
}
}, 0, DRIVER_INITIALIZED_TIME_IN_SECONDS, TimeUnit.SECONDS);
} else {

View File

@ -12,23 +12,27 @@
*/
package org.openhab.binding.insteon.internal.transport;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.insteon.internal.utils.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,11 +51,13 @@ public class HubIOStream extends IOStream {
private static final String BS_START = "<BS>";
private static final String BS_END = "</BS>";
private static final int REQUEST_TIMEOUT = 30; // in seconds
private String host;
private int port;
private String auth;
private int pollInterval;
private HttpClient httpClient;
private ScheduledExecutorService scheduler;
private @Nullable ScheduledFuture<?> job;
// index of the last byte we have read in the buffer
@ -65,14 +71,16 @@ public class HubIOStream extends IOStream {
* @param username hub user name
* @param password hub password
* @param pollInterval hub poll interval (in milliseconds)
* @param httpClient the http client
* @param scheduler the scheduler
*/
public HubIOStream(String host, int port, String username, String password, int pollInterval,
public HubIOStream(String host, int port, String username, String password, int pollInterval, HttpClient httpClient,
ScheduledExecutorService scheduler) {
this.host = host;
this.port = port;
this.auth = Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
this.pollInterval = pollInterval;
this.httpClient = httpClient;
this.scheduler = scheduler;
}
@ -265,54 +273,33 @@ public class HubIOStream extends IOStream {
/**
* Helper method to fetch url from http server
*
* @param resource the url
* @param path the url path
* @return contents returned by http server
* @throws IOException
*/
private String getURL(String resource) throws IOException {
String url = "http://" + host + ":" + port + resource;
private String getURL(String path) throws IOException {
Request request = httpClient.newRequest(host, port).path(path).header(HttpHeader.AUTHORIZATION, "Basic " + auth)
.timeout(REQUEST_TIMEOUT, TimeUnit.SECONDS);
logger.trace("getting {}", request.getURI());
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
try {
connection.setConnectTimeout(30000);
connection.setReadTimeout(10000);
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(false);
connection.setRequestProperty("Authorization", "Basic " + auth);
ContentResponse response = request.send();
logger.trace("getting {}", url);
int responseCode = connection.getResponseCode();
if (responseCode != 200) {
if (responseCode == 401) {
int statusCode = response.getStatus();
switch (statusCode) {
case HttpStatus.OK_200:
return response.getContentAsString();
case HttpStatus.UNAUTHORIZED_401:
throw new IOException(
"Bad username or password. See the label on the bottom of the hub for the correct login information.");
} else {
throw new IOException(url + " failed with the response code: " + responseCode);
default:
throw new IOException("GET " + request.getURI() + " failed with status code: " + statusCode);
}
}
return getData(connection.getInputStream());
} finally {
connection.disconnect();
}
}
private String getData(InputStream is) throws IOException {
BufferedInputStream bis = new BufferedInputStream(is);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = bis.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
String s = baos.toString();
return s;
} finally {
bis.close();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("GET " + request.getURI() + " interrupted");
} catch (TimeoutException | ExecutionException e) {
throw new IOException("GET " + request.getURI() + " failed with error: " + e.getMessage());
}
}

View File

@ -20,6 +20,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
import org.openhab.binding.insteon.internal.config.InsteonHub1Configuration;
import org.openhab.binding.insteon.internal.config.InsteonHub2Configuration;
@ -113,16 +114,17 @@ public abstract class IOStream {
* Creates an IOStream from an insteon bridge config object
*
* @param config
* @param httpClient
* @param scheduler
* @param serialPortManager
* @return reference to IOStream
*/
public static IOStream create(InsteonBridgeConfiguration config, ScheduledExecutorService scheduler,
SerialPortManager serialPortManager) {
public static IOStream create(InsteonBridgeConfiguration config, HttpClient httpClient,
ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
if (config instanceof InsteonHub1Configuration hub1Config) {
return makeTcpIOStream(hub1Config);
} else if (config instanceof InsteonHub2Configuration hub2Config) {
return makeHubIOStream(hub2Config, scheduler);
return makeHubIOStream(hub2Config, httpClient, scheduler);
} else if (config instanceof InsteonPLMConfiguration plmConfig) {
return makeSerialIOStream(plmConfig, serialPortManager);
} else {
@ -130,13 +132,14 @@ public abstract class IOStream {
}
}
private static HubIOStream makeHubIOStream(InsteonHub2Configuration config, ScheduledExecutorService scheduler) {
private static HubIOStream makeHubIOStream(InsteonHub2Configuration config, HttpClient httpClient,
ScheduledExecutorService scheduler) {
String host = config.getHostname();
int port = config.getPort();
String user = config.getUsername();
String pass = config.getPassword();
int pollInterval = config.getHubPollInterval();
return new HubIOStream(host, port, user, pass, pollInterval, scheduler);
return new HubIOStream(host, port, user, pass, pollInterval, httpClient, scheduler);
}
private static SerialIOStream makeSerialIOStream(InsteonPLMConfiguration config,

View File

@ -20,6 +20,7 @@ import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonLegacyNetworkConfiguration;
import org.openhab.binding.insteon.internal.device.InsteonAddress;
import org.openhab.binding.insteon.internal.device.LegacyPollManager;
@ -44,11 +45,11 @@ public class LegacyDriver {
private Map<InsteonAddress, LegacyModemDBEntry> modemDBEntries = new HashMap<>();
private ReentrantLock modemDBEntriesLock = new ReentrantLock();
public LegacyDriver(InsteonLegacyNetworkConfiguration config, LegacyDriverListener listener,
SerialPortManager serialPortManager, ScheduledExecutorService scheduler) {
public LegacyDriver(InsteonLegacyNetworkConfiguration config, LegacyDriverListener listener, HttpClient httpClient,
ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
this.listener = listener;
this.port = new LegacyPort(config, this, serialPortManager, scheduler);
this.port = new LegacyPort(config, this, httpClient, scheduler, serialPortManager);
this.poller = new LegacyPollManager(scheduler);
this.requester = new LegacyRequestManager(scheduler);
}

View File

@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.InsteonLegacyBindingConstants;
import org.openhab.binding.insteon.internal.config.InsteonLegacyNetworkConfiguration;
import org.openhab.binding.insteon.internal.device.InsteonAddress;
@ -74,10 +75,10 @@ public class LegacyPort {
private IOStream ioStream;
private String name;
private Modem modem;
private Modem modem = new Modem();
private ScheduledExecutorService scheduler;
private IOStreamReader reader;
private IOStreamWriter writer;
private IOStreamReader reader = new IOStreamReader();
private IOStreamWriter writer = new IOStreamWriter();
private @Nullable ScheduledFuture<?> readJob;
private @Nullable ScheduledFuture<?> writeJob;
private boolean running = false;
@ -94,19 +95,16 @@ public class LegacyPort {
*
* @param config the network bridge config
* @param driver the driver that manages this port
* @param serialPortManager the serial port manager
* @param httpClient the http client
* @param scheduler the scheduler
* @param serialPortManager the serial port manager
*/
public LegacyPort(InsteonLegacyNetworkConfiguration config, LegacyDriver driver,
SerialPortManager serialPortManager, ScheduledExecutorService scheduler) {
public LegacyPort(InsteonLegacyNetworkConfiguration config, LegacyDriver driver, HttpClient httpClient,
ScheduledExecutorService scheduler, SerialPortManager serialPortManager) {
this.name = config.getRedactedPort();
this.driver = driver;
this.scheduler = scheduler;
this.modem = new Modem();
addListener(modem);
this.ioStream = IOStream.create(config.parse(), scheduler, serialPortManager);
this.reader = new IOStreamReader();
this.writer = new IOStreamWriter();
this.ioStream = IOStream.create(config.parse(), httpClient, scheduler, serialPortManager);
this.mdbb = new LegacyModemDBBuilder(this, scheduler);
}
@ -115,7 +113,7 @@ public class LegacyPort {
}
public synchronized boolean isModemDBComplete() {
return (modemDBComplete);
return modemDBComplete;
}
public boolean isRunning() {
@ -496,6 +494,7 @@ public class LegacyPort {
public void initialize() {
try {
Msg msg = Msg.makeMessage("GetIMInfo");
addListener(this);
writeMessage(msg);
} catch (IOException e) {
logger.warn("modem init failed!", e);

View File

@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.insteon.internal.config.InsteonBridgeConfiguration;
import org.openhab.binding.insteon.internal.transport.message.Msg;
import org.openhab.binding.insteon.internal.transport.message.MsgFactory;
@ -64,8 +65,8 @@ public class Port {
private String name;
private ScheduledExecutorService scheduler;
private IOStream ioStream;
private IOStreamReader reader;
private IOStreamWriter writer;
private IOStreamReader reader = new IOStreamReader();
private IOStreamWriter writer = new IOStreamWriter();
private @Nullable ScheduledFuture<?> readJob;
private @Nullable ScheduledFuture<?> writeJob;
private MsgFactory msgFactory = new MsgFactory();
@ -73,13 +74,11 @@ public class Port {
private LinkedBlockingQueue<Msg> writeQueue = new LinkedBlockingQueue<>();
private AtomicBoolean connected = new AtomicBoolean(false);
public Port(InsteonBridgeConfiguration config, ScheduledExecutorService scheduler,
public Port(InsteonBridgeConfiguration config, HttpClient httpClient, ScheduledExecutorService scheduler,
SerialPortManager serialPortManager) {
this.name = config.getId();
this.scheduler = scheduler;
this.ioStream = IOStream.create(config, scheduler, serialPortManager);
this.reader = new IOStreamReader();
this.writer = new IOStreamWriter();
this.ioStream = IOStream.create(config, httpClient, scheduler, serialPortManager);
}
public String getName() {