M5 komplett umgesetzt
This commit is contained in:
@@ -9,31 +9,43 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.cli.SchedulerBatchCommand;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.ai.OpenAiHttpAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.bootstrap.validation.InvalidStartConfigurationException;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.bootstrap.validation.StartConfigurationValidator;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.clock.SystemClockAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.configuration.ConfigurationLoadingException;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.configuration.PropertiesConfigurationPortAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.fingerprint.Sha256FingerprintAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.lock.FilesystemRunLockPortAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.pdfextraction.PdfTextExtractionPortAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.prompt.FilesystemPromptPortAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sourcedocument.SourceDocumentCandidatesPortAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteDocumentRecordRepositoryAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteProcessingAttemptRepositoryAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteSchemaInitializationAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteUnitOfWorkAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.targetcopy.FilesystemTargetFileCopyAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.targetfolder.FilesystemTargetFolderAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.AiInvocationPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.TargetFileCopyPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.TargetFolderPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentPersistenceException;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordRepository;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.PersistenceSchemaInitializationPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttemptRepository;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingLogger;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.PromptPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.RunLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.UnitOfWorkPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.service.AiNamingService;
|
||||
import de.gecheckt.pdf.umbenenner.application.service.AiResponseValidator;
|
||||
import de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingCoordinator;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultBatchRunProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.bootstrap.adapter.Log4jProcessingLogger;
|
||||
@@ -71,11 +83,15 @@ import de.gecheckt.pdf.umbenenner.domain.model.RunId;
|
||||
* <ul>
|
||||
* <li>{@link PropertiesConfigurationPortAdapter} — loads configuration from properties and environment.</li>
|
||||
* <li>{@link FilesystemRunLockPortAdapter} — ensures exclusive execution via a lock file.</li>
|
||||
* <li>{@link SqliteSchemaInitializationAdapter} — initializes SQLite schema at startup.</li>
|
||||
* <li>{@link SqliteSchemaInitializationAdapter} — initializes SQLite schema (including target-copy
|
||||
* schema evolution) at startup.</li>
|
||||
* <li>{@link Sha256FingerprintAdapter} — provides content-based document identification.</li>
|
||||
* <li>{@link SqliteDocumentRecordRepositoryAdapter} — manages document master records.</li>
|
||||
* <li>{@link SqliteProcessingAttemptRepositoryAdapter} — maintains attempt history.</li>
|
||||
* <li>{@link SqliteUnitOfWorkAdapter} — coordinates atomic persistence operations.</li>
|
||||
* <li>{@link FilesystemTargetFolderAdapter} — resolves unique filenames in the configured target folder.</li>
|
||||
* <li>{@link FilesystemTargetFileCopyAdapter} — copies source documents to the target folder via
|
||||
* a temporary file and final move/rename.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Schema initialization is performed exactly once in {@link #run()} before the batch processing loop
|
||||
@@ -162,12 +178,22 @@ public class BootstrapRunner {
|
||||
* <li>{@link SourceDocumentCandidatesPortAdapter} for PDF candidate discovery.</li>
|
||||
* <li>{@link PdfTextExtractionPortAdapter} for PDFBox-based text and page count extraction.</li>
|
||||
* <li>{@link Sha256FingerprintAdapter} for SHA-256 content fingerprinting.</li>
|
||||
* <li>{@link SqliteSchemaInitializationAdapter} for SQLite schema DDL at startup.</li>
|
||||
* <li>{@link SqliteSchemaInitializationAdapter} for SQLite schema DDL and target-copy schema
|
||||
* evolution at startup.</li>
|
||||
* <li>{@link SqliteDocumentRecordRepositoryAdapter} for document master record CRUD.</li>
|
||||
* <li>{@link SqliteProcessingAttemptRepositoryAdapter} for attempt history CRUD.</li>
|
||||
* <li>{@link SqliteUnitOfWorkAdapter} for atomic persistence operations.</li>
|
||||
* <li>{@link FilesystemTargetFolderAdapter} for duplicate-safe filename resolution in the
|
||||
* configured {@code target.folder}.</li>
|
||||
* <li>{@link FilesystemTargetFileCopyAdapter} for copying source documents to the target folder
|
||||
* via a temporary file and final atomic move/rename.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Target folder availability and write access are validated in
|
||||
* {@link #loadAndValidateConfiguration()} via {@link StartConfigurationValidator} before
|
||||
* schema initialisation and batch processing begin. If the target folder does not yet exist,
|
||||
* the validator creates it; failure to do so is a hard startup error.
|
||||
* <p>
|
||||
* Schema initialisation is performed explicitly in {@link #run()} before the batch loop
|
||||
* begins. Failure during initialisation aborts the run with exit code 1.
|
||||
*/
|
||||
@@ -189,8 +215,23 @@ public class BootstrapRunner {
|
||||
UnitOfWorkPort unitOfWorkPort =
|
||||
new SqliteUnitOfWorkAdapter(jdbcUrl);
|
||||
ProcessingLogger coordinatorLogger = new Log4jProcessingLogger(DocumentProcessingCoordinator.class);
|
||||
TargetFolderPort targetFolderPort = new FilesystemTargetFolderAdapter(startConfig.targetFolder());
|
||||
TargetFileCopyPort targetFileCopyPort = new FilesystemTargetFileCopyAdapter(startConfig.targetFolder());
|
||||
DocumentProcessingCoordinator documentProcessingCoordinator =
|
||||
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository, unitOfWorkPort, coordinatorLogger);
|
||||
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository, unitOfWorkPort, targetFolderPort, targetFileCopyPort, coordinatorLogger);
|
||||
|
||||
// Wire AI naming pipeline
|
||||
AiInvocationPort aiInvocationPort = new OpenAiHttpAdapter(startConfig);
|
||||
PromptPort promptPort = new FilesystemPromptPortAdapter(startConfig.promptTemplateFile());
|
||||
ClockPort clockPort = new SystemClockAdapter();
|
||||
AiResponseValidator aiResponseValidator = new AiResponseValidator(clockPort);
|
||||
AiNamingService aiNamingService = new AiNamingService(
|
||||
aiInvocationPort,
|
||||
promptPort,
|
||||
aiResponseValidator,
|
||||
startConfig.apiModel(),
|
||||
startConfig.maxTextCharacters());
|
||||
|
||||
ProcessingLogger useCaseLogger = new Log4jProcessingLogger(DefaultBatchRunProcessingUseCase.class);
|
||||
return new DefaultBatchRunProcessingUseCase(
|
||||
runtimeConfig,
|
||||
@@ -199,6 +240,7 @@ public class BootstrapRunner {
|
||||
new PdfTextExtractionPortAdapter(),
|
||||
fingerprintPort,
|
||||
documentProcessingCoordinator,
|
||||
aiNamingService,
|
||||
useCaseLogger);
|
||||
};
|
||||
this.commandFactory = SchedulerBatchCommand::new;
|
||||
@@ -272,8 +314,17 @@ public class BootstrapRunner {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* {@link StartConfigurationValidator}.
|
||||
* <p>
|
||||
* Validation includes:
|
||||
* <ul>
|
||||
* <li>{@code source.folder}: must exist, be a directory, and be readable.</li>
|
||||
* <li>{@code target.folder}: must exist as a writable directory, or be technically
|
||||
* creatable (validator attempts {@code Files.createDirectories} if absent;
|
||||
* failure here is a hard startup error).</li>
|
||||
* <li>{@code sqlite.file}: parent directory must exist.</li>
|
||||
* <li>All numeric and URI constraints.</li>
|
||||
* </ul>
|
||||
*/
|
||||
private StartConfiguration loadAndValidateConfiguration() {
|
||||
ConfigurationPort configPort = configPortFactory.create();
|
||||
|
||||
@@ -28,11 +28,15 @@
|
||||
* Startup sequence:
|
||||
* <ul>
|
||||
* <li>Load and validate complete startup configuration from properties file and environment variables</li>
|
||||
* <li>Initialize SQLite persistence schema via {@link de.gecheckt.pdf.umbenenner.application.port.out.PersistenceSchemaInitializationPort},
|
||||
* <li>Validate target folder availability and write access; create target folder if absent
|
||||
* (failure is a hard startup error)</li>
|
||||
* <li>Initialize SQLite persistence schema (including target-copy schema evolution) via
|
||||
* {@link de.gecheckt.pdf.umbenenner.application.port.out.PersistenceSchemaInitializationPort},
|
||||
* ensuring the database is ready before any batch processing</li>
|
||||
* <li>Schema initialization failure is treated as a hard bootstrap error and causes exit code 1</li>
|
||||
* <li>Create run lock adapter and acquire exclusive lock</li>
|
||||
* <li>Wire all outbound adapters (document candidates, PDF extraction, fingerprint, persistence, logging)</li>
|
||||
* <li>Wire all outbound adapters (document candidates, PDF extraction, fingerprint, persistence,
|
||||
* target folder duplicate resolution, target file copy, logging)</li>
|
||||
* <li>Wire and invoke the batch processing CLI adapter</li>
|
||||
* <li>Map batch outcome to process exit code</li>
|
||||
* </ul>
|
||||
|
||||
Reference in New Issue
Block a user