]> git.basschouten.com Git - openhab-addons.git/commitdiff
[hdpowerview] Eliminate nightly crash dump in OH3 (#10118)
authorAndrew Fiddian-Green <software@whitebear.ch>
Tue, 16 Feb 2021 20:22:27 +0000 (20:22 +0000)
committerGitHub <noreply@github.com>
Tue, 16 Feb 2021 20:22:27 +0000 (12:22 -0800)
* [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 <software@whitebear.ch>
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HubProcessingException.java [new file with mode: 0644]
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewShadeDiscoveryService.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java
bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java
bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java

index 6fb6c4747296b0b21bb0afe6e7d6ef66a766a845..8b4dc8aeb8be191c55e000cc7f91d57cf89d6b47 100644 (file)
@@ -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)) {
index 94174ba16348a42fec93a2f9767b332cf6b95320..4d40d9927bee22f3d15d4b7fd3600bea817754c3 100644 (file)
 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 (file)
index 0000000..87594d1
--- /dev/null
@@ -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);
+    }
+}
index e733ff3c41eca38e50aad9630c48b3c25f2eecbc..ad4ad4e390cf5e291162168862f09d0ab9ba0e30 100644 (file)
@@ -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
index b67d4a20d67558f2152e05c6418fd7045bf0506c..0827a682e31e40855df9dd9f6d1e4e9d947d5448 100644 (file)
@@ -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");
index 5bbf5fa294c0d6d8e919f17ed1b85e43942415c2..3856de9d7561fd9a7aec253be10edef628043760 100644 (file)
@@ -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
index cb0d41910896ab846935c32cb80f5f151fb6060d..7f6891dd3d2819771d1aec3cd43ed97002922837 100644 (file)
@@ -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());
                 }
             }