From ade62c7c4613e9a19bd288345557362afb1f220c Mon Sep 17 00:00:00 2001 From: granny Date: Fri, 12 Apr 2024 05:09:23 -0700 Subject: [PATCH] wrap ServerSentEventHandler class --- .../net/pl3x/map/core/httpd/HttpdServer.java | 39 ++--- .../pl3x/map/core/httpd/LiveDataHandler.java | 150 ++++++++++++++++++ .../core/renderer/task/UpdateLiveData.java | 2 +- .../renderer/task/UpdateSettingsData.java | 2 +- .../java/net/pl3x/map/core/world/World.java | 13 +- 5 files changed, 168 insertions(+), 38 deletions(-) create mode 100644 core/src/main/java/net/pl3x/map/core/httpd/LiveDataHandler.java diff --git a/core/src/main/java/net/pl3x/map/core/httpd/HttpdServer.java b/core/src/main/java/net/pl3x/map/core/httpd/HttpdServer.java index e621e0f5c..1423a3409 100644 --- a/core/src/main/java/net/pl3x/map/core/httpd/HttpdServer.java +++ b/core/src/main/java/net/pl3x/map/core/httpd/HttpdServer.java @@ -31,8 +31,6 @@ import io.undertow.server.handlers.resource.PathResourceManager; import io.undertow.server.handlers.resource.ResourceHandler; import io.undertow.server.handlers.resource.ResourceManager; -import io.undertow.server.handlers.sse.ServerSentEventConnection; -import io.undertow.server.handlers.sse.ServerSentEventHandler; import io.undertow.util.ETag; import io.undertow.util.Headers; import io.undertow.util.HttpString; @@ -54,30 +52,10 @@ public class HttpdServer { private HttpString X_ACCEL_BUFFERING = new HttpString("X-Accel-Buffering"); private Undertow server; - private ServerSentEventHandler serverSentEventHandler = Handlers.serverSentEvents(); + private LiveDataHandler liveDataHandler = new LiveDataHandler(); - public void sendSSE(ServerSentEventHandler serverSentEventHandler, String event, String data) { - for (ServerSentEventConnection connection : serverSentEventHandler.getConnections()) { - connection.send(data, event, null, null); - } - } - - public void sendSSE(ServerSentEventHandler serverSentEventHandler, String data) { - sendSSE(serverSentEventHandler, null, data); - } - - public void sendSSE(String event, String data) { - sendSSE(this.serverSentEventHandler, event, data); - } - - public void sendSSE(String data) { - sendSSE(this.serverSentEventHandler, null, data); - } - - public void closeSSEConnections(ServerSentEventHandler serverSentEventHandler) { - for (ServerSentEventConnection connection : serverSentEventHandler.getConnections()) { - connection.shutdown(); - } + public LiveDataHandler getLiveDataHandler() { + return liveDataHandler; } public void startServer() { @@ -134,7 +112,7 @@ public void startServer() { String worldName = exchange.getQueryParameters().get("world").peek(); if (worldName == null || worldName.isEmpty()) { exchange.getResponseHeaders().put(X_ACCEL_BUFFERING, "no"); - serverSentEventHandler.handleRequest(exchange); + liveDataHandler.handle(exchange); return; } @@ -146,14 +124,15 @@ public void startServer() { .map(World::getName).collect(Collectors.joining(", ")); handleError(exchange, "Could not find world named '%s'. Available worlds: %s" .formatted(worldName, listOfValidWorlds)); + exchange.endExchange(); return; } if (exchange.isInIoThread()) { - exchange.dispatch(world.getServerSentEventHandler()); + exchange.dispatch(world.getServerSentEventHandler().get()); } else { exchange.getResponseHeaders().put(X_ACCEL_BUFFERING, "no"); - world.getServerSentEventHandler().handleRequest(exchange); + world.getServerSentEventHandler().handle(exchange); } }) ) @@ -189,9 +168,9 @@ public void stopServer() { } LogFilter.HIDE_UNDERTOW_LOGS = true; - this.closeSSEConnections(this.serverSentEventHandler); + this.liveDataHandler.closeConnections(); Pl3xMap.api().getWorldRegistry().forEach(world -> { - this.closeSSEConnections(world.getServerSentEventHandler()); + world.getServerSentEventHandler().closeConnections(); }); this.server.stop(); LogFilter.HIDE_UNDERTOW_LOGS = false; diff --git a/core/src/main/java/net/pl3x/map/core/httpd/LiveDataHandler.java b/core/src/main/java/net/pl3x/map/core/httpd/LiveDataHandler.java new file mode 100644 index 000000000..8e873a68a --- /dev/null +++ b/core/src/main/java/net/pl3x/map/core/httpd/LiveDataHandler.java @@ -0,0 +1,150 @@ +/* + * MIT License + * + * Copyright (c) 2020-2023 William Blake Galbreath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.pl3x.map.core.httpd; + +import io.undertow.Handlers; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.sse.ServerSentEventConnection; +import io.undertow.server.handlers.sse.ServerSentEventHandler; +import java.io.IOException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LiveDataHandler { + private ServerSentEventHandler serverSentEventHandler; + + public LiveDataHandler() { + this.serverSentEventHandler = Handlers.serverSentEvents(); + } + + /** + * + * @param event The message event + * @param data The message data + * @param success The callback that is called when a message is sucessfully sent. + * @param failure The callback that is called when a message send fails. + */ + public void send(String event, String data, SuccessCallback success, FailureCallback failure) { + if (serverSentEventHandler == null) { + return; + } + + Callback callback = new Callback(success, failure); + for (ServerSentEventConnection connection : this.serverSentEventHandler.getConnections()) { + connection.send(data, event, null, callback); + } + } + + /** + * + * @param event The message event + * @param data The message data + * @param success The callback that is called when a message is sucessfully sent. + */ + public void send(String event, String data, SuccessCallback success) { + this.send(event, data, success, null); + } + + /** + * + * @param event The message event + * @param data The message data + */ + public void send(String event, String data) { + this.send(event, data, null, null); + } + + /** + * + * @param data The message data + */ + public void send(String data) { + this.send(null, data); + } + + public void closeConnections() { + for (ServerSentEventConnection connection : serverSentEventHandler.getConnections()) { + connection.shutdown(); + } + } + + public void handle(HttpServerExchange exchange) throws Exception { + this.serverSentEventHandler.handleRequest(exchange); + } + + public ServerSentEventHandler get() { + return this.serverSentEventHandler; + } + + /** + * Notification that is called when a message is sucessfully sent + */ + public interface SuccessCallback { + /** + * @param connection The connection + * @param data The message data + * @param event The message event + * @param id The message id + */ + void apply(@NotNull ServerSentEventConnection connection, @Nullable String data, @Nullable String event, @Nullable String id); + } + + /** + * Notification that is called when a message send fails. + */ + public interface FailureCallback { + /** + * @param connection The connection + * @param data The message data + * @param event The message event + * @param id The message id + * @param exception The exception + */ + void apply(@NotNull ServerSentEventConnection connection, @Nullable String data, @Nullable String event, @Nullable String id, @NotNull IOException exception); + } + + private class Callback implements ServerSentEventConnection.EventCallback { + private SuccessCallback success; + private FailureCallback failure; + + public Callback(SuccessCallback success, FailureCallback failure) { + this.success = success; + this.failure = failure; + } + + @Override + public void done(ServerSentEventConnection connection, String data, String event, String id) { + if (success != null) { + success.apply(connection, data, event, id); + } + } + + @Override + public void failed(ServerSentEventConnection connection, String data, String event, String id, IOException e) { + if (failure != null) { + failure.apply(connection, data, event, id, e); + } + } + } +} diff --git a/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateLiveData.java b/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateLiveData.java index 794fcc22c..b50f15aa3 100644 --- a/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateLiveData.java +++ b/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateLiveData.java @@ -79,7 +79,7 @@ public void parse() { int markerHashCode = list.hashCode(); if (markerCacheIfPresent == null || !markerCacheIfPresent.equals(markerHashCode)) { Logger.debug("[%s/%s] sending through sse %d".formatted(this.world.getName(), key, (System.currentTimeMillis()))); - Pl3xMap.api().getHttpdServer().sendSSE(world.getServerSentEventHandler(), "markers", String.format("{\"key\": \"%s\", \"markers\": %s}", key, this.gson.toJson(list))); + world.getServerSentEventHandler().send("markers", String.format("{\"key\": \"%s\", \"markers\": %s}", key, this.gson.toJson(list))); markerCache.put(key, markerHashCode); } } catch (Throwable t) { diff --git a/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateSettingsData.java b/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateSettingsData.java index d1d544693..76e4a42a7 100644 --- a/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateSettingsData.java +++ b/core/src/main/java/net/pl3x/map/core/renderer/task/UpdateSettingsData.java @@ -164,7 +164,7 @@ private void parseSettings() { String json = this.gson.toJson(map); if (jsonHashCache != json.hashCode()) { - Pl3xMap.api().getHttpdServer().sendSSE("settings", json); + Pl3xMap.api().getHttpdServer().getLiveDataHandler().send("settings", json); jsonHashCache = json.hashCode(); } diff --git a/core/src/main/java/net/pl3x/map/core/world/World.java b/core/src/main/java/net/pl3x/map/core/world/World.java index 7fc4e7145..d20f71943 100644 --- a/core/src/main/java/net/pl3x/map/core/world/World.java +++ b/core/src/main/java/net/pl3x/map/core/world/World.java @@ -25,8 +25,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import io.undertow.Handlers; -import io.undertow.server.handlers.sse.ServerSentEventHandler; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -48,6 +46,7 @@ import net.pl3x.map.core.configuration.SpawnLayerConfig; import net.pl3x.map.core.configuration.WorldBorderLayerConfig; import net.pl3x.map.core.configuration.WorldConfig; +import net.pl3x.map.core.httpd.LiveDataHandler; import net.pl3x.map.core.image.IconImage; import net.pl3x.map.core.log.Logger; import net.pl3x.map.core.markers.Point; @@ -82,7 +81,8 @@ public abstract class World extends Keyed { private final long seed; private final Point spawn; private final Type type; - private final ServerSentEventHandler serverSentEventHandler; + + private final LiveDataHandler liveDataHandler; private final BiomeManager biomeManager; private final BiomeRegistry biomeRegistry; @@ -101,7 +101,8 @@ public World(@NotNull String name, long seed, @NotNull Point spawn, @NotNull Typ this.seed = seed; this.spawn = spawn; this.type = type; - this.serverSentEventHandler = Handlers.serverSentEvents(); + + this.liveDataHandler = new LiveDataHandler(); String safeNameForDirectories = name.replace(":", "-"); @@ -266,8 +267,8 @@ public int getSkylight() { return this.type; } - public ServerSentEventHandler getServerSentEventHandler() { - return serverSentEventHandler; + public LiveDataHandler getServerSentEventHandler() { + return liveDataHandler; } public @NotNull BiomeManager getBiomeManager() {