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

daim pushed a commit to branch OAK-11916
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit 789a893121c5fa29310742a65e008c117d2a50ab
Author: rishabhdaim <[email protected]>
AuthorDate: Mon Nov 24 12:05:46 2025 +0530

    OAK-11916 : added exiting executor service in oak-commons
---
 .../commons/internal/concurrent/ExecutorUtils.java | 36 ++++++++++++++++++++++
 .../internal/concurrent/ExecutorUtilsTest.java     | 28 +++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git 
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtils.java
 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtils.java
index 4ef9de91e1..d6392b5f18 100644
--- 
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtils.java
+++ 
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtils.java
@@ -20,6 +20,11 @@ package 
org.apache.jackrabbit.oak.commons.internal.concurrent;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Util methods for {@link java.util.concurrent.Executor}
@@ -36,4 +41,35 @@ public class ExecutorUtils {
     public static ExecutorService newDirectExecutorService() {
         return new DirectExecutorService();
     }
+
+    public static ExecutorService getExitingExecutorService(ThreadPoolExecutor 
executor) {
+        setDeamonThreadFactory(executor);
+        final ExecutorService service = 
Executors.unconfigurableExecutorService(executor);
+        // JVM shutdown hook for graceful executor shutdown
+        addRuntimeShutdownHook(executor, service);
+        return service;
+
+    }
+
+    private static void addRuntimeShutdownHook(ThreadPoolExecutor executor, 
ExecutorService service) {
+        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+            executor.shutdown();
+            try {
+                if (!executor.awaitTermination(120, TimeUnit.SECONDS)) {
+                    executor.shutdownNow();
+                }
+            } catch (InterruptedException e) {
+                executor.shutdownNow();
+                Thread.currentThread().interrupt();
+            }
+        }, "RuntimeShutdownHook-for-" + service));
+    }
+
+    private static void setDeamonThreadFactory(final ThreadPoolExecutor 
executor) {
+        executor.setThreadFactory(
+                new ThreadFactoryBuilder()
+                        .setDaemon(true)
+                        .setThreadFactory(executor.getThreadFactory())
+                        .build());
+    }
 }
diff --git 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtilsTest.java
 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtilsTest.java
index 20b9c81b83..d7d55465a9 100644
--- 
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtilsTest.java
+++ 
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/ExecutorUtilsTest.java
@@ -23,7 +23,10 @@ import org.junit.Test;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit cases for ExecutorUtils
@@ -72,4 +75,29 @@ public class ExecutorUtilsTest {
         Assert.assertEquals("test", future.get());
     }
 
+    @Test
+    public void testGetExitingExecutorServiceReturnsUnconfigurableExecutor() {
+        ThreadPoolExecutor executor = (ThreadPoolExecutor) 
Executors.newFixedThreadPool(1);
+        ExecutorService service = 
ExecutorUtils.getExitingExecutorService(executor);
+        Assert.assertNotNull(service);
+        
Assert.assertFalse(service.getClass().getName().contains("ThreadPoolExecutor"));
+    }
+
+    @Test
+    public void testDaemonThreadFactoryIsSet() {
+        ThreadPoolExecutor executor = (ThreadPoolExecutor) 
Executors.newFixedThreadPool(1);
+        ExecutorUtils.getExitingExecutorService(executor);
+        Assert.assertTrue(executor.getThreadFactory().newThread(() -> 
{}).isDaemon());
+    }
+
+    @Test
+    public void testShutdownHookIsRegisteredAndShutsDownExecutor() throws 
Exception {
+        ThreadPoolExecutor executor = (ThreadPoolExecutor) 
Executors.newFixedThreadPool(1);
+        ExecutorUtils.getExitingExecutorService(executor);
+        // Simulate JVM shutdown hook
+        executor.shutdown();
+        boolean terminated = executor.awaitTermination(1, TimeUnit.SECONDS);
+        Assert.assertTrue(terminated || executor.isShutdown());
+    }
+
 }
\ No newline at end of file

Reply via email to