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

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 95a6c24cef SonarQube bug fixes
95a6c24cef is described below

commit 95a6c24ceff9b7edc19e7d96d91c31f553a64c57
Author: James Bognar <[email protected]>
AuthorDate: Thu Feb 5 10:40:20 2026 -0500

    SonarQube bug fixes
---
 .../apache/juneau/commons/collections/Cache.java   | 21 +++++++++
 .../apache/juneau/commons/collections/Cache2.java  | 15 ++++++
 .../apache/juneau/commons/collections/Cache3.java  | 15 ++++++
 .../apache/juneau/commons/collections/Cache4.java  | 15 ++++++
 .../apache/juneau/commons/collections/Cache5.java  | 15 ++++++
 .../juneau/commons/io/CharSequenceReader.java      |  2 +-
 .../apache/juneau/commons/settings/Settings.java   | 14 ++++++
 .../org/apache/juneau/commons/utils/IoUtils.java   | 18 +++++++
 .../examples/core/config/store/SqlStore.java       |  2 +-
 .../juneau/commons/collections/Cache2_Test.java    | 44 +++++++++++++++++
 .../juneau/commons/collections/Cache3_Test.java    | 44 +++++++++++++++++
 .../juneau/commons/collections/Cache4_Test.java    | 44 +++++++++++++++++
 .../juneau/commons/collections/Cache5_Test.java    | 44 +++++++++++++++++
 .../juneau/commons/collections/Cache_Test.java     | 55 ++++++++++++++++++++++
 .../juneau/commons/settings/Settings_Test.java     | 49 +++++++++++++++++++
 .../apache/juneau/commons/utils/IoUtils_Test.java  | 16 +++++++
 16 files changed, 411 insertions(+), 2 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache.java
index c307bddd29..b761c590a9 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache.java
@@ -454,6 +454,7 @@ public class Cache<K,V> {
        // Internal map with Tuple1 keys for content-based equality (especially 
for arrays)
        // If threadLocal is true, this is null and threadLocalMap is used 
instead
        private final Map<Tuple1<K>,V> map;
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<Map<Tuple1<K>,V>> threadLocalMap;
 
        private final boolean isThreadLocal;
@@ -468,6 +469,7 @@ public class Cache<K,V> {
         */
        private final Map<K,Tuple1<K>> wrapperCache;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<Map<K,Tuple1<K>>> threadLocalWrapperCache;
 
        private final int maxSize;
@@ -525,6 +527,25 @@ public class Cache<K,V> {
                getWrapperCache().clear(); // Clean up wrapper cache
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
cache to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        * Only has an effect if this cache was created with thread-local mode 
enabled.
+        */
+       public void cleanup() {
+               if (isThreadLocal) {
+                       if (threadLocalMap != null) {
+                               threadLocalMap.remove();
+                       }
+                       if (threadLocalWrapperCache != null) {
+                               threadLocalWrapperCache.remove();
+                       }
+               }
+       }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified key.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache2.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache2.java
index 273f5f5246..39b017f9ba 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache2.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache2.java
@@ -391,6 +391,7 @@ public class Cache2<K1,K2,V> {
        // If threadLocal is true, this is null and threadLocalMap is used 
instead
        private final java.util.Map<Tuple2<K1,K2>,V> map;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<Map<Tuple2<K1,K2>,V>> threadLocalMap;
 
        private final boolean isThreadLocal;
@@ -440,6 +441,20 @@ public class Cache2<K1,K2,V> {
                getMap().clear();
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
cache to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        * Only has an effect if this cache was created with thread-local mode 
enabled.
+        */
+       public void cleanup() {
+               if (isThreadLocal && threadLocalMap != null) {
+                       threadLocalMap.remove();
+               }
+       }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified key pair.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache3.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache3.java
index 1183dc9c42..b829ed87ae 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache3.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache3.java
@@ -259,6 +259,7 @@ public class Cache3<K1,K2,K3,V> {
        // If threadLocal is true, this is null and threadLocalMap is used 
instead
        private final java.util.Map<Tuple3<K1,K2,K3>,V> map;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<Map<Tuple3<K1,K2,K3>,V>> threadLocalMap;
 
        private final boolean isThreadLocal;
@@ -308,6 +309,20 @@ public class Cache3<K1,K2,K3,V> {
                getMap().clear();
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
cache to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        * Only has an effect if this cache was created with thread-local mode 
enabled.
+        */
+       public void cleanup() {
+               if (isThreadLocal && threadLocalMap != null) {
+                       threadLocalMap.remove();
+               }
+       }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified key triplet.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache4.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache4.java
index 1ee8436bb2..f5ab6ed5bd 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache4.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache4.java
@@ -249,6 +249,7 @@ public class Cache4<K1,K2,K3,K4,V> {
        // If threadLocal is true, this is null and threadLocalMap is used 
instead
        private final java.util.Map<Tuple4<K1,K2,K3,K4>,V> map;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<java.util.Map<Tuple4<K1,K2,K3,K4>,V>> 
threadLocalMap;
 
        private final boolean isThreadLocal;
@@ -298,6 +299,20 @@ public class Cache4<K1,K2,K3,K4,V> {
                getMap().clear();
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
cache to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        * Only has an effect if this cache was created with thread-local mode 
enabled.
+        */
+       public void cleanup() {
+               if (isThreadLocal && threadLocalMap != null) {
+                       threadLocalMap.remove();
+               }
+       }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified four-part key.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache5.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache5.java
index 516999a2e7..54eaaac350 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache5.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/collections/Cache5.java
@@ -255,6 +255,7 @@ public class Cache5<K1,K2,K3,K4,K5,V> {
        // If threadLocal is true, this is null and threadLocalMap is used 
instead
        private final java.util.Map<Tuple5<K1,K2,K3,K4,K5>,V> map;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<java.util.Map<Tuple5<K1,K2,K3,K4,K5>,V>> 
threadLocalMap;
 
        private final boolean isThreadLocal;
@@ -304,6 +305,20 @@ public class Cache5<K1,K2,K3,K4,K5,V> {
                getMap().clear();
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
cache to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        * Only has an effect if this cache was created with thread-local mode 
enabled.
+        */
+       public void cleanup() {
+               if (isThreadLocal && threadLocalMap != null) {
+                       threadLocalMap.remove();
+               }
+       }
+
        /**
         * Returns <jk>true</jk> if the cache contains a mapping for the 
specified five-part key.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/CharSequenceReader.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/CharSequenceReader.java
index 4477755835..cddaafeea9 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/CharSequenceReader.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/CharSequenceReader.java
@@ -175,7 +175,7 @@ public class CharSequenceReader extends BufferedReader {
        public long skip(long ns) {
                if (next >= length)
                        return 0;
-               long n = Math.min(length - next, ns);
+               long n = Math.min((long) length - next, ns);
                n = Math.max(-next, n);
                next += n;
                return n;
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
index e2bf5e6761..8a6f19c54f 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java
@@ -323,6 +323,7 @@ public class Settings {
        }
 
        private final ResettableSupplier<SettingStore> globalStore;
+       @SuppressWarnings("java:S5164") // Cleanup method provided: cleanup()
        private final ThreadLocal<SettingStore> localStore;
        private final List<SettingSource> sources;
        private final Map<Class<?>,Function<String,?>> toTypeFunctions;
@@ -525,6 +526,19 @@ public class Settings {
                return this;
        }
 
+       /**
+        * Cleans up thread-local storage for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using this 
Settings instance to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        */
+       public void cleanup() {
+               if (localStore != null) {
+                       localStore.remove();
+               }
+       }
+
        /**
         * Clears all global overrides.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
index 8447e56f8a..b720ec3a96 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
@@ -47,7 +47,9 @@ public class IoUtils {
 
        private static final int BUFF_SIZE = 1024;
 
+       @SuppressWarnings("java:S5164") // Cleanup method provided: 
cleanupThreadLocals()
        private static final ThreadLocal<byte[]> BYTE_BUFFER_CACHE = 
(Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new 
ThreadLocal<>());
+       @SuppressWarnings("java:S5164") // Cleanup method provided: 
cleanupThreadLocals()
        private static final ThreadLocal<char[]> CHAR_BUFFER_CACHE = 
(Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new 
ThreadLocal<>());
        static final AtomicInteger BYTE_BUFFER_CACHE_HITS = new AtomicInteger();
 
@@ -59,6 +61,22 @@ public class IoUtils {
                shutdownMessage(() -> "Char buffer cache:  hits=" + 
CHAR_BUFFER_CACHE_HITS.get() + ", misses=" + CHAR_BUFFER_CACHE_MISSES);
        }
 
+       /**
+        * Cleans up thread-local buffer caches for the current thread.
+        *
+        * <p>
+        * This method should be called when a thread is finished using the 
buffer caches to prevent memory leaks
+        * in thread pool environments where threads are reused.
+        */
+       public static void cleanupThreadLocals() {
+               if (BYTE_BUFFER_CACHE != null) {
+                       BYTE_BUFFER_CACHE.remove();
+               }
+               if (CHAR_BUFFER_CACHE != null) {
+                       CHAR_BUFFER_CACHE.remove();
+               }
+       }
+
        /** Reusable empty reader. */
        public static final Reader EMPTY_READER = new Reader() {
                @Override
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/config/store/SqlStore.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/config/store/SqlStore.java
index c5a51a822d..fab473ccc5 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/config/store/SqlStore.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/config/store/SqlStore.java
@@ -167,7 +167,7 @@ public class SqlStore extends ConfigStore {
                };
 
                this.watcher = new Timer("MyTimer");
-               watcher.scheduleAtFixedRate(timerTask, 0, pollInterval * 1000);
+               watcher.scheduleAtFixedRate(timerTask, 0, (long) pollInterval * 
1000);
        }
 
        @Override /* Closeable */
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache2_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache2_Test.java
index b34c199f52..558b960c96 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache2_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache2_Test.java
@@ -906,5 +906,49 @@ class Cache2_Test extends TestBase {
                x.get("k4", 4);
                assertSize(1, x);
        }
+
+       
//====================================================================================================
+       // cleanup() - Thread-local cleanup
+       
//====================================================================================================
+
+       @Test
+       void q01_cleanup_threadLocal() {
+               var x = Cache2.of(String.class, Integer.class, String.class)
+                       .threadLocal()
+                       .supplier((k1, k2) -> k1 + ":" + k2)
+                       .build();
+
+               // Use the cache to populate ThreadLocal
+               x.get("key1", 1);
+               x.get("key2", 2);
+               assertSize(2, x);
+
+               // Cleanup should remove ThreadLocal values
+               x.cleanup();
+
+               // After cleanup, cache should be empty (ThreadLocal was 
cleared)
+               assertEmpty(x);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               x.cleanup();
+               // Should not throw
+       }
+
+       @Test
+       void q02_cleanup_nonThreadLocal_noEffect() {
+               var x = Cache2.of(String.class, Integer.class, String.class)
+                       .supplier((k1, k2) -> k1 + ":" + k2)
+                       .build(); // Not thread-local
+
+               x.get("key1", 1);
+               x.get("key2", 2);
+               assertSize(2, x);
+
+               // Cleanup on non-thread-local cache should not affect the cache
+               x.cleanup();
+
+               // Cache should still have values (cleanup only affects 
thread-local caches)
+               assertSize(2, x);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache3_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache3_Test.java
index 560092ac8d..99a7373c24 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache3_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache3_Test.java
@@ -603,5 +603,49 @@ class Cache3_Test extends TestBase {
                x.get("es", "ES", 4);
                assertSize(1, x);
        }
+
+       
//====================================================================================================
+       // cleanup() - Thread-local cleanup
+       
//====================================================================================================
+
+       @Test
+       void q01_cleanup_threadLocal() {
+               var x = Cache3.of(String.class, String.class, Integer.class, 
String.class)
+                       .threadLocal()
+                       .supplier((k1, k2, k3) -> k1 + ":" + k2 + ":" + k3)
+                       .build();
+
+               // Use the cache to populate ThreadLocal
+               x.get("key1", "key2", 1);
+               x.get("key3", "key4", 2);
+               assertSize(2, x);
+
+               // Cleanup should remove ThreadLocal values
+               x.cleanup();
+
+               // After cleanup, cache should be empty (ThreadLocal was 
cleared)
+               assertEmpty(x);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               x.cleanup();
+               // Should not throw
+       }
+
+       @Test
+       void q02_cleanup_nonThreadLocal_noEffect() {
+               var x = Cache3.of(String.class, String.class, Integer.class, 
String.class)
+                       .supplier((k1, k2, k3) -> k1 + ":" + k2 + ":" + k3)
+                       .build(); // Not thread-local
+
+               x.get("key1", "key2", 1);
+               x.get("key3", "key4", 2);
+               assertSize(2, x);
+
+               // Cleanup on non-thread-local cache should not affect the cache
+               x.cleanup();
+
+               // Cache should still have values (cleanup only affects 
thread-local caches)
+               assertSize(2, x);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache4_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache4_Test.java
index b1b2e55259..52538b2793 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache4_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache4_Test.java
@@ -603,5 +603,49 @@ class Cache4_Test extends TestBase {
                x.get("es", "ES", "informal", 4);
                assertSize(1, x);
        }
+
+       
//====================================================================================================
+       // cleanup() - Thread-local cleanup
+       
//====================================================================================================
+
+       @Test
+       void q01_cleanup_threadLocal() {
+               var x = Cache4.of(String.class, String.class, String.class, 
Integer.class, String.class)
+                       .threadLocal()
+                       .supplier((k1, k2, k3, k4) -> k1 + ":" + k2 + ":" + k3 
+ ":" + k4)
+                       .build();
+
+               // Use the cache to populate ThreadLocal
+               x.get("key1", "key2", "key3", 1);
+               x.get("key4", "key5", "key6", 2);
+               assertSize(2, x);
+
+               // Cleanup should remove ThreadLocal values
+               x.cleanup();
+
+               // After cleanup, cache should be empty (ThreadLocal was 
cleared)
+               assertEmpty(x);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               x.cleanup();
+               // Should not throw
+       }
+
+       @Test
+       void q02_cleanup_nonThreadLocal_noEffect() {
+               var x = Cache4.of(String.class, String.class, String.class, 
Integer.class, String.class)
+                       .supplier((k1, k2, k3, k4) -> k1 + ":" + k2 + ":" + k3 
+ ":" + k4)
+                       .build(); // Not thread-local
+
+               x.get("key1", "key2", "key3", 1);
+               x.get("key4", "key5", "key6", 2);
+               assertSize(2, x);
+
+               // Cleanup on non-thread-local cache should not affect the cache
+               x.cleanup();
+
+               // Cache should still have values (cleanup only affects 
thread-local caches)
+               assertSize(2, x);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache5_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache5_Test.java
index b0a1bf3eea..f81959210d 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache5_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache5_Test.java
@@ -604,5 +604,49 @@ class Cache5_Test extends TestBase {
                x.get("es", "ES", "south", "informal", 4);
                assertSize(1, x);
        }
+
+       
//====================================================================================================
+       // cleanup() - Thread-local cleanup
+       
//====================================================================================================
+
+       @Test
+       void q01_cleanup_threadLocal() {
+               var x = Cache5.of(String.class, String.class, String.class, 
String.class, Integer.class, String.class)
+                       .threadLocal()
+                       .supplier((k1, k2, k3, k4, k5) -> k1 + ":" + k2 + ":" + 
k3 + ":" + k4 + ":" + k5)
+                       .build();
+
+               // Use the cache to populate ThreadLocal
+               x.get("key1", "key2", "key3", "key4", 1);
+               x.get("key5", "key6", "key7", "key8", 2);
+               assertSize(2, x);
+
+               // Cleanup should remove ThreadLocal values
+               x.cleanup();
+
+               // After cleanup, cache should be empty (ThreadLocal was 
cleared)
+               assertEmpty(x);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               x.cleanup();
+               // Should not throw
+       }
+
+       @Test
+       void q02_cleanup_nonThreadLocal_noEffect() {
+               var x = Cache5.of(String.class, String.class, String.class, 
String.class, Integer.class, String.class)
+                       .supplier((k1, k2, k3, k4, k5) -> k1 + ":" + k2 + ":" + 
k3 + ":" + k4 + ":" + k5)
+                       .build(); // Not thread-local
+
+               x.get("key1", "key2", "key3", "key4", 1);
+               x.get("key5", "key6", "key7", "key8", 2);
+               assertSize(2, x);
+
+               // Cleanup on non-thread-local cache should not affect the cache
+               x.cleanup();
+
+               // Cache should still have values (cleanup only affects 
thread-local caches)
+               assertSize(2, x);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache_Test.java
index c7ee9138e0..9afab8441c 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/collections/Cache_Test.java
@@ -1216,5 +1216,60 @@ class Cache_Test extends TestBase {
                cache.get("five", () -> 5);
                assertSize(1, cache);
        }
+
+       
//====================================================================================================
+       // cleanup() - Thread-local cleanup
+       
//====================================================================================================
+
+       @Test void a64_cleanup_threadLocal() {
+               var cache = Cache.of(String.class, String.class)
+                       .threadLocal()
+                       .build();
+
+               // Use the cache to populate ThreadLocal
+               cache.get("key1", () -> "value1");
+               cache.get("key2", () -> "value2");
+               assertSize(2, cache);
+
+               // Cleanup should remove ThreadLocal values
+               cache.cleanup();
+
+               // After cleanup, cache should be empty (ThreadLocal was 
cleared)
+               assertEmpty(cache);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               cache.cleanup();
+               // Should not throw
+       }
+
+       @Test void a65_cleanup_nonThreadLocal_noEffect() {
+               var cache = Cache.of(String.class, String.class)
+                       .build(); // Not thread-local
+
+               cache.get("key1", () -> "value1");
+               cache.get("key2", () -> "value2");
+               assertSize(2, cache);
+
+               // Cleanup on non-thread-local cache should not affect the cache
+               cache.cleanup();
+
+               // Cache should still have values (cleanup only affects 
thread-local caches)
+               assertSize(2, cache);
+       }
+
+       @Test void a66_cleanup_threadLocal_weakMode() {
+               var cache = Cache.of(String.class, String.class)
+                       .threadLocal()
+                       .cacheMode(WEAK)
+                       .build();
+
+               cache.get("key1", () -> "value1");
+               cache.get("key2", () -> "value2");
+               assertSize(2, cache);
+
+               // Cleanup should work with weak mode too
+               cache.cleanup();
+               assertEmpty(cache);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
index c7e27176a3..a9d974e885 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/settings/Settings_Test.java
@@ -1267,5 +1267,54 @@ class Settings_Test extends TestBase {
                assertTrue(optional2.isPresent());
                assertEquals("value1", optional2.get()); // Still value1 since 
system property hasn't changed
        }
+
+       
//====================================================================================================
+       // cleanup()
+       
//====================================================================================================
+       @Test
+       void u01_cleanup() {
+               // Set a local value to ensure ThreadLocal has a value
+               Settings.get().setLocal(TEST_PROP, "local-value");
+               var result = Settings.get().get(TEST_PROP);
+               assertTrue(result.isPresent());
+               assertEquals("local-value", result.get());
+
+               // Cleanup should remove the ThreadLocal value
+               Settings.get().cleanup();
+
+               // After cleanup, the local value should be gone (falls back to 
system property if set)
+               // If system property is not set, should return empty
+               var resultAfterCleanup = Settings.get().get(TEST_PROP);
+               // The value may still be present if there's a system property, 
but the local override should be gone
+               // We can verify cleanup was called by ensuring it doesn't throw
+               assertNotNull(resultAfterCleanup);
+
+               // Call cleanup again to ensure it's safe to call multiple times
+               Settings.get().cleanup();
+               // Should not throw
+       }
+
+       @Test
+       void u02_cleanup_removesLocalOverride() {
+               // Set both system property and local override
+               System.setProperty(TEST_PROP, "system-value");
+               Settings.get().setLocal(TEST_PROP, "local-value");
+
+               // Verify local override takes precedence
+               var result = Settings.get().get(TEST_PROP);
+               assertTrue(result.isPresent());
+               assertEquals("local-value", result.get());
+
+               // Cleanup should remove local override
+               Settings.get().cleanup();
+
+               // After cleanup, should fall back to system property
+               var resultAfterCleanup = Settings.get().get(TEST_PROP);
+               assertTrue(resultAfterCleanup.isPresent());
+               assertEquals("system-value", resultAfterCleanup.get());
+
+               // Clean up
+               System.clearProperty(TEST_PROP);
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/IoUtils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/IoUtils_Test.java
index 64ca73ea36..0f1a3af5d5 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/IoUtils_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/IoUtils_Test.java
@@ -961,6 +961,22 @@ class IoUtils_Test extends TestBase {
                assertEquals(-1, EMPTY_READER.read(buf, 0, 10));
        }
 
+       
//====================================================================================================
+       // cleanupThreadLocals()
+       
//====================================================================================================
+       @Test
+       void a046_cleanupThreadLocals() {
+               // Test that cleanupThreadLocals() can be called without 
throwing
+               // Note: We can't easily verify the ThreadLocal was cleared 
without accessing private fields,
+               // but we can verify the method executes successfully
+               cleanupThreadLocals();
+               // Should not throw
+
+               // Call again to ensure it's safe to call multiple times
+               cleanupThreadLocals();
+               // Should not throw
+       }
+
        
//====================================================================================================
        // Test helper classes
        
//====================================================================================================

Reply via email to