diff --git a/currentmonitor/lantern-currentmonitor/pom.xml b/currentmonitor/lantern-currentmonitor/pom.xml index 360449b..06b2cb3 100644 --- a/currentmonitor/lantern-currentmonitor/pom.xml +++ b/currentmonitor/lantern-currentmonitor/pom.xml @@ -2,7 +2,7 @@ 4.0.0 lantern-currentmonitor jar - 1.1.1 + 1.1.2 lantern-currentmonitor diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java index 9290598..e0fa9ec 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BluetoothConfig.java @@ -7,27 +7,38 @@ import com.lanternsoftware.currentmonitor.bluetooth.BleHelper; import com.lanternsoftware.currentmonitor.bluetooth.BleService; import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.util.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; public class BluetoothConfig { + private static final Logger LOG = LoggerFactory.getLogger(BluetoothConfig.class); private final BleApplication app; public BluetoothConfig(String _hubName, BleCharacteristicListener _listener) { - BleHelper.getAdapter().setPowered(true); - BleHelper.requestBusName("com.lanternsoftware"); - BleHelper.setBasePath("/com/lanternsoftware"); - HubConfigService service = new HubConfigService(); - List chars = CollectionUtils.transform(service.getCharacteristics(), _c->new BleCharacteristic("HubConfig", _c.getUUID(), _c.name(), _c.getFlags())); - chars.forEach(_c->_c.setListener(_listener)); - app = new BleApplication("Lantern", _hubName, new BleService("HubConfig", service.getServiceUUID(), chars)); + BleApplication a = null; + try { + BleHelper.getAdapter().setPowered(true); + BleHelper.requestBusName("com.lanternsoftware"); + BleHelper.setBasePath("/com/lanternsoftware"); + List chars = CollectionUtils.transform(HubConfigService.getCharacteristics(), _c -> new BleCharacteristic("HubConfig", _c.getUUID(), _c.name(), _c.getFlags())); + chars.forEach(_c -> _c.setListener(_listener)); + a = new BleApplication("Lantern", _hubName, new BleService("HubConfig", HubConfigService.getServiceUUID(), chars)); + } + catch (Throwable _t) { + LOG.error("Failed to initialize BLE service", _t); + } + app = a; } public void start() { - app.start(); + if (app != null) + app.start(); } public void stop() { - app.stop(); + if (app != null) + app.stop(); } } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java index 5d73f14..2150866 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java @@ -219,7 +219,7 @@ public class MonitorApp { config = new MonitorConfig(); ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); } - pool = new HttpPool(10, 10, config.getSocketTimeout(), config.getConnectTimeout(), config.getSocketTimeout()); + pool = HttpPool.builder().withValidateSSLCertificates(!config.isAcceptSelfSignedCertificates()).build(); if (NullUtils.isNotEmpty(config.getHost())) host = NullUtils.terminateWith(config.getHost(), "/"); monitor.setDebug(config.isDebug()); diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java index dd971fd..6ef3911 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java @@ -19,6 +19,7 @@ public class MonitorConfig { private int socketTimeout; private boolean postSamples = false; private boolean needsCalibration = true; + private boolean acceptSelfSignedCertificates = false; private String mqttBrokerUrl; private String mqttUserName; private String mqttPassword; @@ -115,6 +116,14 @@ public class MonitorConfig { needsCalibration = _needsCalibration; } + public boolean isAcceptSelfSignedCertificates() { + return acceptSelfSignedCertificates; + } + + public void setAcceptSelfSignedCertificates(boolean _acceptSelfSignedCertificates) { + acceptSelfSignedCertificates = _acceptSelfSignedCertificates; + } + public String getMqttBrokerUrl() { return mqttBrokerUrl; } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java index a4e3e2b..374b2ac 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java @@ -36,6 +36,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer getCharacteristics() { + public static List getCharacteristics() { return Arrays.asList(HubConfigCharacteristic.values()); } diff --git a/pigpio/lantern-pigpio/src/main/resources/lib/armhf/lantern-pigpio.so b/pigpio/lantern-pigpio/src/main/resources/lib/armhf/lantern-pigpio.so new file mode 100644 index 0000000..9561f72 Binary files /dev/null and b/pigpio/lantern-pigpio/src/main/resources/lib/armhf/lantern-pigpio.so differ diff --git a/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpPool.java b/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpPool.java index 049f1c7..ab71d3f 100644 --- a/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpPool.java +++ b/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpPool.java @@ -1,27 +1,36 @@ package com.lanternsoftware.util.http; -import java.io.IOException; -import java.io.InputStream; - +import com.lanternsoftware.util.NullUtils; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; import org.apache.http.client.CookieStore; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.lanternsoftware.util.NullUtils; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; public class HttpPool { private static final Logger LOG = LoggerFactory.getLogger(HttpPool.class); @@ -32,17 +41,43 @@ public class HttpPool { private final PoolingHttpClientConnectionManager connectionManager; public HttpPool(int _maxTotalConnections, int _maxPerRoute) { - this(_maxTotalConnections, _maxPerRoute, 10000, 5000, 10000); + this(_maxTotalConnections, _maxPerRoute, null, null); + } + + public HttpPool(int _maxTotalConnections, int _maxPerRoute, KeyStore _keystore, String _keystorePassword) { + this(_maxTotalConnections, _maxPerRoute, 10000, 5000, 10000, _keystore, _keystorePassword, true); } public HttpPool(int _maxTotalConnections, int _maxPerRoute, int _socketTimeoutMs, int _connectTimeoutMs, int _connectionRequestTimeoutMs) { + this(_maxTotalConnections, _maxPerRoute, _socketTimeoutMs, _connectTimeoutMs, _connectionRequestTimeoutMs, null, null, true); + } + + public HttpPool(int _maxTotalConnections, int _maxPerRoute, int _socketTimeoutMs, int _connectTimeoutMs, int _connectionRequestTimeoutMs, KeyStore _keystore, String _keystorePassword, boolean _validateSSLCertificates) { requestConfig = RequestConfig.custom().setSocketTimeout(_socketTimeoutMs).setConnectTimeout(_connectTimeoutMs).setConnectionRequestTimeout(_connectionRequestTimeoutMs).setCookieSpec(CookieSpecs.STANDARD).build(); keepAliveStrategy = (HttpResponse response, HttpContext context) -> 0; - connectionManager = new PoolingHttpClientConnectionManager(); + Registry registry = null; + if ((_keystore != null) || !_validateSSLCertificates) { + try { + SSLContextBuilder contextBuilder = SSLContexts.custom(); + if (_keystore != null) + contextBuilder.loadKeyMaterial(_keystore, _keystorePassword.toCharArray()); + if (!_validateSSLCertificates) + contextBuilder.loadTrustMaterial(null, (x509CertChain, authType) -> true); + SSLConnectionSocketFactory socketFactory = _validateSSLCertificates ? new SSLConnectionSocketFactory(contextBuilder.build()) : new SSLConnectionSocketFactory(contextBuilder.build(), NoopHostnameVerifier.INSTANCE); + registry = RegistryBuilder.create().register("https", socketFactory).register("http", new PlainConnectionSocketFactory()).build(); + } catch (Exception _e) { + LOG.error("Failed to load SSL keystore", _e); + } + } + connectionManager = registry != null ? new PoolingHttpClientConnectionManager(registry) : new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(_maxTotalConnections); connectionManager.setDefaultMaxPerRoute(_maxPerRoute); } + public static Builder builder() { + return new Builder(); + } + public void shutdown() { connectionManager.shutdown(); } @@ -76,26 +111,28 @@ public class HttpPool { } public byte[] executeToByteArray(HttpUriRequest _request) { + return executeToPayload(_request).getPayload(); + } + + public HttpResponsePayload executeToPayload(HttpUriRequest _request) { InputStream is = null; CloseableHttpResponse resp = null; try { resp = execute(_request); if (resp == null) - return null; - if ((resp.getStatusLine().getStatusCode() < 200) || (resp.getStatusLine().getStatusCode() >= 300)) { + return new HttpResponsePayload(HttpStatus.SC_INTERNAL_SERVER_ERROR, null); + if ((resp.getStatusLine().getStatusCode() < 200) || (resp.getStatusLine().getStatusCode() >= 300)) LOG.error("Failed to make http request to " + _request.getURI().toString() + ". Status code: " + resp.getStatusLine().getStatusCode()); - return null; - } HttpEntity entity = resp.getEntity(); if (entity != null) { is = entity.getContent(); - return IOUtils.toByteArray(is); + return new HttpResponsePayload(resp.getStatusLine().getStatusCode(), IOUtils.toByteArray(is)); } - return null; + return new HttpResponsePayload(resp.getStatusLine().getStatusCode(), null); } catch (Exception _e) { LOG.error("Failed to make http request to " + _request.getURI().toString(), _e); - return null; + return new HttpResponsePayload(HttpStatus.SC_INTERNAL_SERVER_ERROR, null); } finally { IOUtils.closeQuietly(is); @@ -106,4 +143,57 @@ public class HttpPool { public static void addBasicAuthHeader(HttpUriRequest _request, String _username, String _password) { _request.addHeader("Authorization", "Basic " + Base64.encodeBase64String(NullUtils.toByteArray(_username + ":" + _password))); } + + public static final class Builder { + private int maxTotalConnections = 10; + private int maxPerRoute = 10; + private int socketTimeoutMs = 10000; + private int connectTimeoutMs = 5000; + private int connectionRequestTimeoutMs = 10000; + private KeyStore keystore; + private String keystorePassword; + private boolean validateSSLCertificates = true; + + private Builder() { + } + + public Builder withMaxTotalConnections(int val) { + maxTotalConnections = val; + return this; + } + + public Builder withMaxPerRoute(int val) { + maxPerRoute = val; + return this; + } + + public Builder withSocketTimeoutMs(int val) { + socketTimeoutMs = val; + return this; + } + + public Builder withConnectTimeoutMs(int val) { + connectTimeoutMs = val; + return this; + } + + public Builder withConnectionRequestTimeoutMs(int val) { + connectionRequestTimeoutMs = val; + return this; + } + + public Builder withKeystore(KeyStore _keystore, String _password) { + keystore = _keystore; + keystorePassword = _password; + return this; + } + + public Builder withValidateSSLCertificates(boolean val) { + validateSSLCertificates = val; + return this; + } + public HttpPool build() { + return new HttpPool(maxTotalConnections, maxPerRoute, socketTimeoutMs, connectTimeoutMs, connectionRequestTimeoutMs, keystore, keystorePassword, validateSSLCertificates); + } + } } diff --git a/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpResponsePayload.java b/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpResponsePayload.java new file mode 100644 index 0000000..062ab9d --- /dev/null +++ b/util/lantern-util-http/src/main/java/com/lanternsoftware/util/http/HttpResponsePayload.java @@ -0,0 +1,29 @@ +package com.lanternsoftware.util.http; + +import com.lanternsoftware.util.NullUtils; + +public class HttpResponsePayload { + private final int status; + private final byte[] payload; + + public HttpResponsePayload(int _status, byte[] _payload) { + status = _status; + payload = _payload; + } + + public int getStatus() { + return status; + } + + public byte[] getPayload() { + return payload; + } + + public boolean isSuccess() { + return (status >= 200) && (status < 300); + } + + public String asString() { + return NullUtils.toString(payload); + } +} diff --git a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java index 7a28df3..a9255c7 100644 --- a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java +++ b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java @@ -76,7 +76,6 @@ public class ZWaveApp { private HttpPool pool; private SwitchScheduleTask nextScheduleTask; private final Map sensors = new HashMap<>(); - private final Object ZWAVE_MUTEX = new Object(); private ExecutorService executor = null; public void start() { @@ -559,17 +558,15 @@ public class ZWaveApp { if (_sw.isSourceUrlValid()) return DaoSerializer.getDouble(DaoSerializer.parse(pool.executeToString(new HttpGet(_sw.getSourceUrl()))), "temp"); else if (_sw.isZWaveThermostat() && config.isMySwitch(_sw)) { - synchronized (ZWAVE_MUTEX) { - synchronized (sensors) { - controller.send(new MultilevelSensorGetRequest((byte) _sw.getNodeId())); - try { - sensors.wait(3000); - } catch (InterruptedException _e) { - _e.printStackTrace(); - } - Double temp = sensors.get(_sw.getNodeId()); - return (temp == null) ? 0.0 : temp; + synchronized (sensors) { + controller.send(new MultilevelSensorGetRequest((byte) _sw.getNodeId())); + try { + sensors.wait(3000); + } catch (InterruptedException _e) { + _e.printStackTrace(); } + Double temp = sensors.get(_sw.getNodeId()); + return (temp == null) ? 0.0 : temp; } } return 0.0;