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 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 14:14:49 +02:00
parent ef985fb6af
commit ac5b74917f
@@ -1725,9 +1725,7 @@ public class BootstrapRunner {
} }
try { try {
migrateConfigurationIfNeededForGui(configFilePath); StartConfiguration config = resolveStartConfigurationForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
ManualFileRenameUseCase useCase = buildProductionManualFileRenameUseCase(config); ManualFileRenameUseCase useCase = buildProductionManualFileRenameUseCase(config);
ManualFileRenameResult result = useCase.rename(request); ManualFileRenameResult result = useCase.rename(request);
LOG.info("GUI-Umbenennung abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName()); LOG.info("GUI-Umbenennung abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName());
@@ -1789,9 +1787,7 @@ public class BootstrapRunner {
} }
try { try {
migrateConfigurationIfNeededForGui(configFilePath); StartConfiguration config = resolveStartConfigurationForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
ManualFileCopyUseCase useCase = buildProductionManualFileCopyUseCase(config); ManualFileCopyUseCase useCase = buildProductionManualFileCopyUseCase(config);
ManualFileCopyResult result = useCase.copy(request); ManualFileCopyResult result = useCase.copy(request);
LOG.info("GUI-Dateikopie abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName()); LOG.info("GUI-Dateikopie abgeschlossen: Ergebnis={}.", result.getClass().getSimpleName());
@@ -1851,10 +1847,7 @@ public class BootstrapRunner {
} }
try { try {
migrateConfigurationIfNeededForGui(configFilePath); String jdbcUrl = resolveJdbcUrlForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
String jdbcUrl = resolveActiveJdbcUrl(config);
DocumentRecordRepository documentRecordRepository = DocumentRecordRepository documentRecordRepository =
new SqliteDocumentRecordRepositoryAdapter(jdbcUrl); new SqliteDocumentRecordRepositoryAdapter(jdbcUrl);
ResolveHistoricalDocumentContextUseCase useCase = ResolveHistoricalDocumentContextUseCase useCase =
@@ -1884,10 +1877,7 @@ public class BootstrapRunner {
Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL); Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL);
Objects.requireNonNull(query, "query must not be null"); Objects.requireNonNull(query, "query must not be null");
try { try {
migrateConfigurationIfNeededForGui(configFilePath); String jdbcUrl = resolveJdbcUrlForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
String jdbcUrl = resolveActiveJdbcUrl(config);
HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl); HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl);
DefaultHistoryOverviewUseCase useCase = new DefaultHistoryOverviewUseCase(historyQueryPort); DefaultHistoryOverviewUseCase useCase = new DefaultHistoryOverviewUseCase(historyQueryPort);
return useCase.loadOverview(query); return useCase.loadOverview(query);
@@ -1914,10 +1904,7 @@ public class BootstrapRunner {
Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL); Objects.requireNonNull(configFilePath, CONFIG_FILE_NOT_NULL);
Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL);
try { try {
migrateConfigurationIfNeededForGui(configFilePath); String jdbcUrl = resolveJdbcUrlForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
String jdbcUrl = resolveActiveJdbcUrl(config);
HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl); HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl);
DefaultHistoryDetailsUseCase useCase = new DefaultHistoryDetailsUseCase(historyQueryPort); DefaultHistoryDetailsUseCase useCase = new DefaultHistoryDetailsUseCase(historyQueryPort);
return useCase.loadDetails(fingerprint); return useCase.loadDetails(fingerprint);
@@ -1945,10 +1932,7 @@ public class BootstrapRunner {
Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL);
LOG.info("Historien-Status-Reset für Fingerprint: {}", fingerprint.sha256Hex()); LOG.info("Historien-Status-Reset für Fingerprint: {}", fingerprint.sha256Hex());
try { try {
migrateConfigurationIfNeededForGui(configFilePath); String jdbcUrl = resolveJdbcUrlForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
String jdbcUrl = resolveActiveJdbcUrl(config);
UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl); UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl);
DefaultHistoryResetDocumentStatusUseCase useCase = DefaultHistoryResetDocumentStatusUseCase useCase =
new DefaultHistoryResetDocumentStatusUseCase(unitOfWorkPort); new DefaultHistoryResetDocumentStatusUseCase(unitOfWorkPort);
@@ -1977,10 +1961,7 @@ public class BootstrapRunner {
Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL); Objects.requireNonNull(fingerprint, FINGERPRINT_NOT_NULL);
LOG.info("Historien-Löschen für Fingerprint: {}", fingerprint.sha256Hex()); LOG.info("Historien-Löschen für Fingerprint: {}", fingerprint.sha256Hex());
try { try {
migrateConfigurationIfNeededForGui(configFilePath); String jdbcUrl = resolveJdbcUrlForGui(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
String jdbcUrl = resolveActiveJdbcUrl(config);
UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl); UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl);
DefaultDeleteDocumentHistoryUseCase useCase = DefaultDeleteDocumentHistoryUseCase useCase =
new DefaultDeleteDocumentHistoryUseCase(unitOfWorkPort); 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.
* <p> * <p>
* Steht der Kontext bereits, wurde die Migration in * Steht der GUI-Anwendungskontext bereits, wird die JDBC-URL daraus übernommen
* {@link #initializeApplicationRunContext(Path)} schon einmal abgeschlossen. Ein erneuter * die Konfigurationsdatei wird in diesem Fall nicht erneut gelesen und das Schema
* Lese-/Schreibzugriff auf die Konfigurationsdatei ist nicht nur redundant, sondern * nicht erneut initialisiert. Das vermeidet Kollisionen mit einem aktiven
* kollidiert auch mit einem aktiven Scheduler-Lock auf derselben Datei und führt * Scheduler-Lock auf der Konfigurationsdatei. Steht kein Kontext, läuft die
* dann zu {@code IOException: Datei gesperrt}. * 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) { private String resolveJdbcUrlForGui(Path configFilePath) {
if (guiApplicationRunContext.isEmpty()) { Optional<ApplicationRunContext> ctx = guiApplicationRunContext;
migrateConfigurationIfNeeded(configFilePath); 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).
* <p>
* 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<ApplicationRunContext> ctx = guiApplicationRunContext;
if (ctx.isPresent()) {
return ctx.get().startConfiguration();
}
migrateConfigurationIfNeeded(configFilePath);
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
initializeSchema(config);
return config;
} }
/** /**