Optimierung: BootstrapRunner strukturell verschlankt
This commit is contained in:
@@ -212,20 +212,15 @@ public class BootstrapRunner {
|
||||
* <p>
|
||||
* Startup flow:
|
||||
* <ol>
|
||||
* <li>Load configuration via {@link ConfigurationPort}.</li>
|
||||
* <li>Validate the configuration via {@link StartConfigurationValidator}; validation
|
||||
* includes checking that the {@code sqlite.file} parent directory exists or is
|
||||
* 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>Load and validate the configuration.</li>
|
||||
* <li>Initialise the SQLite persistence schema. 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>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>
|
||||
* </ol>
|
||||
* <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
|
||||
* infrastructure error.
|
||||
*
|
||||
@@ -235,51 +230,78 @@ public class BootstrapRunner {
|
||||
public int run() {
|
||||
LOG.info("Bootstrap flow started.");
|
||||
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();
|
||||
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.
|
||||
StartConfigurationValidator validator = validatorFactory.create();
|
||||
validator.validate(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
|
||||
/**
|
||||
* Resolves the run-lock file path from the configuration, applying a default when not set.
|
||||
*/
|
||||
private Path resolveLockFilePath(StartConfiguration config) {
|
||||
Path lockFilePath = config.runtimeLockFile();
|
||||
if (lockFilePath == null || lockFilePath.toString().isBlank()) {
|
||||
lockFilePath = Paths.get("pdf-umbenenner.lock");
|
||||
LOG.info("runtime.lock.file not configured, using default lock path: {}",
|
||||
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());
|
||||
BatchRunContext runContext = new BatchRunContext(runId, Instant.now());
|
||||
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.
|
||||
// Adapters (source document port, PDF extraction port, M4 ports) are wired by the factory.
|
||||
BatchRunProcessingUseCase useCase = useCaseFactory.create(config, runLockPort);
|
||||
|
||||
// Step 8: Create the CLI command adapter with the use case
|
||||
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());
|
||||
|
||||
/**
|
||||
* Maps a {@link BatchRunOutcome} to a process exit code and logs the run result.
|
||||
*
|
||||
* @return 0 if the batch run completed successfully; 1 otherwise
|
||||
*/
|
||||
private int mapOutcomeToExitCode(BatchRunOutcome outcome, BatchRunContext runContext) {
|
||||
if (outcome.isSuccess()) {
|
||||
LOG.info("Batch run completed successfully. RunId: {}", runContext.runId());
|
||||
return 0;
|
||||
@@ -291,22 +313,6 @@ public class BootstrapRunner {
|
||||
LOG.error("Batch run failed. RunId: {}", runContext.runId());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user