1
0

Implementierung für M2 vorläufig abgeschlossen

This commit is contained in:
2026-03-31 21:52:48 +02:00
parent 301f1acf08
commit 9d66a446b3
29 changed files with 1892 additions and 52 deletions

View File

@@ -0,0 +1,110 @@
package de.gecheckt.pdf.umbenenner.domain.model;
import java.time.Instant;
import java.util.Objects;
/**
* Technical context representing a single batch processing run.
* <p>
* Each batch run is assigned a unique {@link RunId} and has associated timestamp information.
* The context flows through the entire execution from Bootstrap through Use Case,
* enabling correlation of all activities and results within a single run.
* <p>
* Responsibilities:
* <ul>
* <li>Track the unique identity of the batch run</li>
* <li>Record when the run started (and eventually when it ends)</li>
* <li>Provide run context to persistence, logging, and result tracking (future milestones)</li>
* </ul>
* <p>
* This context is independent of individual document processing and contains
* no business logic. It is purely a technical container for run identity and timing.
*
* @since M2-AP-003
*/
public final class BatchRunContext {
private final RunId runId;
private final Instant startInstant;
private Instant endInstant;
/**
* Creates a new BatchRunContext with the given run ID and start time.
* <p>
* The end instant is initially null and may be set later via {@link #setEndInstant(Instant)}.
*
* @param runId the unique identifier for this run, must not be null
* @param startInstant the moment when the run started, must not be null
* @throws NullPointerException if runId or startInstant is null
*/
public BatchRunContext(RunId runId, Instant startInstant) {
this.runId = Objects.requireNonNull(runId, "RunId must not be null");
this.startInstant = Objects.requireNonNull(startInstant, "Start instant must not be null");
this.endInstant = null;
}
/**
* Returns the unique identifier of this run.
*
* @return the run ID, never null
*/
public RunId runId() {
return runId;
}
/**
* Returns the instant when this run started.
*
* @return the start instant, never null
*/
public Instant startInstant() {
return startInstant;
}
/**
* Returns the instant when this run ended, or null if the run has not yet completed.
* <p>
* The end instant is set by {@link #setEndInstant(Instant)} during run completion.
*
* @return the end instant, or null if the run is still in progress
*/
public Instant endInstant() {
return endInstant;
}
/**
* Sets the instant when this run ended.
* <p>
* This should be called once at the end of the batch run to mark completion.
* Typically called by the orchestration layer (Bootstrap) after the use case completes.
*
* @param endInstant the moment when the run completed, must not be null
* @throws NullPointerException if endInstant is null
* @throws IllegalStateException if end instant has already been set
*/
public void setEndInstant(Instant endInstant) {
Objects.requireNonNull(endInstant, "End instant must not be null");
if (this.endInstant != null) {
throw new IllegalStateException("End instant has already been set");
}
this.endInstant = endInstant;
}
/**
* Returns true if this run has completed (end instant is set).
*
* @return true if end instant is not null, false otherwise
*/
public boolean isCompleted() {
return endInstant != null;
}
@Override
public String toString() {
return "BatchRunContext{" +
"runId=" + runId +
", startInstant=" + startInstant +
", endInstant=" + endInstant +
'}';
}
}

View File

@@ -0,0 +1,79 @@
package de.gecheckt.pdf.umbenenner.domain.model;
/**
* Enumeration of all valid processing status values for a document within a batch run.
* <p>
* Each status reflects the outcome or current state of a document processing attempt.
* Status transitions follow the rules defined in the architecture specification and persist
* across multiple batch runs via the repository layer.
* <p>
* Status Categories:
* <ul>
* <li><strong>Final Success:</strong> {@link #SUCCESS}</li>
* <li><strong>Retryable Failure:</strong> {@link #FAILED_RETRYABLE}</li>
* <li><strong>Final Failure:</strong> {@link #FAILED_FINAL}</li>
* <li><strong>Skip (Already Processed):</strong> {@link #SKIPPED_ALREADY_PROCESSED}</li>
* <li><strong>Skip (Final Failure):</strong> {@link #SKIPPED_FINAL_FAILURE}</li>
* <li><strong>Processing (Transient):</strong> {@link #PROCESSING}</li>
* </ul>
*
* @since M2-AP-001
*/
public enum ProcessingStatus {
/**
* Document was successfully processed and written to the target location.
* <p>
* A document with this status will be skipped in all future batch runs.
* Status is final and irreversible.
*/
SUCCESS,
/**
* Processing failed with a transient error (temporary infrastructure problem).
* <p>
* Examples: API timeout, temporary file lock, momentary network issue.
* <p>
* A document with this status may be retried in a later batch run, up to the
* configured maximum retry count. Retry count is tracked separately.
*/
FAILED_RETRYABLE,
/**
* Processing failed with a deterministic content error (non-recoverable problem).
* <p>
* Examples: PDF has no extractable text, page limit exceeded, document is ambiguous.
* <p>
* A document with this status receives exactly one retry in a later batch run.
* After that retry, if it still fails, status becomes {@link #FAILED_FINAL}.
* No further retries are attempted.
*/
FAILED_FINAL,
/**
* Document was skipped because it has already been successfully processed.
* <p>
* This is a final skip state. The document will remain skipped in all future runs.
* Document fingerprint matching ensures that identical content is never reprocessed.
*/
SKIPPED_ALREADY_PROCESSED,
/**
* Document was skipped because it has already failed finally.
* <p>
* This is a final skip state. The document will remain skipped in all future runs.
* No further processing attempts will be made.
*/
SKIPPED_FINAL_FAILURE,
/**
* Technical transient status: Document is currently being processed.
* <p>
* This status is used internally during a batch run to mark documents
* that are actively being worked on. If a batch run is interrupted,
* documents in this state will be re-attempted in the next run.
* <p>
* This status should not persist across batch runs in normal operation.
*/
PROCESSING
}

View File

@@ -0,0 +1,73 @@
package de.gecheckt.pdf.umbenenner.domain.model;
import java.io.Serializable;
import java.util.Objects;
/**
* Unique identifier for a batch run.
* <p>
* Each invocation of the PDF Umbenenner application receives a unique RunId
* that persists for the entire batch processing cycle. The RunId is used to:
* <ul>
* <li>Correlate all documents processed in a single run</li>
* <li>Track attempt history and retry decisions across runs</li>
* <li>Identify logs and audit trails for a specific execution</li>
* </ul>
* <p>
* RunId is intentionally simple: a non-null, immutable string value.
* Implementations may choose UUID format, timestamp-based IDs, or sequential IDs.
* The internal structure is opaque to consumers.
*
* @since M2-AP-003
*/
public final class RunId implements Serializable {
private static final long serialVersionUID = 1L;
private final String value;
/**
* Creates a new RunId with the given string value.
* <p>
* The value must be non-null and non-empty. No format validation is enforced;
* implementations are free to choose their own ID scheme.
*
* @param value the unique identifier string, must not be null or empty
* @throws NullPointerException if value is null
* @throws IllegalArgumentException if value is empty
*/
public RunId(String value) {
Objects.requireNonNull(value, "RunId value must not be null");
if (value.trim().isEmpty()) {
throw new IllegalArgumentException("RunId value must not be empty");
}
this.value = value;
}
/**
* Returns the string representation of this RunId.
*
* @return the unique identifier value
*/
public String value() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RunId runId = (RunId) o;
return Objects.equals(value, runId.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public String toString() {
return value;
}
}

View File

@@ -0,0 +1,18 @@
/**
* Domain model package containing core value objects and enumerations.
* <p>
* This package contains the fundamental domain entities and status models required for document processing:
* <ul>
* <li>{@link de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus} — enumeration of all valid document processing states</li>
* </ul>
* <p>
* All classes in this package are:
* <ul>
* <li>Infrastructure-agnostic (no database, filesystem, network, or framework dependencies)</li>
* <li>Immutable value objects or enumerations</li>
* <li>Reusable across all layers via the Application and Adapter contracts</li>
* </ul>
*
* @since M2-AP-001
*/
package de.gecheckt.pdf.umbenenner.domain.model;

View File

@@ -1,7 +1,18 @@
/**
* Domain layer containing business entities and value objects.
* <p>
* This package is infrastructure-agnostic and contains no dependencies on external systems.
* AP-003: Currently empty as no domain logic has been implemented yet.
* This package is infrastructure-agnostic and contains no dependencies on external systems,
* databases, filesystems, HTTP clients, or any other technical infrastructure.
* <p>
* M2-AP-001 Implementation:
* <ul>
* <li>{@link de.gecheckt.pdf.umbenenner.domain.model} — Core domain model including
* {@link de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus} enumeration</li>
* </ul>
* <p>
* Subpackages:
* <ul>
* <li><strong>model:</strong> Domain entities and value objects (e.g., status enumerations)</li>
* </ul>
*/
package de.gecheckt.pdf.umbenenner.domain;