From 3c5ce72397ee9060a4af9ba36eee44e8b70e5c9e Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Sun, 18 Jun 2023 09:06:19 +0200 Subject: [PATCH] [robonect] Fix `NullPointerException` on reinitialization (#15003) * Fix NullPointerException on reinitialization Fixes #15001 --- .../internal/RobonectBindingConstants.java | 2 +- .../internal/RobonectHandlerFactory.java | 17 +++++++------ .../internal/handler/RobonectHandler.java | 25 +++++++++++++------ .../internal/handler/RobonectHandlerTest.java | 16 +++++------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectBindingConstants.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectBindingConstants.java index 13e8083212..0334f1bcf5 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectBindingConstants.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectBindingConstants.java @@ -22,7 +22,7 @@ import org.openhab.core.thing.ThingTypeUID; */ public class RobonectBindingConstants { - private static final String BINDING_ID = "robonect"; + public static final String BINDING_ID = "robonect"; // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "mower"); diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java index ad07800321..525afb547f 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/RobonectHandlerFactory.java @@ -12,12 +12,12 @@ */ package org.openhab.binding.robonect.internal; -import static org.openhab.binding.robonect.internal.RobonectBindingConstants.THING_TYPE_AUTOMOWER; +import static org.openhab.binding.robonect.internal.RobonectBindingConstants.*; -import java.util.Collections; import java.util.Set; -import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.robonect.internal.handler.RobonectHandler; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.io.net.http.HttpClientFactory; @@ -36,18 +36,19 @@ import org.osgi.service.component.annotations.Reference; * * @author Marco Meyer - Initial contribution */ +@NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.robonect") public class RobonectHandlerFactory extends BaseThingHandlerFactory { - private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_AUTOMOWER); + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AUTOMOWER); - private HttpClient httpClient; + private HttpClientFactory httpClientFactory; private TimeZoneProvider timeZoneProvider; @Activate public RobonectHandlerFactory(@Reference HttpClientFactory httpClientFactory, @Reference TimeZoneProvider timeZoneProvider) { - this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClientFactory = httpClientFactory; this.timeZoneProvider = timeZoneProvider; } @@ -57,11 +58,11 @@ public class RobonectHandlerFactory extends BaseThingHandlerFactory { } @Override - protected ThingHandler createHandler(Thing thing) { + protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(THING_TYPE_AUTOMOWER)) { - return new RobonectHandler(thing, httpClient, timeZoneProvider); + return new RobonectHandler(thing, httpClientFactory, timeZoneProvider); } return null; diff --git a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java index 35ca401dc5..ebf18a9d4b 100644 --- a/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java +++ b/bundles/org.openhab.binding.robonect/src/main/java/org/openhab/binding/robonect/internal/handler/RobonectHandler.java @@ -38,6 +38,7 @@ import org.openhab.binding.robonect.internal.model.RobonectAnswer; import org.openhab.binding.robonect.internal.model.VersionInfo; import org.openhab.binding.robonect.internal.model.cmd.ModeCommand; import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -50,6 +51,7 @@ import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.util.ThingWebClientUtil; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -80,9 +82,10 @@ public class RobonectHandler extends BaseThingHandler { private RobonectClient robonectClient; - public RobonectHandler(Thing thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) { + public RobonectHandler(Thing thing, HttpClientFactory httpClientFactory, TimeZoneProvider timeZoneProvider) { super(thing); - this.httpClient = httpClient; + httpClient = httpClientFactory + .createHttpClient(ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null)); this.timeZoneProvider = timeZoneProvider; } @@ -243,7 +246,7 @@ public class RobonectHandler extends BaseThingHandler { if (info.getHealth() != null) { updateState(CHANNEL_HEALTH_TEMP, new QuantityType<>(info.getHealth().getTemperature(), SIUnits.CELSIUS)); - updateState(CHANNEL_HEALTH_HUM, new QuantityType(info.getHealth().getHumidity(), Units.PERCENT)); + updateState(CHANNEL_HEALTH_HUM, new QuantityType<>(info.getHealth().getHumidity(), Units.PERCENT)); } if (info.getTimer() != null) { if (info.getTimer().getNext() != null) { @@ -364,11 +367,12 @@ public class RobonectHandler extends BaseThingHandler { try { httpClient.start(); - robonectClient = new RobonectClient(httpClient, endpoint); } catch (Exception e) { - logger.error("Exception while trying to start http client", e); - throw new RuntimeException("Exception while trying to start http client", e); + logger.error("Exception while trying to start HTTP client", e); + throw new IllegalStateException("Could not create HttpClient"); } + + robonectClient = new RobonectClient(httpClient, endpoint); Runnable runnable = new MowerChannelPoller(TimeUnit.SECONDS.toMillis(robonectConfig.getOfflineTimeout())); int pollInterval = robonectConfig.getPollInterval(); pollingJob = scheduler.scheduleWithFixedDelay(runnable, 0, pollInterval, TimeUnit.SECONDS); @@ -376,12 +380,17 @@ public class RobonectHandler extends BaseThingHandler { @Override public void dispose() { + ScheduledFuture pollingJob = this.pollingJob; if (pollingJob != null) { pollingJob.cancel(true); - pollingJob = null; + this.pollingJob = null; } - httpClient = null; + try { + httpClient.stop(); + } catch (Exception e) { + logger.warn("Exception while trying to stop HTTP client", e); + } } /** diff --git a/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java b/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java index 58c6e9c34d..8f29c4f6cf 100644 --- a/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java +++ b/bundles/org.openhab.binding.robonect/src/test/java/org/openhab/binding/robonect/internal/handler/RobonectHandlerTest.java @@ -20,7 +20,6 @@ import java.time.Month; import java.time.ZoneId; import java.time.ZonedDateTime; -import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,6 +41,7 @@ import org.openhab.binding.robonect.internal.model.Status; import org.openhab.binding.robonect.internal.model.Timer; import org.openhab.binding.robonect.internal.model.Wlan; import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -69,16 +69,17 @@ public class RobonectHandlerTest { private @Mock Thing robonectThingMock; private @Mock RobonectClient robonectClientMock; private @Mock ThingHandlerCallback callbackMock; - private @Mock HttpClient httpClientMock; + private @Mock HttpClientFactory httpClientFactoryMock; private @Mock TimeZoneProvider timezoneProvider; @BeforeEach public void setUp() { - subject = new RobonectHandler(robonectThingMock, httpClientMock, timezoneProvider); + Mockito.when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); + Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin")); + + subject = new RobonectHandler(robonectThingMock, httpClientFactoryMock, timezoneProvider); subject.setCallback(callbackMock); subject.setRobonectClient(robonectClientMock); - - Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin")); } @Test @@ -97,7 +98,6 @@ public class RobonectHandlerTest { // when when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); - when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_TIMER_NEXT_TIMER), RefreshType.REFRESH); @@ -141,7 +141,6 @@ public class RobonectHandlerTest { // when when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); when(robonectClientMock.errorList()).thenReturn(errorList); - when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS), RefreshType.REFRESH); @@ -192,7 +191,6 @@ public class RobonectHandlerTest { // when when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); - when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS), RefreshType.REFRESH); @@ -223,7 +221,6 @@ public class RobonectHandlerTest { // when when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); - when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS), RefreshType.REFRESH); @@ -259,7 +256,6 @@ public class RobonectHandlerTest { // when when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); when(robonectClientMock.errorList()).thenReturn(errorList); - when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3")); subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS), RefreshType.REFRESH); -- 2.47.3