From 8a2e854aa5cf914bbaa56a3c915bef67f01a0329 Mon Sep 17 00:00:00 2001 From: J-N-K Date: Tue, 26 Dec 2023 18:06:14 +0100 Subject: [PATCH] [http] Use core ChannelHandler (#547) Signed-off-by: Jan N. Klug --- .../http/internal/HttpHandlerFactory.java | 6 +- .../http/internal/HttpThingHandler.java | 72 +++++++++---------- .../internal/config/HttpChannelConfig.java | 4 +- .../internal/http/HttpResponseListener.java | 12 ++-- .../internal/http/RefreshingUrlCache.java | 16 ++--- .../binding/http/RefreshingUrlCacheTest.java | 6 +- 6 files changed, 55 insertions(+), 61 deletions(-) diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpHandlerFactory.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpHandlerFactory.java index 0d0960812d..502721dda0 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpHandlerFactory.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpHandlerFactory.java @@ -34,7 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smarthomej.commons.SimpleDynamicStateDescriptionProvider; -import org.smarthomej.commons.transform.ValueTransformationProvider; /** * The {@link HttpHandlerFactory} is responsible for creating things and thing @@ -50,17 +49,14 @@ public class HttpHandlerFactory extends BaseThingHandlerFactory implements HttpC private final HttpClient secureClient; private final HttpClient insecureClient; - private final ValueTransformationProvider valueTransformationProvider; private final SimpleDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider; @Activate public HttpHandlerFactory(@Reference HttpClientFactory httpClientFactory, - @Reference ValueTransformationProvider valueTransformationProvider, @Reference SimpleDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider) { this.secureClient = new HttpClient(new SslContextFactory.Client()); this.insecureClient = new HttpClient(new SslContextFactory.Client(true)); - this.valueTransformationProvider = valueTransformationProvider; // clear user agent, this needs to be set later in the thing configuration as additional header this.secureClient.setUserAgentField(null); this.insecureClient.setUserAgentField(null); @@ -96,7 +92,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (THING_TYPE_URL.equals(thingTypeUID)) { - return new HttpThingHandler(thing, this, valueTransformationProvider, httpDynamicStateDescriptionProvider); + return new HttpThingHandler(thing, this, httpDynamicStateDescriptionProvider); } return null; diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java index 106629d98d..0004dcd594 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/HttpThingHandler.java @@ -45,6 +45,19 @@ import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.generic.ChannelHandler; +import org.openhab.core.thing.binding.generic.ChannelHandlerContent; +import org.openhab.core.thing.binding.generic.ChannelMode; +import org.openhab.core.thing.binding.generic.ChannelTransformation; +import org.openhab.core.thing.binding.generic.converter.ColorChannelHandler; +import org.openhab.core.thing.binding.generic.converter.DimmerChannelHandler; +import org.openhab.core.thing.binding.generic.converter.FixedValueMappingChannelHandler; +import org.openhab.core.thing.binding.generic.converter.GenericChannelHandler; +import org.openhab.core.thing.binding.generic.converter.ImageChannelHandler; +import org.openhab.core.thing.binding.generic.converter.NumberChannelHandler; +import org.openhab.core.thing.binding.generic.converter.PlayerChannelHandler; +import org.openhab.core.thing.binding.generic.converter.RollershutterChannelHandler; +import org.openhab.core.thing.internal.binding.generic.converter.AbstractTransformingChannelHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -61,19 +74,6 @@ import org.smarthomej.binding.http.internal.http.RefreshingUrlCache; import org.smarthomej.commons.SimpleDynamicStateDescriptionProvider; import org.smarthomej.commons.UpdatingBaseThingHandler; -import org.smarthomej.commons.itemvalueconverter.ChannelMode; -import org.smarthomej.commons.itemvalueconverter.ContentWrapper; -import org.smarthomej.commons.itemvalueconverter.ItemValueConverter; -import org.smarthomej.commons.itemvalueconverter.converter.AbstractTransformingItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.ColorItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.DimmerItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.FixedValueMappingItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.GenericItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.ImageItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.NumberItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.PlayerItemConverter; -import org.smarthomej.commons.itemvalueconverter.converter.RollershutterItemConverter; -import org.smarthomej.commons.transform.ValueTransformationProvider; /** * The {@link HttpThingHandler} is responsible for handling commands, which are @@ -86,29 +86,26 @@ public class HttpThingHandler extends UpdatingBaseThingHandler implements HttpSt private static final Set URL_PART_DELIMITER = Set.of('/', '?', '&'); private final Logger logger = LoggerFactory.getLogger(HttpThingHandler.class); - private final ValueTransformationProvider valueTransformationProvider; private final HttpClientProvider httpClientProvider; private final RateLimitedHttpClient rateLimitedHttpClient; private final SimpleDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider; private HttpThingConfig config = new HttpThingConfig(); private final Map urlHandlers = new HashMap<>(); - private final Map channels = new HashMap<>(); + private final Map channels = new HashMap<>(); private final Map channelUrls = new HashMap<>(); public HttpThingHandler(Thing thing, HttpClientProvider httpClientProvider, - ValueTransformationProvider valueTransformationProvider, SimpleDynamicStateDescriptionProvider httpDynamicStateDescriptionProvider) { super(thing); this.httpClientProvider = httpClientProvider; this.rateLimitedHttpClient = new RateLimitedHttpClient(httpClientProvider.getSecureClient(), scheduler); - this.valueTransformationProvider = valueTransformationProvider; this.httpDynamicStateDescriptionProvider = httpDynamicStateDescriptionProvider; } @Override public void handleCommand(ChannelUID channelUID, Command command) { - ItemValueConverter itemValueConverter = channels.get(channelUID); + ChannelHandler itemValueConverter = channels.get(channelUID); if (itemValueConverter == null) { logger.warn("Cannot find channel implementation for channel {}.", channelUID); return; @@ -279,45 +276,46 @@ private void createChannel(Channel channel) { return; } - ItemValueConverter itemValueConverter; + ChannelHandler itemValueConverter; switch (acceptedItemType) { case "Color": - itemValueConverter = createItemConverter(ColorItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(ColorChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "DateTime": - itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, + itemValueConverter = createGenericChannelHandler(commandUrl, channelUID, channelConfig, DateTimeType::new); break; case "Dimmer": - itemValueConverter = createItemConverter(DimmerItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(DimmerChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "Contact": case "Switch": - itemValueConverter = createItemConverter(FixedValueMappingItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(FixedValueMappingChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "Image": - itemValueConverter = new ImageItemConverter(state -> updateState(channelUID, state)); + itemValueConverter = new ImageChannelHandler(state -> updateState(channelUID, state)); break; case "Location": - itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, PointType::new); + itemValueConverter = createGenericChannelHandler(commandUrl, channelUID, channelConfig, PointType::new); break; case "Number": - itemValueConverter = createItemConverter(NumberItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(NumberChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "Player": - itemValueConverter = createItemConverter(PlayerItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(PlayerChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "Rollershutter": - itemValueConverter = createItemConverter(RollershutterItemConverter::new, commandUrl, channelUID, + itemValueConverter = createChannelHandler(RollershutterChannelHandler::new, commandUrl, channelUID, channelConfig); break; case "String": - itemValueConverter = createGenericItemConverter(commandUrl, channelUID, channelConfig, StringType::new); + itemValueConverter = createGenericChannelHandler(commandUrl, channelUID, channelConfig, + StringType::new); break; default: logger.warn("Unsupported item-type '{}'", channel.getAcceptedItemType()); @@ -373,7 +371,7 @@ private void sendHttpValue(String commandUrl, String command, boolean isRetry) { request.timeout(config.timeout, TimeUnit.MILLISECONDS); config.getHeaders().forEach(request::header); - CompletableFuture<@Nullable ContentWrapper> responseContentFuture = new CompletableFuture<>(); + CompletableFuture<@Nullable ChannelHandlerContent> responseContentFuture = new CompletableFuture<>(); responseContentFuture.exceptionally(t -> { if (t instanceof HttpAuthException) { if (isRetry || !rateLimitedHttpClient.reAuth(uri)) { @@ -412,18 +410,18 @@ private String concatenateUrlParts(String baseUrl, @Nullable String extension) { } } - private ItemValueConverter createItemConverter(AbstractTransformingItemConverter.Factory factory, String commandUrl, + private ChannelHandler createChannelHandler(AbstractTransformingChannelHandler.Factory factory, String commandUrl, ChannelUID channelUID, HttpChannelConfig channelConfig) { return factory.create(state -> updateState(channelUID, state), command -> postCommand(channelUID, command), command -> sendHttpValue(commandUrl, command), - valueTransformationProvider.getValueTransformation(channelConfig.stateTransformation), - valueTransformationProvider.getValueTransformation(channelConfig.commandTransformation), channelConfig); + new ChannelTransformation(channelConfig.stateTransformation), + new ChannelTransformation(channelConfig.commandTransformation), channelConfig); } - private ItemValueConverter createGenericItemConverter(String commandUrl, ChannelUID channelUID, + private ChannelHandler createGenericChannelHandler(String commandUrl, ChannelUID channelUID, HttpChannelConfig channelConfig, Function toState) { - AbstractTransformingItemConverter.Factory factory = (state, command, value, stateTrans, commandTrans, - config) -> new GenericItemConverter(toState, state, command, value, stateTrans, commandTrans, config); - return createItemConverter(factory, commandUrl, channelUID, channelConfig); + AbstractTransformingChannelHandler.Factory factory = (state, command, value, stateTrans, commandTrans, + config) -> new GenericChannelHandler(toState, state, command, value, stateTrans, commandTrans, config); + return createChannelHandler(factory, commandUrl, channelUID, channelConfig); } } diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/config/HttpChannelConfig.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/config/HttpChannelConfig.java index c405dce719..ed049a8657 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/config/HttpChannelConfig.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/config/HttpChannelConfig.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.smarthomej.commons.itemvalueconverter.ItemValueConverterChannelConfig; +import org.openhab.core.thing.binding.generic.ChannelValueConverterConfig; /** * The {@link HttpChannelConfig} class contains fields mapping channel configuration parameters. @@ -23,7 +23,7 @@ * @author Jan N. Klug - Initial contribution */ @NonNullByDefault -public class HttpChannelConfig extends ItemValueConverterChannelConfig { +public class HttpChannelConfig extends ChannelValueConverterConfig { public @Nullable String stateExtension; public @Nullable String commandExtension; diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/HttpResponseListener.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/HttpResponseListener.java index a942f1b1e3..5695a9735d 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/HttpResponseListener.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/HttpResponseListener.java @@ -25,9 +25,9 @@ import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpStatus; +import org.openhab.core.thing.binding.generic.ChannelHandlerContent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.smarthomej.commons.itemvalueconverter.ContentWrapper; /** * The {@link HttpResponseListener} is responsible for processing the result of a HTTP request @@ -37,7 +37,7 @@ @NonNullByDefault public class HttpResponseListener extends BufferingResponseListener { private final Logger logger = LoggerFactory.getLogger(HttpResponseListener.class); - private final CompletableFuture<@Nullable ContentWrapper> future; + private final CompletableFuture<@Nullable ChannelHandlerContent> future; private final HttpStatusListener httpStatusListener; private final String fallbackEncoding; @@ -48,8 +48,8 @@ public class HttpResponseListener extends BufferingResponseListener { * @param fallbackEncoding a fallback encoding for the content (UTF-8 if null) * @param bufferSize the buffer size for the content in kB (default 2048 kB) */ - public HttpResponseListener(CompletableFuture<@Nullable ContentWrapper> future, @Nullable String fallbackEncoding, - int bufferSize, HttpStatusListener httpStatusListener) { + public HttpResponseListener(CompletableFuture<@Nullable ChannelHandlerContent> future, + @Nullable String fallbackEncoding, int bufferSize, HttpStatusListener httpStatusListener) { super(bufferSize * 1024); this.future = future; this.fallbackEncoding = fallbackEncoding != null ? fallbackEncoding : StandardCharsets.UTF_8.name(); @@ -81,8 +81,8 @@ public void onComplete(Result result) { byte[] content = getContent(); String encoding = getEncoding(); if (content != null) { - future.complete(new ContentWrapper(content, encoding == null ? fallbackEncoding : encoding, - getMediaType())); + future.complete(new ChannelHandlerContent(content, + encoding == null ? fallbackEncoding : encoding, getMediaType())); } else { future.complete(null); } diff --git a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java index 90df5514a2..91f7fa3fac 100644 --- a/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java +++ b/bundles/org.smarthomej.binding.http/src/main/java/org/smarthomej/binding/http/internal/http/RefreshingUrlCache.java @@ -31,11 +31,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.http.HttpMethod; +import org.openhab.core.thing.binding.generic.ChannelHandlerContent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smarthomej.binding.http.internal.Util; import org.smarthomej.binding.http.internal.config.HttpThingConfig; -import org.smarthomej.commons.itemvalueconverter.ContentWrapper; /** * The {@link RefreshingUrlCache} is responsible for requesting from a single URL and passing the content to the @@ -53,7 +53,7 @@ public class RefreshingUrlCache { private final int timeout; private final int bufferSize; private final @Nullable String fallbackEncoding; - private final Set> consumers = ConcurrentHashMap.newKeySet(); + private final Set> consumers = ConcurrentHashMap.newKeySet(); private final Map headers; private final HttpMethod httpMethod; private final String httpContent; @@ -61,7 +61,7 @@ public class RefreshingUrlCache { private final HttpStatusListener httpStatusListener; private final ScheduledFuture future; - private @Nullable ContentWrapper lastContent; + private @Nullable ChannelHandlerContent lastContent; public RefreshingUrlCache(ScheduledExecutorService executor, RateLimitedHttpClient httpClient, String url, HttpThingConfig thingConfig, String httpContent, @Nullable String httpContentType, @@ -101,7 +101,7 @@ private void refresh(boolean isRetry) { request.timeout(timeout, TimeUnit.MILLISECONDS); headers.forEach(request::header); - CompletableFuture<@Nullable ContentWrapper> responseContentFuture = new CompletableFuture<>(); + CompletableFuture<@Nullable ChannelHandlerContent> responseContentFuture = new CompletableFuture<>(); responseContentFuture.exceptionally(t -> { if (t instanceof HttpAuthException) { if (isRetry || !httpClient.reAuth(uri)) { @@ -140,17 +140,17 @@ public void stop() { logger.trace("Stopped refresh task for URL '{}'", url); } - public void addConsumer(Consumer<@Nullable ContentWrapper> consumer) { + public void addConsumer(Consumer<@Nullable ChannelHandlerContent> consumer) { consumers.add(consumer); } - public Optional get() { + public Optional get() { return Optional.ofNullable(lastContent); } - private void processResult(@Nullable ContentWrapper content) { + private void processResult(@Nullable ChannelHandlerContent content) { if (content != null || strictErrorHandling) { - for (Consumer<@Nullable ContentWrapper> consumer : consumers) { + for (Consumer<@Nullable ChannelHandlerContent> consumer : consumers) { try { consumer.accept(content); } catch (IllegalArgumentException | IllegalStateException e) { diff --git a/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java b/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java index 7e188b19ca..fad6c7968b 100644 --- a/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java +++ b/bundles/org.smarthomej.binding.http/src/test/java/org/smarthomej/binding/http/RefreshingUrlCacheTest.java @@ -42,11 +42,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.openhab.core.thing.binding.generic.ChannelHandlerContent; import org.smarthomej.binding.http.internal.config.HttpThingConfig; import org.smarthomej.binding.http.internal.http.HttpStatusListener; import org.smarthomej.binding.http.internal.http.RateLimitedHttpClient; import org.smarthomej.binding.http.internal.http.RefreshingUrlCache; -import org.smarthomej.commons.itemvalueconverter.ContentWrapper; /** * The {@link RefreshingUrlCacheTest} implements tests for the {@link RefreshingUrlCache} @@ -64,7 +64,7 @@ public class RefreshingUrlCacheTest extends AbstractWireMockTest { private @NonNullByDefault({}) String url; private @NonNullByDefault({}) HttpStatusListener statusListener; - private List<@Nullable ContentWrapper> contentWrappers = new CopyOnWriteArrayList<>(); + private final List<@Nullable ChannelHandlerContent> contentWrappers = new CopyOnWriteArrayList<>(); @BeforeEach public void initTest() { @@ -107,7 +107,7 @@ public void testUpdateOnSuccessfulRequest() { verify(statusListener, times(contentWrappers.size())).onHttpSuccess(); // assert all content equals the correct value - assertTrue(contentWrappers.stream().map(Objects::requireNonNull).map(ContentWrapper::getAsString) + assertTrue(contentWrappers.stream().map(Objects::requireNonNull).map(ChannelHandlerContent::getAsString) .allMatch(TEST_CONTENT::equals)); }