1
0

Optimierung: Anwendungskonfiguration auf Minimalbedarf zugeschnitten

This commit is contained in:
2026-04-05 09:45:31 +02:00
parent 7764a50308
commit 3657b0c3de
8 changed files with 103 additions and 146 deletions

View File

@@ -0,0 +1,20 @@
package de.gecheckt.pdf.umbenenner.application.config;
/**
* Minimal runtime configuration for the application layer.
* <p>
* Contains only the application-level runtime parameters that are needed during
* batch document processing. Technical infrastructure configuration (paths, API keys,
* persistence parameters, etc.) is kept in the bootstrap layer.
* <p>
* This intentionally small contract ensures the application layer depends only on
* the configuration values it actually uses, following hexagonal architecture principles.
*/
public record RuntimeConfiguration(
/**
* Maximum number of pages a document can have to be processed.
* Documents exceeding this limit are rejected during pre-checks.
*/
int maxPages
)
{ }

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.service; package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome; import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
@@ -48,23 +48,23 @@ public class DocumentProcessingService {
* *
* @param candidate the document candidate to process * @param candidate the document candidate to process
* @param extractionResult the result from PDF extraction (from {@link de.gecheckt.pdf.umbenenner.application.port.out.PdfTextExtractionPort}) * @param extractionResult the result from PDF extraction (from {@link de.gecheckt.pdf.umbenenner.application.port.out.PdfTextExtractionPort})
* @param configuration the startup configuration (used for pre-check validation) * @param runtimeConfig the runtime configuration (used for pre-check validation)
* @return the complete processing outcome (implementing {@link DocumentProcessingOutcome}) * @return the complete processing outcome (implementing {@link DocumentProcessingOutcome})
* @throws NullPointerException if any parameter is null * @throws NullPointerException if any parameter is null
*/ */
public static DocumentProcessingOutcome processDocument( public static DocumentProcessingOutcome processDocument(
SourceDocumentCandidate candidate, SourceDocumentCandidate candidate,
PdfExtractionResult extractionResult, PdfExtractionResult extractionResult,
StartConfiguration configuration) { RuntimeConfiguration runtimeConfig) {
Objects.requireNonNull(candidate, "candidate must not be null"); Objects.requireNonNull(candidate, "candidate must not be null");
Objects.requireNonNull(extractionResult, "extractionResult must not be null"); Objects.requireNonNull(extractionResult, "extractionResult must not be null");
Objects.requireNonNull(configuration, "configuration must not be null"); Objects.requireNonNull(runtimeConfig, "runtimeConfig must not be null");
return switch (extractionResult) { return switch (extractionResult) {
case PdfExtractionSuccess success -> case PdfExtractionSuccess success ->
// Extraction succeeded: evaluate pre-checks // Extraction succeeded: evaluate pre-checks
PreCheckEvaluator.evaluate(candidate, success, configuration); PreCheckEvaluator.evaluate(candidate, success, runtimeConfig);
case PdfExtractionContentError contentError -> case PdfExtractionContentError contentError ->
// PDF content not extractable: classify as pre-check failed (deterministic content error) // PDF content not extractable: classify as pre-check failed (deterministic content error)

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.service; package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome; import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
@@ -44,18 +44,18 @@ public class PreCheckEvaluator {
* *
* @param candidate the source document metadata * @param candidate the source document metadata
* @param extraction the successfully extracted PDF content * @param extraction the successfully extracted PDF content
* @param configuration the startup configuration (used for maxPages limit) * @param runtimeConfig the runtime configuration (used for maxPages limit)
* @return the pre-check outcome: passed or failed with reason (both implement {@link DocumentProcessingOutcome}) * @return the pre-check outcome: passed or failed with reason (both implement {@link DocumentProcessingOutcome})
* @throws NullPointerException if any parameter is null * @throws NullPointerException if any parameter is null
*/ */
public static DocumentProcessingOutcome evaluate( public static DocumentProcessingOutcome evaluate(
SourceDocumentCandidate candidate, SourceDocumentCandidate candidate,
PdfExtractionSuccess extraction, PdfExtractionSuccess extraction,
StartConfiguration configuration) { RuntimeConfiguration runtimeConfig) {
Objects.requireNonNull(candidate, "candidate must not be null"); Objects.requireNonNull(candidate, "candidate must not be null");
Objects.requireNonNull(extraction, "extraction must not be null"); Objects.requireNonNull(extraction, "extraction must not be null");
Objects.requireNonNull(configuration, "configuration must not be null"); Objects.requireNonNull(runtimeConfig, "runtimeConfig must not be null");
// Pre-check 1: Verify document has usable text // Pre-check 1: Verify document has usable text
if (!hasUsableText(extraction.extractedText())) { if (!hasUsableText(extraction.extractedText())) {
@@ -66,7 +66,7 @@ public class PreCheckEvaluator {
} }
// Pre-check 2: Verify document page count does not exceed configured limit // Pre-check 2: Verify document page count does not exceed configured limit
if (extraction.pageCount().exceedsLimit(configuration.maxPages())) { if (extraction.pageCount().exceedsLimit(runtimeConfig.maxPages())) {
return new PreCheckFailed( return new PreCheckFailed(
candidate, candidate,
PreCheckFailureReason.PAGE_LIMIT_EXCEEDED PreCheckFailureReason.PAGE_LIMIT_EXCEEDED

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.usecase; package de.gecheckt.pdf.umbenenner.application.usecase;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome; 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.in.BatchRunProcessingUseCase;
import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintPort; import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintPort;
@@ -77,7 +77,7 @@ import java.util.Objects;
*/ */
public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCase { public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCase {
private final StartConfiguration configuration; private final RuntimeConfiguration runtimeConfiguration;
private final RunLockPort runLockPort; private final RunLockPort runLockPort;
private final SourceDocumentCandidatesPort sourceDocumentCandidatesPort; private final SourceDocumentCandidatesPort sourceDocumentCandidatesPort;
private final PdfTextExtractionPort pdfTextExtractionPort; private final PdfTextExtractionPort pdfTextExtractionPort;
@@ -86,13 +86,13 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
private final ProcessingLogger logger; private final ProcessingLogger logger;
/** /**
* Creates the batch use case with the already-loaded startup configuration and all * Creates the batch use case with the runtime configuration and all required ports for the flow.
* required ports for the flow.
* <p> * <p>
* The configuration is loaded and validated by Bootstrap before use case creation; * The runtime configuration contains only the application-level settings needed
* the use case receives the result directly and does not re-read the properties file. * during batch processing. Technical infrastructure configuration (paths, persistence,
* API details, etc.) is managed by Bootstrap.
* *
* @param configuration the validated startup configuration; must not be null * @param runtimeConfiguration the runtime configuration containing application-level settings; must not be null
* @param runLockPort for exclusive run locking; must not be null * @param runLockPort for exclusive run locking; must not be null
* @param sourceDocumentCandidatesPort for loading PDF candidates from the source folder; * @param sourceDocumentCandidatesPort for loading PDF candidates from the source folder;
* must not be null * must not be null
@@ -106,14 +106,14 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
* @throws NullPointerException if any parameter is null * @throws NullPointerException if any parameter is null
*/ */
public DefaultBatchRunProcessingUseCase( public DefaultBatchRunProcessingUseCase(
StartConfiguration configuration, RuntimeConfiguration runtimeConfiguration,
RunLockPort runLockPort, RunLockPort runLockPort,
SourceDocumentCandidatesPort sourceDocumentCandidatesPort, SourceDocumentCandidatesPort sourceDocumentCandidatesPort,
PdfTextExtractionPort pdfTextExtractionPort, PdfTextExtractionPort pdfTextExtractionPort,
FingerprintPort fingerprintPort, FingerprintPort fingerprintPort,
DocumentProcessingCoordinator documentProcessingCoordinator, DocumentProcessingCoordinator documentProcessingCoordinator,
ProcessingLogger logger) { ProcessingLogger logger) {
this.configuration = Objects.requireNonNull(configuration, "configuration must not be null"); this.runtimeConfiguration = Objects.requireNonNull(runtimeConfiguration, "runtimeConfiguration must not be null");
this.runLockPort = Objects.requireNonNull(runLockPort, "runLockPort must not be null"); this.runLockPort = Objects.requireNonNull(runLockPort, "runLockPort must not be null");
this.sourceDocumentCandidatesPort = Objects.requireNonNull( this.sourceDocumentCandidatesPort = Objects.requireNonNull(
sourceDocumentCandidatesPort, "sourceDocumentCandidatesPort must not be null"); sourceDocumentCandidatesPort, "sourceDocumentCandidatesPort must not be null");
@@ -142,8 +142,6 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
return BatchRunOutcome.LOCK_UNAVAILABLE; return BatchRunOutcome.LOCK_UNAVAILABLE;
} }
logger.debug("Configuration in use: source={}, target={}",
configuration.sourceFolder(), configuration.targetFolder());
logger.info("Batch run started. RunId: {}, Start: {}", logger.info("Batch run started. RunId: {}, Start: {}",
context.runId(), context.startInstant()); context.runId(), context.startInstant());
@@ -301,7 +299,7 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
logExtractionResult(candidate, extractionResult); logExtractionResult(candidate, extractionResult);
DocumentProcessingOutcome outcome = DocumentProcessingOutcome outcome =
DocumentProcessingService.processDocument(candidate, extractionResult, configuration); DocumentProcessingService.processDocument(candidate, extractionResult, runtimeConfiguration);
logProcessingOutcome(candidate, outcome); logProcessingOutcome(candidate, outcome);

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.service; package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome; import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
@@ -34,7 +34,7 @@ class DocumentProcessingServiceTest {
Path tempDir; Path tempDir;
private SourceDocumentCandidate candidate; private SourceDocumentCandidate candidate;
private StartConfiguration configuration; private RuntimeConfiguration runtimeConfig;
@BeforeEach @BeforeEach
void setUp() throws Exception { void setUp() throws Exception {
@@ -44,30 +44,8 @@ class DocumentProcessingServiceTest {
SourceDocumentLocator locator = new SourceDocumentLocator(pdfFile.toString()); SourceDocumentLocator locator = new SourceDocumentLocator(pdfFile.toString());
candidate = new SourceDocumentCandidate("document.pdf", 2048L, locator); candidate = new SourceDocumentCandidate("document.pdf", 2048L, locator);
// Create directories and files for configuration // Create runtime configuration with maxPages limit
Path sourceDir = Files.createDirectories(tempDir.resolve("source")); runtimeConfig = new RuntimeConfiguration(10);
Path targetDir = Files.createDirectories(tempDir.resolve("target"));
Path dbFile = tempDir.resolve("db.sqlite");
Files.createFile(dbFile);
Path promptFile = tempDir.resolve("prompt.txt");
Files.createFile(promptFile);
configuration = new StartConfiguration(
sourceDir,
targetDir,
dbFile,
URI.create("http://localhost:8000"),
"gpt-4",
30,
3,
10,
5000,
promptFile,
tempDir.resolve("lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
);
} }
@Test @Test
@@ -77,7 +55,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, extraction, configuration); candidate, extraction, runtimeConfig);
// Assert: Should produce PreCheckPassed // Assert: Should produce PreCheckPassed
assertInstanceOf(PreCheckPassed.class, outcome); assertInstanceOf(PreCheckPassed.class, outcome);
@@ -93,7 +71,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, extraction, configuration); candidate, extraction, runtimeConfig);
// Assert: Should produce PreCheckFailed with appropriate reason // Assert: Should produce PreCheckFailed with appropriate reason
assertInstanceOf(PreCheckFailed.class, outcome); assertInstanceOf(PreCheckFailed.class, outcome);
@@ -109,7 +87,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, extraction, configuration); candidate, extraction, runtimeConfig);
// Assert: Should produce PreCheckFailed with page limit reason // Assert: Should produce PreCheckFailed with page limit reason
assertInstanceOf(PreCheckFailed.class, outcome); assertInstanceOf(PreCheckFailed.class, outcome);
@@ -125,7 +103,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, contentError, configuration); candidate, contentError, runtimeConfig);
// Assert: Should produce PreCheckFailed // Assert: Should produce PreCheckFailed
assertInstanceOf(PreCheckFailed.class, outcome); assertInstanceOf(PreCheckFailed.class, outcome);
@@ -142,7 +120,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, technicalError, configuration); candidate, technicalError, runtimeConfig);
// Assert: Should produce TechnicalDocumentError // Assert: Should produce TechnicalDocumentError
assertInstanceOf(TechnicalDocumentError.class, outcome); assertInstanceOf(TechnicalDocumentError.class, outcome);
@@ -159,7 +137,7 @@ class DocumentProcessingServiceTest {
// Act // Act
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument( DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
candidate, technicalError, configuration); candidate, technicalError, runtimeConfig);
// Assert // Assert
assertInstanceOf(TechnicalDocumentError.class, outcome); assertInstanceOf(TechnicalDocumentError.class, outcome);
@@ -174,14 +152,14 @@ class DocumentProcessingServiceTest {
// Act & Assert // Act & Assert
assertThrows(NullPointerException.class, assertThrows(NullPointerException.class,
() -> DocumentProcessingService.processDocument(null, extraction, configuration)); () -> DocumentProcessingService.processDocument(null, extraction, runtimeConfig));
} }
@Test @Test
void testProcessDocument_WithNullExtractionResult_ThrowsException() { void testProcessDocument_WithNullExtractionResult_ThrowsException() {
// Act & Assert // Act & Assert
assertThrows(NullPointerException.class, assertThrows(NullPointerException.class,
() -> DocumentProcessingService.processDocument(candidate, null, configuration)); () -> DocumentProcessingService.processDocument(candidate, null, runtimeConfig));
} }
@Test @Test

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.service; package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome; import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason; import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
@@ -31,7 +31,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWhenDocumentHasUsableTextAndValidPageCount() throws Exception { void evaluate_passesWhenDocumentHasUsableTextAndValidPageCount() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Some meaningful text", new PdfPageCount(5)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Some meaningful text", new PdfPageCount(5));
@@ -45,7 +45,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithNoUsableTextWhenExtractedTextIsEmpty() throws Exception { void evaluate_failsWithNoUsableTextWhenExtractedTextIsEmpty() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(1));
@@ -58,7 +58,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithNoUsableTextWhenTextIsOnlyWhitespace() throws Exception { void evaluate_failsWithNoUsableTextWhenTextIsOnlyWhitespace() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" \n\t \r\n ", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess(" \n\t \r\n ", new PdfPageCount(1));
@@ -71,7 +71,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithNoUsableTextWhenTextContainsOnlySpecialCharacters() throws Exception { void evaluate_failsWithNoUsableTextWhenTextContainsOnlySpecialCharacters() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#$%^&*()_+-=[]{}|;:',.<>?/", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#$%^&*()_+-=[]{}|;:',.<>?/", new PdfPageCount(1));
@@ -84,7 +84,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithTextContainingSingleLetter() throws Exception { void evaluate_passesWithTextContainingSingleLetter() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("a", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("a", new PdfPageCount(1));
@@ -95,7 +95,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithTextContainingSingleDigit() throws Exception { void evaluate_passesWithTextContainingSingleDigit() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("5", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("5", new PdfPageCount(1));
@@ -106,7 +106,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithTextMixedWithSpecialCharactersIfLettersOrDigitsPresent() throws Exception { void evaluate_passesWithTextMixedWithSpecialCharactersIfLettersOrDigitsPresent() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#a$%^&*", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#a$%^&*", new PdfPageCount(1));
@@ -117,7 +117,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithWhitespaceAroundUsableText() throws Exception { void evaluate_passesWithWhitespaceAroundUsableText() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" meaningful text ", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess(" meaningful text ", new PdfPageCount(1));
@@ -128,7 +128,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithPageLimitExceededWhenPageCountEqualsLimit() throws Exception { void evaluate_failsWithPageLimitExceededWhenPageCountEqualsLimit() throws Exception {
StartConfiguration config = buildConfig(maxPages(5)); RuntimeConfiguration config = buildConfig(maxPages(5));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(5)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(5));
@@ -139,7 +139,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithPageLimitExceededWhenPageCountExceedsLimit() throws Exception { void evaluate_failsWithPageLimitExceededWhenPageCountExceedsLimit() throws Exception {
StartConfiguration config = buildConfig(maxPages(5)); RuntimeConfiguration config = buildConfig(maxPages(5));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(6)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(6));
@@ -152,7 +152,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_failsWithPageLimitExceededEvenIfTextIsValid() throws Exception { void evaluate_failsWithPageLimitExceededEvenIfTextIsValid() throws Exception {
StartConfiguration config = buildConfig(maxPages(2)); RuntimeConfiguration config = buildConfig(maxPages(2));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Excellent meaningful text with lots of content", new PdfPageCount(100)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Excellent meaningful text with lots of content", new PdfPageCount(100));
@@ -167,7 +167,7 @@ class PreCheckEvaluatorTest {
void evaluate_prefersPageLimitCheckOverTextCheck() throws Exception { void evaluate_prefersPageLimitCheckOverTextCheck() throws Exception {
// If both checks fail, page limit check should take precedence (not tested for priority, // If both checks fail, page limit check should take precedence (not tested for priority,
// but we verify that one failure is reported consistently) // but we verify that one failure is reported consistently)
StartConfiguration config = buildConfig(maxPages(1)); RuntimeConfiguration config = buildConfig(maxPages(1));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(10)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(10));
@@ -181,7 +181,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_throwsNullPointerExceptionWhenCandidateIsNull() throws Exception { void evaluate_throwsNullPointerExceptionWhenCandidateIsNull() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(1));
assertThrows(NullPointerException.class, () -> assertThrows(NullPointerException.class, () ->
@@ -191,7 +191,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_throwsNullPointerExceptionWhenExtractionIsNull() throws Exception { void evaluate_throwsNullPointerExceptionWhenExtractionIsNull() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
assertThrows(NullPointerException.class, () -> assertThrows(NullPointerException.class, () ->
@@ -211,7 +211,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithUnicodeGermanUmlauts() throws Exception { void evaluate_passesWithUnicodeGermanUmlauts() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Äußerst äöüß Großes", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Äußerst äöüß Großes", new PdfPageCount(1));
@@ -222,7 +222,7 @@ class PreCheckEvaluatorTest {
@Test @Test
void evaluate_passesWithOtherUnicodeCharacters() throws Exception { void evaluate_passesWithOtherUnicodeCharacters() throws Exception {
StartConfiguration config = buildConfig(maxPages(10)); RuntimeConfiguration config = buildConfig(maxPages(10));
SourceDocumentCandidate candidate = buildCandidate(); SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Αβγδ 中文 καλημέρα", new PdfPageCount(1)); PdfExtractionSuccess extraction = new PdfExtractionSuccess("Αβγδ 中文 καλημέρα", new PdfPageCount(1));
@@ -235,30 +235,8 @@ class PreCheckEvaluatorTest {
// Helpers // Helpers
// ========================================================================= // =========================================================================
private StartConfiguration buildConfig(int maxPages) throws Exception { private RuntimeConfiguration buildConfig(int maxPages) throws Exception {
Path sourceDir = Files.createDirectories(tempDir.resolve("source")); return new RuntimeConfiguration(maxPages);
Path targetDir = Files.createDirectories(tempDir.resolve("target"));
Path dbFile = tempDir.resolve("db.sqlite");
Files.createFile(dbFile);
Path promptFile = tempDir.resolve("prompt.txt");
Files.createFile(promptFile);
return new StartConfiguration(
sourceDir,
targetDir,
dbFile,
URI.create("https://api.example.com"),
"gpt-4",
30,
3,
maxPages,
50000,
promptFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
);
} }
private int maxPages(int limit) { private int maxPages(int limit) {

View File

@@ -1,6 +1,6 @@
package de.gecheckt.pdf.umbenenner.application.usecase; package de.gecheckt.pdf.umbenenner.application.usecase;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord; import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordLookupResult; import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordLookupResult;
@@ -71,7 +71,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_successfullyAcquiresAndReleasesLock() throws Exception { void execute_successfullyAcquiresAndReleasesLock() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
DefaultBatchRunProcessingUseCase useCase = buildUseCase( DefaultBatchRunProcessingUseCase useCase = buildUseCase(
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(), config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
@@ -88,7 +88,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_returnsLockUnavailableWhenLockCannotBeAcquired() throws Exception { void execute_returnsLockUnavailableWhenLockCannotBeAcquired() throws Exception {
CountingRunLockPort lockPort = new CountingRunLockPort(true); CountingRunLockPort lockPort = new CountingRunLockPort(true);
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
DefaultBatchRunProcessingUseCase useCase = buildUseCase( DefaultBatchRunProcessingUseCase useCase = buildUseCase(
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(), config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
@@ -108,7 +108,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_doesNotReleaseLockWhenAcquireFails() throws Exception { void execute_doesNotReleaseLockWhenAcquireFails() throws Exception {
CountingRunLockPort lockPort = new CountingRunLockPort(true); CountingRunLockPort lockPort = new CountingRunLockPort(true);
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
DefaultBatchRunProcessingUseCase useCase = buildUseCase( DefaultBatchRunProcessingUseCase useCase = buildUseCase(
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(), config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
@@ -125,7 +125,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_releasesLockEvenOnUnexpectedError() throws Exception { void execute_releasesLockEvenOnUnexpectedError() throws Exception {
ErrorAfterAcquireLockPort lockPort = new ErrorAfterAcquireLockPort(); ErrorAfterAcquireLockPort lockPort = new ErrorAfterAcquireLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
DefaultBatchRunProcessingUseCase useCase = buildUseCase( DefaultBatchRunProcessingUseCase useCase = buildUseCase(
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(), config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
@@ -146,7 +146,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_withNoCandidates_returnsSuccess() throws Exception { void execute_withNoCandidates_returnsSuccess() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
DefaultBatchRunProcessingUseCase useCase = buildUseCase( DefaultBatchRunProcessingUseCase useCase = buildUseCase(
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(), config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
@@ -161,7 +161,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_happyPath_candidatePassesPreChecks_persistenceInvoked() throws Exception { void execute_happyPath_candidatePassesPreChecks_persistenceInvoked() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("document.pdf"); SourceDocumentCandidate candidate = makeCandidate("document.pdf");
PdfExtractionSuccess success = new PdfExtractionSuccess("Invoice text", new PdfPageCount(1)); PdfExtractionSuccess success = new PdfExtractionSuccess("Invoice text", new PdfPageCount(1));
@@ -184,7 +184,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_noUsableText_candidateEndsControlled_batchContinues() throws Exception { void execute_noUsableText_candidateEndsControlled_batchContinues() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("image-only.pdf"); SourceDocumentCandidate candidate = makeCandidate("image-only.pdf");
PdfExtractionSuccess emptySuccess = new PdfExtractionSuccess(" ", new PdfPageCount(1)); PdfExtractionSuccess emptySuccess = new PdfExtractionSuccess(" ", new PdfPageCount(1));
@@ -206,7 +206,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_pageLimitExceeded_candidateEndsControlled_batchContinues() throws Exception { void execute_pageLimitExceeded_candidateEndsControlled_batchContinues() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("big.pdf"); SourceDocumentCandidate candidate = makeCandidate("big.pdf");
PdfExtractionSuccess manyPages = new PdfExtractionSuccess("Some text", new PdfPageCount(10)); PdfExtractionSuccess manyPages = new PdfExtractionSuccess("Some text", new PdfPageCount(10));
@@ -228,7 +228,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_extractionContentError_candidateEndsControlled_batchContinues() throws Exception { void execute_extractionContentError_candidateEndsControlled_batchContinues() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("encrypted.pdf"); SourceDocumentCandidate candidate = makeCandidate("encrypted.pdf");
PdfExtractionContentError contentError = new PdfExtractionContentError("PDF is encrypted"); PdfExtractionContentError contentError = new PdfExtractionContentError("PDF is encrypted");
@@ -250,7 +250,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_extractionTechnicalError_candidateEndsControlled_batchContinues() throws Exception { void execute_extractionTechnicalError_candidateEndsControlled_batchContinues() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("corrupt.pdf"); SourceDocumentCandidate candidate = makeCandidate("corrupt.pdf");
PdfExtractionTechnicalError technicalError = new PdfExtractionTechnicalError("I/O error reading file", null); PdfExtractionTechnicalError technicalError = new PdfExtractionTechnicalError("I/O error reading file", null);
@@ -272,7 +272,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_sourceAccessException_returnsFailure() throws Exception { void execute_sourceAccessException_returnsFailure() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidatesPort failingPort = () -> { SourceDocumentCandidatesPort failingPort = () -> {
throw new SourceDocumentAccessException("Source folder not readable"); throw new SourceDocumentAccessException("Source folder not readable");
@@ -297,7 +297,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_fingerprintFailure_candidateNotHistorised_batchContinues() throws Exception { void execute_fingerprintFailure_candidateNotHistorised_batchContinues() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf"); SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf");
FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate)); FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate));
@@ -322,7 +322,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_fingerprintFailure_extractionNotCalled() throws Exception { void execute_fingerprintFailure_extractionNotCalled() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf"); SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf");
FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate)); FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate));
@@ -350,7 +350,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_mixedBatch_allOutcomeTypes_batchOverallSucceeds() throws Exception { void execute_mixedBatch_allOutcomeTypes_batchOverallSucceeds() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
SourceDocumentCandidate goodCandidate = makeCandidate("good.pdf"); SourceDocumentCandidate goodCandidate = makeCandidate("good.pdf");
SourceDocumentCandidate noTextCandidate = makeCandidate("notext.pdf"); SourceDocumentCandidate noTextCandidate = makeCandidate("notext.pdf");
@@ -400,7 +400,7 @@ class BatchRunProcessingUseCaseTest {
@Test @Test
void execute_multipleCandidates_allProcessed_batchSucceeds() throws Exception { void execute_multipleCandidates_allProcessed_batchSucceeds() throws Exception {
MockRunLockPort lockPort = new MockRunLockPort(); MockRunLockPort lockPort = new MockRunLockPort();
StartConfiguration config = buildConfig(tempDir); RuntimeConfiguration config = buildConfig(tempDir);
List<SourceDocumentCandidate> candidates = List.of( List<SourceDocumentCandidate> candidates = List.of(
makeCandidate("a.pdf"), makeCandidate("a.pdf"),
@@ -429,41 +429,20 @@ class BatchRunProcessingUseCaseTest {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
private static DefaultBatchRunProcessingUseCase buildUseCase( private static DefaultBatchRunProcessingUseCase buildUseCase(
StartConfiguration config, RuntimeConfiguration runtimeConfig,
RunLockPort lockPort, RunLockPort lockPort,
SourceDocumentCandidatesPort candidatesPort, SourceDocumentCandidatesPort candidatesPort,
PdfTextExtractionPort extractionPort, PdfTextExtractionPort extractionPort,
FingerprintPort fingerprintPort, FingerprintPort fingerprintPort,
DocumentProcessingCoordinator processor) { DocumentProcessingCoordinator processor) {
return new DefaultBatchRunProcessingUseCase( return new DefaultBatchRunProcessingUseCase(
config, lockPort, candidatesPort, extractionPort, fingerprintPort, processor, runtimeConfig, lockPort, candidatesPort, extractionPort, fingerprintPort, processor,
new NoOpProcessingLogger()); new NoOpProcessingLogger());
} }
private static StartConfiguration buildConfig(Path tempDir) throws Exception { private static RuntimeConfiguration buildConfig(Path tempDir) throws Exception {
Path sourceDir = Files.createDirectories(tempDir.resolve("source")); // maxPages set to 3 useful for page-limit tests
Path targetDir = Files.createDirectories(tempDir.resolve("target")); return new RuntimeConfiguration(3);
Path dbFile = tempDir.resolve("db.sqlite");
if (!Files.exists(dbFile)) Files.createFile(dbFile);
Path promptFile = tempDir.resolve("prompt.txt");
if (!Files.exists(promptFile)) Files.createFile(promptFile);
return new StartConfiguration(
sourceDir,
targetDir,
dbFile,
URI.create("https://api.example.com"),
"gpt-4",
30,
3, // maxRetries
3, // maxPages (low limit useful for page-limit tests)
50000,
promptFile,
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
);
} }
private static SourceDocumentCandidate makeCandidate(String filename) { private static SourceDocumentCandidate makeCandidate(String filename) {

View File

@@ -20,6 +20,7 @@ import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteDocumentRecordReposit
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteProcessingAttemptRepositoryAdapter; 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.SqliteSchemaInitializationAdapter;
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteUnitOfWorkAdapter; import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteUnitOfWorkAdapter;
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome; 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.in.BatchRunProcessingUseCase;
@@ -122,14 +123,14 @@ public class BootstrapRunner {
/** /**
* Functional interface for creating a BatchRunProcessingUseCase. * Functional interface for creating a BatchRunProcessingUseCase.
* <p> * <p>
* Receives the already-loaded and validated {@link StartConfiguration} and run lock port. * Receives the full startup configuration (for infrastructure adapter wiring) and run lock port.
* The factory is responsible for creating and wiring any additional outbound ports * The factory extracts the runtime configuration and wires all outbound ports
* required by the use case (e.g., source document port, PDF extraction port, * required by the use case (e.g., source document port, PDF extraction port,
* persistence and fingerprint ports). * persistence and fingerprint ports).
*/ */
@FunctionalInterface @FunctionalInterface
public interface UseCaseFactory { public interface UseCaseFactory {
BatchRunProcessingUseCase create(StartConfiguration config, RunLockPort runLockPort); BatchRunProcessingUseCase create(StartConfiguration startConfig, RunLockPort runLockPort);
} }
/** /**
@@ -164,8 +165,11 @@ public class BootstrapRunner {
this.runLockPortFactory = FilesystemRunLockPortAdapter::new; this.runLockPortFactory = FilesystemRunLockPortAdapter::new;
this.validatorFactory = StartConfigurationValidator::new; this.validatorFactory = StartConfigurationValidator::new;
this.schemaInitPortFactory = SqliteSchemaInitializationAdapter::new; this.schemaInitPortFactory = SqliteSchemaInitializationAdapter::new;
this.useCaseFactory = (config, lock) -> { this.useCaseFactory = (startConfig, lock) -> {
String jdbcUrl = buildJdbcUrl(config); // Extract runtime configuration from startup configuration
RuntimeConfiguration runtimeConfig = new RuntimeConfiguration(startConfig.maxPages());
String jdbcUrl = buildJdbcUrl(startConfig);
FingerprintPort fingerprintPort = new Sha256FingerprintAdapter(); FingerprintPort fingerprintPort = new Sha256FingerprintAdapter();
DocumentRecordRepository documentRecordRepository = DocumentRecordRepository documentRecordRepository =
new SqliteDocumentRecordRepositoryAdapter(jdbcUrl); new SqliteDocumentRecordRepositoryAdapter(jdbcUrl);
@@ -178,9 +182,9 @@ public class BootstrapRunner {
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository, unitOfWorkPort, coordinatorLogger); new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository, unitOfWorkPort, coordinatorLogger);
ProcessingLogger useCaseLogger = new Log4jProcessingLogger(DefaultBatchRunProcessingUseCase.class); ProcessingLogger useCaseLogger = new Log4jProcessingLogger(DefaultBatchRunProcessingUseCase.class);
return new DefaultBatchRunProcessingUseCase( return new DefaultBatchRunProcessingUseCase(
config, runtimeConfig,
lock, lock,
new SourceDocumentCandidatesPortAdapter(config.sourceFolder()), new SourceDocumentCandidatesPortAdapter(startConfig.sourceFolder()),
new PdfTextExtractionPortAdapter(), new PdfTextExtractionPortAdapter(),
fingerprintPort, fingerprintPort,
documentProcessingCoordinator, documentProcessingCoordinator,