1
0

Optimierung: BootstrapRunner strukturell verschlankt

This commit is contained in:
2026-04-04 13:15:46 +02:00
parent 3ab10a89f0
commit efb4d0b222

View File

@@ -212,20 +212,15 @@ public class BootstrapRunner {
* <p> * <p>
* Startup flow: * Startup flow:
* <ol> * <ol>
* <li>Load configuration via {@link ConfigurationPort}.</li> * <li>Load and validate the configuration.</li>
* <li>Validate the configuration via {@link StartConfigurationValidator}; validation * <li>Initialise the SQLite persistence schema. A {@link DocumentPersistenceException}
* includes checking that the {@code sqlite.file} parent directory exists or is * here is a hard startup failure and causes exit code 1.</li>
* technically creatable.</li>
* <li>Initialise the SQLite persistence schema explicitly via
* {@link PersistenceSchemaInitializationPort}. This step happens once, before the
* batch document loop begins. A {@link DocumentPersistenceException} here is a hard
* startup failure and causes exit code 1.</li>
* <li>Resolve the run-lock file path, apply default if not configured.</li> * <li>Resolve the run-lock file path, apply default if not configured.</li>
* <li>Create the batch use case with all required adapters wired.</li> * <li>Create the batch use case with all required adapters wired.</li>
* <li>Execute the CLI command and map the outcome to an exit code.</li> * <li>Execute the CLI command and map the outcome to an exit code.</li>
* </ol> * </ol>
* <p> * <p>
* Document-level failures during the batch loop (step 6) are not startup failures and * Document-level failures during the batch loop are not startup failures and
* do not change the exit code as long as the run itself completes without a hard * do not change the exit code as long as the run itself completes without a hard
* infrastructure error. * infrastructure error.
* *
@@ -235,51 +230,78 @@ public class BootstrapRunner {
public int run() { public int run() {
LOG.info("Bootstrap flow started."); LOG.info("Bootstrap flow started.");
try { try {
// Step 1: Create the configuration port adapter (adapter-out layer) StartConfiguration config = loadAndValidateConfiguration();
initializeSchema(config);
RunLockPort runLockPort = runLockPortFactory.create(resolveLockFilePath(config));
BatchRunContext runContext = createRunContext();
BatchRunProcessingUseCase useCase = useCaseFactory.create(config, runLockPort);
SchedulerBatchCommand command = commandFactory.create(useCase);
BatchRunOutcome outcome = command.run(runContext);
runContext.setEndInstant(Instant.now());
return mapOutcomeToExitCode(outcome, runContext);
} catch (InvalidStartConfigurationException e) {
LOG.error("Configuration validation failed: {}", e.getMessage());
return 1;
} catch (IllegalStateException e) {
LOG.error("Configuration loading failed: {}", e.getMessage());
return 1;
} catch (DocumentPersistenceException e) {
LOG.error("Persistence operation failed: {}", e.getMessage(), e);
return 1;
} catch (Exception e) {
LOG.error("Bootstrap failure during startup.", e);
return 1;
}
}
/**
* Loads configuration via {@link ConfigurationPort} and validates it via
* {@link StartConfigurationValidator}. Validation includes checking that the
* {@code sqlite.file} parent directory exists or is technically creatable.
*/
private StartConfiguration loadAndValidateConfiguration() {
ConfigurationPort configPort = configPortFactory.create(); ConfigurationPort configPort = configPortFactory.create();
StartConfiguration config = configPort.loadConfiguration();
validatorFactory.create().validate(config);
return config;
}
// Step 2: Load configuration /**
var config = configPort.loadConfiguration(); * Initialises the SQLite persistence schema once at startup, before the batch loop begins.
* Failure here is a hard bootstrap error and results in exit code 1.
*/
private void initializeSchema(StartConfiguration config) {
schemaInitPortFactory.create(buildJdbcUrl(config)).initializeSchema();
}
// Step 3: Validate configuration. /**
// Includes checking that sqlite.file parent directory exists or is creatable. * Resolves the run-lock file path from the configuration, applying a default when not set.
StartConfigurationValidator validator = validatorFactory.create(); */
validator.validate(config); private Path resolveLockFilePath(StartConfiguration config) {
// Step 4 (M4-AP-007): Initialise SQLite persistence schema before the batch loop.
// Must happen once at startup; failure here is a hard bootstrap error → exit code 1.
String jdbcUrl = buildJdbcUrl(config);
PersistenceSchemaInitializationPort schemaInitPort = schemaInitPortFactory.create(jdbcUrl);
schemaInitPort.initializeSchema();
// Step 5: Resolve lock file path apply default if not configured
Path lockFilePath = config.runtimeLockFile(); Path lockFilePath = config.runtimeLockFile();
if (lockFilePath == null || lockFilePath.toString().isBlank()) { if (lockFilePath == null || lockFilePath.toString().isBlank()) {
lockFilePath = Paths.get("pdf-umbenenner.lock"); lockFilePath = Paths.get("pdf-umbenenner.lock");
LOG.info("runtime.lock.file not configured, using default lock path: {}", LOG.info("runtime.lock.file not configured, using default lock path: {}",
lockFilePath.toAbsolutePath()); lockFilePath.toAbsolutePath());
} }
RunLockPort runLockPort = runLockPortFactory.create(lockFilePath); return lockFilePath;
}
// Step 6: Create the batch run context /**
* Creates a new {@link BatchRunContext} with a fresh run ID and the current timestamp.
*/
private BatchRunContext createRunContext() {
RunId runId = new RunId(UUID.randomUUID().toString()); RunId runId = new RunId(UUID.randomUUID().toString());
BatchRunContext runContext = new BatchRunContext(runId, Instant.now());
LOG.info("Batch run started. RunId: {}", runId); LOG.info("Batch run started. RunId: {}", runId);
return new BatchRunContext(runId, Instant.now());
}
// Step 7: Create the use case with the validated config and run lock. /**
// Config is passed directly; the use case does not re-read the properties file. * Maps a {@link BatchRunOutcome} to a process exit code and logs the run result.
// Adapters (source document port, PDF extraction port, M4 ports) are wired by the factory. *
BatchRunProcessingUseCase useCase = useCaseFactory.create(config, runLockPort); * @return 0 if the batch run completed successfully; 1 otherwise
*/
// Step 8: Create the CLI command adapter with the use case private int mapOutcomeToExitCode(BatchRunOutcome outcome, BatchRunContext runContext) {
SchedulerBatchCommand command = commandFactory.create(useCase);
// Step 9: Execute the command with the run context and handle the outcome
BatchRunOutcome outcome = command.run(runContext);
// Mark run as completed
runContext.setEndInstant(Instant.now());
if (outcome.isSuccess()) { if (outcome.isSuccess()) {
LOG.info("Batch run completed successfully. RunId: {}", runContext.runId()); LOG.info("Batch run completed successfully. RunId: {}", runContext.runId());
return 0; return 0;
@@ -291,22 +313,6 @@ public class BootstrapRunner {
LOG.error("Batch run failed. RunId: {}", runContext.runId()); LOG.error("Batch run failed. RunId: {}", runContext.runId());
return 1; return 1;
} }
} catch (InvalidStartConfigurationException e) {
// Controlled failure for invalid configuration log clearly without stack trace
LOG.error("Configuration validation failed: {}", e.getMessage());
return 1;
} catch (IllegalStateException e) {
// Configuration loading failed due to missing/invalid required properties
LOG.error("Configuration loading failed: {}", e.getMessage());
return 1;
} catch (DocumentPersistenceException e) {
// Persistence operation failed hard error
LOG.error("Persistence operation failed: {}", e.getMessage(), e);
return 1;
} catch (Exception e) {
LOG.error("Bootstrap failure during startup.", e);
return 1;
}
} }
/** /**