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:
+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