diff --git a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderImpl.java b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderImpl.java index 8b332a20d7f..7a3367ef0bc 100644 --- a/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderImpl.java +++ b/drools-compiler/src/main/java/org/drools/compiler/builder/impl/KnowledgeBuilderImpl.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.function.Supplier; import org.drools.base.RuleBase; @@ -500,10 +501,11 @@ protected PackageRegistryManager getPackageRegistryManager() { } public static class ForkJoinPoolHolder { - public static final ForkJoinPool COMPILER_POOL = new ForkJoinPool(); // avoid common pool + public static final ForkJoinPool COMPILER_POOL = new ForkJoinPool(Math.min(32767, Runtime.getRuntime().availableProcessors()), pool -> new ForkJoinWorkerThread(pool) {{ + setContextClassLoader(Thread.currentThread().getContextClassLoader()); + }}, null, false); // avoid common pool } - public boolean filterAccepts(ResourceChange.Type type, String namespace, String name) { return assetFilter == null || !AssetFilter.Action.DO_NOTHING.equals(assetFilter.accept(type, namespace, name)); } diff --git a/drools-compiler/src/test/java/org/drools/compiler/builder/impl/KnowledgeBuilderImplCompilerFJPoolTest.java b/drools-compiler/src/test/java/org/drools/compiler/builder/impl/KnowledgeBuilderImplCompilerFJPoolTest.java new file mode 100644 index 00000000000..68938f15131 --- /dev/null +++ b/drools-compiler/src/test/java/org/drools/compiler/builder/impl/KnowledgeBuilderImplCompilerFJPoolTest.java @@ -0,0 +1,75 @@ +package org.drools.compiler.builder.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertSame; + +public class KnowledgeBuilderImplCompilerFJPoolTest { + + private ClassLoader originalClassLoader; + private ClassLoader customClassLoader; + + @BeforeEach + void setUp() { + // Save the original classloader + originalClassLoader = Thread.currentThread().getContextClassLoader(); + + // Create a custom classloader for testing + customClassLoader = new ClassLoader(ClassLoader.getSystemClassLoader()) {}; + Thread.currentThread().setContextClassLoader(customClassLoader); + } + + @AfterEach + void tearDown() { + // Restore original classloader + Thread.currentThread().setContextClassLoader(originalClassLoader); + } + + private static class ClassLoaderCheckTask extends RecursiveAction { + private final List results; + private final int index; + + public ClassLoaderCheckTask(List results, int index) { + this.results = results; + this.index = index; + } + + @Override + protected void compute() { + results.set(index, Thread.currentThread().getContextClassLoader()); + } + } + + @Test + void testCompilerPoolClassLoader() throws Exception { + int numTasks = 5; + List results = new ArrayList<>(numTasks); + for (int i = 0; i < numTasks; i++) { + results.add(null); + } + + // Submit tasks to the COMPILER_POOL + for (int i = 0; i < numTasks; i++) { + KnowledgeBuilderImpl.ForkJoinPoolHolder.COMPILER_POOL.submit(new ClassLoaderCheckTask(results, i)); + } + + // Wait for completion + KnowledgeBuilderImpl.ForkJoinPoolHolder.COMPILER_POOL.awaitQuiescence(1, TimeUnit.SECONDS); + + // Verify all worker threads have the expected classloader + for (ClassLoader workerClassLoader : results) { + assertSame( + customClassLoader, + workerClassLoader, + "Worker thread should have inherited the custom classloader" + ); + } + } +}