Scheduler-Ports und Typen im Application-Modul anlegen (Schritt 3)

Neue Typen (port/in):
- SchedulerControlUseCase – Inbound-Port: start(), stop(), getStatus()
- SchedulerState – Enum: STOPPED, STARTING, RUNNING_IDLE, RUNNING_BATCH_ACTIVE, STOPPING_BATCH_ACTIVE
- SchedulerStatus – Immutable Record mit AtomicReference-ready Snapshot
- SchedulerStartException – Unchecked Exception für Start-Fehler

Neue Typen (port/out):
- RunLockHandle – AutoCloseable für tryAcquire() in try-with-resources
- RunSummary – Aggregierte Lauf-Ergebniszähler (success/failed/skipped)
- BatchRunTrigger – @FunctionalInterface für synchronen Lauf-Trigger
- BatchRunTriggerResult – Sealed Interface: Started, SkippedBusy, Failed
- SchedulerConfig – Betriebskonfiguration (intervalSeconds >= 30)
- SchedulerSettings – Persistierte Properties-Werte mit Defaults
- SchedulerPort – startScheduler() / stopScheduler()
- ConfigurationFileLockPort – acquireLock() / releaseLock() / isLocked()
- ConfigurationFileLockException – Unchecked bei Lock-Erwerb-Fehler
- SchedulerSettingsPort – loadSettings() / saveEnabled() / saveIntervalSeconds()
- SchedulerSettingsWriteException – Unchecked bei Schreib-Fehler

Erweiterungen:
- RunLockPort: neue Methode tryAcquire() → Optional<RunLockHandle>
- FilesystemRunLockPortAdapter: implementiert tryAcquire() atomar via
  CREATE_NEW; idempotentes Handle via AtomicBoolean

Test-Fixes:
- 9 Mock-Klassen in application- und bootstrap-Tests um tryAcquire()
  ergänzt (liefern Optional.empty(), da nur blockierender Pfad getestet)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:42:42 +02:00
parent 93a2473c36
commit c2a7921675
24 changed files with 898 additions and 50 deletions
@@ -452,6 +452,10 @@ class BootstrapRunnerConfigPathSemanticsTest {
private static class MockRunLockPort implements de.gecheckt.pdf.umbenenner.application.port.out.RunLockPort {
@Override public void acquire() {}
@Override public void release() {}
@Override
public java.util.Optional<de.gecheckt.pdf.umbenenner.application.port.out.RunLockHandle> tryAcquire() {
return java.util.Optional.empty();
}
}
private static class MockSchemaInitializationPort
@@ -571,6 +571,11 @@ class BootstrapRunnerEdgeCasesTest {
@Override
public void release() { }
@Override
public java.util.Optional<de.gecheckt.pdf.umbenenner.application.port.out.RunLockHandle> tryAcquire() {
return java.util.Optional.empty();
}
}
private static class MockSchemaInitializationPort implements PersistenceSchemaInitializationPort {
@@ -258,6 +258,10 @@ class BootstrapRunnerStartupDispatchTest {
private static class MockRunLockPort implements de.gecheckt.pdf.umbenenner.application.port.out.RunLockPort {
@Override public void acquire() {}
@Override public void release() {}
@Override
public java.util.Optional<de.gecheckt.pdf.umbenenner.application.port.out.RunLockHandle> tryAcquire() {
return java.util.Optional.empty();
}
}
private static class MockSchemaInitializationPort
@@ -550,6 +550,11 @@ class BootstrapRunnerTest {
@Override
public void release() { }
@Override
public java.util.Optional<de.gecheckt.pdf.umbenenner.application.port.out.RunLockHandle> tryAcquire() {
return java.util.Optional.empty();
}
}
private static class MockSchemaInitializationPort implements PersistenceSchemaInitializationPort {
@@ -191,6 +191,10 @@ class BootstrapSmokeTest {
private static class NoOpRunLockPort implements RunLockPort {
@Override public void acquire() { }
@Override public void release() { }
@Override
public java.util.Optional<de.gecheckt.pdf.umbenenner.application.port.out.RunLockHandle> tryAcquire() {
return java.util.Optional.empty();
}
}
private static class NoOpSchemaInitializationPort implements PersistenceSchemaInitializationPort {