Optimzed ShutdownManager for only one clean shutdown

This commit is contained in:
Niklas Aumueller 2026-03-06 17:47:08 +01:00
parent 0804b48d68
commit 6478e046b4
3 changed files with 69 additions and 49 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}