This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 0b73e4cffcdb (chores): fix SonarCloud S2276, S128, S3014 blocker 
issues (#24488)
0b73e4cffcdb is described below

commit 0b73e4cffcdb32291dc98a159fe2b6f3021251e3
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Jul 7 22:56:21 2026 +0200

    (chores): fix SonarCloud S2276, S128, S3014 blocker issues (#24488)
    
    (chores): fix SonarCloud S2276, S128, S3014 blocker issues
    
    - S2276: Replace Thread.sleep() with wait() on a private monitor in
      synchronized methods in JfrMemoryLeakDevConsole. Uses Camel StopWatch
      (monotonic) for the deadline and guards against spurious wakeups.
    - S128: Add missing break in switch case in XmlRoutesBuilderLoader.
    - S3014: Refactor IsolatedThreadGroup in RunMojo to use composition
      with Thread.UncaughtExceptionHandler instead of extending ThreadGroup.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../impl/console/JfrMemoryLeakDevConsole.java      | 21 +++++++-
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   |  1 +
 .../main/java/org/apache/camel/maven/RunMojo.java  | 56 ++++++++++++++--------
 3 files changed, 56 insertions(+), 22 deletions(-)

diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/JfrMemoryLeakDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/JfrMemoryLeakDevConsole.java
index 3815b6cc1b02..ae395ebc6b03 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/JfrMemoryLeakDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/JfrMemoryLeakDevConsole.java
@@ -40,6 +40,7 @@ import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -81,6 +82,10 @@ public class JfrMemoryLeakDevConsole extends 
AbstractDevConsole {
     private static final int MAX_STACK_FRAMES = 10;
     private static final int MAX_CHAIN_DEPTH = 20;
 
+    // Private monitor for GC wait delays so that wait() does not release the
+    // 'this' monitor of synchronized methods (which guards recording state).
+    private final Object gcWaitMonitor = new Object();
+
     private volatile Recording activeRecording;
     private volatile JsonObject cachedResults;
     private volatile JsonObject previousResults;
@@ -137,7 +142,13 @@ public class JfrMemoryLeakDevConsole extends 
AbstractDevConsole {
             // trigger GC before starting to establish a cleaner baseline
             System.gc();
             try {
-                Thread.sleep(500);
+                synchronized (gcWaitMonitor) {
+                    StopWatch watch = new StopWatch();
+                    long remaining;
+                    while ((remaining = 500 - watch.taken()) > 0) {
+                        gcWaitMonitor.wait(remaining);
+                    }
+                }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
             }
@@ -210,7 +221,13 @@ public class JfrMemoryLeakDevConsole extends 
AbstractDevConsole {
             // trigger GC before stopping to flush objects into the recording
             System.gc();
             try {
-                Thread.sleep(500);
+                synchronized (gcWaitMonitor) {
+                    StopWatch watch = new StopWatch();
+                    long remaining;
+                    while ((remaining = 500 - watch.taken()) > 0) {
+                        gcWaitMonitor.wait(remaining);
+                    }
+                }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
             }
diff --git 
a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
 
b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
index 181aea18d486..163e88b4501c 100644
--- 
a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
+++ 
b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
@@ -188,6 +188,7 @@ public class XmlRoutesBuilderLoader extends 
RouteBuilderLoaderSupport {
                         new XmlModelParser(resource, 
xmlInfo.getRootElementNamespace())
                                 .parseRouteConfigurationsDefinition()
                                 .ifPresent(this::addConfigurations);
+                        break;
                     }
                     default: {
                         // NO-OP
diff --git 
a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
 
b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
index a9337714a207..00bcc5f41e6f 100644
--- 
a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
+++ 
b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
@@ -390,7 +390,7 @@ public class RunMojo extends AbstractExecMojo {
                     "Cannot run Kamelet Main because camel-kamelet-main JAR is 
not available on classpath");
         }
 
-        final Thread bootstrapThread = new Thread(threadGroup, () -> {
+        final Thread bootstrapThread = threadGroup.newThread(() -> {
             try {
                 beforeBootstrapCamel();
 
@@ -406,7 +406,7 @@ public class RunMojo extends AbstractExecMojo {
                 getLog().error("Error occurred while running main from: " + 
mainClass);
                 getLog().error(e);
                 getLog().error("*************************************");
-                
Thread.currentThread().getThreadGroup().uncaughtException(Thread.currentThread(),
 e);
+                threadGroup.uncaughtException(Thread.currentThread(), e);
             }
         }, mainClass + ".main()");
 
@@ -459,11 +459,13 @@ public class RunMojo extends AbstractExecMojo {
         // noop
     }
 
-    class IsolatedThreadGroup extends ThreadGroup {
+    @SuppressWarnings("java:S3014") // ThreadGroup is needed internally for 
thread containment; no modern alternative in Java 17
+    class IsolatedThreadGroup implements Thread.UncaughtExceptionHandler {
+        private final ThreadGroup threadGroup;
         Throwable uncaughtException; // synchronize access to this
 
         IsolatedThreadGroup(String name) {
-            super(name);
+            this.threadGroup = new ThreadGroup(name);
         }
 
         @Override
@@ -482,13 +484,38 @@ public class RunMojo extends AbstractExecMojo {
                 getLog().warn("an additional exception was thrown", throwable);
             }
         }
+
+        Thread newThread(Runnable task, String name) {
+            Thread t = new Thread(threadGroup, task, name);
+            t.setUncaughtExceptionHandler(this);
+            return t;
+        }
+
+        Collection<Thread> getActiveThreads() {
+            Thread[] threads = new Thread[threadGroup.activeCount()];
+            threadGroup.enumerate(threads);
+            Collection<Thread> result = new ArrayList<>(threads.length);
+            for (int i = 0; i < threads.length && threads[i] != null; i++) {
+                result.add(threads[i]);
+            }
+            // note: the result should be modifiable
+            return result;
+        }
+
+        int activeCount() {
+            return threadGroup.activeCount();
+        }
+
+        void enumerate(Thread[] threads) {
+            threadGroup.enumerate(threads);
+        }
     }
 
-    private void joinNonDaemonThreads(ThreadGroup threadGroup) {
+    private void joinNonDaemonThreads(IsolatedThreadGroup threadGroup) {
         boolean foundNonDaemon;
         do {
             foundNonDaemon = false;
-            Collection<Thread> threads = getActiveThreads(threadGroup);
+            Collection<Thread> threads = threadGroup.getActiveThreads();
             for (Thread thread : threads) {
                 if (thread.isDaemon()) {
                     continue;
@@ -516,13 +543,13 @@ public class RunMojo extends AbstractExecMojo {
         }
     }
 
-    private void terminateThreads(ThreadGroup threadGroup) {
+    private void terminateThreads(IsolatedThreadGroup threadGroup) {
         StopWatch watch = new StopWatch();
         Set<Thread> uncooperativeThreads = new HashSet<>(); // these were not 
responsive
         // to interruption
-        for (Collection<Thread> threads = getActiveThreads(threadGroup);
+        for (Collection<Thread> threads = threadGroup.getActiveThreads();
              !threads.isEmpty();
-             threads = getActiveThreads(threadGroup), threads
+             threads = threadGroup.getActiveThreads(), threads
                      .removeAll(uncooperativeThreads)) {
             // Interrupt all threads we know about as of this instant (harmless
             // if spuriously went dead (! isAlive())
@@ -582,17 +609,6 @@ public class RunMojo extends AbstractExecMojo {
         }
     }
 
-    private Collection<Thread> getActiveThreads(ThreadGroup threadGroup) {
-        Thread[] threads = new Thread[threadGroup.activeCount()];
-        int numThreads = threadGroup.enumerate(threads);
-        Collection<Thread> result = new ArrayList<>(numThreads);
-        for (int i = 0; i < threads.length && threads[i] != null; i++) {
-            result.add(threads[i]);
-        }
-        // note: the result should be modifiable
-        return result;
-    }
-
     /**
      * Pass any given system properties to the java system properties.
      */

Reply via email to