From: Andrew Fiddian-Green Date: Tue, 16 Feb 2021 20:22:27 +0000 (+0000) Subject: [hdpowerview] Eliminate nightly crash dump in OH3 (#10118) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=596b261d4738557392e1c1dfdc92dcd69bbaa46b;p=openhab-addons.git [hdpowerview] Eliminate nightly crash dump in OH3 (#10118) * [hdpowerview] refactor from jax-rs to http client * [hdpowerview] adopt proposals of code reviewer * [hdpowerview] adopt additional proposals of code reviewer * [hdpowerview] provide exception class name Signed-off-by: Andrew Fiddian-Green --- diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 6fb6c47472..8b4dc8aeb8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -14,14 +14,14 @@ package org.openhab.binding.hdpowerview.internal; import java.util.Hashtable; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewShadeDiscoveryService; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -41,11 +41,12 @@ import org.osgi.service.component.annotations.Reference; @NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.hdpowerview") public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory { - private final ClientBuilder clientBuilder; + + private final HttpClient httpClient; @Activate - public HDPowerViewHandlerFactory(@Reference ClientBuilder clientBuilder) { - this.clientBuilder = clientBuilder; + public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory) { + this.httpClient = httpClientFactory.getCommonHttpClient(); } @Override @@ -58,7 +59,7 @@ public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_HUB)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, clientBuilder); + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient); registerService(new HDPowerViewShadeDiscoveryService(handler)); return handler; } else if (thingTypeUID.equals(HDPowerViewBindingConstants.THING_TYPE_SHADE)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 94174ba163..4d40d9927b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -13,17 +13,18 @@ package org.openhab.binding.hdpowerview.internal; import java.time.Instant; - -import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import java.util.concurrent.ExecutionException; +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.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; @@ -45,14 +46,6 @@ import com.google.gson.JsonParseException; @NonNullByDefault public class HDPowerViewWebTargets { - private static final String PUT = "PUT"; - private static final String GET = "GET"; - private static final String SCENE_ID = "sceneId"; - private static final String ID = "id"; - private static final String REFRESH = "refresh"; - private static final String CONN_HDR = "Connection"; - private static final String CONN_VAL = "close"; // versus "keep-alive" - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); /* @@ -64,128 +57,159 @@ public class HDPowerViewWebTargets { private final int maintenancePeriod = 300; private Instant maintenanceScheduledEnd = Instant.now().minusSeconds(2 * maintenancePeriod); - private WebTarget base; - private WebTarget shades; - private WebTarget shade; - private WebTarget sceneActivate; - private WebTarget scenes; + private final String base; + private final String shades; + private final String sceneActivate; + private final String scenes; private final Gson gson = new Gson(); + private final HttpClient httpClient; + + /** + * private helper class for passing http url query parameters + */ + private static class Query { + private final String key; + private final String value; + + private Query(String key, String value) { + this.key = key; + this.value = value; + } + + public static Query of(String key, String value) { + return new Query(key, value); + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + } /** * Initialize the web targets - * - * @param client the Javax RS client (the binding) + * + * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(Client client, String ipAddress) { - base = client.target("http://" + ipAddress + "/api"); - shades = base.path("shades/"); - shade = base.path("shades/{id}"); - sceneActivate = base.path("scenes"); - scenes = base.path("scenes/"); + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { + base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + this.httpClient = httpClient; } /** * Fetches a JSON package that describes all shades in the hub, and wraps it in * a Shades class instance - * + * * @return Shades class instance * @throws JsonParseException if there is a JSON parsing error - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shades getShades() throws JsonParseException, ProcessingException, HubMaintenanceException { - String json = invoke(shades.request().header(CONN_HDR, CONN_VAL).buildGet(), shades, null); + public @Nullable Shades getShades() throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); return gson.fromJson(json, Shades.class); } /** * Instructs the hub to move a specific shade - * + * * @param shadeId id of the shade to be moved * @param position instance of ShadePosition containing the new position - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void moveShade(int shadeId, ShadePosition position) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); + public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException, HubMaintenanceException { String json = gson.toJson(new ShadeMove(shadeId, position)); - invoke(target.request().header(CONN_HDR, CONN_VAL) - .buildPut(Entity.entity(json, MediaType.APPLICATION_JSON_TYPE)), target, json); - return; + invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, json); } /** * Fetches a JSON package that describes all scenes in the hub, and wraps it in * a Scenes class instance - * + * * @return Scenes class instance * @throws JsonParseException if there is a JSON parsing error - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Scenes getScenes() throws JsonParseException, ProcessingException, HubMaintenanceException { - String json = invoke(scenes.request().header(CONN_HDR, CONN_VAL).buildGet(), scenes, null); + public @Nullable Scenes getScenes() throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); return gson.fromJson(json, Scenes.class); } /** * Instructs the hub to execute a specific scene - * + * * @param sceneId id of the scene to be executed - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateScene(int sceneId) throws ProcessingException, HubMaintenanceException { - WebTarget target = sceneActivate.queryParam(SCENE_ID, sceneId); - invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); } - private synchronized String invoke(Invocation invocation, WebTarget target, @Nullable String jsonCommand) - throws ProcessingException, HubMaintenanceException { + /** + * Invoke a call on the hub server to retrieve information or send a command + * + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubProcessingException + * @throws HubMaintenanceException + * @throws HubProcessingException + */ + private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { if (logger.isTraceEnabled()) { - logger.trace("API command {} {}", jsonCommand == null ? GET : PUT, target.getUri()); + logger.trace("API command {} {}", method, url); if (jsonCommand != null) { logger.trace("JSON command = {}", jsonCommand); } } - Response response; + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; try { - response = invocation.invoke(); - } catch (ProcessingException e) { + response = request.send(); + } catch (InterruptedException | TimeoutException | ExecutionException e) { if (Instant.now().isBefore(maintenanceScheduledEnd)) { // throw "softer" exception during maintenance window logger.debug("Hub still undergoing maintenance"); throw new HubMaintenanceException("Hub still undergoing maintenance"); } - throw e; + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); } int statusCode = response.getStatus(); - if (statusCode == 423) { + if (statusCode == HttpStatus.LOCKED_423) { // set end of maintenance window, and throw a "softer" exception maintenanceScheduledEnd = Instant.now().plusSeconds(maintenancePeriod); logger.debug("Hub undergoing maintenance"); - if (response.hasEntity()) { - response.readEntity(String.class); - } - response.close(); throw new HubMaintenanceException("Hub undergoing maintenance"); } - if (statusCode != 200) { - logger.warn("Hub returned HTTP error '{}'", statusCode); - if (response.hasEntity()) { - response.readEntity(String.class); - } - response.close(); - throw new ProcessingException(String.format("HTTP %d error", statusCode)); + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); } - if (!response.hasEntity()) { + String jsonResponse = response.getContentAsString(); + if ("".equals(jsonResponse)) { logger.warn("Hub returned no content"); - response.close(); - throw new ProcessingException("Missing response entity"); + throw new HubProcessingException("Missing response entity"); } - String jsonResponse = response.readEntity(String.class); if (logger.isTraceEnabled()) { logger.trace("JSON response = {}", jsonResponse); } @@ -195,15 +219,16 @@ public class HDPowerViewWebTargets { /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it * in a Shade class instance - * + * * @param shadeId id of the shade to be fetched * @return Shade class instance - * @throws ProcessingException if there is any processing error + * @throws JsonParseException if there is a JSON parsing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shade getShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); - String json = invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public @Nullable Shade getShade(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); return gson.fromJson(json, Shade.class); } @@ -211,30 +236,29 @@ public class HDPowerViewWebTargets { * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on * a specific shade; fetches a JSON package that describes that shade, and wraps * it in a Shade class instance - * + * * @param shadeId id of the shade to be refreshed * @return Shade class instance - * @throws ProcessingException if there is any processing error + * @throws JsonParseException if there is a JSON parsing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public @Nullable Shade refreshShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId).queryParam(REFRESH, true); - String json = invoke(target.request().header(CONN_HDR, CONN_VAL).buildGet(), target, null); + public @Nullable Shade refreshShade(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); return gson.fromJson(json, Shade.class); } /** * Tells the hub to stop movement of a specific shade - * + * * @param shadeId id of the shade to be stopped - * @throws ProcessingException if there is any processing error + * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void stopShade(int shadeId) throws ProcessingException, HubMaintenanceException { - WebTarget target = shade.resolveTemplate(ID, shadeId); + public void stopShade(int shadeId) throws HubProcessingException, HubMaintenanceException { String json = gson.toJson(new ShadeStop(shadeId)); - invoke(target.request().header(CONN_HDR, CONN_VAL) - .buildPut(Entity.entity(json, MediaType.APPLICATION_JSON_TYPE)), target, json); - return; + invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, json); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java new file mode 100644 index 0000000000..87594d1af7 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.hdpowerview.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link HubProcessingException} is a custom exception for the HD PowerView hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HubProcessingException extends Exception { + + private static final long serialVersionUID = 4307088023775166450L; + + public HubProcessingException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java index e733ff3c41..ad4ad4e390 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java @@ -17,13 +17,12 @@ import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.ProcessingException; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; @@ -85,7 +84,7 @@ public class HDPowerViewShadeDiscoveryService extends AbstractDiscoveryService { try { HDPowerViewWebTargets webTargets = hub.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } Shades shades = webTargets.getShades(); if (shades != null && shades.shadeData != null) { @@ -107,7 +106,7 @@ public class HDPowerViewShadeDiscoveryService extends AbstractDiscoveryService { } } } - } catch (ProcessingException | JsonParseException e) { + } catch (HubProcessingException | JsonParseException e) { logger.warn("Unexpected error: {}", e.getMessage()); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b67d4a20d6..0827a682e3 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -21,13 +21,14 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.ClientBuilder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; @@ -63,7 +64,7 @@ import com.google.gson.JsonParseException; public class HDPowerViewHubHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); - private final ClientBuilder clientBuilder; + private final HttpClient httpClient; private long refreshInterval; private long hardRefreshInterval; @@ -75,9 +76,9 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { private final ChannelTypeUID sceneChannelTypeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID, HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE); - public HDPowerViewHubHandler(Bridge bridge, ClientBuilder clientBuilder) { + public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient) { super(bridge); - this.clientBuilder = clientBuilder; + this.httpClient = httpClient; } @Override @@ -98,7 +99,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { webTargets.activateScene(Integer.parseInt(channelUID.getId())); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets - } catch (NumberFormatException | ProcessingException e) { + } catch (NumberFormatException | HubProcessingException e) { logger.debug("Unexpected error {}", e.getMessage()); } } @@ -116,7 +117,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { return; } - webTargets = new HDPowerViewWebTargets(clientBuilder.build(), host); + webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshInterval = config.hardRefresh; schedulePoll(); @@ -178,7 +179,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { pollScenes(); } catch (JsonParseException e) { logger.warn("Bridge returned a bad JSON response: {}", e.getMessage()); - } catch (ProcessingException e) { + } catch (HubProcessingException e) { logger.warn("Error connecting to bridge: {}", e.getMessage()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.getMessage()); } catch (HubMaintenanceException e) { @@ -186,7 +187,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { } } - private void pollShades() throws JsonParseException, ProcessingException, HubMaintenanceException { + private void pollShades() throws JsonParseException, HubProcessingException, HubMaintenanceException { HDPowerViewWebTargets webTargets = this.webTargets; if (webTargets == null) { throw new ProcessingException("Web targets not initialized"); @@ -229,7 +230,7 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler { thingHandler.onReceiveUpdate(shadeData); } - private void pollScenes() throws JsonParseException, ProcessingException, HubMaintenanceException { + private void pollScenes() throws JsonParseException, HubProcessingException, HubMaintenanceException { HDPowerViewWebTargets webTargets = this.webTargets; if (webTargets == null) { throw new ProcessingException("Web targets not initialized"); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 5bbf5fa294..3856de9d75 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -19,12 +19,11 @@ import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.ProcessingException; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.ActuatorClass; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; @@ -128,7 +127,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { /** * Update the state of the channels based on the ShadeData provided - * + * * @param shadeData the ShadeData to be used; may be null */ protected void onReceiveUpdate(@Nullable ShadeData shadeData) { @@ -157,11 +156,11 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); @@ -190,7 +189,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { webTargets.moveShade(shadeId, ShadePosition.create(ZERO_IS_CLOSED, primaryPercent, ZERO_IS_OPEN, newPercent)); } - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); return; } catch (HubMaintenanceException e) { @@ -211,16 +210,16 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); webTargets.stopShade(shadeId); requestRefreshShade(); - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); return; } catch (HubMaintenanceException e) { @@ -242,11 +241,11 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { try { HDPowerViewHubHandler bridge; if ((bridge = getBridgeHandler()) == null) { - throw new ProcessingException("Missing bridge handler"); + throw new HubProcessingException("Missing bridge handler"); } HDPowerViewWebTargets webTargets = bridge.getWebTargets(); if (webTargets == null) { - throw new ProcessingException("Web targets not initialized"); + throw new HubProcessingException("Web targets not initialized"); } int shadeId = getShadeId(); Shade shade = webTargets.refreshShade(shadeId); @@ -258,7 +257,7 @@ public class HDPowerViewShadeHandler extends AbstractHubbedThingHandler { } } } - } catch (ProcessingException | NumberFormatException e) { + } catch (HubProcessingException | NumberFormatException e) { logger.warn("Unexpected error: {}", e.getMessage()); } catch (HubMaintenanceException e) { // exceptions are logged in HDPowerViewWebTargets diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index cb0d419108..7f6891dd3d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -22,15 +22,13 @@ import java.io.IOException; import java.util.List; import java.util.regex.Pattern; -import javax.ws.rs.ProcessingException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.HubProcessingException; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; @@ -76,7 +74,7 @@ public class HDPowerViewJUnitTests { /** * Run a series of ONLINE tests on the communication with a hub - * + * * @param hubIPAddress must be a valid hub IP address to run the * tests on; or an INVALID IP address to * suppress the tests @@ -99,10 +97,18 @@ public class HDPowerViewJUnitTests { boolean allowShadeMovementCommands = false; if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) { - // initialize stuff - Client client = ClientBuilder.newClient(); + // ==== initialize stuff ==== + HttpClient client = new HttpClient(); assertNotNull(client); - // client.register(new Logger()); + + // ==== start the client ==== + try { + client.start(); + assertTrue(client.isStarted()); + } catch (Exception e) { + fail(e.getMessage()); + } + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); assertNotNull(webTargets); @@ -180,7 +186,7 @@ public class HDPowerViewJUnitTests { String shadeName = shadexData.getName(); assertNotNull(shadeName); } - } catch (JsonParseException | ProcessingException | HubMaintenanceException e) { + } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -203,7 +209,7 @@ public class HDPowerViewJUnitTests { String sceneName = scene.getName(); assertNotNull(sceneName); } - } catch (JsonParseException | ProcessingException | HubMaintenanceException e) { + } catch (JsonParseException | HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -214,7 +220,7 @@ public class HDPowerViewJUnitTests { assertNotEquals(0, shadeId); shade = webTargets.refreshShade(shadeId); assertNotNull(shade); - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -245,7 +251,7 @@ public class HDPowerViewJUnitTests { if (allowShadeMovementCommands) { webTargets.moveShade(shadeId, newPos); } - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { fail(e.getMessage()); } @@ -254,7 +260,26 @@ public class HDPowerViewJUnitTests { try { assertNotNull(sceneId); webTargets.activateScene(sceneId); - } catch (ProcessingException | HubMaintenanceException e) { + } catch (HubProcessingException | HubMaintenanceException e) { + fail(e.getMessage()); + } + } + + // ==== test stop command ==== + if (allowShadeMovementCommands) { + try { + assertNotNull(sceneId); + webTargets.stopShade(shadeId); + } catch (HubProcessingException | HubMaintenanceException e) { + fail(e.getMessage()); + } + } + + // ==== stop the client ==== + if (client.isRunning()) { + try { + client.stop(); + } catch (Exception e) { fail(e.getMessage()); } }