Scheduler: Autostart-Feature entfernen
Der Scheduler startet niemals automatisch beim Programmstart. Der Nutzer startet ihn ausschliesslich bewusst ueber den Start-Button im Scheduler-Tab. scheduler.enabled wird nicht mehr gelesen oder geschrieben; das Property ist obsolet. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+1
-14
@@ -25,7 +25,6 @@ public interface SchedulerControlUseCase {
|
||||
* folgende Sequenz gestartet:
|
||||
* <ol>
|
||||
* <li>Zustand auf {@code STARTING} setzen</li>
|
||||
* <li>{@code scheduler.enabled=true} persistieren</li>
|
||||
* <li>Exklusiven OS-Lock auf Konfigurationsdatei erwerben</li>
|
||||
* <li>Scheduler-Adapter starten (erster Tick sofort)</li>
|
||||
* <li>Zustand auf {@code RUNNING_IDLE} setzen</li>
|
||||
@@ -45,8 +44,7 @@ public interface SchedulerControlUseCase {
|
||||
* Ist der Scheduler bereits gestoppt, hat dieser Aufruf keine Wirkung.
|
||||
* Läuft gerade ein Tick, wechselt der Zustand zu
|
||||
* {@code STOPPING_BATCH_ACTIVE}; der laufende Batch wird regulär
|
||||
* zu Ende geführt. Danach werden {@code scheduler.enabled=false}
|
||||
* persistiert und der OS-Lock freigegeben.
|
||||
* zu Ende geführt. Danach wird der OS-Lock freigegeben.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
@@ -85,15 +83,4 @@ public interface SchedulerControlUseCase {
|
||||
* @param seconds Intervall in Sekunden; sollte ≥ 30 sein
|
||||
*/
|
||||
void saveIntervalSeconds(int seconds);
|
||||
|
||||
/**
|
||||
* Deaktiviert den Autostart durch Persistieren von {@code scheduler.enabled=false}.
|
||||
* <p>
|
||||
* Wird vom Scheduler-Tab aufgerufen, wenn der Benutzer den fehlgeschlagenen
|
||||
* Autostart dauerhaft deaktivieren möchte. Sicher aufzurufen wenn der Scheduler
|
||||
* gestoppt ist.
|
||||
* <p>
|
||||
* Muss auf einem Hintergrund-Thread aufgerufen werden.
|
||||
*/
|
||||
void disableAutostart();
|
||||
}
|
||||
|
||||
+2
-3
@@ -3,9 +3,8 @@ package de.gecheckt.pdf.umbenenner.application.port.in;
|
||||
/**
|
||||
* Wird geworfen, wenn der Start des automatischen Schedulers fehlschlägt.
|
||||
* <p>
|
||||
* Mögliche Ursachen sind: Fehler beim Erwerb des Konfigurations-Datei-Locks,
|
||||
* Fehler beim Persistieren von {@code scheduler.enabled=true} oder
|
||||
* technische Fehler beim Starten des Scheduler-Adapters.
|
||||
* Mögliche Ursachen sind: Fehler beim Erwerb des Konfigurations-Datei-Locks
|
||||
* oder technische Fehler beim Starten des Scheduler-Adapters.
|
||||
* <p>
|
||||
* Diese Ausnahme ist ungeprüft (extends {@link RuntimeException}) und
|
||||
* wird in der Callchain bis zum GUI-Layer weitergeleitet, der eine
|
||||
|
||||
+4
-8
@@ -26,16 +26,13 @@ import de.gecheckt.pdf.umbenenner.application.port.out.RunSummary;
|
||||
* @param lastError letzte aufgetretene deutsche Fehlermeldung;
|
||||
* wird bei erfolgreichem Lauf gelöscht,
|
||||
* bei {@code SkippedBusy} unverändert gelassen
|
||||
* @param autostartFailed {@code true}, wenn ein konfigurierter Autostart
|
||||
* beim Programmstart fehlgeschlagen ist
|
||||
*/
|
||||
public record SchedulerStatus(
|
||||
SchedulerState state,
|
||||
Optional<Instant> lastRunEndedAt,
|
||||
Optional<RunSummary> lastRunSummary,
|
||||
Optional<Instant> nextTickAt,
|
||||
Optional<String> lastError,
|
||||
boolean autostartFailed
|
||||
Optional<String> lastError
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -62,8 +59,8 @@ public record SchedulerStatus(
|
||||
/**
|
||||
* Erzeugt den initialen Scheduler-Status beim Programmstart.
|
||||
* <p>
|
||||
* Zustand ist {@link SchedulerState#STOPPED}, alle optionalen Felder
|
||||
* sind leer und {@code autostartFailed} ist {@code false}.
|
||||
* Zustand ist {@link SchedulerState#STOPPED} und alle optionalen Felder
|
||||
* sind leer.
|
||||
*
|
||||
* @return initialer Scheduler-Status
|
||||
*/
|
||||
@@ -73,8 +70,7 @@ public record SchedulerStatus(
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
false
|
||||
Optional.empty()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-14
@@ -3,23 +3,16 @@ package de.gecheckt.pdf.umbenenner.application.port.out;
|
||||
/**
|
||||
* Persistierte Scheduler-Einstellungen aus der {@code .properties}-Datei.
|
||||
* <p>
|
||||
* Dieses DTO repräsentiert die beiden Scheduler-Properties
|
||||
* {@code scheduler.enabled} und {@code scheduler.interval.seconds},
|
||||
* wie sie aus der Konfigurationsdatei gelesen werden. Es wird von
|
||||
* {@link SchedulerSettingsPort#loadSettings()} zurückgegeben und
|
||||
* dient als Eingabe für die Autostart-Entscheidung und die
|
||||
* Scheduler-Tab-Anzeige.
|
||||
* Dieses DTO repräsentiert die Scheduler-Property
|
||||
* {@code scheduler.interval.seconds}, wie sie aus der Konfigurationsdatei
|
||||
* gelesen wird. Es wird von {@link SchedulerSettingsPort#loadSettings()}
|
||||
* zurückgegeben und dient als Eingabe für die Scheduler-Tab-Anzeige.
|
||||
*
|
||||
* @param enabled {@code true}, wenn der Scheduler beim nächsten
|
||||
* Programmstart automatisch gestartet werden soll
|
||||
* @param intervalSeconds konfigurierte Wartezeit in Sekunden zwischen
|
||||
* Läufen; entspricht dem gelesenen Rohwert
|
||||
* ohne weitere Validierung
|
||||
*/
|
||||
public record SchedulerSettings(boolean enabled, int intervalSeconds) {
|
||||
|
||||
/** Standardwert für {@code scheduler.enabled}, wenn der Key fehlt oder leer ist. */
|
||||
public static final boolean DEFAULT_ENABLED = false;
|
||||
public record SchedulerSettings(int intervalSeconds) {
|
||||
|
||||
/** Standardwert für {@code scheduler.interval.seconds}, wenn der Key fehlt oder leer ist. */
|
||||
public static final int DEFAULT_INTERVAL_SECONDS = 180;
|
||||
@@ -27,9 +20,9 @@ public record SchedulerSettings(boolean enabled, int intervalSeconds) {
|
||||
/**
|
||||
* Erzeugt eine {@code SchedulerSettings}-Instanz mit Standardwerten.
|
||||
*
|
||||
* @return Instanz mit {@code enabled=false} und {@code intervalSeconds=180}
|
||||
* @return Instanz mit {@code intervalSeconds=180}
|
||||
*/
|
||||
public static SchedulerSettings defaults() {
|
||||
return new SchedulerSettings(DEFAULT_ENABLED, DEFAULT_INTERVAL_SECONDS);
|
||||
return new SchedulerSettings(DEFAULT_INTERVAL_SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-14
@@ -4,9 +4,8 @@ package de.gecheckt.pdf.umbenenner.application.port.out;
|
||||
* Outbound-Port zum Lesen und Schreiben der Scheduler-Einstellungen
|
||||
* in der {@code .properties}-Konfigurationsdatei.
|
||||
* <p>
|
||||
* Schreiboperationen aktualisieren ausschließlich die beiden
|
||||
* Scheduler-Keys ({@code scheduler.enabled} und
|
||||
* {@code scheduler.interval.seconds}). Alle übrigen Zeilen, Kommentare
|
||||
* Schreiboperationen aktualisieren ausschließlich den Scheduler-Key
|
||||
* {@code scheduler.interval.seconds}. Alle übrigen Zeilen, Kommentare
|
||||
* und unbekannten Properties bleiben unverändert erhalten.
|
||||
* <p>
|
||||
* Schreibvorgänge sind atomar: Sie erfolgen über eine temporäre Datei,
|
||||
@@ -33,17 +32,6 @@ public interface SchedulerSettingsPort {
|
||||
*/
|
||||
SchedulerSettings loadSettings();
|
||||
|
||||
/**
|
||||
* Schreibt den Wert von {@code scheduler.enabled} in die
|
||||
* Konfigurationsdatei.
|
||||
* <p>
|
||||
* Alle übrigen Inhalte der Datei bleiben unverändert.
|
||||
*
|
||||
* @param enabled neuer Wert für {@code scheduler.enabled}
|
||||
* @throws SchedulerSettingsWriteException wenn der Schreibvorgang fehlschlägt
|
||||
*/
|
||||
void saveEnabled(boolean enabled) throws SchedulerSettingsWriteException;
|
||||
|
||||
/**
|
||||
* Schreibt den Wert von {@code scheduler.interval.seconds} in die
|
||||
* Konfigurationsdatei.
|
||||
|
||||
+7
-62
@@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
* Dieser Use Case:
|
||||
* <ul>
|
||||
* <li>Verwaltet den Scheduler-Lebenszyklus (Start, Stop) über einen
|
||||
* {@link SchedulerPort} und persistiert dabei den {@code enabled}-Wert.</li>
|
||||
* {@link SchedulerPort}.</li>
|
||||
* <li>Hält den exklusiven OS-Lock auf die Konfigurationsdatei über
|
||||
* {@link ConfigurationFileLockPort}, solange der Scheduler aktiv ist.</li>
|
||||
* <li>Liest beim Erstellen das konfigurierte Intervall via
|
||||
@@ -111,40 +111,28 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
// Schritt 1: Zustand auf STARTING setzen
|
||||
statusRef.set(withState(current, SchedulerState.STARTING, Optional.empty()));
|
||||
|
||||
// Schritt 2: scheduler.enabled=true persistieren
|
||||
try {
|
||||
settingsPort.saveEnabled(true);
|
||||
} catch (Exception e) {
|
||||
logger.error("Scheduler konnte nicht gestartet werden: Einstellung nicht persistierbar.", e);
|
||||
statusRef.set(SchedulerStatus.initial());
|
||||
throw new SchedulerStartException(
|
||||
"Scheduler-Einstellung konnte nicht gespeichert werden.", e);
|
||||
}
|
||||
|
||||
// Schritt 3: OS-Lock erwerben
|
||||
// Schritt 2: OS-Lock erwerben
|
||||
try {
|
||||
lockPort.acquireLock();
|
||||
} catch (Exception e) {
|
||||
logger.error("Scheduler konnte nicht gestartet werden: Lock nicht erwerbbar.", e);
|
||||
tryPersistDisabled();
|
||||
statusRef.set(SchedulerStatus.initial());
|
||||
throw new SchedulerStartException(
|
||||
"Konfigurationsdatei konnte nicht gesperrt werden.", e);
|
||||
}
|
||||
|
||||
// Schritt 4: Scheduler-Adapter starten
|
||||
// Schritt 3: Scheduler-Adapter starten
|
||||
try {
|
||||
schedulerPort.startScheduler(new SchedulerConfig(intervalSeconds), this::executeWrappedTick);
|
||||
} catch (Exception e) {
|
||||
logger.error("Scheduler konnte nicht gestartet werden: Adapter-Start fehlgeschlagen.", e);
|
||||
lockPort.releaseLock();
|
||||
tryPersistDisabled();
|
||||
statusRef.set(SchedulerStatus.initial());
|
||||
throw new SchedulerStartException(
|
||||
"Scheduler-Adapter konnte nicht gestartet werden.", e);
|
||||
}
|
||||
|
||||
// Schritt 5: Zustand auf RUNNING_IDLE setzen
|
||||
// Schritt 4: Zustand auf RUNNING_IDLE setzen
|
||||
Instant nextTick = Instant.now().plusSeconds(intervalSeconds);
|
||||
statusRef.updateAndGet(s -> withState(s, SchedulerState.RUNNING_IDLE, Optional.of(nextTick)));
|
||||
logger.info("Scheduler gestartet. Intervall: {} Sekunden.", intervalSeconds);
|
||||
@@ -179,7 +167,6 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
schedulerPort.stopScheduler();
|
||||
|
||||
if (!batchWasRunning) {
|
||||
tryPersistDisabled();
|
||||
lockPort.releaseLock();
|
||||
logger.info("Scheduler gestoppt.");
|
||||
} else {
|
||||
@@ -220,36 +207,6 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
settingsPort.saveIntervalSeconds(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deaktiviert den Autostart durch Persistieren von {@code scheduler.enabled=false}.
|
||||
*/
|
||||
@Override
|
||||
public void disableAutostart() {
|
||||
try {
|
||||
settingsPort.saveEnabled(false);
|
||||
logger.info("Autostart deaktiviert.");
|
||||
} catch (Exception e) {
|
||||
logger.warn("Fehler beim Deaktivieren des Autostarts.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert den Autostart als fehlgeschlagen.
|
||||
* <p>
|
||||
* Wird von der Bootstrap-Schicht aufgerufen, wenn ein konfigurierter Autostart
|
||||
* beim Programmstart fehlgeschlagen ist. Alle übrigen Statusfelder bleiben erhalten;
|
||||
* lediglich {@link SchedulerStatus#autostartFailed()} wird auf {@code true} gesetzt.
|
||||
* <p>
|
||||
* Diese Methode darf nur aufgerufen werden, wenn der Scheduler noch gestoppt ist
|
||||
* (unmittelbar nach einem fehlgeschlagenen {@link #start()}).
|
||||
*/
|
||||
public void markAutostartFailed() {
|
||||
statusRef.updateAndGet(s -> new SchedulerStatus(
|
||||
s.state(), s.lastRunEndedAt(), s.lastRunSummary(),
|
||||
s.nextTickAt(), s.lastError(), true));
|
||||
logger.warn("Scheduler-Status: Autostart als fehlgeschlagen markiert.");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tick-Wrapper (package-private für Testbarkeit)
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -265,8 +222,7 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
* <li>{@link SchedulerState#RUNNING_IDLE} bei normalem Weiterlauf</li>
|
||||
* <li>{@link SchedulerState#STOPPED} wenn ein Stopp-Befehl empfangen wurde</li>
|
||||
* </ul>
|
||||
* Im Fall {@link SchedulerState#STOPPED} werden außerdem {@code enabled=false}
|
||||
* persistiert und der OS-Lock freigegeben.
|
||||
* Im Fall {@link SchedulerState#STOPPED} wird außerdem der OS-Lock freigegeben.
|
||||
* <p>
|
||||
* Package-private, damit Unit-Tests den Tick-Wrapper direkt aufrufen können
|
||||
* ohne den Scheduler-Executor zu starten.
|
||||
@@ -315,11 +271,9 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
lastRunEndedAt,
|
||||
lastRunSummary,
|
||||
nextTickAt,
|
||||
lastError,
|
||||
afterBatch.autostartFailed()));
|
||||
lastError));
|
||||
|
||||
if (stopping) {
|
||||
tryPersistDisabled();
|
||||
lockPort.releaseLock();
|
||||
logger.info("Scheduler gestoppt nach Abschluss des laufenden Batches.");
|
||||
}
|
||||
@@ -331,14 +285,6 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
// Hilfsmethoden
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tryPersistDisabled() {
|
||||
try {
|
||||
settingsPort.saveEnabled(false);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Fehler beim Zurücksetzen von scheduler.enabled auf false.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SchedulerStatus withState(
|
||||
SchedulerStatus base, SchedulerState state, Optional<Instant> nextTickAt) {
|
||||
return new SchedulerStatus(
|
||||
@@ -346,7 +292,6 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
base.lastRunEndedAt(),
|
||||
base.lastRunSummary(),
|
||||
nextTickAt,
|
||||
base.lastError(),
|
||||
base.autostartFailed());
|
||||
base.lastError());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user