This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
commit b26f550516b9b9f3fbefa9364d71acce91ca91d1 Author: Robert Lazarski <[email protected]> AuthorDate: Sat Feb 14 10:33:07 2026 -1000 Fix AXIS2-5696: Prevent ThreadPool thread leaks by enabling core thread timeout Enable allowCoreThreadTimeOut(true) on the ThreadPoolExecutor, which was commented out with a JDK 1.6 FIXME that is long obsolete. Idle core threads now terminate after the 10-second keepAlive period instead of persisting indefinitely. Also shut down the ThreadPool in ConfigurationContext.terminate() as defense-in-depth. Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../apache/axis2/context/ConfigurationContext.java | 4 +++ .../apache/axis2/util/threadpool/ThreadPool.java | 3 +- .../axis2/util/threadpool/TestThreadPool.java | 36 +++++++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/kernel/src/org/apache/axis2/context/ConfigurationContext.java b/modules/kernel/src/org/apache/axis2/context/ConfigurationContext.java index 23b417f6d2..69ebc4eb8a 100644 --- a/modules/kernel/src/org/apache/axis2/context/ConfigurationContext.java +++ b/modules/kernel/src/org/apache/axis2/context/ConfigurationContext.java @@ -737,6 +737,10 @@ public class ConfigurationContext extends AbstractContext { */ public void terminate() throws AxisFault { shutdownModulesAndServices(); + // AXIS2-5696: Shut down the thread pool to prevent thread leaks + if (threadPool instanceof ThreadPool) { + ((ThreadPool) threadPool).safeShutDown(); + } if (listenerManager != null) { listenerManager.destroy(); } diff --git a/modules/kernel/src/org/apache/axis2/util/threadpool/ThreadPool.java b/modules/kernel/src/org/apache/axis2/util/threadpool/ThreadPool.java index a6d4a753ec..bb05949922 100644 --- a/modules/kernel/src/org/apache/axis2/util/threadpool/ThreadPool.java +++ b/modules/kernel/src/org/apache/axis2/util/threadpool/ThreadPool.java @@ -114,8 +114,7 @@ public class ThreadPool implements ThreadFactory { TimeUnit.SECONDS, new LinkedBlockingQueue(), new DefaultThreadFactory(name, daemon, priority)); } -// FIXME: This API is only in JDK 1.6 - Use reflection? -// rc.allowCoreThreadTimeOut(true); + rc.allowCoreThreadTimeOut(true); return rc; } diff --git a/modules/kernel/test/org/apache/axis2/util/threadpool/TestThreadPool.java b/modules/kernel/test/org/apache/axis2/util/threadpool/TestThreadPool.java index e3fe71ed66..4257cc67bf 100644 --- a/modules/kernel/test/org/apache/axis2/util/threadpool/TestThreadPool.java +++ b/modules/kernel/test/org/apache/axis2/util/threadpool/TestThreadPool.java @@ -24,6 +24,8 @@ import org.apache.axis2.AxisFault; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; public class TestThreadPool extends AbstractTestCase { /** @@ -46,7 +48,7 @@ public class TestThreadPool extends AbstractTestCase { } - public void testPool() throws AxisFault { + public void testPool() throws Exception { ThreadPool tPool = new ThreadPool(); List workerList = new ArrayList(); @@ -57,6 +59,8 @@ public class TestThreadPool extends AbstractTestCase { } tPool.safeShutDown(); + ThreadPoolExecutor executor = (ThreadPoolExecutor) tPool.getExecutor(); + executor.awaitTermination(5, TimeUnit.SECONDS); for (int i = 0; i < 5; i++) { assertEquals(true, ((TestWorker) workerList.get(i)).isWorkDone()); @@ -64,4 +68,34 @@ public class TestThreadPool extends AbstractTestCase { } + /** + * Test that core threads time out and terminate after the keepAlive period + * when allowCoreThreadTimeOut is enabled (AXIS2-5696). + */ + public void testCoreThreadsTimeOut() throws Exception { + ThreadPool tPool = new ThreadPool(); + ThreadPoolExecutor executor = (ThreadPoolExecutor) tPool.getExecutor(); + + // Verify allowCoreThreadTimeOut is enabled + assertTrue("allowCoreThreadTimeOut should be enabled", + executor.allowsCoreThreadTimeOut()); + + // Submit a task directly to the executor to create a core thread + TestWorker worker = new TestWorker(); + executor.execute(worker); + + // Wait briefly for the task to complete and the thread to be created + Thread.sleep(500); + assertTrue("Worker should have completed", worker.isWorkDone()); + assertTrue("Pool should have at least one thread", + executor.getPoolSize() > 0); + + // Wait for keepAlive timeout (10 seconds) plus buffer + Thread.sleep(12_000); + + // Core threads should have timed out and terminated + assertEquals("All core threads should have timed out", 0, + executor.getPoolSize()); + } + }
