Erwirb Config-Lock vor manuellem Verarbeitungslauf in der GUI
GuiBatchRunCoordinator erwirbt vor jedem Verarbeitungslauf (regulär und Mini-Lauf) einen exklusiven OS-Lock auf die Konfigurationsdatei via ConfigurationFileLockPort. Bei ConfigurationFileLockException wird ein deutscher Fehlerdialog angezeigt und der Lauf abgebrochen. In finally wird der Lock immer freigegeben. GuiStartupContext erhält das 27. Feld configurationFileLockPort; BootstrapRunner befüllt es mit einem FileChannelConfigurationAccessAdapter wenn eine Konfigurationsdatei geladen wurde. GuiBatchRunTab und GuiConfigurationEditorWorkspace reichen den Port durch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -553,7 +553,8 @@ public final class GuiConfigurationEditorWorkspace {
|
||||
() -> this.manualFileCopyPort,
|
||||
() -> this.historicalDocumentContextPort,
|
||||
this::editorSourceFolder,
|
||||
this::editorTargetFolder);
|
||||
this::editorTargetFolder,
|
||||
effectiveContext.configurationFileLockPort());
|
||||
|
||||
this.historyTab = new de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryTab(
|
||||
effectiveContext.historyOverviewPort(),
|
||||
|
||||
+83
-2
@@ -19,6 +19,7 @@ import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorSt
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorStateFactory;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerControlUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.AiModelCatalogPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor;
|
||||
import de.gecheckt.pdf.umbenenner.application.validation.editor.ApiKeyResolutionPort;
|
||||
@@ -62,6 +63,13 @@ import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
|
||||
* was successfully wired at startup. An empty value means scheduler control is not
|
||||
* available in this startup context (e.g., no valid configuration was loaded at startup).
|
||||
* <p>
|
||||
* The optional {@code configurationFileLockPort} is present when the GUI can acquire an
|
||||
* OS-level exclusive lock on the configuration file before a manual batch run. When present,
|
||||
* it is acquired by the {@link de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunCoordinator}
|
||||
* on the worker thread before each run and released in a finally block. An empty value means
|
||||
* no locking is performed (e.g., no valid configuration was loaded at startup, or locking is
|
||||
* not required in this context).
|
||||
* <p>
|
||||
* All ports and services are supplied by Bootstrap so that the GUI adapter does not need to
|
||||
* know about provider-specific HTTP details or adapter wiring.
|
||||
*/
|
||||
@@ -91,7 +99,8 @@ public record GuiStartupContext(
|
||||
GuiPromptEditorPortFactory promptEditorPortFactory,
|
||||
GuiCreateNewDatabasePort createNewDatabasePort,
|
||||
Optional<String> applicationContextError,
|
||||
Optional<SchedulerControlUseCase> schedulerControlUseCase) {
|
||||
Optional<SchedulerControlUseCase> schedulerControlUseCase,
|
||||
Optional<ConfigurationFileLockPort> configurationFileLockPort) {
|
||||
|
||||
/**
|
||||
* Creates a fully wired startup context.
|
||||
@@ -175,6 +184,7 @@ public record GuiStartupContext(
|
||||
createNewDatabasePort = Objects.requireNonNull(createNewDatabasePort,
|
||||
"createNewDatabasePort must not be null");
|
||||
schedulerControlUseCase = schedulerControlUseCase == null ? Optional.empty() : schedulerControlUseCase;
|
||||
configurationFileLockPort = configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +252,78 @@ public record GuiStartupContext(
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort,
|
||||
historyOverviewPort, historyDetailsPort, historyResetDocumentStatusPort,
|
||||
deleteDocumentHistoryPort, promptEditorPortFactory, createNewDatabasePort,
|
||||
applicationContextError, Optional.empty());
|
||||
applicationContextError, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward-compatible constructor that fills {@code configurationFileLockPort} with
|
||||
* {@link Optional#empty()}.
|
||||
* <p>
|
||||
* Preserves existing callers that were written before the configuration file lock port
|
||||
* was added.
|
||||
*
|
||||
* @param initialState initial editor state; must not be {@code null}
|
||||
* @param startupNotice optional startup notice; {@code null} becomes empty
|
||||
* @param configurationFileLoader file-loading callback; must not be {@code null}
|
||||
* @param configurationFileWriter file-writing callback; must not be {@code null}
|
||||
* @param modelCatalogPort port for retrieving AI model lists; must not be {@code null}
|
||||
* @param apiKeyResolutionPort port for resolving API key provenance; must not be {@code null}
|
||||
* @param providerTechnicalTestService service for provider-specific technical checks; must not be {@code null}
|
||||
* @param pathCheckPort port for filesystem path accessibility checks; must not be {@code null}
|
||||
* @param technicalTestOrchestrator orchestrator for the full technical test run; must not be {@code null}
|
||||
* @param correctionExecutionService service for executing confirmed corrective actions; must not be {@code null}
|
||||
* @param batchRunLauncher bridge that executes a regular batch run; must not be {@code null}
|
||||
* @param miniRunLauncher bridge that executes a targeted mini-run; must not be {@code null}
|
||||
* @param resetDocumentStatusPort bridge that resets document status; must not be {@code null}
|
||||
* @param manualFileRenamePort bridge that renames a target file; must not be {@code null}
|
||||
* @param manualFileCopyPort bridge that copies a source file; must not be {@code null}
|
||||
* @param historicalDocumentContextPort bridge for historical processing context; must not be {@code null}
|
||||
* @param applicationVersion resolved application version string; {@code null} defaults to {@code "dev"}
|
||||
* @param promptEditorPort bridge zum Prompt-Editor-Use-Case; must not be {@code null}
|
||||
* @param historyOverviewPort bridge for history overview; must not be {@code null}
|
||||
* @param historyDetailsPort bridge for history details; must not be {@code null}
|
||||
* @param historyResetDocumentStatusPort bridge for history reset; must not be {@code null}
|
||||
* @param deleteDocumentHistoryPort bridge for history deletion; must not be {@code null}
|
||||
* @param promptEditorPortFactory factory for prompt editor ports; must not be {@code null}
|
||||
* @param createNewDatabasePort bridge for new database creation; must not be {@code null}
|
||||
* @param applicationContextError optional error from context init; {@code null} becomes empty
|
||||
* @param schedulerControlUseCase optional scheduler control use case; {@code null} becomes empty
|
||||
*/
|
||||
public GuiStartupContext(
|
||||
GuiConfigurationEditorState initialState,
|
||||
Optional<String> startupNotice,
|
||||
GuiConfigurationFileLoader configurationFileLoader,
|
||||
GuiConfigurationFileWriter configurationFileWriter,
|
||||
AiModelCatalogPort modelCatalogPort,
|
||||
ApiKeyResolutionPort apiKeyResolutionPort,
|
||||
ProviderTechnicalTestService providerTechnicalTestService,
|
||||
PathCheckPort pathCheckPort,
|
||||
TechnicalTestOrchestrator technicalTestOrchestrator,
|
||||
CorrectionExecutionService correctionExecutionService,
|
||||
GuiBatchRunLauncher batchRunLauncher,
|
||||
GuiMiniRunLauncher miniRunLauncher,
|
||||
GuiResetDocumentStatusPort resetDocumentStatusPort,
|
||||
GuiManualFileRenamePort manualFileRenamePort,
|
||||
GuiManualFileCopyPort manualFileCopyPort,
|
||||
GuiHistoricalDocumentContextPort historicalDocumentContextPort,
|
||||
String applicationVersion,
|
||||
GuiPromptEditorPort promptEditorPort,
|
||||
GuiHistoryOverviewPort historyOverviewPort,
|
||||
GuiHistoryDetailsPort historyDetailsPort,
|
||||
GuiHistoryResetDocumentStatusPort historyResetDocumentStatusPort,
|
||||
GuiDeleteDocumentHistoryPort deleteDocumentHistoryPort,
|
||||
GuiPromptEditorPortFactory promptEditorPortFactory,
|
||||
GuiCreateNewDatabasePort createNewDatabasePort,
|
||||
Optional<String> applicationContextError,
|
||||
Optional<SchedulerControlUseCase> schedulerControlUseCase) {
|
||||
this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
|
||||
modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
|
||||
technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
|
||||
miniRunLauncher, resetDocumentStatusPort, manualFileRenamePort, manualFileCopyPort,
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort,
|
||||
historyOverviewPort, historyDetailsPort, historyResetDocumentStatusPort,
|
||||
deleteDocumentHistoryPort, promptEditorPortFactory, createNewDatabasePort,
|
||||
applicationContextError, schedulerControlUseCase, Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+125
-10
@@ -21,9 +21,12 @@ import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionStatus;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.HistoricalDocumentContext;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.RunSummary;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockException;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.RunId;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
|
||||
/**
|
||||
* Coordinates a single batch run (regular or targeted mini-run) triggered from the
|
||||
@@ -115,6 +118,7 @@ public final class GuiBatchRunCoordinator {
|
||||
private final Consumer<Runnable> fxDispatcher;
|
||||
private final Listener listener;
|
||||
private final GuiHistoricalDocumentContextPort historicalDocumentContextPort;
|
||||
private final Optional<ConfigurationFileLockPort> configurationFileLockPort;
|
||||
private final AtomicReference<Thread> activeWorker = new AtomicReference<>();
|
||||
private final AtomicBoolean cancellationRequested = new AtomicBoolean();
|
||||
|
||||
@@ -176,6 +180,33 @@ public final class GuiBatchRunCoordinator {
|
||||
defaultThreadFactory(), defaultFxDispatcher(), listener, historicalDocumentContextPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the coordinator with all ports and the configuration file lock port, using
|
||||
* the default worker-thread factory and JavaFX Application Thread dispatcher.
|
||||
* <p>
|
||||
* This constructor is intended for production wiring in {@code GuiBatchRunTab} where
|
||||
* the lock port is supplied by Bootstrap.
|
||||
*
|
||||
* @param launcher bridge to Bootstrap for regular batch runs; must not be null
|
||||
* @param miniRunLauncher bridge to Bootstrap for targeted mini-runs; must not be null
|
||||
* @param resetPort bridge to Bootstrap for status-reset-only operations; must
|
||||
* not be null
|
||||
* @param listener GUI listener invoked on the FX thread; must not be null
|
||||
* @param historicalDocumentContextPort port for resolving historical context; must not be null
|
||||
* @param configurationFileLockPort optional OS-lock on the configuration file; when present,
|
||||
* acquired before each run; {@code null} is treated as empty
|
||||
*/
|
||||
public GuiBatchRunCoordinator(GuiBatchRunLauncher launcher,
|
||||
GuiMiniRunLauncher miniRunLauncher,
|
||||
GuiResetDocumentStatusPort resetPort,
|
||||
Listener listener,
|
||||
GuiHistoricalDocumentContextPort historicalDocumentContextPort,
|
||||
Optional<ConfigurationFileLockPort> configurationFileLockPort) {
|
||||
this(launcher, miniRunLauncher, resetPort,
|
||||
defaultThreadFactory(), defaultFxDispatcher(), listener,
|
||||
historicalDocumentContextPort, configurationFileLockPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the coordinator with custom hooks for the worker-thread factory and the
|
||||
* UI-thread dispatcher.
|
||||
@@ -205,8 +236,8 @@ public final class GuiBatchRunCoordinator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the coordinator with all ports, custom thread factory, FX dispatcher and
|
||||
* historical file name port.
|
||||
* Creates the coordinator with all ports, custom thread factory, FX dispatcher,
|
||||
* historical file name port, and an optional configuration file lock port.
|
||||
* <p>
|
||||
* This is the canonical constructor. All other constructors delegate here.
|
||||
*
|
||||
@@ -221,6 +252,47 @@ public final class GuiBatchRunCoordinator {
|
||||
* @param listener GUI listener; must not be null
|
||||
* @param historicalDocumentContextPort port for resolving the historical AI-proposed filename for
|
||||
* skipped documents; must not be null
|
||||
* @param configurationFileLockPort optional OS-lock on the configuration file; when present,
|
||||
* acquired before each run and released in a finally block;
|
||||
* {@code null} is treated as empty
|
||||
*/
|
||||
public GuiBatchRunCoordinator(GuiBatchRunLauncher launcher,
|
||||
GuiMiniRunLauncher miniRunLauncher,
|
||||
GuiResetDocumentStatusPort resetPort,
|
||||
Function<Runnable, Thread> threadFactory,
|
||||
Consumer<Runnable> fxDispatcher,
|
||||
Listener listener,
|
||||
GuiHistoricalDocumentContextPort historicalDocumentContextPort,
|
||||
Optional<ConfigurationFileLockPort> configurationFileLockPort) {
|
||||
this.launcher = Objects.requireNonNull(launcher, "launcher must not be null");
|
||||
this.miniRunLauncher = Objects.requireNonNull(miniRunLauncher, "miniRunLauncher must not be null");
|
||||
this.resetPort = Objects.requireNonNull(resetPort, "resetPort must not be null");
|
||||
this.threadFactory = Objects.requireNonNull(threadFactory, "threadFactory must not be null");
|
||||
this.fxDispatcher = Objects.requireNonNull(fxDispatcher, "fxDispatcher must not be null");
|
||||
this.listener = Objects.requireNonNull(listener, "listener must not be null");
|
||||
this.historicalDocumentContextPort = Objects.requireNonNull(
|
||||
historicalDocumentContextPort, "historicalDocumentContextPort must not be null");
|
||||
this.configurationFileLockPort =
|
||||
configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward-compatible constructor that omits the configuration file lock port.
|
||||
* <p>
|
||||
* Preserves existing callers that were written before the lock port was added.
|
||||
* Delegates to the canonical constructor with {@code configurationFileLockPort} empty.
|
||||
*
|
||||
* @param launcher bridge to Bootstrap for regular batch runs; must not be null
|
||||
* @param miniRunLauncher bridge to Bootstrap for targeted mini-runs; must not be null
|
||||
* @param resetPort bridge to Bootstrap for status-reset-only operations; must
|
||||
* not be null
|
||||
* @param threadFactory factory returning a ready-to-start worker thread; must not
|
||||
* be null
|
||||
* @param fxDispatcher dispatcher that schedules a runnable on the JavaFX Application
|
||||
* Thread; must not be null
|
||||
* @param listener GUI listener; must not be null
|
||||
* @param historicalDocumentContextPort port for resolving the historical AI-proposed filename for
|
||||
* skipped documents; must not be null
|
||||
*/
|
||||
public GuiBatchRunCoordinator(GuiBatchRunLauncher launcher,
|
||||
GuiMiniRunLauncher miniRunLauncher,
|
||||
@@ -229,14 +301,8 @@ public final class GuiBatchRunCoordinator {
|
||||
Consumer<Runnable> fxDispatcher,
|
||||
Listener listener,
|
||||
GuiHistoricalDocumentContextPort historicalDocumentContextPort) {
|
||||
this.launcher = Objects.requireNonNull(launcher, "launcher must not be null");
|
||||
this.miniRunLauncher = Objects.requireNonNull(miniRunLauncher, "miniRunLauncher must not be null");
|
||||
this.resetPort = Objects.requireNonNull(resetPort, "resetPort must not be null");
|
||||
this.threadFactory = Objects.requireNonNull(threadFactory, "threadFactory must not be null");
|
||||
this.fxDispatcher = Objects.requireNonNull(fxDispatcher, "fxDispatcher must not be null");
|
||||
this.listener = Objects.requireNonNull(listener, "listener must not be null");
|
||||
this.historicalDocumentContextPort = Objects.requireNonNull(
|
||||
historicalDocumentContextPort, "historicalDocumentContextPort must not be null");
|
||||
this(launcher, miniRunLauncher, resetPort, threadFactory, fxDispatcher, listener,
|
||||
historicalDocumentContextPort, Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,6 +503,21 @@ public final class GuiBatchRunCoordinator {
|
||||
LOG.info("GUI-Verarbeitungslauf: Worker-Thread gestartet für Konfiguration {}.",
|
||||
configFilePath);
|
||||
observerSummary.set(null);
|
||||
|
||||
if (configurationFileLockPort.isPresent()) {
|
||||
try {
|
||||
configurationFileLockPort.get().acquireLock();
|
||||
} catch (ConfigurationFileLockException e) {
|
||||
LOG.warn("GUI-Verarbeitungslauf: Konfigurationsdatei gesperrt – Lauf abgebrochen: {}",
|
||||
e.getMessage());
|
||||
fxDispatcher.accept(() -> showLockErrorAlert());
|
||||
finishRun(GuiBatchRunLaunchOutcome.rejected(
|
||||
"Konfigurationsdatei gesperrt – Lauf wurde abgebrochen."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
BatchRunProgressObserver observer = buildDispatchingObserver(configFilePath);
|
||||
BatchRunCancellationToken token = cancellationRequested::get;
|
||||
GuiBatchRunLaunchOutcome outcome;
|
||||
@@ -454,12 +535,30 @@ public final class GuiBatchRunCoordinator {
|
||||
+ (e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage()));
|
||||
}
|
||||
finishRun(outcome);
|
||||
} finally {
|
||||
configurationFileLockPort.ifPresent(ConfigurationFileLockPort::releaseLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeMiniRun(Path configFilePath, Set<DocumentFingerprint> fingerprintFilter) {
|
||||
LOG.info("GUI-Mini-Verarbeitungslauf: Worker-Thread gestartet für {} Dokument(e), "
|
||||
+ "Konfiguration {}.", fingerprintFilter.size(), configFilePath);
|
||||
observerSummary.set(null);
|
||||
|
||||
if (configurationFileLockPort.isPresent()) {
|
||||
try {
|
||||
configurationFileLockPort.get().acquireLock();
|
||||
} catch (ConfigurationFileLockException e) {
|
||||
LOG.warn("GUI-Mini-Verarbeitungslauf: Konfigurationsdatei gesperrt – Lauf abgebrochen: {}",
|
||||
e.getMessage());
|
||||
fxDispatcher.accept(() -> showLockErrorAlert());
|
||||
finishRun(GuiBatchRunLaunchOutcome.rejected(
|
||||
"Konfigurationsdatei gesperrt – Mini-Lauf wurde abgebrochen."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
BatchRunProgressObserver observer = buildDispatchingObserver(configFilePath);
|
||||
BatchRunCancellationToken token = cancellationRequested::get;
|
||||
GuiBatchRunLaunchOutcome outcome;
|
||||
@@ -477,6 +576,9 @@ public final class GuiBatchRunCoordinator {
|
||||
+ (e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage()));
|
||||
}
|
||||
finishRun(outcome);
|
||||
} finally {
|
||||
configurationFileLockPort.ifPresent(ConfigurationFileLockPort::releaseLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeReset(Path configFilePath, Set<DocumentFingerprint> fingerprints) {
|
||||
@@ -611,6 +713,19 @@ public final class GuiBatchRunCoordinator {
|
||||
historicalContext);
|
||||
}
|
||||
|
||||
private static void showLockErrorAlert() {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Verarbeitungslauf nicht möglich");
|
||||
alert.setHeaderText("Konfigurationsdatei gesperrt");
|
||||
alert.setContentText(
|
||||
"Der Verarbeitungslauf konnte nicht gestartet werden, da die "
|
||||
+ "Konfigurationsdatei nicht gesperrt werden konnte.\n\n"
|
||||
+ "Mögliche Ursache: Der automatische Scheduler ist aktiv oder "
|
||||
+ "ein anderer Prozess hält die Datei belegt.\n\n"
|
||||
+ "Bitte stoppen Sie den Scheduler und versuchen Sie es erneut.");
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
private static GuiHistoricalDocumentContextPort noOpHistoricalDocumentContextPort() {
|
||||
return (configPath, fingerprint) -> Optional.empty();
|
||||
}
|
||||
|
||||
+57
-3
@@ -41,6 +41,7 @@ import de.gecheckt.pdf.umbenenner.application.port.in.ManualFileRenameSourceFile
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.ManualFileRenameSuccess;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.RunSummary;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.RunId;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts;
|
||||
@@ -231,7 +232,8 @@ public final class GuiBatchRunTab {
|
||||
|
||||
/**
|
||||
* Erstellt den Verarbeitungslauf-Tab mit allen Verarbeitungs-, Mini-Lauf- und
|
||||
* Rücksetz-Fähigkeiten sowie dem Dateiname-Editor und der PDF-Vorschau.
|
||||
* Rücksetz-Fähigkeiten sowie dem Dateiname-Editor, der PDF-Vorschau und einem
|
||||
* optionalen OS-Lock auf die Konfigurationsdatei.
|
||||
*
|
||||
* @param launcherSupplier Supplier für den aktiven Batch-Lauf-Launcher;
|
||||
* darf nicht null sein
|
||||
@@ -256,6 +258,9 @@ public final class GuiBatchRunTab {
|
||||
* darf leeres Optional zurückliefern
|
||||
* @param targetFolderSupplier Supplier für den konfigurierten Zielordner als
|
||||
* Pfad-String; darf leeres Optional zurückliefern
|
||||
* @param configurationFileLockPort optionaler OS-Lock auf die Konfigurationsdatei;
|
||||
* wird vor jedem Lauf erworben und danach freigegeben;
|
||||
* {@code null} wird als leer behandelt
|
||||
*/
|
||||
public GuiBatchRunTab(Supplier<GuiBatchRunLauncher> launcherSupplier,
|
||||
Supplier<GuiMiniRunLauncher> miniRunLauncherSupplier,
|
||||
@@ -267,7 +272,8 @@ public final class GuiBatchRunTab {
|
||||
Supplier<GuiManualFileCopyPort> manualFileCopyPortSupplier,
|
||||
Supplier<GuiHistoricalDocumentContextPort> historicalDocumentContextPortSupplier,
|
||||
Supplier<Optional<Path>> sourceFolderSupplier,
|
||||
Supplier<Optional<String>> targetFolderSupplier) {
|
||||
Supplier<Optional<String>> targetFolderSupplier,
|
||||
Optional<ConfigurationFileLockPort> configurationFileLockPort) {
|
||||
Objects.requireNonNull(launcherSupplier, "launcherSupplier must not be null");
|
||||
Objects.requireNonNull(miniRunLauncherSupplier, "miniRunLauncherSupplier must not be null");
|
||||
Objects.requireNonNull(resetPortSupplier, "resetPortSupplier must not be null");
|
||||
@@ -286,6 +292,8 @@ public final class GuiBatchRunTab {
|
||||
this.targetFolderSupplier = Objects.requireNonNull(
|
||||
targetFolderSupplier, "targetFolderSupplier must not be null");
|
||||
|
||||
Optional<ConfigurationFileLockPort> effectiveLockPort =
|
||||
configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort;
|
||||
this.coordinator = new GuiBatchRunCoordinator(
|
||||
(configPath, observer, token) ->
|
||||
launcherSupplier.get().launch(configPath, observer, token),
|
||||
@@ -294,7 +302,8 @@ public final class GuiBatchRunTab {
|
||||
(configPath, fingerprints) ->
|
||||
resetPortSupplier.get().reset(configPath, fingerprints),
|
||||
new CoordinatorListener(),
|
||||
historicalDocumentContextPortSupplier.get());
|
||||
historicalDocumentContextPortSupplier.get(),
|
||||
effectiveLockPort);
|
||||
|
||||
this.tab.setClosable(false);
|
||||
this.tab.setContent(buildContent());
|
||||
@@ -313,6 +322,51 @@ public final class GuiBatchRunTab {
|
||||
updateButtonStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rückwärtskompatible Variante ohne OS-Lock auf die Konfigurationsdatei.
|
||||
* <p>
|
||||
* Alle bestehenden Aufrufer, die vor der Lock-Port-Erweiterung erstellt wurden,
|
||||
* nutzen diesen Konstruktor. Er delegiert an den kanonischen Konstruktor mit
|
||||
* {@code configurationFileLockPort = Optional.empty()}.
|
||||
*
|
||||
* @param launcherSupplier Supplier für den aktiven Batch-Lauf-Launcher;
|
||||
* darf nicht null sein
|
||||
* @param miniRunLauncherSupplier Supplier für den Mini-Lauf-Launcher;
|
||||
* darf nicht null sein
|
||||
* @param resetPortSupplier Supplier für den Rücksetz-Port;
|
||||
* darf nicht null sein
|
||||
* @param configPathSupplier Supplier für den letzten gespeicherten
|
||||
* Konfigurationspfad; darf null zurückliefern
|
||||
* @param savedConfigurationReadyCheck Prüfung vor jedem Startversuch; darf nicht
|
||||
* null sein
|
||||
* @param onRunStateChanged Callback wenn das Lauf-Flag kippt; darf nicht
|
||||
* null sein
|
||||
* @param manualFileRenamePortSupplier Supplier für den manuellen Umbenennungs-Port;
|
||||
* darf nicht null sein
|
||||
* @param manualFileCopyPortSupplier Supplier für den manuellen Kopier-Port;
|
||||
* darf nicht null sein
|
||||
* @param historicalDocumentContextPortSupplier Supplier für den historischen Kontext-Port;
|
||||
* darf nicht null sein
|
||||
* @param sourceFolderSupplier Supplier für den konfigurierten Quellordner
|
||||
* @param targetFolderSupplier Supplier für den konfigurierten Zielordner
|
||||
*/
|
||||
public GuiBatchRunTab(Supplier<GuiBatchRunLauncher> launcherSupplier,
|
||||
Supplier<GuiMiniRunLauncher> miniRunLauncherSupplier,
|
||||
Supplier<GuiResetDocumentStatusPort> resetPortSupplier,
|
||||
Supplier<Path> configPathSupplier,
|
||||
BooleanSupplier savedConfigurationReadyCheck,
|
||||
Runnable onRunStateChanged,
|
||||
Supplier<GuiManualFileRenamePort> manualFileRenamePortSupplier,
|
||||
Supplier<GuiManualFileCopyPort> manualFileCopyPortSupplier,
|
||||
Supplier<GuiHistoricalDocumentContextPort> historicalDocumentContextPortSupplier,
|
||||
Supplier<Optional<Path>> sourceFolderSupplier,
|
||||
Supplier<Optional<String>> targetFolderSupplier) {
|
||||
this(launcherSupplier, miniRunLauncherSupplier, resetPortSupplier, configPathSupplier,
|
||||
savedConfigurationReadyCheck, onRunStateChanged, manualFileRenamePortSupplier,
|
||||
manualFileCopyPortSupplier, historicalDocumentContextPortSupplier,
|
||||
sourceFolderSupplier, targetFolderSupplier, Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rückwärtskompatible Variante für Aufrufer ohne Mini-Lauf- oder Rücksetz-Fähigkeiten.
|
||||
*
|
||||
|
||||
+4
-1
@@ -78,6 +78,7 @@ import de.gecheckt.pdf.umbenenner.application.port.in.ResolveHistoricalDocumentC
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ActiveDatabaseContextPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.AiContentSensitivity;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.BatchRunTrigger;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.BatchRunTriggerResult;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.RunSummary;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.AiInvocationPort;
|
||||
@@ -974,8 +975,10 @@ public class BootstrapRunner {
|
||||
GuiPromptEditorPort promptEditorPort = buildGuiPromptEditorPort(
|
||||
loadedState.values().promptTemplateFile());
|
||||
Optional<String> contextError = initializeApplicationRunContext(configPath);
|
||||
Optional<ConfigurationFileLockPort> guiRunLockPort = Optional.empty();
|
||||
if (contextError.isEmpty()) {
|
||||
tryInitializeScheduler(configPath);
|
||||
guiRunLockPort = Optional.of(new FileChannelConfigurationAccessAdapter(configPath));
|
||||
}
|
||||
Optional<SchedulerControlUseCase> schedulerUseCase =
|
||||
guiSchedulerUseCase.map(s -> (SchedulerControlUseCase) s);
|
||||
@@ -986,7 +989,7 @@ public class BootstrapRunner {
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort,
|
||||
historyOverviewPort, historyDetailsPort, historyResetPort, deleteHistoryPort,
|
||||
this::buildGuiPromptEditorPort, createNewDatabasePort, contextError,
|
||||
schedulerUseCase);
|
||||
schedulerUseCase, guiRunLockPort);
|
||||
} catch (GuiConfigurationLoadException e) {
|
||||
LOG.error("GUI startup: configuration could not be loaded, starting without it: {}",
|
||||
e.getMessage(), e);
|
||||
|
||||
Reference in New Issue
Block a user