Optimierung: BootstrapRunner strukturell verschlankt
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user