diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiSchedulerTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiSchedulerTab.java index cc596cc..4b7960f 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiSchedulerTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiSchedulerTab.java @@ -4,6 +4,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -105,7 +106,7 @@ public final class GuiSchedulerTab { public GuiSchedulerTab( Optional schedulerUseCase, Supplier isConfigDirty) { - this.schedulerUseCase = schedulerUseCase == null ? Optional.empty() : schedulerUseCase; + this.schedulerUseCase = Objects.requireNonNullElse(schedulerUseCase, Optional.empty()); this.isConfigDirty = isConfigDirty != null ? isConfigDirty : () -> false; tab.setClosable(false); buildUi(); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStartupContext.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStartupContext.java index 027d35c..5291370 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStartupContext.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiStartupContext.java @@ -146,8 +146,8 @@ public record GuiStartupContext( */ public GuiStartupContext { initialState = Objects.requireNonNull(initialState, "initialState must not be null"); - startupNotice = startupNotice == null ? Optional.empty() : startupNotice; - applicationContextError = applicationContextError == null ? Optional.empty() : applicationContextError; + startupNotice = Objects.requireNonNullElse(startupNotice, Optional.empty()); + applicationContextError = Objects.requireNonNullElse(applicationContextError, Optional.empty()); configurationFileLoader = Objects.requireNonNull(configurationFileLoader, "configurationFileLoader must not be null"); configurationFileWriter = Objects.requireNonNull(configurationFileWriter, @@ -191,8 +191,8 @@ public record GuiStartupContext( "promptEditorPortFactory must not be null"); createNewDatabasePort = Objects.requireNonNull(createNewDatabasePort, "createNewDatabasePort must not be null"); - schedulerControlUseCase = schedulerControlUseCase == null ? Optional.empty() : schedulerControlUseCase; - configurationFileLockPort = configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort; + schedulerControlUseCase = Objects.requireNonNullElse(schedulerControlUseCase, Optional.empty()); + configurationFileLockPort = Objects.requireNonNullElse(configurationFileLockPort, Optional.empty()); applicationContextInitializer = applicationContextInitializer == null ? GuiApplicationContextInitializer.noOp() : applicationContextInitializer; } diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java index 9923822..8f9ee0b 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java @@ -276,7 +276,7 @@ public final class GuiBatchRunCoordinator { this.historicalDocumentContextPort = Objects.requireNonNull( historicalDocumentContextPort, "historicalDocumentContextPort must not be null"); this.configurationFileLockPort = - configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort; + Objects.requireNonNullElse(configurationFileLockPort, Optional.empty()); } /** diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunLaunchOutcome.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunLaunchOutcome.java index 0a072a0..01c5898 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunLaunchOutcome.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunLaunchOutcome.java @@ -33,7 +33,7 @@ public record GuiBatchRunLaunchOutcome( * Compact constructor normalising the failure message holder. */ public GuiBatchRunLaunchOutcome { - failureMessage = failureMessage == null ? Optional.empty() : failureMessage; + failureMessage = Objects.requireNonNullElse(failureMessage, Optional.empty()); } /** diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunResultRow.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunResultRow.java index a552fd0..66c71b5 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunResultRow.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunResultRow.java @@ -88,16 +88,16 @@ public record GuiBatchRunResultRow( } Objects.requireNonNull(fingerprint, "fingerprint must not be null"); Objects.requireNonNull(status, "status must not be null"); - finalFileName = finalFileName == null ? Optional.empty() : finalFileName; - correctedFileName = correctedFileName == null ? Optional.empty() : correctedFileName; - resolvedDate = resolvedDate == null ? Optional.empty() : resolvedDate; - aiReasoning = aiReasoning == null ? Optional.empty() : aiReasoning; - aiFailureMessage = aiFailureMessage == null ? Optional.empty() : aiFailureMessage; + finalFileName = Objects.requireNonNullElse(finalFileName, Optional.empty()); + correctedFileName = Objects.requireNonNullElse(correctedFileName, Optional.empty()); + resolvedDate = Objects.requireNonNullElse(resolvedDate, Optional.empty()); + aiReasoning = Objects.requireNonNullElse(aiReasoning, Optional.empty()); + aiFailureMessage = Objects.requireNonNullElse(aiFailureMessage, Optional.empty()); Objects.requireNonNull(processingDuration, "processingDuration must not be null"); if (processingDuration.isNegative()) { throw new IllegalArgumentException("processingDuration must not be negative"); } - historicalContext = historicalContext == null ? Optional.empty() : historicalContext; + historicalContext = Objects.requireNonNullElse(historicalContext, Optional.empty()); } /** diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java index d4bfe9e..3e4919d 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java @@ -302,7 +302,7 @@ public final class GuiBatchRunTab { targetFolderSupplier, "targetFolderSupplier must not be null"); Optional effectiveLockPort = - configurationFileLockPort == null ? Optional.empty() : configurationFileLockPort; + Objects.requireNonNullElse(configurationFileLockPort, Optional.empty()); this.coordinator = new GuiBatchRunCoordinator( (configPath, observer, token) -> launcherSupplier.get().launch(configPath, observer, token), @@ -618,7 +618,7 @@ public final class GuiBatchRunTab { HBox selectionButtonBar = new HBox(SECONDARY_SPACING, reprocessButton, resetStatusButton); selectionButtonBar.setAlignment(Pos.CENTER_LEFT); - selectionButtonBar.setPadding(new Insets(SECONDARY_SPACING / 2, 0, SECONDARY_SPACING / 2, 0)); + selectionButtonBar.setPadding(new Insets(SECONDARY_SPACING / 2.0, 0, SECONDARY_SPACING / 2.0, 0)); // Meldungsbereich unterhalb der Selektions-Buttons (linke Spalte) messageArea.setId("batch-run-message-area"); @@ -1246,7 +1246,7 @@ public final class GuiBatchRunTab { HBox runButtonBar = new HBox(SECONDARY_SPACING, startButton, cancelButton); runButtonBar.setAlignment(Pos.CENTER_LEFT); - runButtonBar.setPadding(new Insets(SECONDARY_SPACING / 2, 0, 0, 0)); + runButtonBar.setPadding(new Insets(SECONDARY_SPACING / 2.0, 0, 0, 0)); return runButtonBar; } diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java index 5e3c11b..ab82957 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java @@ -8,6 +8,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -170,18 +171,18 @@ public final class PdfPreviewPane { /** * Aktuell geöffnetes PDF-Dokument. Zugriff ausschließlich vom Worker-Thread. - * {@code null} wenn kein Dokument geöffnet ist. + * Leerer Referenzwert wenn kein Dokument geöffnet ist. */ - private volatile PDDocument currentDocument = null; + private final AtomicReference currentDocument = new AtomicReference<>(); /** * Renderer für das aktuell geöffnete Dokument. Zugriff ausschließlich vom Worker-Thread. - * {@code null} wenn kein Dokument geöffnet ist. + * Leerer Referenzwert wenn kein Dokument geöffnet ist. */ - private volatile PDFRenderer currentRenderer = null; + private final AtomicReference currentRenderer = new AtomicReference<>(); - /** Aktuell geladene Quelldatei; null wenn keine Selektion vorliegt. */ - private volatile Path currentSourceFile = null; + /** Aktuell geladene Quelldatei; leerer Referenzwert wenn keine Selektion vorliegt. */ + private final AtomicReference currentSourceFile = new AtomicReference<>(); /** Aktuell angezeigte Seite (1-basiert; 0 wenn keine Datei geladen). */ private volatile int currentPage = 0; @@ -303,7 +304,7 @@ public final class PdfPreviewPane { clear(); return; } - currentSourceFile = sourceFile; + currentSourceFile.set(sourceFile); currentPage = 0; totalPages = -1; pageCache.clear(); @@ -318,7 +319,7 @@ public final class PdfPreviewPane { * Muss auf dem JavaFX Application Thread aufgerufen werden. */ public void clear() { - currentSourceFile = null; + currentSourceFile.set(null); currentPage = 0; totalPages = -1; pageCache.clear(); @@ -472,12 +473,13 @@ public final class PdfPreviewPane { try { PDDocument doc = Loader.loadPDF(ioFile); - currentDocument = doc; - currentRenderer = new PDFRenderer(doc); + currentDocument.set(doc); + PDFRenderer renderer = new PDFRenderer(doc); + currentRenderer.set(renderer); int pages = Math.max(1, doc.getNumberOfPages()); BufferedImage buffered = - currentRenderer.renderImageWithDPI(0, RENDER_DPI, ImageType.RGB); + renderer.renderImageWithDPI(0, RENDER_DPI, ImageType.RGB); Image fxImage = SwingFXUtils.toFXImage(buffered, null); final int totalPagesFinal = pages; @@ -513,7 +515,7 @@ public final class PdfPreviewPane { * @param seq die Sequenznummer dieser Anforderung */ private void renderPageOnWorker(int page, long seq) { - PDFRenderer renderer = currentRenderer; + PDFRenderer renderer = currentRenderer.get(); if (renderer == null) { // Dokument wurde zwischenzeitlich geschlossen – nichts zu tun return; @@ -542,9 +544,8 @@ public final class PdfPreviewPane { * auf dem Worker-Thread und ist idempotent. */ private void closeCurrentDocumentOnWorker() { - PDDocument doc = currentDocument; - currentDocument = null; - currentRenderer = null; + PDDocument doc = currentDocument.getAndSet(null); + currentRenderer.set(null); if (doc != null) { try { doc.close(); @@ -824,7 +825,7 @@ public final class PdfPreviewPane { } private void updateNavigationButtons() { - boolean canNavigate = enabled && currentSourceFile != null && totalPages > 0; + boolean canNavigate = enabled && currentSourceFile.get() != null && totalPages > 0; prevButton.setDisable(!canNavigate || currentPage <= 1); nextButton.setDisable(!canNavigate || currentPage >= totalPages); } diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiConfigurationEditorState.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiConfigurationEditorState.java index 6d8010a..207bc3a 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiConfigurationEditorState.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiConfigurationEditorState.java @@ -29,10 +29,10 @@ public record GuiConfigurationEditorState( * @param values current editable configuration values; must not be {@code null} */ public GuiConfigurationEditorState { - loadedFileSnapshot = loadedFileSnapshot == null ? Optional.empty() : loadedFileSnapshot; + loadedFileSnapshot = Objects.requireNonNullElse(loadedFileSnapshot, Optional.empty()); baselineValues = Objects.requireNonNull(baselineValues, "baselineValues must not be null"); values = Objects.requireNonNull(values, "values must not be null"); - pendingMigrationMessage = pendingMigrationMessage == null ? Optional.empty() : pendingMigrationMessage; + pendingMigrationMessage = Objects.requireNonNullElse(pendingMigrationMessage, Optional.empty()); } /** diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiMessageEntry.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiMessageEntry.java index a46cd95..f0d932b 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiMessageEntry.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/editor/GuiMessageEntry.java @@ -39,7 +39,7 @@ public record GuiMessageEntry( Objects.requireNonNull(severity, "severity must not be null"); Objects.requireNonNull(text, "text must not be null"); Objects.requireNonNull(timestamp, "timestamp must not be null"); - source = source == null ? Optional.empty() : source; + source = Objects.requireNonNullElse(source, Optional.empty()); } /** diff --git a/pdf-umbenenner-adapter-in-scheduler/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/scheduler/ScheduledExecutorServiceSchedulerAdapter.java b/pdf-umbenenner-adapter-in-scheduler/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/scheduler/ScheduledExecutorServiceSchedulerAdapter.java index 7a40ff4..28f82ca 100644 --- a/pdf-umbenenner-adapter-in-scheduler/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/scheduler/ScheduledExecutorServiceSchedulerAdapter.java +++ b/pdf-umbenenner-adapter-in-scheduler/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/scheduler/ScheduledExecutorServiceSchedulerAdapter.java @@ -54,7 +54,7 @@ public class ScheduledExecutorServiceSchedulerAdapter implements SchedulerPort { */ final AtomicReference currentTrigger = new AtomicReference<>(); - private volatile ScheduledExecutorService executor; + private final AtomicReference executor = new AtomicReference<>(); /** * Erstellt einen neuen Adapter. @@ -85,7 +85,7 @@ public class ScheduledExecutorServiceSchedulerAdapter implements SchedulerPort { public void startScheduler(SchedulerConfig config, BatchRunTrigger trigger) { Objects.requireNonNull(config, "config darf nicht null sein"); Objects.requireNonNull(trigger, "trigger darf nicht null sein"); - if (executor != null) { + if (executor.get() != null) { logger.debug("Scheduler ist bereits aktiv – Start-Aufruf wird ignoriert."); return; } @@ -105,7 +105,7 @@ public class ScheduledExecutorServiceSchedulerAdapter implements SchedulerPort { 0L, config.intervalSeconds(), TimeUnit.SECONDS); - executor = newExecutor; + executor.set(newExecutor); logger.info("Scheduler gestartet. Intervall: {} Sekunden.", config.intervalSeconds()); } @@ -118,12 +118,11 @@ public class ScheduledExecutorServiceSchedulerAdapter implements SchedulerPort { */ @Override public void stopScheduler() { - ScheduledExecutorService localExecutor = executor; + ScheduledExecutorService localExecutor = executor.getAndSet(null); if (localExecutor == null) { logger.debug("Scheduler ist bereits gestoppt – Stop-Aufruf wird ignoriert."); return; } - executor = null; currentTrigger.set(null); localExecutor.shutdown(); logger.info("Scheduler angehalten."); diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/HistoricalDocumentContext.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/HistoricalDocumentContext.java index 4cc2092..8fe6e16 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/HistoricalDocumentContext.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/HistoricalDocumentContext.java @@ -1,6 +1,7 @@ package de.gecheckt.pdf.umbenenner.application.port.in; import java.time.Instant; +import java.util.Objects; import java.util.Optional; /** @@ -37,9 +38,9 @@ public record HistoricalDocumentContext( * {@code lastFailureInstant} {@code null} sind */ public HistoricalDocumentContext { - lastTargetFileName = lastTargetFileName == null ? Optional.empty() : lastTargetFileName; - lastSuccessInstant = lastSuccessInstant == null ? Optional.empty() : lastSuccessInstant; - lastFailureInstant = lastFailureInstant == null ? Optional.empty() : lastFailureInstant; + lastTargetFileName = Objects.requireNonNullElse(lastTargetFileName, Optional.empty()); + lastSuccessInstant = Objects.requireNonNullElse(lastSuccessInstant, Optional.empty()); + lastFailureInstant = Objects.requireNonNullElse(lastFailureInstant, Optional.empty()); } /** diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/SchedulerStatus.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/SchedulerStatus.java index 4bd1cc4..668c4cd 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/SchedulerStatus.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/in/SchedulerStatus.java @@ -1,6 +1,7 @@ package de.gecheckt.pdf.umbenenner.application.port.in; import java.time.Instant; +import java.util.Objects; import java.util.Optional; import de.gecheckt.pdf.umbenenner.application.port.out.RunSummary; @@ -48,18 +49,10 @@ public record SchedulerStatus( if (state == null) { throw new IllegalArgumentException("state darf nicht null sein"); } - if (lastRunEndedAt == null) { - throw new IllegalArgumentException("lastRunEndedAt darf nicht null sein"); - } - if (lastRunSummary == null) { - throw new IllegalArgumentException("lastRunSummary darf nicht null sein"); - } - if (nextTickAt == null) { - throw new IllegalArgumentException("nextTickAt darf nicht null sein"); - } - if (lastError == null) { - throw new IllegalArgumentException("lastError darf nicht null sein"); - } + Objects.requireNonNull(lastRunEndedAt, "lastRunEndedAt darf nicht null sein"); + Objects.requireNonNull(lastRunSummary, "lastRunSummary darf nicht null sein"); + Objects.requireNonNull(nextTickAt, "nextTickAt darf nicht null sein"); + Objects.requireNonNull(lastError, "lastError darf nicht null sein"); } /** diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/EffectiveApiKeyDescriptor.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/EffectiveApiKeyDescriptor.java index c6d5ace..6277890 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/EffectiveApiKeyDescriptor.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/EffectiveApiKeyDescriptor.java @@ -32,7 +32,7 @@ public record EffectiveApiKeyDescriptor( */ public EffectiveApiKeyDescriptor { Objects.requireNonNull(origin, "origin must not be null"); - envVarName = envVarName == null ? Optional.empty() : envVarName; + envVarName = Objects.requireNonNullElse(envVarName, Optional.empty()); } /** diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/ModelCatalogRequest.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/ModelCatalogRequest.java index 2b363ca..203b9f4 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/ModelCatalogRequest.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/modelcatalog/ModelCatalogRequest.java @@ -45,7 +45,7 @@ public record ModelCatalogRequest( if (timeoutSeconds <= 0) { throw new IllegalArgumentException("timeoutSeconds must be positive, was: " + timeoutSeconds); } - baseUrl = baseUrl == null ? Optional.empty() : baseUrl; - apiKey = apiKey == null ? Optional.empty() : apiKey; + baseUrl = Objects.requireNonNullElse(baseUrl, Optional.empty()); + apiKey = Objects.requireNonNullElse(apiKey, Optional.empty()); } } diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/editor/EditorValidationFinding.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/editor/EditorValidationFinding.java index d4ba8cf..fa56650 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/editor/EditorValidationFinding.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/editor/EditorValidationFinding.java @@ -38,7 +38,7 @@ public record EditorValidationFinding( public EditorValidationFinding { Objects.requireNonNull(severity, "severity must not be null"); Objects.requireNonNull(message, "message must not be null"); - fieldKey = fieldKey == null ? Optional.empty() : fieldKey; + fieldKey = Objects.requireNonNullElse(fieldKey, Optional.empty()); } /** diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/technicaltest/CheckpointResult.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/technicaltest/CheckpointResult.java index 2e392b7..9d2709a 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/technicaltest/CheckpointResult.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/validation/technicaltest/CheckpointResult.java @@ -89,9 +89,7 @@ public sealed interface CheckpointResult Objects.requireNonNull(checkpointId, "checkpointId must not be null"); Objects.requireNonNull(severity, "severity must not be null"); Objects.requireNonNull(message, "message must not be null"); - correctionSuggestion = correctionSuggestion == null - ? Optional.empty() - : correctionSuggestion; + correctionSuggestion = Objects.requireNonNullElse(correctionSuggestion, Optional.empty()); } /** diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java index 8be38d3..96ccd2e 100644 --- a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -260,10 +261,11 @@ public class BootstrapRunner { * or via the {@link GuiApplicationContextInitializer} callback that the workspace invokes * on a background thread after each successful file open. Read by * {@link #launchGuiBatchRun}, {@link #launchGuiMiniBatchRun}, and - * {@link #resetDocumentStatusForGui}. {@code volatile} ensures visibility across threads - * without explicit synchronisation on the happy path. + * {@link #resetDocumentStatusForGui}. {@link AtomicReference} ensures atomic publication + * of the context across threads. */ - private volatile Optional guiApplicationRunContext = Optional.empty(); + private final AtomicReference> guiApplicationRunContext = + new AtomicReference<>(Optional.empty()); /** * Der Scheduler-Use-Case, der beim GUI-Start mit einer gültigen Konfiguration @@ -1083,7 +1085,7 @@ public class BootstrapRunner { result -> LOG.debug("Scheduler-Tick-Ergebnis: {}", result.getClass().getSimpleName())); BatchRunTrigger batchRunTrigger = () -> { - Optional ctxOpt = guiApplicationRunContext; + Optional ctxOpt = guiApplicationRunContext.get(); if (ctxOpt.isEmpty()) { return new BatchRunTriggerResult.Failed( "Kein Anwendungskontext verfügbar.", @@ -1248,28 +1250,28 @@ public class BootstrapRunner { migrateConfigurationIfNeeded(configFilePath); StartConfiguration config = loadAndValidateConfiguration(configFilePath); initializeSchema(config); - guiApplicationRunContext = Optional.of( - new ApplicationRunContext(config, resolveActiveJdbcUrl(config))); + guiApplicationRunContext.set(Optional.of( + new ApplicationRunContext(config, resolveActiveJdbcUrl(config)))); LOG.info("GUI-Anwendungskontext initialisiert für Konfiguration: {}", configFilePath); return Optional.empty(); } catch (ConfigurationLoadingException e) { LOG.warn("GUI-Anwendungskontext: Konfiguration konnte nicht geladen werden: {}", e.getMessage()); - guiApplicationRunContext = Optional.empty(); + guiApplicationRunContext.set(Optional.empty()); return Optional.of(CONFIG_LOAD_FAILED_PREFIX + e.getMessage()); } catch (InvalidStartConfigurationException e) { LOG.warn("GUI-Anwendungskontext: Konfiguration nicht lauffähig: {}", e.getMessage()); - guiApplicationRunContext = Optional.empty(); + guiApplicationRunContext.set(Optional.empty()); return Optional.of("Konfiguration nicht lauffähig: " + e.getMessage()); } catch (DocumentPersistenceException e) { LOG.warn("GUI-Anwendungskontext: SQLite-Initialisierung fehlgeschlagen: {}", e.getMessage()); - guiApplicationRunContext = Optional.empty(); + guiApplicationRunContext.set(Optional.empty()); return Optional.of("SQLite konnte nicht initialisiert werden: " + e.getMessage()); } catch (RuntimeException e) { LOG.warn("GUI-Anwendungskontext: Unerwarteter Fehler bei Initialisierung: {}", e.getMessage()); - guiApplicationRunContext = Optional.empty(); + guiApplicationRunContext.set(Optional.empty()); return Optional.of("Unerwarteter Fehler bei der Kontextinitialisierung: " + (e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage())); } @@ -1422,7 +1424,7 @@ public class BootstrapRunner { Objects.requireNonNull(cancellationToken, "cancellationToken must not be null"); LOG.info("GUI-Verarbeitungslauf: Startanforderung für Konfiguration {}.", configFilePath); - Optional ctx = guiApplicationRunContext; + Optional ctx = guiApplicationRunContext.get(); if (ctx.isPresent()) { LOG.debug("GUI-Verarbeitungslauf: Verwende vorbereiteten Anwendungskontext."); return executeRun(ctx.get(), progressObserver, cancellationToken); @@ -1512,7 +1514,7 @@ public class BootstrapRunner { LOG.info("GUI-Mini-Verarbeitungslauf: Startanforderung für {} Dokument(e), Konfiguration {}.", fingerprintFilter.size(), configFilePath); - Optional ctx = guiApplicationRunContext; + Optional ctx = guiApplicationRunContext.get(); if (ctx.isPresent()) { LOG.debug("GUI-Mini-Verarbeitungslauf: Verwende vorbereiteten Anwendungskontext."); return executeRunWithFilter(ctx.get(), fingerprintFilter, progressObserver, cancellationToken); @@ -1595,7 +1597,7 @@ public class BootstrapRunner { LOG.info("GUI-Status-Reset: {} Dokument(e) zurücksetzen, Konfiguration {}.", fingerprints.size(), configFilePath); - Optional ctx = guiApplicationRunContext; + Optional ctx = guiApplicationRunContext.get(); if (ctx.isPresent()) { LOG.debug("GUI-Status-Reset: Verwende vorbereiteten Anwendungskontext."); return executeResetWithContext(ctx.get(), fingerprints); @@ -2181,7 +2183,7 @@ public class BootstrapRunner { * @return aktive JDBC-URL; nie {@code null} */ private String resolveJdbcUrlForGui(Path configFilePath) { - Optional ctx = guiApplicationRunContext; + Optional ctx = guiApplicationRunContext.get(); if (ctx.isPresent()) { return ctx.get().jdbcUrl(); } @@ -2204,7 +2206,7 @@ public class BootstrapRunner { * @return validierte Konfiguration; nie {@code null} */ private StartConfiguration resolveStartConfigurationForGui(Path configFilePath) { - Optional ctx = guiApplicationRunContext; + Optional ctx = guiApplicationRunContext.get(); if (ctx.isPresent()) { return ctx.get().startConfiguration(); } diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/singleinstance/SingleInstanceGuard.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/singleinstance/SingleInstanceGuard.java index 8a04a93..b3ae382 100644 --- a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/singleinstance/SingleInstanceGuard.java +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/singleinstance/SingleInstanceGuard.java @@ -5,6 +5,7 @@ import java.net.BindException; import java.net.InetAddress; import java.net.ServerSocket; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; /** * Prozessweiter Einzelinstanz-Schutz auf Basis eines Loopback-ServerSocket-Binds. @@ -44,7 +45,7 @@ public class SingleInstanceGuard implements AutoCloseable { public static final int DEFAULT_PORT = 47832; private final int port; - private volatile ServerSocket socket; + private final AtomicReference socket = new AtomicReference<>(); private final AtomicBoolean closed = new AtomicBoolean(false); /** @@ -85,7 +86,7 @@ public class SingleInstanceGuard implements AutoCloseable { try { InetAddress loopback = InetAddress.getLoopbackAddress(); ServerSocket serverSocket = new ServerSocket(port, 1, loopback); - this.socket = serverSocket; + this.socket.set(serverSocket); Runtime.getRuntime().addShutdownHook(new Thread(() -> schliesseSilent(serverSocket), "single-instance-guard-shutdown")); } catch (BindException e) { @@ -106,7 +107,7 @@ public class SingleInstanceGuard implements AutoCloseable { @Override public void close() { if (closed.compareAndSet(false, true)) { - schliesseSilent(socket); + schliesseSilent(socket.get()); } }