You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tv...@apache.org on 2026/08/26 11:12:55 UTC

(commons-jcs) branch master updated (e6bf3cb7 -> 85b906cf)

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

asf-gitbox-commits pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jcs.git


    from e6bf3cb7 Bump github/codeql-action/init from 4.37.7 to 4.37.8
     new 36960889 Remove obsolete SuppressWarnings annotation
     new 85b906cf Unify the handling of thread pools and their lifecycle. Fixes JCS-248

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../commons/jcs4/engine/CacheEventQueue.java       |  10 +-
 .../commons/jcs4/engine/PooledCacheEventQueue.java |  31 +--
 .../engine/control/event/ElementEventQueue.java    |   9 +-
 .../jcs4/utils/config/ConfigurationBuilder.java    |   1 -
 .../commons/jcs4/utils/config/PropertySetter.java  |   1 -
 .../jcs4/utils/discovery/UDPDiscoveryReceiver.java |   5 +-
 .../jcs4/utils/threadpool/PoolConfiguration.java   |  29 ++-
 .../jcs4/utils/threadpool/ThreadPoolManager.java   | 232 ++++++++++++++++-----
 .../control/event/ElementEventQueueUnitTest.java   |  87 ++++++++
 9 files changed, 312 insertions(+), 93 deletions(-)
 create mode 100644 commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueueUnitTest.java


(commons-jcs) 02/02: Unify the handling of thread pools and their lifecycle. Fixes JCS-248

Posted by tv...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jcs.git

commit 85b906cf4611bc4837c05d69eabfe406a414b047
Author: Thomas Vandahl <tv...@apache.org>
AuthorDate: Wed Aug 26 13:12:42 2026 +0200

    Unify the handling of thread pools and their lifecycle. Fixes JCS-248
---
 .../commons/jcs4/engine/CacheEventQueue.java       |  10 +-
 .../commons/jcs4/engine/PooledCacheEventQueue.java |  31 +--
 .../engine/control/event/ElementEventQueue.java    |   9 +-
 .../jcs4/utils/discovery/UDPDiscoveryReceiver.java |   5 +-
 .../jcs4/utils/threadpool/PoolConfiguration.java   |  29 ++-
 .../jcs4/utils/threadpool/ThreadPoolManager.java   | 232 ++++++++++++++++-----
 .../control/event/ElementEventQueueUnitTest.java   |  87 ++++++++
 7 files changed, 312 insertions(+), 91 deletions(-)

diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/CacheEventQueue.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/CacheEventQueue.java
index 0dc68083..e9a75bb9 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/CacheEventQueue.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/CacheEventQueue.java
@@ -56,22 +56,20 @@ public class CacheEventQueue<K, V>
     public CacheEventQueue( final ICacheListener<K, V> listener, final long listenerId, final String cacheName, final int maxFailure,
                             final int waitBeforeRetry )
     {
-        super( listener, listenerId, cacheName, maxFailure, waitBeforeRetry, null );
+        super( listener, listenerId, cacheName, maxFailure, waitBeforeRetry, "CacheEventQueue.QProcessor-" + cacheName);
     }
 
     /**
      * Create the thread pool.
      *
-     * @param threadPoolName
      * @since 3.1
      */
     @Override
-    protected ExecutorService createPool(final String threadPoolName)
+    protected ExecutorService createPool()
     {
         // create a default pool with one worker thread to mimic the SINGLE queue behavior
-        return ThreadPoolManager.getInstance().createPool(
-                new PoolConfiguration(false, 0, 1, 1, getWaitToDie(), WhenBlockedPolicy.RUN, 1),
-                "CacheEventQueue.QProcessor-" + getCacheName());
+        return ThreadPoolManager.getInstance().getExecutorService(poolName,
+                new PoolConfiguration(false, 0, 1, 1, getWaitToDie(), WhenBlockedPolicy.RUN, 1));
     }
 
     /**
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/PooledCacheEventQueue.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/PooledCacheEventQueue.java
index 1f8178e8..b27914af 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/PooledCacheEventQueue.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/PooledCacheEventQueue.java
@@ -24,7 +24,6 @@ import java.time.Duration;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.jcs4.engine.behavior.ICacheListener;
 import org.apache.commons.jcs4.engine.stats.Stats;
@@ -51,6 +50,9 @@ public class PooledCacheEventQueue<K, V>
     /** The Thread Pool to execute events with. */
     protected ExecutorService pool;
 
+    /** The Thread Pool name in ThreadPoolManager. */
+    protected String poolName;
+
     /** The Thread Pool queue */
     protected BlockingQueue<Runnable> queue;
 
@@ -73,14 +75,12 @@ public class PooledCacheEventQueue<K, V>
     /**
      * Create the thread pool.
      *
-     * @param threadPoolName
      * @since 3.1
      */
-    protected ExecutorService createPool(final String threadPoolName)
+    protected ExecutorService createPool()
     {
         // this will share the same pool with other event queues by default.
-        return ThreadPoolManager.getInstance().getExecutorService(
-                threadPoolName == null ? "cache_event_queue" : threadPoolName );
+        return ThreadPoolManager.getInstance().getExecutorService(poolName);
     }
 
     /**
@@ -94,23 +94,7 @@ public class PooledCacheEventQueue<K, V>
         if ( isWorking() )
         {
             setWorking(false);
-            pool.shutdown();
-
-            if (wait.toSeconds() > 0)
-            {
-                try
-                {
-                    if (!pool.awaitTermination(wait.toSeconds(), TimeUnit.SECONDS))
-                    {
-                        log.info( "No longer waiting for event queue to finish: {0}",
-                                this::getStatistics);
-                    }
-                }
-                catch (final InterruptedException e)
-                {
-                    // ignore
-                }
-            }
+            ThreadPoolManager.getInstance().disposeExecutorService(poolName, wait);
             log.info( "Cache event queue destroyed: {0}", this );
         }
     }
@@ -160,7 +144,8 @@ public class PooledCacheEventQueue<K, V>
     {
         super.initialize(listener, listenerId, cacheName, maxFailure, waitBeforeRetry);
 
-        pool = createPool(threadPoolName);
+        poolName = threadPoolName == null ? "cache_event_queue" : threadPoolName;
+        pool = createPool();
 
         if (pool instanceof ThreadPoolExecutor tpe)
         {
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueue.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueue.java
index 0a0948a9..cffc420f 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueue.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueue.java
@@ -38,7 +38,7 @@ import org.apache.commons.jcs4.utils.threadpool.ThreadPoolManager;
 public class ElementEventQueue
     implements IElementEventQueue
 {
-    private static final String THREAD_PREFIX = "JCS-ElementEventQueue-";
+    protected static final String POOL_NAME = "ElementEventQueue";
 
     /** The logger */
     private static final Log log = Log.getLog( ElementEventQueue.class );
@@ -54,8 +54,8 @@ public class ElementEventQueue
      */
     public ElementEventQueue()
     {
-        queueProcessor = ThreadPoolManager.getInstance().createPool(
-        		new PoolConfiguration(false, 0, 1, 1, Duration.ZERO, WhenBlockedPolicy.RUN, 1), THREAD_PREFIX);
+        queueProcessor = ThreadPoolManager.getInstance().getExecutorService(POOL_NAME,
+        		new PoolConfiguration(false, 0, 1, 1, Duration.ZERO, WhenBlockedPolicy.RUN, 1));
 
         log.debug( "Constructed: {0}", this );
     }
@@ -91,8 +91,7 @@ public class ElementEventQueue
     {
         if (destroyed.compareAndSet(false, true))
         {
-            // Pool will be shut down by the ThreadPoolManager
-            // queueProcessor.shutdownNow();
+            ThreadPoolManager.getInstance().disposeExecutorService(POOL_NAME);
             log.info( "Element event queue destroyed: {0}", this );
         }
     }
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/discovery/UDPDiscoveryReceiver.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/discovery/UDPDiscoveryReceiver.java
index 51d41191..eb217107 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/discovery/UDPDiscoveryReceiver.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/discovery/UDPDiscoveryReceiver.java
@@ -105,10 +105,9 @@ public class UDPDiscoveryReceiver
         setService(service);
 
         // create a small thread pool to handle a barrage
-        this.pooledExecutor = ThreadPoolManager.getInstance().createPool(
+        this.pooledExecutor = ThreadPoolManager.getInstance().getExecutorService("UDPDiscoveryReceiver",
                 new PoolConfiguration(false, 0, maxPoolSize, maxPoolSize, Duration.ZERO,
-                        WhenBlockedPolicy.DISCARDOLDEST, maxPoolSize),
-                "JCS-UDPDiscoveryReceiver-", Thread.MIN_PRIORITY);
+                        WhenBlockedPolicy.DISCARDOLDEST, maxPoolSize, Thread.MIN_PRIORITY));
 
         log.info( "Constructing listener, [{0}:{1}]", multicastAddress, multicastPort );
         createSocket( multicastInterfaceString, multicastAddress, multicastPort );
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/PoolConfiguration.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/PoolConfiguration.java
index aa88096b..c022080b 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/PoolConfiguration.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/PoolConfiguration.java
@@ -47,7 +47,10 @@ public record PoolConfiguration(
         WhenBlockedPolicy whenBlockedPolicy,
 
         /** The number of threads to create on startup */
-        int startUpSize
+        int startUpSize,
+
+        /** The thread priority */
+        int threadPriority
 ) implements Cloneable
 {
     public enum WhenBlockedPolicy
@@ -88,12 +91,15 @@ public record PoolConfiguration(
     /** Default startup size */
     private static final int DEFAULT_STARTUP_SIZE = DEFAULT_MINIMUM_POOL_SIZE;
 
+    /** Default thread priority */
+    private static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY;
+
     /**
      * Default
      */
     private static PoolConfiguration DEFAULT = new PoolConfiguration(DEFAULT_USE_BOUNDARY,
             DEFAULT_BOUNDARY_SIZE, DEFAULT_MAXIMUM_POOL_SIZE, DEFAULT_MINIMUM_POOL_SIZE,
-            DEFAULT_KEEPALIVE_TIME, DEFAULT_WHEN_BLOCKED_POLICY, DEFAULT_STARTUP_SIZE);
+            DEFAULT_KEEPALIVE_TIME, DEFAULT_WHEN_BLOCKED_POLICY, DEFAULT_STARTUP_SIZE, DEFAULT_THREAD_PRIORITY);
 
     /**
      * @return An object containing the default settings
@@ -103,6 +109,22 @@ public record PoolConfiguration(
         return DEFAULT;
     }
 
+    /**
+     * Convenience constructor
+     *
+     * @param useBoundary Should we bound the queue
+     * @param boundarySize If the queue is bounded, how big can it get
+     * @param maximumPoolSize Only has meaning if a boundary is used
+     * @param minimumPoolSize the exact number that will be used in a boundless queue
+     * @param keepAliveTime How long idle threads above the minimum should be kept alive
+     * @param whenBlockedPolicy Should be ABORT, BLOCK, RUN, WAIT, DISCARDOLDEST
+     * @param startUpSize The number of threads to create on startup
+     */
+    public PoolConfiguration(boolean useBoundary, int boundarySize, int maximumPoolSize, int minimumPoolSize, Duration keepAliveTime,
+            WhenBlockedPolicy whenBlockedPolicy, int startUpSize)
+    {
+        this(useBoundary, boundarySize, maximumPoolSize, minimumPoolSize, keepAliveTime, whenBlockedPolicy, startUpSize, DEFAULT_THREAD_PRIORITY);
+    }
 
     /**
      * To string for debugging purposes.
@@ -118,7 +140,8 @@ public record PoolConfiguration(
         buf.append("minimumPoolSize = [").append(minimumPoolSize()).append("] ");
         buf.append("keepAliveTime = [").append(keepAliveTime()).append("] ");
         buf.append("whenBlockedPolicy = [").append(whenBlockedPolicy()).append("] ");
-        buf.append("startUpSize = [").append(startUpSize()).append("]" );
+        buf.append("startUpSize = [").append(startUpSize()).append("] " );
+        buf.append("threadPriority = [").append(threadPriority()).append("]" );
         return buf.toString();
     }
 }
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/ThreadPoolManager.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/ThreadPoolManager.java
index ddbf7aeb..b9e807ea 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/ThreadPoolManager.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/threadpool/ThreadPoolManager.java
@@ -1,5 +1,7 @@
 package org.apache.commons.jcs4.utils.threadpool;
 
+import java.time.Duration;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -34,6 +36,24 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.jcs4.log.Log;
 import org.apache.commons.jcs4.utils.config.ConfigurationBuilder;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 
 /**
  * This manages threadpools for an application
@@ -68,6 +88,9 @@ public class ThreadPoolManager
     /** The logger */
     private static final Log log = Log.getLog( ThreadPoolManager.class );
 
+    /** The common prefix for all thread names managed by the ThreadPoolManager */
+    public static final String JCS_THREAD_POOL_MANAGER_PREFIX = "JCS-ThreadPoolManager-";
+
     /** The root property name */
     private static final String PROP_NAME_ROOT = "thread_pool";
 
@@ -81,46 +104,154 @@ public class ThreadPoolManager
     private static final String DEFAULT_PROP_NAME_SCHEDULER_ROOT = "scheduler_pool.default";
 
     /**
-         * You can specify the properties to be used to configure the thread pool. Setting this post
-         * initialization will have no effect.
-         */
-        private static volatile Properties props;
+     * You can specify the properties to be used to configure the thread pool. Setting this post
+     * initialization will have no effect.
+     */
+    private static volatile Properties props;
 
-   /**
- * Dispose of the instance of the ThreadPoolManger and shut down all thread pools
- */
-public static void dispose()
-{
-    for ( final Iterator<Map.Entry<String, ExecutorService>> i =
-            getInstance().pools.entrySet().iterator(); i.hasNext(); )
+    /**
+     * Dispose of the instance of the ThreadPoolManger and shut down all thread pools
+     */
+    public static void dispose()
     {
-        final Map.Entry<String, ExecutorService> entry = i.next();
-        try
+        for ( final Iterator<Map.Entry<String, ExecutorService>> i =
+                getInstance().pools.entrySet().iterator(); i.hasNext(); )
         {
-            entry.getValue().shutdownNow();
+            final Map.Entry<String, ExecutorService> entry = i.next();
+            try
+            {
+                entry.getValue().shutdownNow();
+            }
+            catch (final Throwable t)
+            {
+                log.warn("Failed to close pool {0}", entry.getKey(), t);
+            }
+            i.remove();
         }
-        catch (final Throwable t)
+
+        for ( final Iterator<Map.Entry<String, ScheduledExecutorService>> i =
+                getInstance().schedulerPools.entrySet().iterator(); i.hasNext(); )
         {
-            log.warn("Failed to close pool {0}", entry.getKey(), t);
+            final Map.Entry<String, ScheduledExecutorService> entry = i.next();
+            try
+            {
+                entry.getValue().shutdownNow();
+            }
+            catch (final Throwable t)
+            {
+                log.warn("Failed to close pool {0}", entry.getKey(), t);
+            }
+            i.remove();
         }
-        i.remove();
     }
 
-    for ( final Iterator<Map.Entry<String, ScheduledExecutorService>> i =
-            getInstance().schedulerPools.entrySet().iterator(); i.hasNext(); )
+    /**
+     * Dispose of a thread pool
+     *
+     * @param poolName the name of the pool
+     */
+    public void disposeExecutorService(String poolName)
+    {
+        disposeExecutorService(poolName, Duration.ZERO);
+    }
+
+    /**
+     * Dispose of a thread pool
+     *
+     * @param poolName the name of the pool
+     * @param wait Duration to wait for termination
+     */
+    public void disposeExecutorService(String poolName, Duration wait)
     {
-        final Map.Entry<String, ScheduledExecutorService> entry = i.next();
-        try
+        ExecutorService pool = pools.remove(poolName);
+        if (pool == null)
         {
-            entry.getValue().shutdownNow();
+            log.warn("Failed to close non-existing pool {0}", poolName);
         }
-        catch (final Throwable t)
+        else
         {
-            log.warn("Failed to close pool {0}", entry.getKey(), t);
+            try
+            {
+                if (wait == null || wait.isZero())
+                {
+                    pool.shutdownNow();
+                }
+                else
+                {
+                    pool.shutdown();
+                    try
+                    {
+                        if (!pool.awaitTermination(wait.toSeconds(), TimeUnit.SECONDS))
+                        {
+                            log.info( "No longer waiting for pool {0} to terminate", poolName);
+                        }
+                    }
+                    catch (final InterruptedException e)
+                    {
+                        // ignore
+                    }
+                }
+            }
+            catch (final Throwable t)
+            {
+                log.warn("Failed to close pool {0}", poolName, t);
+            }
+        }
+    }
+
+    /**
+     * Dispose of a scheduler thread pool
+     *
+     * @param poolName the name of the pool
+     */
+    public void disposeSchedulerPool(String poolName)
+    {
+        disposeSchedulerPool(poolName, Duration.ZERO);
+    }
+
+    /**
+     * Dispose of a scheduler thread pool
+     *
+     * @param poolName the name of the pool
+     * @param wait Duration to wait for termination
+     */
+    public void disposeSchedulerPool(String poolName, Duration wait)
+    {
+        ExecutorService pool = schedulerPools.remove(poolName);
+        if (pool == null)
+        {
+            log.warn("Failed to close non-existing pool {0}", poolName);
+        }
+        else
+        {
+            try
+            {
+                if (wait == null || wait.isZero())
+                {
+                    pool.shutdownNow();
+                }
+                else
+                {
+                    pool.shutdown();
+                    try
+                    {
+                        if (!pool.awaitTermination(wait.toSeconds(), TimeUnit.SECONDS))
+                        {
+                            log.info( "No longer waiting for pool {0} to terminate", poolName);
+                        }
+                    }
+                    catch (final InterruptedException e)
+                    {
+                        // ignore
+                    }
+                }
+            }
+            catch (final Throwable t)
+            {
+                log.warn("Failed to close pool {0}", poolName, t);
+            }
         }
-        i.remove();
     }
-}
 
     /**
      * Returns a configured instance of the ThreadPoolManger To specify a configuration file or
@@ -210,20 +341,7 @@ public static void dispose()
      * @param threadNamePrefix prefix for the thread names of the pool
      * @return A ThreadPool wrapper
      */
-    public ExecutorService createPool( final PoolConfiguration config, final String threadNamePrefix)
-    {
-    	return createPool(config, threadNamePrefix, Thread.NORM_PRIORITY);
-    }
-
-    /**
-     * Creates a pool based on the configuration info.
-     *
-     * @param config The pool configuration
-     * @param threadNamePrefix prefix for the thread names of the pool
-     * @param threadPriority The priority of the created threads
-     * @return A ThreadPool wrapper
-     */
-    public ExecutorService createPool( final PoolConfiguration config, final String threadNamePrefix, final int threadPriority )
+    private ExecutorService createPool( final PoolConfiguration config, final String threadNamePrefix)
     {
         BlockingQueue<Runnable> queue = null;
         if ( config.useBoundary() )
@@ -243,7 +361,7 @@ public static void dispose()
             config.keepAliveTime().toMillis(),
             TimeUnit.MILLISECONDS,
             queue,
-            new DaemonThreadFactory(threadNamePrefix, threadPriority));
+            new DaemonThreadFactory(threadNamePrefix, config.threadPriority()));
 
         // when blocked policy
         switch (config.whenBlockedPolicy())
@@ -275,15 +393,13 @@ public static void dispose()
      *
      * @param config The pool configuration
      * @param threadNamePrefix prefix for the thread names of the pool
-     * @param threadPriority The priority of the created threads
      * @return A ScheduledExecutorService
      */
-    public ScheduledExecutorService createSchedulerPool( final PoolConfiguration config, final String threadNamePrefix, final int threadPriority )
+    private ScheduledExecutorService createSchedulerPool(final PoolConfiguration config, final String threadNamePrefix)
     {
-
         return Executors.newScheduledThreadPool(
                 config.maximumPoolSize(),
-                new DaemonThreadFactory(threadNamePrefix, threadPriority));
+                new DaemonThreadFactory(threadNamePrefix, config.threadPriority()));
     }
 
     /**
@@ -297,11 +413,25 @@ public static void dispose()
      */
     public ExecutorService getExecutorService( final String name )
     {
-    	return pools.computeIfAbsent(name, key -> {
-            log.debug( "Creating pool for name [{0}]", key );
-            final PoolConfiguration config = loadConfig( PROP_NAME_ROOT + "." + key, defaultConfig );
-            return createPool( config, "JCS-ThreadPoolManager-" + key + "-" );
-    	});
+    	return getExecutorService(name, loadConfig( PROP_NAME_ROOT + "." + name, defaultConfig ));
+    }
+
+    /**
+     * Returns an executor service by name. If a service by this name does not exist in the configuration file or
+     * properties, one will be created using the default values.
+     * <p>
+     * Services are lazily created.
+     *
+     * @param name
+     * @param config The pool configuration
+     * @return The executor service configured for the name.
+     */
+    public ExecutorService getExecutorService(final String name, final PoolConfiguration config)
+    {
+        return pools.computeIfAbsent(name, key -> {
+            log.debug("Creating pool for name [{0}]", key);
+            return createPool(config, JCS_THREAD_POOL_MANAGER_PREFIX + key + "-");
+        });
     }
 
     /**
@@ -329,7 +459,7 @@ public static void dispose()
             log.debug( "Creating scheduler pool for name [{0}]", key );
             final PoolConfiguration config = loadConfig( PROP_NAME_SCHEDULER_ROOT + "." + key,
                     defaultSchedulerConfig );
-            return createSchedulerPool( config, "JCS-ThreadPoolManager-" + key + "-", Thread.NORM_PRIORITY );
+            return createSchedulerPool( config, JCS_THREAD_POOL_MANAGER_PREFIX + key + "-");
     	});
     }
 }
diff --git a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueueUnitTest.java b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueueUnitTest.java
new file mode 100644
index 00000000..96ca2d40
--- /dev/null
+++ b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/ElementEventQueueUnitTest.java
@@ -0,0 +1,87 @@
+package org.apache.commons.jcs4.engine.control.event;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.jcs4.utils.threadpool.ThreadPoolManager;
+import org.junit.jupiter.api.Test;
+
+/** Tests the lifecycle of the element-event worker owned by the queue. */
+class ElementEventQueueUnitTest
+{
+    private static final String THREAD_PREFIX = ThreadPoolManager.JCS_THREAD_POOL_MANAGER_PREFIX + ElementEventQueue.POOL_NAME;
+
+    @Test
+    void testDisposeStopsOwnedWorkerThread()
+        throws InterruptedException
+    {
+        final Set<Long> threadsBefore = eventQueueThreadIds();
+        final ElementEventQueue queue = new ElementEventQueue();
+        final Thread worker = waitForNewWorker( threadsBefore );
+
+        assertNotNull( worker, "The element-event queue did not start its worker" );
+
+        queue.dispose();
+        worker.join( 2000 );
+
+        assertFalse( worker.isAlive(), "The element-event worker is still alive after dispose" );
+
+        // Disposal is a lifecycle operation and must be safe when invoked more than once.
+        queue.dispose();
+    }
+
+    private static Set<Long> eventQueueThreadIds()
+    {
+        final Set<Long> result = new HashSet<>();
+        for ( final Thread thread : Thread.getAllStackTraces().keySet() )
+        {
+            if (thread.getName().startsWith(THREAD_PREFIX))
+            {
+                result.add( thread.getId() );
+            }
+        }
+        return result;
+    }
+
+    private static Thread waitForNewWorker( final Set<Long> threadsBefore )
+        throws InterruptedException
+    {
+        final long deadline = System.currentTimeMillis() + 2000;
+        do
+        {
+            for ( final Thread thread : Thread.getAllStackTraces().keySet() )
+            {
+                if (thread.getName().startsWith(THREAD_PREFIX) && !threadsBefore.contains(thread.getId()))
+                {
+                    return thread;
+                }
+            }
+            Thread.sleep( 10 );
+        }
+        while (System.currentTimeMillis() < deadline);
+
+        return null;
+    }
+}
\ No newline at end of file


(commons-jcs) 01/02: Remove obsolete SuppressWarnings annotation

Posted by tv...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jcs.git

commit 36960889080aed49b797c64c79779e0da2065aaf
Author: Thomas Vandahl <tv...@apache.org>
AuthorDate: Wed Aug 26 10:28:27 2026 +0200

    Remove obsolete SuppressWarnings annotation
---
 .../java/org/apache/commons/jcs4/utils/config/ConfigurationBuilder.java  | 1 -
 .../main/java/org/apache/commons/jcs4/utils/config/PropertySetter.java   | 1 -
 2 files changed, 2 deletions(-)

diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/ConfigurationBuilder.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/ConfigurationBuilder.java
index 64c4a486..1481b0e6 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/ConfigurationBuilder.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/ConfigurationBuilder.java
@@ -198,7 +198,6 @@ public class ConfigurationBuilder<T>
         }
         else if( type.isEnum() )
         {
-            @SuppressWarnings({ "unchecked" })
             final Enum<?> valueOf = Enum.valueOf(type.asSubclass(Enum.class), v);
             return valueOf;
         }
diff --git a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/PropertySetter.java b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/PropertySetter.java
index 6e3b128d..876898eb 100644
--- a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/PropertySetter.java
+++ b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/config/PropertySetter.java
@@ -118,7 +118,6 @@ public class PropertySetter
         }
         else if( type.isEnum() )
         {
-            @SuppressWarnings("unchecked")
             final Enum<?> valueOf = Enum.valueOf(type.asSubclass(Enum.class), v);
             return valueOf;
         }