diff --git a/README.md b/README.md index 2d28d23..76a43ae 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,10 @@ km200.endpoints().forEach(System.out::println); ### Thread safety -Code wise the API is thread safe, however your KM200 itself might not be. I did observe issues when querying my KM200 concurrently. It performed badly and with errors. It is advised to use this API not concurrently. +Code wise this API is thread safe, it is highly recommended to not +use it concurrently. Your KM200 gateway itself is not thread safe. In order +to protect users from wrong usage, this API will serialize all requests, i.e. +concurrent requests will not happen concurrently. ## License diff --git a/src/main/java/de/malkusch/km200/KM200.java b/src/main/java/de/malkusch/km200/KM200.java index 6131940..8f53c84 100644 --- a/src/main/java/de/malkusch/km200/KM200.java +++ b/src/main/java/de/malkusch/km200/KM200.java @@ -56,9 +56,10 @@ * } * * - * Although code wise this class is thread safe, it is highly recommended to not - * use it concurrently. Chances are that your KM200 gateway itself is not thread - * safe. + * Code wise this class is thread safe, it is highly recommended to not use it + * concurrently. Your KM200 gateway itself is not thread safe. In order to + * protect users from wrong usage, this API will serialize all requests, i.e. + * concurrent requests will not happen concurrently. */ public final class KM200 { @@ -96,8 +97,9 @@ public KM200(String uri, Duration timeout, String gatewayPassword, String privat * The base URI of your KM200 e.g. http://192.168.0.44 * @param retries * The amount of retries. Set to {@link #RETRY_DISABLED} to - * disable retrying. Retries add a waiting delay between each retry - * of several seconds, because the km200 recovers very slowly. + * disable retrying. Retries add a waiting delay between each + * retry of several seconds, because the km200 recovers very + * slowly. * @param timeout * An IO timeout for individual requests to your heater. Retries * might block the API longer than this timeout. @@ -249,23 +251,31 @@ private HttpResponse sendWithRetries(FailsafeExecutor> re } } + private final Object serializedSendLock = new Object(); + private HttpResponse send(HttpRequest request, BodyHandler bodyHandler) throws IOException, InterruptedException, KM200Exception { - var response = http.send(request, bodyHandler); - var status = response.statusCode(); + /* + * The KM200 itself is not thread safe. This lock serializes all + * requests to protect users from a wrong concurrent usage of this API. + */ + synchronized (serializedSendLock) { + var response = http.send(request, bodyHandler); + var status = response.statusCode(); - if (status >= 200 && status <= 299) { - return response; + if (status >= 200 && status <= 299) { + return response; - } else { - throw switch (status) { - case 403 -> new KM200Exception.Forbidden(request.uri() + " is forbidden"); - case 404 -> new KM200Exception.NotFound(request.uri() + " was not found"); - case 423 -> new KM200Exception.Locked(request.uri() + " was locked"); - case 500 -> new KM200Exception.ServerError(request.uri() + " resulted in a server error"); - default -> new KM200Exception(request.uri() + " failed with response code " + status); - }; + } else { + throw switch (status) { + case 403 -> new KM200Exception.Forbidden(request.uri() + " is forbidden"); + case 404 -> new KM200Exception.NotFound(request.uri() + " was not found"); + case 423 -> new KM200Exception.Locked(request.uri() + " was locked"); + case 500 -> new KM200Exception.ServerError(request.uri() + " resulted in a server error"); + default -> new KM200Exception(request.uri() + " failed with response code " + status); + }; + } } }