[robonect] Fix `NullPointerException` on reinitialization (#15003)

* Fix NullPointerException on reinitialization

Fixes #15001
This commit is contained in:
Jacob Laursen 2023-06-18 09:06:19 +02:00 committed by GitHub
parent 801b860c59
commit 3c5ce72397
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 27 deletions

View File

@ -22,7 +22,7 @@ import org.openhab.core.thing.ThingTypeUID;
*/ */
public class RobonectBindingConstants { public class RobonectBindingConstants {
private static final String BINDING_ID = "robonect"; public static final String BINDING_ID = "robonect";
// List of all Thing Type UIDs // List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "mower"); public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "mower");

View File

@ -12,12 +12,12 @@
*/ */
package org.openhab.binding.robonect.internal; 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 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.binding.robonect.internal.handler.RobonectHandler;
import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.io.net.http.HttpClientFactory;
@ -36,18 +36,19 @@ import org.osgi.service.component.annotations.Reference;
* *
* @author Marco Meyer - Initial contribution * @author Marco Meyer - Initial contribution
*/ */
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.robonect") @Component(service = ThingHandlerFactory.class, configurationPid = "binding.robonect")
public class RobonectHandlerFactory extends BaseThingHandlerFactory { public class RobonectHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_AUTOMOWER); private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_AUTOMOWER);
private HttpClient httpClient; private HttpClientFactory httpClientFactory;
private TimeZoneProvider timeZoneProvider; private TimeZoneProvider timeZoneProvider;
@Activate @Activate
public RobonectHandlerFactory(@Reference HttpClientFactory httpClientFactory, public RobonectHandlerFactory(@Reference HttpClientFactory httpClientFactory,
@Reference TimeZoneProvider timeZoneProvider) { @Reference TimeZoneProvider timeZoneProvider) {
this.httpClient = httpClientFactory.getCommonHttpClient(); this.httpClientFactory = httpClientFactory;
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
} }
@ -57,11 +58,11 @@ public class RobonectHandlerFactory extends BaseThingHandlerFactory {
} }
@Override @Override
protected ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_AUTOMOWER)) { if (thingTypeUID.equals(THING_TYPE_AUTOMOWER)) {
return new RobonectHandler(thing, httpClient, timeZoneProvider); return new RobonectHandler(thing, httpClientFactory, timeZoneProvider);
} }
return null; return null;

View File

@ -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.VersionInfo;
import org.openhab.binding.robonect.internal.model.cmd.ModeCommand; import org.openhab.binding.robonect.internal.model.cmd.ModeCommand;
import org.openhab.core.i18n.TimeZoneProvider; 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.DateTimeType;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType; 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.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler; 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.Command;
import org.openhab.core.types.RefreshType; import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State; import org.openhab.core.types.State;
@ -80,9 +82,10 @@ public class RobonectHandler extends BaseThingHandler {
private RobonectClient robonectClient; private RobonectClient robonectClient;
public RobonectHandler(Thing thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) { public RobonectHandler(Thing thing, HttpClientFactory httpClientFactory, TimeZoneProvider timeZoneProvider) {
super(thing); super(thing);
this.httpClient = httpClient; httpClient = httpClientFactory
.createHttpClient(ThingWebClientUtil.buildWebClientConsumerName(thing.getUID(), null));
this.timeZoneProvider = timeZoneProvider; this.timeZoneProvider = timeZoneProvider;
} }
@ -243,7 +246,7 @@ public class RobonectHandler extends BaseThingHandler {
if (info.getHealth() != null) { if (info.getHealth() != null) {
updateState(CHANNEL_HEALTH_TEMP, updateState(CHANNEL_HEALTH_TEMP,
new QuantityType<>(info.getHealth().getTemperature(), SIUnits.CELSIUS)); 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() != null) {
if (info.getTimer().getNext() != null) { if (info.getTimer().getNext() != null) {
@ -364,11 +367,12 @@ public class RobonectHandler extends BaseThingHandler {
try { try {
httpClient.start(); httpClient.start();
robonectClient = new RobonectClient(httpClient, endpoint);
} catch (Exception e) { } catch (Exception e) {
logger.error("Exception while trying to start http client", e); logger.error("Exception while trying to start HTTP client", e);
throw new RuntimeException("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())); Runnable runnable = new MowerChannelPoller(TimeUnit.SECONDS.toMillis(robonectConfig.getOfflineTimeout()));
int pollInterval = robonectConfig.getPollInterval(); int pollInterval = robonectConfig.getPollInterval();
pollingJob = scheduler.scheduleWithFixedDelay(runnable, 0, pollInterval, TimeUnit.SECONDS); pollingJob = scheduler.scheduleWithFixedDelay(runnable, 0, pollInterval, TimeUnit.SECONDS);
@ -376,12 +380,17 @@ public class RobonectHandler extends BaseThingHandler {
@Override @Override
public void dispose() { public void dispose() {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) { if (pollingJob != null) {
pollingJob.cancel(true); 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);
}
} }
/** /**

View File

@ -20,7 +20,6 @@ import java.time.Month;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import org.eclipse.jetty.client.HttpClient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; 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.Timer;
import org.openhab.binding.robonect.internal.model.Wlan; import org.openhab.binding.robonect.internal.model.Wlan;
import org.openhab.core.i18n.TimeZoneProvider; 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.DateTimeType;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
@ -69,16 +69,17 @@ public class RobonectHandlerTest {
private @Mock Thing robonectThingMock; private @Mock Thing robonectThingMock;
private @Mock RobonectClient robonectClientMock; private @Mock RobonectClient robonectClientMock;
private @Mock ThingHandlerCallback callbackMock; private @Mock ThingHandlerCallback callbackMock;
private @Mock HttpClient httpClientMock; private @Mock HttpClientFactory httpClientFactoryMock;
private @Mock TimeZoneProvider timezoneProvider; private @Mock TimeZoneProvider timezoneProvider;
@BeforeEach @BeforeEach
public void setUp() { 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.setCallback(callbackMock);
subject.setRobonectClient(robonectClientMock); subject.setRobonectClient(robonectClientMock);
Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin"));
} }
@Test @Test
@ -97,7 +98,6 @@ public class RobonectHandlerTest {
// when // when
when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); 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), subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_TIMER_NEXT_TIMER),
RefreshType.REFRESH); RefreshType.REFRESH);
@ -141,7 +141,6 @@ public class RobonectHandlerTest {
// when // when
when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
when(robonectClientMock.errorList()).thenReturn(errorList); 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), subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
RefreshType.REFRESH); RefreshType.REFRESH);
@ -192,7 +191,6 @@ public class RobonectHandlerTest {
// when // when
when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); 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), subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
RefreshType.REFRESH); RefreshType.REFRESH);
@ -223,7 +221,6 @@ public class RobonectHandlerTest {
// when // when
when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); 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), subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
RefreshType.REFRESH); RefreshType.REFRESH);
@ -259,7 +256,6 @@ public class RobonectHandlerTest {
// when // when
when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo); when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
when(robonectClientMock.errorList()).thenReturn(errorList); 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), subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
RefreshType.REFRESH); RefreshType.REFRESH);