diff --git a/src/main/java/vassistent/App.java b/src/main/java/vassistent/App.java index 09b50df..612622b 100644 --- a/src/main/java/vassistent/App.java +++ b/src/main/java/vassistent/App.java @@ -2,13 +2,10 @@ package vassistent; import vassistent.bootstrap.ApplicationContext; import vassistent.bootstrap.ApplicationInitializer; +import vassistent.bootstrap.ApplicationShutdownManager; import vassistent.controller.AppWindowController; -import vassistent.service.*; -import vassistent.util.Logger; import javax.swing.*; -import java.io.File; - /** * Entry point for the virtual health assistant desktop application. @@ -26,44 +23,13 @@ public class App { ApplicationContext context = ApplicationInitializer.initialize(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - - Logger.info("SYSTEM", "Programm wird beendet"); - - deleteDatabase(); - - Logger.shutdown(); - - })); + ApplicationShutdownManager shutdownManager = + new ApplicationShutdownManager(context); + shutdownManager.registerJvmShutdownHook(); SwingUtilities.invokeLater(() -> { - new AppWindowController(context) + new AppWindowController(context, shutdownManager) .createAndShowWindow(); }); } - - /** - * Deletes the runtime SQLite database file if it exists. - */ - private static void deleteDatabase() { - - try { - - File dbFile = new File("data/health.db"); - - if (dbFile.exists()) { - - boolean deleted = dbFile.delete(); - - if (deleted) { - Logger.info("SYSTEM", "Datenbank gelöscht"); - } else { - Logger.warn("SYSTEM", "Datenbank konnte nicht gelöscht werden"); - } - } - - } catch (Exception e) { - Logger.error("SYSTEM", "DB Löschung fehlgeschlagen", e); - } - } } diff --git a/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java b/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java index 8eef91b..c1fc2b3 100644 --- a/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java +++ b/src/main/java/vassistent/bootstrap/ApplicationShutdownManager.java @@ -4,20 +4,36 @@ import vassistent.service.MqttClientService; import vassistent.service.ProcessManagerService; import vassistent.util.Logger; +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + /** - * Coordinates graceful shutdown of MQTT and managed external processes. + * Coordinates graceful shutdown of MQTT, managed external processes, and runtime artifacts. */ public class ApplicationShutdownManager { + private static final String RUNTIME_DB_FILE = "data/health.db"; + private final ApplicationContext context; + private final AtomicBoolean shutdownStarted = new AtomicBoolean(false); + private final AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false); /** - * Creates a shutdown manager and registers a JVM shutdown hook. + * Creates a shutdown manager for the provided application context. * * @param context initialized application context */ public ApplicationShutdownManager(ApplicationContext context) { this.context = context; + } + + /** + * Registers the JVM shutdown hook once for this manager instance. + */ + public void registerJvmShutdownHook() { + if (!shutdownHookRegistered.compareAndSet(false, true)) { + return; + } Runtime.getRuntime().addShutdownHook( new Thread(this::shutdown) @@ -28,11 +44,18 @@ public class ApplicationShutdownManager { * Executes shutdown steps for managed integrations. */ public void shutdown() { + if (!shutdownStarted.compareAndSet(false, true)) { + return; + } - Logger.info("SHUTDOWN", "Shutdown Sequencing gestartet"); + Logger.info("SHUTDOWN", "Shutdown sequencing started"); disconnectMqtt(); stopExternalProcesses(); + deleteRuntimeDatabase(); + + Logger.info("SHUTDOWN", "Shutdown sequencing finished"); + Logger.shutdown(); } /** @@ -47,9 +70,9 @@ public class ApplicationShutdownManager { mqtt.disconnect(); } - Logger.info("SHUTDOWN", "MQTT getrennt"); + Logger.info("SHUTDOWN", "MQTT disconnected"); } catch (Exception e) { - Logger.error("SHUTDOWN", "MQTT Shutdown Fehler", e); + Logger.error("SHUTDOWN", "MQTT shutdown failed", e); } } @@ -66,10 +89,36 @@ public class ApplicationShutdownManager { pm.shutdown(); } - Logger.info("SHUTDOWN", "Externe Prozesse beendet"); + Logger.info("SHUTDOWN", "External processes stopped"); } catch (Exception e) { - Logger.error("SHUWTDOWN", "Process Shutdown Fehler", e); + Logger.error("SHUTDOWN", "Process shutdown failed", e); + } + } + + /** + * Deletes the runtime SQLite database file when present. + */ + private void deleteRuntimeDatabase() { + + try { + + File dbFile = new File(RUNTIME_DB_FILE); + + if (!dbFile.exists()) { + return; + } + + boolean deleted = dbFile.delete(); + + if (deleted) { + Logger.info("SHUTDOWN", "Runtime database deleted"); + } else { + Logger.warn("SHUTDOWN", "Runtime database could not be deleted"); + } + + } catch (Exception e) { + Logger.error("SHUTDOWN", "Runtime database deletion failed", e); } } } diff --git a/src/main/java/vassistent/controller/AppWindowController.java b/src/main/java/vassistent/controller/AppWindowController.java index 05a20cc..137daa6 100644 --- a/src/main/java/vassistent/controller/AppWindowController.java +++ b/src/main/java/vassistent/controller/AppWindowController.java @@ -12,6 +12,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.Objects; /** * Controls the main window lifecycle and synchronizes UI elements with app state. @@ -27,10 +28,14 @@ public class AppWindowController { * Creates a controller for the main application window. * * @param context initialized application context with required services + * @param shutdownManager shutdown coordinator shared with application bootstrap */ - public AppWindowController(ApplicationContext context) { - this.context = context; - this.shutdownManager = new ApplicationShutdownManager(context); + public AppWindowController( + ApplicationContext context, + ApplicationShutdownManager shutdownManager + ) { + this.context = Objects.requireNonNull(context); + this.shutdownManager = Objects.requireNonNull(shutdownManager); subscribeAppState(); }