mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[energidataservice] Consider Retry-After header (#20899)
* Consider Retry-After header Resolves #20897 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
+27
-1
@@ -14,6 +14,7 @@ package org.openhab.binding.energidataservice.internal;
|
||||
|
||||
import static org.openhab.binding.energidataservice.internal.EnergiDataServiceBindingConstants.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@@ -53,6 +54,7 @@ import org.openhab.binding.energidataservice.internal.api.dto.ElspotpriceRecords
|
||||
import org.openhab.binding.energidataservice.internal.api.serialization.InstantDeserializer;
|
||||
import org.openhab.binding.energidataservice.internal.api.serialization.LocalDateTimeDeserializer;
|
||||
import org.openhab.binding.energidataservice.internal.exception.DataServiceException;
|
||||
import org.openhab.binding.energidataservice.internal.exception.DataServiceRateLimitException;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -198,10 +200,34 @@ public class ApiController {
|
||||
updatePropertiesFromResponse(response, properties);
|
||||
|
||||
int status = response.getStatus();
|
||||
String responseContent = response.getContentAsString();
|
||||
|
||||
if (!HttpStatus.isSuccess(status)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Request failed with HTTP error {}: {}", status, responseContent);
|
||||
logger.trace("Response headers: {}", response.getHeaders());
|
||||
}
|
||||
|
||||
if (status == HttpStatus.TOO_MANY_REQUESTS_429) {
|
||||
String retryAfter = response.getHeaders().get("Retry-After");
|
||||
if (retryAfter != null) {
|
||||
try {
|
||||
int retryAfterSeconds = Integer.parseInt(retryAfter);
|
||||
if (retryAfterSeconds < 0) {
|
||||
logger.debug("Invalid Retry-After header value: '{}'", retryAfter);
|
||||
throw new DataServiceRateLimitException("Rate limit is exceeded");
|
||||
}
|
||||
throw new DataServiceRateLimitException(
|
||||
"Rate limit is exceeded. Retrying after " + retryAfter + " seconds.",
|
||||
Duration.ofSeconds(retryAfterSeconds));
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Invalid Retry-After header value: '{}'", retryAfter);
|
||||
throw new DataServiceRateLimitException("Rate limit is exceeded", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new DataServiceException("The request failed with HTTP error " + status, status);
|
||||
}
|
||||
String responseContent = response.getContentAsString();
|
||||
if (responseContent.isEmpty()) {
|
||||
throw new DataServiceException("Empty response");
|
||||
}
|
||||
|
||||
+1
-2
@@ -16,7 +16,6 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -161,7 +160,7 @@ public class EnergiDataServiceCommandExtension extends AbstractConsoleCommandExt
|
||||
|
||||
@Override
|
||||
public List<String> getUsages() {
|
||||
return Arrays.asList(buildCommandUsage(SUBCMD_UPDATE + " ["
|
||||
return List.of(buildCommandUsage(SUBCMD_UPDATE + " ["
|
||||
+ String.join("|", Stream.of(PriceComponent.values()).map(PriceComponent::toString).toList())
|
||||
+ "] <StartDate> [<EndDate>]", "Update time series in requested period"));
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.energidataservice.internal.exception;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* {@link DataServiceRateLimitException} is a specialized Energi Data Service exception
|
||||
* thrown in case of exceeding the API rate limit. It contains information about how long to
|
||||
* wait before retrying the request.
|
||||
*
|
||||
* @see <a href="https://www.energidataservice.dk/guides/api-guides">Energi Data Service API documentation on rate
|
||||
* limiting</a>
|
||||
*
|
||||
* @author Jacob Laursen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataServiceRateLimitException extends DataServiceException {
|
||||
|
||||
private static final Duration DEFAULT_RETRY_AFTER = Duration.ofMinutes(30);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Duration retryAfter;
|
||||
|
||||
public DataServiceRateLimitException(String message) {
|
||||
this(message, DEFAULT_RETRY_AFTER);
|
||||
}
|
||||
|
||||
public DataServiceRateLimitException(String message, Throwable cause) {
|
||||
super(message, HttpStatus.TOO_MANY_REQUESTS_429);
|
||||
initCause(cause);
|
||||
this.retryAfter = DEFAULT_RETRY_AFTER;
|
||||
}
|
||||
|
||||
public DataServiceRateLimitException(String message, Duration retryAfter) {
|
||||
super(message, HttpStatus.TOO_MANY_REQUESTS_429);
|
||||
this.retryAfter = retryAfter;
|
||||
}
|
||||
|
||||
public Duration getRetryAfter() {
|
||||
return retryAfter;
|
||||
}
|
||||
}
|
||||
+14
-9
@@ -20,6 +20,7 @@ import java.time.ZoneId;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.energidataservice.internal.exception.DataServiceException;
|
||||
import org.openhab.binding.energidataservice.internal.exception.DataServiceRateLimitException;
|
||||
import org.openhab.binding.energidataservice.internal.retry.strategy.ExponentialBackoff;
|
||||
import org.openhab.binding.energidataservice.internal.retry.strategy.FixedTime;
|
||||
import org.openhab.binding.energidataservice.internal.retry.strategy.Linear;
|
||||
@@ -40,16 +41,20 @@ public class RetryPolicyFactory {
|
||||
* @return retry strategy
|
||||
*/
|
||||
public static RetryStrategy fromThrowable(Throwable e) {
|
||||
if (e instanceof DataServiceException dse) {
|
||||
switch (dse.getHttpStatus()) {
|
||||
case HttpStatus.TOO_MANY_REQUESTS_429:
|
||||
return new ExponentialBackoff().withMinimum(Duration.ofMinutes(30));
|
||||
default:
|
||||
return new ExponentialBackoff().withMinimum(Duration.ofMinutes(1)).withJitter(0.2);
|
||||
}
|
||||
}
|
||||
ExponentialBackoff strategy = switch (e) {
|
||||
case DataServiceRateLimitException rle ->
|
||||
new ExponentialBackoff()
|
||||
.withMinimum(rle.getRetryAfter());
|
||||
|
||||
return new ExponentialBackoff().withMinimum(Duration.ofMinutes(1)).withJitter(0.2);
|
||||
case DataServiceException dse when dse.getHttpStatus() == HttpStatus.TOO_MANY_REQUESTS_429 ->
|
||||
new ExponentialBackoff()
|
||||
.withMinimum(Duration.ofMinutes(30));
|
||||
|
||||
default -> new ExponentialBackoff()
|
||||
.withMinimum(Duration.ofMinutes(1));
|
||||
};
|
||||
|
||||
return strategy.withJitter(0.2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user