From ac5b74917ffc1f95b6b26205cbd5a36896875752 Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Thu, 7 May 2026 14:14:49 +0200 Subject: [PATCH] Bugfix: GUI-Methoden nutzen ApplicationRunContext statt erneuten Reload Die GUI-Methoden fuer Historie und manuelle Datei-Aktionen haben Konfig und Schema bei jedem Aufruf neu geladen. Bei aktivem Scheduler-Lock schlug der Properties-Lesezugriff in loadAndValidateConfiguration mit einer IOException fehl. Zwei neue Helper bevorzugen den bereits stehenden ApplicationRunContext und fallen nur ohne Kontext auf das alte Migrate-Load-Validate- Schema-Init-Schema zurueck: - resolveJdbcUrlForGui (nur JDBC-URL benoetigt) - resolveStartConfigurationForGui (volle StartConfiguration benoetigt) Refactoring betrifft: - resolveHistoricalDocumentContextForGui - loadHistoryOverviewForGui - loadHistoryDetailsForGui - resetHistoryDocumentStatusForGui - deleteDocumentHistoryForGui - performGuiManualFileRename - performGuiManualFileCopy Der kurzlebige Helper migrateConfigurationIfNeededForGui wurde durch die beiden neuen Helper ueberfluessig und entfernt. Co-Authored-By: Claude Opus 4.7 --- .../umbenenner/bootstrap/BootstrapRunner.java | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java index 1074bbf..d6fcc26 100644 --- a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java @@ -1725,9 +1725,7 @@ public class BootstrapRunner { } try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); + StartConfiguration config = resolveStartConfigurationForGui(configFilePath); ManualFileRenameUseCase useCase = buildProductionManualFileRenameUseCase(config); ManualFileRenameResult result = useCase.rename(request); LOG.info("GUI-Umbenennung abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName()); @@ -1789,9 +1787,7 @@ public class BootstrapRunner { } try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); + StartConfiguration config = resolveStartConfigurationForGui(configFilePath); ManualFileCopyUseCase useCase = buildProductionManualFileCopyUseCase(config); ManualFileCopyResult result = useCase.copy(request); LOG.info("GUI-Dateikopie abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName()); @@ -1851,10 +1847,7 @@ public class BootstrapRunner { } try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); - String jdbcUrl = resolveActiveJdbcUrl(config); + String jdbcUrl = resolveJdbcUrlForGui(configFilePath); DocumentRecordRepository documentRecordRepository = new SqliteDocumentRecordRepositoryAdapter(jdbcUrl); ResolveHistoricalDocumentContextUseCase useCase = @@ -1884,10 +1877,7 @@ public class BootstrapRunner { Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL); Objects.requireNonNull(query, "query must not be null"); try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); - String jdbcUrl = resolveActiveJdbcUrl(config); + String jdbcUrl = resolveJdbcUrlForGui(configFilePath); HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl); DefaultHistoryOverviewUseCase useCase = new DefaultHistoryOverviewUseCase(historyQueryPort); return useCase.loadOverview(query); @@ -1914,10 +1904,7 @@ public class BootstrapRunner { Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL); Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); - String jdbcUrl = resolveActiveJdbcUrl(config); + String jdbcUrl = resolveJdbcUrlForGui(configFilePath); HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl); DefaultHistoryDetailsUseCase useCase = new DefaultHistoryDetailsUseCase(historyQueryPort); return useCase.loadDetails(fingerprint); @@ -1945,10 +1932,7 @@ public class BootstrapRunner { Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); LOG.info("Historien-Status-Reset für Fingerprint: {}", fingerprint.sha256Hex()); try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); - String jdbcUrl = resolveActiveJdbcUrl(config); + String jdbcUrl = resolveJdbcUrlForGui(configFilePath); UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl); DefaultHistoryResetDocumentStatusUseCase useCase = new DefaultHistoryResetDocumentStatusUseCase(unitOfWorkPort); @@ -1977,10 +1961,7 @@ public class BootstrapRunner { Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); LOG.info("Historien-Löschen für Fingerprint: {}", fingerprint.sha256Hex()); try { - migrateConfigurationIfNeededForGui(configFilePath); - StartConfiguration config = loadAndValidateConfiguration(configFilePath); - initializeSchema(config); - String jdbcUrl = resolveActiveJdbcUrl(config); + String jdbcUrl = resolveJdbcUrlForGui(configFilePath); UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl); DefaultDeleteDocumentHistoryUseCase useCase = new DefaultDeleteDocumentHistoryUseCase(unitOfWorkPort); @@ -2158,20 +2139,50 @@ public class BootstrapRunner { } /** - * Führt die Legacy-Migration nur durch, wenn der GUI-Anwendungskontext noch nicht steht. + * Liefert die aktive JDBC-URL für GUI-Aufrufe, die keinen vollständigen + * {@link StartConfiguration} benötigen. *

- * Steht der Kontext bereits, wurde die Migration in - * {@link #initializeApplicationRunContext(Path)} schon einmal abgeschlossen. Ein erneuter - * Lese-/Schreibzugriff auf die Konfigurationsdatei ist nicht nur redundant, sondern - * kollidiert auch mit einem aktiven Scheduler-Lock auf derselben Datei und führt - * dann zu {@code IOException: Datei gesperrt}. + * Steht der GUI-Anwendungskontext bereits, wird die JDBC-URL daraus übernommen – + * die Konfigurationsdatei wird in diesem Fall nicht erneut gelesen und das Schema + * nicht erneut initialisiert. Das vermeidet Kollisionen mit einem aktiven + * Scheduler-Lock auf der Konfigurationsdatei. Steht kein Kontext, läuft die + * gewohnte Sequenz Migration → Laden → Validieren → Schema-Init durch. * - * @param configFilePath Pfad zur Konfigurationsdatei; darf nicht {@code null} sein + * @param configFilePath Pfad zur Konfigurationsdatei; nur relevant ohne aktiven Kontext + * @return aktive JDBC-URL; nie {@code null} */ - private void migrateConfigurationIfNeededForGui(Path configFilePath) { - if (guiApplicationRunContext.isEmpty()) { - migrateConfigurationIfNeeded(configFilePath); + private String resolveJdbcUrlForGui(Path configFilePath) { + Optional ctx = guiApplicationRunContext; + if (ctx.isPresent()) { + return ctx.get().jdbcUrl(); } + migrateConfigurationIfNeeded(configFilePath); + StartConfiguration config = loadAndValidateConfiguration(configFilePath); + initializeSchema(config); + return resolveActiveJdbcUrl(config); + } + + /** + * Liefert die {@link StartConfiguration} für GUI-Aufrufe, die zusätzlich zur + * JDBC-URL die volle Konfiguration benötigen (z. B. manuelle Umbenennung/Kopie). + *

+ * Steht der GUI-Anwendungskontext bereits, wird die Konfiguration daraus übernommen, + * ohne die Konfigurationsdatei erneut zu lesen oder das Schema erneut zu initialisieren. + * Steht kein Kontext, läuft die gewohnte Sequenz Migration → Laden → Validieren → + * Schema-Init durch. + * + * @param configFilePath Pfad zur Konfigurationsdatei; nur relevant ohne aktiven Kontext + * @return validierte Konfiguration; nie {@code null} + */ + private StartConfiguration resolveStartConfigurationForGui(Path configFilePath) { + Optional ctx = guiApplicationRunContext; + if (ctx.isPresent()) { + return ctx.get().startConfiguration(); + } + migrateConfigurationIfNeeded(configFilePath); + StartConfiguration config = loadAndValidateConfiguration(configFilePath); + initializeSchema(config); + return config; } /**