Skip to content

Commit

Permalink
[http] Use core ChannelHandler (#546)
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <[email protected]>
  • Loading branch information
J-N-K authored Dec 26, 2023
1 parent e45ae52 commit 0c59945
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -86,29 +86,26 @@ public class HttpThingHandler extends UpdatingBaseThingHandler implements HttpSt
private static final Set<Character> 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<String, RefreshingUrlCache> urlHandlers = new HashMap<>();
private final Map<ChannelUID, ItemValueConverter> channels = new HashMap<>();
private final Map<ChannelUID, ChannelHandler> channels = new HashMap<>();
private final Map<ChannelUID, String> 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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<String, State> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@

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.
*
* @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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand All @@ -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();
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -53,15 +53,15 @@ public class RefreshingUrlCache {
private final int timeout;
private final int bufferSize;
private final @Nullable String fallbackEncoding;
private final Set<Consumer<@Nullable ContentWrapper>> consumers = ConcurrentHashMap.newKeySet();
private final Set<Consumer<@Nullable ChannelHandlerContent>> consumers = ConcurrentHashMap.newKeySet();
private final Map<String, String> headers;
private final HttpMethod httpMethod;
private final String httpContent;
private final @Nullable String httpContentType;
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,
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<ContentWrapper> get() {
public Optional<ChannelHandlerContent> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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() {
Expand Down Expand Up @@ -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));
}

Expand Down

0 comments on commit 0c59945

Please sign in to comment.