1
0

M1 Vollständiger Grundstand mit Build, Konfiguration, Tests und Smoke-Tests

This commit is contained in:
2026-03-31 14:04:47 +02:00
commit ea83f8fa8c
52 changed files with 2819 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
package de.gecheckt.pdf.umbenenner.bootstrap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.gecheckt.pdf.umbenenner.adapter.inbound.cli.SchedulerBatchCommand;
import de.gecheckt.pdf.umbenenner.adapter.outbound.configuration.PropertiesConfigurationPortAdapter;
import de.gecheckt.pdf.umbenenner.application.config.InvalidStartConfigurationException;
import de.gecheckt.pdf.umbenenner.application.config.StartConfigurationValidator;
import de.gecheckt.pdf.umbenenner.application.port.in.RunBatchProcessingUseCase;
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
import de.gecheckt.pdf.umbenenner.application.usecase.NoOpRunBatchProcessingUseCase;
/**
* Manual bootstrap runner that constructs the object graph and drives the startup flow.
* <p>
* AP-003 Implementation: Creates all required components using plain Java constructor injection
* and executes the minimal no-op batch processing path.
* <p>
* AP-005: Integrates configuration loading via PropertiesConfigurationPortAdapter.
* <p>
* AP-006: Validates configuration before processing begins, returns exit code 2 on invalid config.
*/
public class BootstrapRunner {
private static final Logger LOG = LogManager.getLogger(BootstrapRunner.class);
private final ConfigurationPortFactory configPortFactory;
private final ValidatorFactory validatorFactory;
private final UseCaseFactory useCaseFactory;
private final CommandFactory commandFactory;
/**
* Functional interface for creating a ConfigurationPort.
*/
@FunctionalInterface
public interface ConfigurationPortFactory {
ConfigurationPort create();
}
/**
* Functional interface for creating a StartConfigurationValidator.
*/
@FunctionalInterface
public interface ValidatorFactory {
StartConfigurationValidator create();
}
/**
* Functional interface for creating a RunBatchProcessingUseCase.
*/
@FunctionalInterface
public interface UseCaseFactory {
RunBatchProcessingUseCase create(ConfigurationPort configPort);
}
/**
* Functional interface for creating a SchedulerBatchCommand.
*/
@FunctionalInterface
public interface CommandFactory {
SchedulerBatchCommand create(RunBatchProcessingUseCase useCase);
}
/**
* Creates the BootstrapRunner with default factories for production use.
*/
public BootstrapRunner() {
this.configPortFactory = PropertiesConfigurationPortAdapter::new;
this.validatorFactory = StartConfigurationValidator::new;
this.useCaseFactory = NoOpRunBatchProcessingUseCase::new;
this.commandFactory = SchedulerBatchCommand::new;
}
/**
* Creates the BootstrapRunner with custom factories for testing.
*
* @param configPortFactory factory for creating ConfigurationPort instances
* @param validatorFactory factory for creating StartConfigurationValidator instances
* @param useCaseFactory factory for creating RunBatchProcessingUseCase instances
* @param commandFactory factory for creating SchedulerBatchCommand instances
*/
public BootstrapRunner(ConfigurationPortFactory configPortFactory,
ValidatorFactory validatorFactory,
UseCaseFactory useCaseFactory,
CommandFactory commandFactory) {
this.configPortFactory = configPortFactory;
this.validatorFactory = validatorFactory;
this.useCaseFactory = useCaseFactory;
this.commandFactory = commandFactory;
}
/**
* Runs the application startup sequence.
* <p>
* AP-003: Manually wires the object graph and invokes the CLI command.
* AP-005: Wires ConfigurationPort adapter and passes it to the use case.
* AP-006: Validates configuration before allowing processing to start.
*
* @return exit code: 0 for success, 1 for unexpected failure, 2 for invalid configuration
*/
public int run() {
LOG.info("Bootstrap flow started.");
try {
// Step 1: Create the configuration port adapter (adapter-out layer)
ConfigurationPort configPort = configPortFactory.create();
// Step 2: Load configuration
var config = configPort.loadConfiguration();
// Step 3: Validate configuration (AP-006)
StartConfigurationValidator validator = validatorFactory.create();
validator.validate(config);
// Step 4: Create the use case with the configuration port (application layer)
RunBatchProcessingUseCase useCase = useCaseFactory.create(configPort);
// Step 5: Create the CLI command adapter with the use case
SchedulerBatchCommand command = commandFactory.create(useCase);
// Step 6: Execute the command
boolean success = command.run();
if (success) {
LOG.info("No-op startup path completed successfully.");
}
return success ? 0 : 1;
} catch (InvalidStartConfigurationException e) {
// Controlled failure for invalid configuration - log clearly without stack trace
LOG.error("Configuration validation failed: {}", e.getMessage());
return 2;
} catch (IllegalStateException e) {
// Configuration loading failed due to missing/invalid required properties
// Treat as invalid configuration for controlled failure
LOG.error("Configuration loading failed: {}", e.getMessage());
return 2;
} catch (Exception e) {
LOG.error("Bootstrap failure during startup.", e);
return 1;
}
}
}

View File

@@ -0,0 +1,37 @@
package de.gecheckt.pdf.umbenenner.bootstrap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Main entry point for the PDF Umbenenner application.
* <p>
* AP-003: Delegates to {@link BootstrapRunner} for manual object graph construction
* and execution of the minimal no-op startup path.
*/
public class PdfUmbenennerApplication {
private static final Logger LOG = LogManager.getLogger(PdfUmbenennerApplication.class);
/**
* Application entry point.
*
* @param args command line arguments (currently unused)
*/
public static void main(String[] args) {
LOG.info("Starting PDF Umbenenner application...");
try {
BootstrapRunner runner = new BootstrapRunner();
int exitCode = runner.run();
if (exitCode == 0) {
LOG.info("PDF Umbenenner application completed successfully.");
} else {
LOG.error("PDF Umbenenner application terminated with error code {}.", exitCode);
}
System.exit(exitCode);
} catch (Exception e) {
LOG.fatal("Unexpected technical bootstrap failure in PDF Umbenenner application.", e);
System.exit(1);
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* Bootstrap module for application startup and technical wiring.
* <p>
* This package contains the main entry point and manual object graph construction.
* AP-003: Provides a minimal, controlled startup path without dependency injection frameworks.
*/
package de.gecheckt.pdf.umbenenner.bootstrap;

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- Console appender for stdout -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- Rolling file appender for logs in ./logs/ directory -->
<RollingFile name="File" fileName="logs/pdf-umbenenner.log"
filePattern="logs/pdf-umbenenner-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<DefaultRolloverStrategy max="7"/>
</RollingFile>
</Appenders>
<Loggers>
<!-- Root logger at INFO level -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>