Fixe SonarQube Reliability-Issues S2789, S3077 und S2184
S2789 (32 Stellen): null-Checks auf Optional-Feldern entfernt bzw. durch Objects.requireNonNullElse(field, Optional.empty()) ersetzt. Die zuvor defensive Behandlung von null-Optionals erfolgt jetzt ueber den Bibliotheksaufruf, sodass das Verhalten unveraendert bleibt, aber die direkte Null-Pruefung gegen Optional entfaellt. S3077 (5 Stellen): volatile-Felder mit Objekt-Referenzen durch AtomicReference ersetzt (ScheduledExecutorServiceSchedulerAdapter, BootstrapRunner.guiApplicationRunContext, PdfPreviewPane.currentDocument/ currentRenderer/currentSourceFile, SingleInstanceGuard.socket). Die PdfPreviewPane-Felder werden auf JavaFX- bzw. Worker-Thread genutzt; AtomicReference bietet hier konsistente atomare Publikation ohne Verhaltensaenderung. S2184 (3 Stellen): Integer-Division SECONDARY_SPACING / 2 durch SECONDARY_SPACING / 2.0 ersetzt, damit das Insets-Argument als double ohne implizite Truncierung berechnet wird.
This commit is contained in:
+2
-1
@@ -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<SchedulerControlUseCase> schedulerUseCase,
|
||||
Supplier<Boolean> 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();
|
||||
|
||||
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-6
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -302,7 +302,7 @@ public final class GuiBatchRunTab {
|
||||
targetFolderSupplier, "targetFolderSupplier must not be null");
|
||||
|
||||
Optional<ConfigurationFileLockPort> 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;
|
||||
}
|
||||
|
||||
+17
-16
@@ -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<PDDocument> 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<PDFRenderer> 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<Path> 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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-5
@@ -54,7 +54,7 @@ public class ScheduledExecutorServiceSchedulerAdapter implements SchedulerPort {
|
||||
*/
|
||||
final AtomicReference<BatchRunTrigger> currentTrigger = new AtomicReference<>();
|
||||
|
||||
private volatile ScheduledExecutorService executor;
|
||||
private final AtomicReference<ScheduledExecutorService> 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.");
|
||||
|
||||
+4
-3
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-12
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-3
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+17
-15
@@ -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<ApplicationRunContext> guiApplicationRunContext = Optional.empty();
|
||||
private final AtomicReference<Optional<ApplicationRunContext>> 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<ApplicationRunContext> ctxOpt = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> 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<ApplicationRunContext> ctx = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> 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<ApplicationRunContext> ctx = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> 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<ApplicationRunContext> ctx = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> 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<ApplicationRunContext> ctx = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> 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<ApplicationRunContext> ctx = guiApplicationRunContext;
|
||||
Optional<ApplicationRunContext> ctx = guiApplicationRunContext.get();
|
||||
if (ctx.isPresent()) {
|
||||
return ctx.get().startConfiguration();
|
||||
}
|
||||
|
||||
+4
-3
@@ -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<ServerSocket> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user