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:
2026-05-07 17:11:29 +02:00
parent 11eac074ef
commit 32e32a9b27
18 changed files with 76 additions and 80 deletions
@@ -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();
}
@@ -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());
}
}