From 6ee826f94b4d8381bd2c1db5bb5a88eb951029c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Wed, 30 Jul 2025 16:46:29 +0200 Subject: [PATCH] REFACTOR: storing Process instead of PID --- application.properties | 2 + src/main/angular/public/minecraft.svg | 189 ++++++++++++++++++ .../angular/src/app/server-list/Server.ts | 8 +- .../server-list/server-list.component.html | 18 +- .../server-list/server-list.component.less | 1 + .../app/server-list/server-list.component.ts | 2 +- src/main/angular/src/styles.less | 9 +- .../de/ph87/mc/server/NoMinecraftServer.java | 17 ++ .../java/de/ph87/mc/server/Properties.java | 45 +++++ src/main/java/de/ph87/mc/server/Server.java | 66 ++---- .../de/ph87/mc/server/ServerController.java | 18 +- .../java/de/ph87/mc/server/ServerDto.java | 31 +++ .../ph87/mc/server/ServerProcessHelper.java | 128 ------------ .../de/ph87/mc/server/ServerRepository.java | 70 ------- .../java/de/ph87/mc/server/ServerService.java | 145 ++++++++++++-- .../ph87/mc/websocket/IWebsocketMessage.java | 2 +- .../ph87/mc/websocket/WebSocketService.java | 4 +- 17 files changed, 469 insertions(+), 286 deletions(-) create mode 100644 src/main/angular/public/minecraft.svg create mode 100644 src/main/java/de/ph87/mc/server/NoMinecraftServer.java create mode 100644 src/main/java/de/ph87/mc/server/Properties.java create mode 100644 src/main/java/de/ph87/mc/server/ServerDto.java delete mode 100644 src/main/java/de/ph87/mc/server/ServerProcessHelper.java delete mode 100644 src/main/java/de/ph87/mc/server/ServerRepository.java diff --git a/application.properties b/application.properties index ba586de..b7a94e1 100644 --- a/application.properties +++ b/application.properties @@ -1 +1,3 @@ +#logging.level.de.ph87=DEBUG +#- server.port=8083 \ No newline at end of file diff --git a/src/main/angular/public/minecraft.svg b/src/main/angular/public/minecraft.svg new file mode 100644 index 0000000..62ad6b9 --- /dev/null +++ b/src/main/angular/public/minecraft.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/angular/src/app/server-list/Server.ts b/src/main/angular/src/app/server-list/Server.ts index ed61c8e..3b91d8a 100644 --- a/src/main/angular/src/app/server-list/Server.ts +++ b/src/main/angular/src/app/server-list/Server.ts @@ -7,9 +7,9 @@ export class Server { readonly name: string, readonly motd: string, readonly mode: Mode, - readonly serverPort: number, + readonly port: number, readonly running: boolean, - readonly hasIcon: boolean, + readonly icon: boolean, ) { // } @@ -19,9 +19,9 @@ export class Server { validateString(json.name), validateString(json.motd), validateString(json.mode) as Mode, - validateNumber(json.serverPort), + validateNumber(json.port), validateBoolean(json.running), - validateBoolean(json.hasIcon), + validateBoolean(json.icon), ); } diff --git a/src/main/angular/src/app/server-list/server-list.component.html b/src/main/angular/src/app/server-list/server-list.component.html index fa41cd3..d0c1a48 100644 --- a/src/main/angular/src/app/server-list/server-list.component.html +++ b/src/main/angular/src/app/server-list/server-list.component.html @@ -1,22 +1,17 @@
- Serverliste -
- -
- (i) - Beim Starten eines Servers werden alle anderen gestoppt. + Minecraft
- {{server.mode}} - {{server.mode}} + {{server.mode}} + {{server.mode}}
{{ server.motd }}
- 10.255.0.1:{{ server.serverPort }} + 10.255.0.1:{{ server.port }}
@@ -32,3 +27,8 @@
+ +
+ (i) + Beim Starten eines Servers werden alle anderen gestoppt. +
diff --git a/src/main/angular/src/app/server-list/server-list.component.less b/src/main/angular/src/app/server-list/server-list.component.less index 2538322..d0fe915 100644 --- a/src/main/angular/src/app/server-list/server-list.component.less +++ b/src/main/angular/src/app/server-list/server-list.component.less @@ -18,6 +18,7 @@ } .name { + color: #c8dddd; flex-grow: 1; font-weight: bold; } diff --git a/src/main/angular/src/app/server-list/server-list.component.ts b/src/main/angular/src/app/server-list/server-list.component.ts index 5634870..1712474 100644 --- a/src/main/angular/src/app/server-list/server-list.component.ts +++ b/src/main/angular/src/app/server-list/server-list.component.ts @@ -33,7 +33,7 @@ export class ServerListComponent extends CrudListComponent a.serverPort - b.serverPort); + return this.list.sort((a, b) => a.port - b.port); } } diff --git a/src/main/angular/src/styles.less b/src/main/angular/src/styles.less index 3ed8064..44f41a7 100644 --- a/src/main/angular/src/styles.less +++ b/src/main/angular/src/styles.less @@ -2,23 +2,26 @@ body { font-family: sans-serif; margin: 0; font-size: 5vw; + background-color: #2b3939; } .heading { padding: 0.25em; font-weight: bold; + text-align: center; + text-decoration: underline; } .hint { background-color: lightyellow; border: 0.1em solid yellow; - margin: 0.25em; + margin: 0.5em; padding: 0.25em; font-size: 60%; border-radius: 0.25em; img { - height: 1.7em; - vertical-align: middle; + height: 1.4em; + vertical-align: bottom; } } diff --git a/src/main/java/de/ph87/mc/server/NoMinecraftServer.java b/src/main/java/de/ph87/mc/server/NoMinecraftServer.java new file mode 100644 index 0000000..0f4ccd7 --- /dev/null +++ b/src/main/java/de/ph87/mc/server/NoMinecraftServer.java @@ -0,0 +1,17 @@ +package de.ph87.mc.server; + +import lombok.NonNull; + +import java.io.File; + +public class NoMinecraftServer extends Exception { + + public NoMinecraftServer(final @NonNull File directory, final String string) { + super("Not a minecraft server: directory=%s, reason=%s".formatted(directory, string)); + } + + public NoMinecraftServer(final @NonNull File directory, final Exception e) { + super("Not a minecraft server: directory=%s".formatted(directory), e); + } + +} diff --git a/src/main/java/de/ph87/mc/server/Properties.java b/src/main/java/de/ph87/mc/server/Properties.java new file mode 100644 index 0000000..fcdc4a1 --- /dev/null +++ b/src/main/java/de/ph87/mc/server/Properties.java @@ -0,0 +1,45 @@ +package de.ph87.mc.server; + +import lombok.Data; +import lombok.NonNull; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Locale; + +@Data +public class Properties { + + public final String name; + + public final String motd; + + public final Mode mode; + + public final int port; + + public final int rconPort; + + public final String rconPassword; + + public Properties(final @NonNull File directory) throws NoMinecraftServer { + final File file = new File(directory, "server.properties"); + if (!file.exists()) { + throw new NoMinecraftServer(directory, "server.properties not found"); + } + final java.util.Properties properties = new java.util.Properties(); + try (final FileReader reader = new FileReader(file)) { + properties.load(reader); + name = properties.getProperty("level-name"); + motd = properties.getProperty("motd"); + mode = Mode.valueOf(properties.getProperty("gamemode").toUpperCase(Locale.ROOT)); + port = Integer.parseInt(properties.getProperty("server-port")); + rconPort = Integer.parseInt(properties.getProperty("rcon.port")); + rconPassword = properties.getProperty("rcon.password"); + } catch (IOException | NumberFormatException e) { + throw new NoMinecraftServer(directory, e); + } + } + +} diff --git a/src/main/java/de/ph87/mc/server/Server.java b/src/main/java/de/ph87/mc/server/Server.java index d596b75..d36783e 100644 --- a/src/main/java/de/ph87/mc/server/Server.java +++ b/src/main/java/de/ph87/mc/server/Server.java @@ -1,79 +1,55 @@ package de.ph87.mc.server; -import com.fasterxml.jackson.annotation.JsonIgnore; -import de.ph87.mc.websocket.IWebsocketMessage; import jakarta.annotation.Nullable; import lombok.Data; import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; import java.io.File; @Data -public class Server implements IWebsocketMessage { +@Slf4j +public class Server { @NonNull - @JsonIgnore public final File directory; @NonNull - @JsonIgnore - public final File propertyFile; - - @NonNull - @JsonIgnore public final File pidFile; @NonNull - public final String name; - - @NonNull - public final String motd; - - @NonNull - public final Mode mode; - - public final int serverPort; - - @JsonIgnore - public final int rconPort; - - @NonNull - @JsonIgnore - public final String rconPassword; - - @JsonIgnore - public final int queryPort; - - @JsonIgnore public final File iconFile; - public final boolean hasIcon; + @NonNull + public final Properties properties; + + public final Object lock = new Object(); @Nullable - private Long pid; + public Process process = null; - public Server(@NonNull final File directory, @NonNull final String name, @NonNull final String motd, @NonNull final Mode mode, final int serverPort, final int rconPort, final String rconPassword, final int queryPort) { + public boolean shutdown = false; + + public Server(@NonNull final File directory) throws NoMinecraftServer { this.directory = directory; - this.propertyFile = new File(directory, "server.properties"); this.pidFile = new File(directory, "pid"); this.iconFile = new File(directory, "McManagerIcon.png"); - this.name = name; - this.motd = motd; - this.mode = mode; - this.serverPort = serverPort; - this.rconPort = rconPort; - this.rconPassword = rconPassword; - this.queryPort = queryPort; - this.hasIcon = iconFile.isFile(); + this.properties = new Properties(directory); + } + + public boolean isRunning() { + synchronized (lock) { + return process != null && process.isAlive(); + } } @Override public String toString() { - return "Server(%s, \"%s\", %s)".formatted(mode, motd, isRunning() ? "RUNNING" : "stopped"); + return "Server(%s, \"%s\", %s)".formatted(properties.mode, properties.motd, isRunning() ? "RUNNING" : "stopped"); } - public boolean isRunning() { - return pid != null; + public boolean eq(@NonNull final Server other) { + return properties.name.equals(other.properties.name); } } diff --git a/src/main/java/de/ph87/mc/server/ServerController.java b/src/main/java/de/ph87/mc/server/ServerController.java index 619bfb9..c8e7a51 100644 --- a/src/main/java/de/ph87/mc/server/ServerController.java +++ b/src/main/java/de/ph87/mc/server/ServerController.java @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.List; @@ -21,27 +22,28 @@ public class ServerController { private final ServerService serverService; - private final ServerRepository serverRepository; - + @NonNull @GetMapping("findAll") - public List findAll() { - return serverRepository.findAll(); + public List findAll() { + return serverService.findAll(); } + @NonNull @GetMapping("{name}/start") - public Server start(@NonNull @PathVariable final String name) { + public ServerDto start(@NonNull @PathVariable final String name) { return serverService.start(name); } + @NonNull @GetMapping("{name}/stop") - public Server stop(@NonNull @PathVariable final String name) { + public ServerDto stop(@NonNull @PathVariable final String name) { return serverService.stop(name); } @GetMapping("{name}/icon") public void icon(@NonNull @PathVariable final String name, @NonNull final HttpServletResponse response) throws IOException { - final Server server = serverRepository.getByName(name); - try (final FileInputStream inputStream = new FileInputStream(server.iconFile)) { + final File iconFile = serverService.getIconFileByName(name); + try (final FileInputStream inputStream = new FileInputStream(iconFile)) { response.getOutputStream().write(inputStream.readAllBytes()); } response.getOutputStream().flush(); diff --git a/src/main/java/de/ph87/mc/server/ServerDto.java b/src/main/java/de/ph87/mc/server/ServerDto.java new file mode 100644 index 0000000..58c10c0 --- /dev/null +++ b/src/main/java/de/ph87/mc/server/ServerDto.java @@ -0,0 +1,31 @@ +package de.ph87.mc.server; + +import de.ph87.mc.websocket.IWebsocketMessage; +import lombok.Data; +import lombok.NonNull; + +@Data +public class ServerDto implements IWebsocketMessage { + + public final String name; + + public final String motd; + + public final Mode mode; + + public final int port; + + public final boolean running; + + public boolean icon; + + public ServerDto(final @NonNull Server server) { + this.name = server.properties.name; + this.motd = server.properties.motd; + this.mode = server.properties.mode; + this.port = server.properties.port; + this.running = server.isRunning(); + this.icon = server.iconFile.isFile(); + } + +} diff --git a/src/main/java/de/ph87/mc/server/ServerProcessHelper.java b/src/main/java/de/ph87/mc/server/ServerProcessHelper.java deleted file mode 100644 index 3269235..0000000 --- a/src/main/java/de/ph87/mc/server/ServerProcessHelper.java +++ /dev/null @@ -1,128 +0,0 @@ -package de.ph87.mc.server; - -import jakarta.annotation.Nullable; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@Slf4j -@Service -@RequiredArgsConstructor -public class ServerProcessHelper { - - public static final String[] CMDLINE = {"java", "-jar", "server.jar"}; - - public static final String CMDLINE_STR = String.join(" ", CMDLINE); - - private final ApplicationEventPublisher applicationEventPublisher; - - public void updatePid(@NonNull final Server server) { - server.setPid(_readAndVerifyPid(server)); - if (server.getPid() == null) { - deletePidFile(server); - } - } - - @Nullable - private Long _readAndVerifyPid(@NonNull final Server server) { - if (!server.pidFile.exists()) { - return null; - } - - final long pid; - try (final FileInputStream stream = new FileInputStream(server.pidFile)) { - pid = Long.parseLong(new String(stream.readAllBytes(), StandardCharsets.UTF_8)); - } catch (IOException | NumberFormatException e) { - log.error("Failed to read pid-file: file={}, error={}", server.pidFile, e.getMessage()); - return null; - } - - if (!validateProcFile(server, pid)) { - return null; - } - - return pid; - } - - private static boolean validateProcFile(@NonNull final Server server, final long pid) { - final File procFile = new File("/proc/%d/cmdline".formatted(pid)); - if (!procFile.exists()) { - log.warn("Server not running: {}", server.name); - return false; - } - - try (final FileInputStream stream = new FileInputStream(procFile)) { - final String cmdline = new String(stream.readAllBytes(), StandardCharsets.UTF_8).replace((char) 0, ' ').trim(); - if (!CMDLINE_STR.equals(cmdline)) { - log.error("cmdline of running Server does not match: pid={}, running={}, expected={}", pid, cmdline, CMDLINE_STR); - return false; - } - } catch (IOException | NumberFormatException e) { - log.error("Failed to read proc-file: file={}, error={}", procFile, e.getMessage()); - return false; - } - - return true; - } - - private void deletePidFile(@NonNull final Server server) { - server.setPid(null); - if (server.pidFile.delete()) { - log.info("PID-file removed: {}", server.pidFile); - applicationEventPublisher.publishEvent(server); - } - } - - public void startProcess(@NonNull final Server server) { - if (server.isRunning()) { - return; - } - log.info("Starting Server: {}", server.name); - final ProcessBuilder builder = new ProcessBuilder(CMDLINE); - builder.directory(server.directory); - try { - final Process process = builder.start(); - server.setPid(process.pid()); - writePid(server, process.pid()); - } catch (IOException e) { - log.error("Failed to start server: error={}, name={}", e.getMessage(), server.name); - } - } - - public void stopProcess(@NonNull final Server server) { - if (!server.isRunning()) { - return; - } - new Thread(() -> { - try { - log.info("Stopping Server: {}", server.name); - new ProcessBuilder("kill", "-15", server.getPid() + "").start(); - while (server.getPid() != null && validateProcFile(server, server.getPid())) { - //noinspection BusyWait - Thread.sleep(1000); - } - deletePidFile(server); - } catch (IOException | InterruptedException e) { - log.error("Failed to stop server: error={}, name={}", e.getMessage(), server.name); - } - }).start(); - } - - private void writePid(@NonNull final Server server, final long pid) throws IOException { - final File file = server.pidFile; - try (final FileOutputStream stream = new FileOutputStream(file)) { - stream.write("%d".formatted(pid).getBytes(StandardCharsets.UTF_8)); - } - log.info("PID-file written: file={} = {}", server.pidFile, pid); - applicationEventPublisher.publishEvent(server); - } - -} diff --git a/src/main/java/de/ph87/mc/server/ServerRepository.java b/src/main/java/de/ph87/mc/server/ServerRepository.java deleted file mode 100644 index b297e1c..0000000 --- a/src/main/java/de/ph87/mc/server/ServerRepository.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.ph87.mc.server; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.web.server.ResponseStatusException; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.Properties; - -@Slf4j -@Service -@RequiredArgsConstructor -public class ServerRepository { - - private final ApplicationEventPublisher applicationEventPublisher; - - private final ServerProcessHelper serverProcessHelper; - - private final ServerConfig serverConfig; - - @NonNull - public List findAll() { - final File ROOT = new File(serverConfig.getPath()); - return Arrays.stream(Objects.requireNonNull(ROOT.listFiles())).map(this::_tryLoadingFromDir).filter(Optional::isPresent).map(Optional::get).toList(); - } - - @NonNull - private Optional _tryLoadingFromDir(@NonNull final File dir) { - final File file = new File(dir, "server.properties"); - if (!file.isFile()) { - log.warn("Server directory without server.properties file: {}", dir); - return Optional.empty(); - } - final Properties properties = new Properties(); - try (final FileReader reader = new FileReader(file)) { - properties.load(reader); - final String name = properties.getProperty("level-name"); - final String motd = properties.getProperty("motd"); - final Mode gamemode = Mode.valueOf(properties.getProperty("gamemode").toUpperCase(Locale.ROOT)); - final int serverPort = Integer.parseInt(properties.getProperty("server-port")); - final int rconPort = Integer.parseInt(properties.getProperty("rcon.port")); - final String rconPassword = properties.getProperty("rcon.password"); - final int queryPort = Integer.parseInt(properties.getProperty("query.port")); - final Server server = new Server(dir, name, motd, gamemode, serverPort, rconPort, rconPassword, queryPort); - serverProcessHelper.updatePid(server); - applicationEventPublisher.publishEvent(server); - return Optional.of(server); - } catch (IOException e) { - log.error("Failed to read server.properties: file={}, error={}", file, e.getMessage()); - return Optional.empty(); - } - } - - @NonNull - public Server getByName(@NonNull final String name) { - return findAll().stream().filter(server -> server.name.equals(name)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - -} diff --git a/src/main/java/de/ph87/mc/server/ServerService.java b/src/main/java/de/ph87/mc/server/ServerService.java index 5d415ab..45d4a40 100644 --- a/src/main/java/de/ph87/mc/server/ServerService.java +++ b/src/main/java/de/ph87/mc/server/ServerService.java @@ -1,38 +1,151 @@ package de.ph87.mc.server; +import jakarta.annotation.PostConstruct; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + @Slf4j @Service @RequiredArgsConstructor public class ServerService { - private final ServerRepository repository; + private final ApplicationEventPublisher applicationEventPublisher; - private final ServerProcessHelper serverProcessHelper; + private final ServerConfig serverConfig; - @NonNull - public Server start(@NonNull final String name) { - final Server server = repository.getByName(name); - if (server.isRunning()) { - return server; + private final Object lock = new Object(); + + private List list = new ArrayList<>(); + + @PostConstruct + public void startup() { + final File ROOT = new File(serverConfig.getPath()); + synchronized (lock) { + list = Arrays.stream(Objects.requireNonNull(ROOT.listFiles())).map(this::_tryLoadingFromDir).filter(Optional::isPresent).map(Optional::get).toList(); } - repository.findAll().forEach(serverProcessHelper::stopProcess); - serverProcessHelper.startProcess(server); - return server; } @NonNull - public Server stop(@NonNull final String name) { - final Server server = repository.getByName(name); - if (!server.isRunning()) { - return server; + private Optional _tryLoadingFromDir(@NonNull final File directory) { + try { + final Server server = new Server(directory); + list.stream().filter(server::eq).findFirst().ifPresent(old -> server.process = old.process); + return Optional.of(server); + } catch (NoMinecraftServer e) { + log.warn(e.getMessage()); + } + return Optional.empty(); + } + + @NonNull + public ServerDto start(@NonNull final String name) { + return set(name, this::start); + } + + @NonNull + public ServerDto stop(@NonNull final String name) { + return set(name, this::stop); + } + + private void start(@NonNull final Server server) { + synchronized (server.lock) { + if (server.isRunning()) { + log.warn("Server is already running: name={}", server.properties.name); + return; + } + log.info("Starting server: name={}", server.properties.name); + final ProcessBuilder builder = new ProcessBuilder("java", "-jar", "server.jar"); + builder.directory(server.directory); + try { + server.process = builder.start(); + log.info("Server started: name={}", server.properties.name); + } catch (IOException e) { + log.error("Failed to start Server: name={}, error={}", server.properties.name, e.getMessage()); + } + } + } + + private void stop(@NonNull final Server server) { + synchronized (server.lock) { + if (!server.isRunning()) { + log.warn("Server is not running: name={}", server.properties.name); + return; + } + if (server.shutdown) { + log.warn("Server is already shutting down: name={}", server.properties.name); + return; + } + server.shutdown = true; + log.info("Stopping Server: name={}", server.properties.name); + new Thread(() -> _stop_async(server), "STOP-" + server.properties.name).start(); + } + } + + private void _stop_async(@NonNull final Server server) { + log.debug("Thread spawned: name={}", server.properties.name); + synchronized (server.lock) { + assert server.process != null; + log.info("Stopping server: name={}", server.properties.name); + server.process.destroy(); + try { + server.process.waitFor(); + log.info("Server stopped: name={}", server.properties.name); + } catch (InterruptedException e) { + log.error("Interrupted while waiting for server to stop: name={}", server.properties.name); + } finally { + server.process = null; + server.shutdown = false; + publish(server); + log.debug("Thread terminated: name={}", server.properties.name); + } + } + } + + @NonNull + private ServerDto set(final @NonNull String name, @NonNull final Consumer modifier) { + final Server server = getByName(name); + modifier.accept(server); + return publish(server); + } + + @NonNull + private ServerDto publish(@NonNull final Server server) { + final ServerDto dto = new ServerDto(server); + applicationEventPublisher.publishEvent(dto); + return dto; + } + + @NonNull + private Server getByName(@NonNull final String name) { + synchronized (lock) { + return list.stream().filter(server -> server.properties.name.equals(name)).findFirst().orElseThrow(); + } + } + + @NonNull + public List findAll() { + synchronized (lock) { + return list.stream().map(ServerDto::new).toList(); + } + } + + @NonNull + public File getIconFileByName(@NonNull final String name) { + synchronized (lock) { + return getByName(name).iconFile; } - serverProcessHelper.stopProcess(server); - return server; } } diff --git a/src/main/java/de/ph87/mc/websocket/IWebsocketMessage.java b/src/main/java/de/ph87/mc/websocket/IWebsocketMessage.java index 448bb03..4b0bdc3 100644 --- a/src/main/java/de/ph87/mc/websocket/IWebsocketMessage.java +++ b/src/main/java/de/ph87/mc/websocket/IWebsocketMessage.java @@ -5,7 +5,7 @@ public interface IWebsocketMessage { default String getWebsocketDestination() { String name = getClass().getSimpleName(); if (name.endsWith("Dto")) { - return name.substring(0, name.length() - 4); + return name.substring(0, name.length() - 3); } return name; } diff --git a/src/main/java/de/ph87/mc/websocket/WebSocketService.java b/src/main/java/de/ph87/mc/websocket/WebSocketService.java index 42c9f04..6dd3fcd 100644 --- a/src/main/java/de/ph87/mc/websocket/WebSocketService.java +++ b/src/main/java/de/ph87/mc/websocket/WebSocketService.java @@ -16,7 +16,9 @@ public class WebSocketService { @EventListener public void send(@NonNull final IWebsocketMessage message) { - simpMessageSendingOperations.convertAndSend(message.getWebsocketDestination(), message); + final String destination = message.getWebsocketDestination(); + log.debug("Websocket: destination={}", destination); + simpMessageSendingOperations.convertAndSend(destination, message); } }