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

tkobayas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new 82697e0c701 [incubator-kie-drools-6758] Register concrete project 
classloaders as parallel capable (#6809)
82697e0c701 is described below

commit 82697e0c7018bd89a413f92aa297f064267db932
Author: Nikhil Ramashasthri <[email protected]>
AuthorDate: Tue Jul 21 04:20:59 2026 -0400

    [incubator-kie-drools-6758] Register concrete project classloaders as 
parallel capable (#6809)
    
    Parallel capability is per-class, not inherited: ProjectClassLoader
    registers, but the JVM checks the loader's concrete runtime class, so
    DynamicProjectClassLoader, IBMDynamicClassLoader, StaticProjectClassLoader,
    IBMStaticClassLoader and DummyInternalTypesClassLoader all fell back to
    locking the loader instance in getClassLoadingLock, serializing every
    class load through one monitor. Register all five; the defineType/
    defineClass/undefineClass paths they expose to parallel callers were
    already synchronized and the caches are ConcurrentHashMaps.
---
 .../wiring/dynamic/DynamicProjectClassLoader.java  | 11 ++++
 .../dynamic/ParallelCapableClassLoaderTest.java    | 61 ++++++++++++++++++++++
 .../wiring/statics/StaticProjectClassLoader.java   | 16 ++++++
 .../StaticParallelCapableClassLoaderTest.java      | 56 ++++++++++++++++++++
 4 files changed, 144 insertions(+)

diff --git 
a/drools-wiring/drools-wiring-dynamic/src/main/java/org/drools/wiring/dynamic/DynamicProjectClassLoader.java
 
b/drools-wiring/drools-wiring-dynamic/src/main/java/org/drools/wiring/dynamic/DynamicProjectClassLoader.java
index 31dba3af859..2a7437a9d94 100755
--- 
a/drools-wiring/drools-wiring-dynamic/src/main/java/org/drools/wiring/dynamic/DynamicProjectClassLoader.java
+++ 
b/drools-wiring/drools-wiring-dynamic/src/main/java/org/drools/wiring/dynamic/DynamicProjectClassLoader.java
@@ -33,6 +33,13 @@ import org.drools.wiring.api.util.ClassUtils;
 
 public class DynamicProjectClassLoader extends ProjectClassLoader {
 
+    static {
+        // Parallel capability is per-class, not inherited: without this 
registration the JVM
+        // ignores the base class' one and getClassLoadingLock falls back to 
locking `this`,
+        // serializing all class loading through this loader (issue #6758).
+        registerAsParallelCapable();
+    }
+
     private static boolean isIBM_JVM = 
System.getProperty("java.vendor").toLowerCase().contains("ibm");
 
     protected DynamicProjectClassLoader(ClassLoader parent, ResourceProvider 
resourceProvider) {
@@ -51,6 +58,10 @@ public class DynamicProjectClassLoader extends 
ProjectClassLoader {
 
     public static class IBMDynamicClassLoader extends 
DynamicProjectClassLoader {
 
+        static {
+            registerAsParallelCapable();
+        }
+
         private final boolean parentImplementsFindResources;
 
         private static final Enumeration<URL> EMPTY_RESOURCE_ENUM = new 
Vector<URL>().elements();
diff --git 
a/drools-wiring/drools-wiring-dynamic/src/test/java/org/drools/wiring/dynamic/ParallelCapableClassLoaderTest.java
 
b/drools-wiring/drools-wiring-dynamic/src/test/java/org/drools/wiring/dynamic/ParallelCapableClassLoaderTest.java
new file mode 100644
index 00000000000..b7ffc6c277b
--- /dev/null
+++ 
b/drools-wiring/drools-wiring-dynamic/src/test/java/org/drools/wiring/dynamic/ParallelCapableClassLoaderTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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
+ *
+ *   http://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.
+ */
+package org.drools.wiring.dynamic;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Parallel capability is per-class, not inherited: the JVM only enables 
per-name class loading
+ * locks when the loader's concrete runtime class and its whole superclass 
chain are registered.
+ * Without the subclass registration, getClassLoadingLock falls back to 
locking the loader itself
+ * and all class loading serializes on one monitor (issue #6758).
+ *
+ * <p>The probe subclass verifies this deterministically: {@link
+ * ClassLoader#registerAsParallelCapable()} returns {@code false} when any 
ancestor class is not
+ * itself registered, so these assertions fail if the registration is ever 
removed from the
+ * loaders under test.
+ */
+class ParallelCapableClassLoaderTest {
+
+    @Test
+    void dynamicProjectClassLoaderChainIsParallelCapable() {
+        assertTrue(DynamicProbe.PARALLEL_CAPABLE,
+                "registerAsParallelCapable() in a subclass only succeeds when 
the whole "
+                        + "DynamicProjectClassLoader ancestry is registered");
+        DynamicProbe probe = new DynamicProbe(getClass().getClassLoader());
+        Object lockA = probe.lockFor("com.example.A");
+        assertNotSame(probe, lockA, "a parallel-capable loader must not lock 
on itself");
+        assertNotSame(lockA, probe.lockFor("com.example.B"));
+    }
+
+    private static class DynamicProbe extends DynamicProjectClassLoader {
+        private static final boolean PARALLEL_CAPABLE = 
registerAsParallelCapable();
+
+        private DynamicProbe(ClassLoader parent) {
+            super(parent, null);
+        }
+
+        private Object lockFor(String className) {
+            return getClassLoadingLock(className);
+        }
+    }
+}
diff --git 
a/drools-wiring/drools-wiring-static/src/main/java/org/drools/wiring/statics/StaticProjectClassLoader.java
 
b/drools-wiring/drools-wiring-static/src/main/java/org/drools/wiring/statics/StaticProjectClassLoader.java
index 491e935a432..3ec0d1a7da3 100755
--- 
a/drools-wiring/drools-wiring-static/src/main/java/org/drools/wiring/statics/StaticProjectClassLoader.java
+++ 
b/drools-wiring/drools-wiring-static/src/main/java/org/drools/wiring/statics/StaticProjectClassLoader.java
@@ -30,6 +30,13 @@ import org.drools.wiring.api.classloader.ProjectClassLoader;
 
 public class StaticProjectClassLoader extends ProjectClassLoader {
 
+    static {
+        // Parallel capability is per-class, not inherited: without this 
registration the JVM
+        // ignores the base class' one and getClassLoadingLock falls back to 
locking `this`,
+        // serializing all class loading through this loader (issue #6758).
+        registerAsParallelCapable();
+    }
+
     private static boolean isIBM_JVM = 
System.getProperty("java.vendor").toLowerCase().contains("ibm");
 
     protected StaticProjectClassLoader(ClassLoader parent, ResourceProvider 
resourceProvider) {
@@ -42,6 +49,11 @@ public class StaticProjectClassLoader extends 
ProjectClassLoader {
     }
 
     public static class IBMStaticClassLoader extends StaticProjectClassLoader {
+
+        static {
+            registerAsParallelCapable();
+        }
+
         private final boolean parentImplementsFindResources;
 
         private static final Enumeration<URL> EMPTY_RESOURCE_ENUM = new 
Vector<URL>().elements();
@@ -75,6 +87,10 @@ public class StaticProjectClassLoader extends 
ProjectClassLoader {
 
     private static class DummyInternalTypesClassLoader extends ClassLoader 
implements InternalTypesClassLoader {
 
+        static {
+            registerAsParallelCapable();
+        }
+
         private final ProjectClassLoader projectClassLoader;
 
         private DummyInternalTypesClassLoader(ProjectClassLoader 
projectClassLoader) {
diff --git 
a/drools-wiring/drools-wiring-static/src/test/java/org/drools/wiring/statics/StaticParallelCapableClassLoaderTest.java
 
b/drools-wiring/drools-wiring-static/src/test/java/org/drools/wiring/statics/StaticParallelCapableClassLoaderTest.java
new file mode 100644
index 00000000000..9e2801afd41
--- /dev/null
+++ 
b/drools-wiring/drools-wiring-static/src/test/java/org/drools/wiring/statics/StaticParallelCapableClassLoaderTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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
+ *
+ *   http://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.
+ */
+package org.drools.wiring.statics;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * See ParallelCapableClassLoaderTest in drools-wiring-dynamic: parallel 
capability is per-class,
+ * so StaticProjectClassLoader needs its own registration despite the base 
class' one
+ * (issue #6758). registerAsParallelCapable() in the probe returns {@code 
false} when any
+ * ancestor class is not itself registered.
+ */
+class StaticParallelCapableClassLoaderTest {
+
+    @Test
+    void staticProjectClassLoaderChainIsParallelCapable() {
+        assertThat(StaticProbe.PARALLEL_CAPABLE)
+                .as("registerAsParallelCapable() in a subclass only succeeds 
when the whole "
+                        + "StaticProjectClassLoader ancestry is registered")
+                .isTrue();
+        StaticProbe probe = new StaticProbe(getClass().getClassLoader());
+        Object lockA = probe.lockFor("com.example.A");
+        assertThat(lockA).as("a parallel-capable loader must not lock on 
itself").isNotSameAs(probe);
+        assertThat(probe.lockFor("com.example.B")).isNotSameAs(lockA);
+    }
+
+    private static class StaticProbe extends StaticProjectClassLoader {
+        private static final boolean PARALLEL_CAPABLE = 
registerAsParallelCapable();
+
+        private StaticProbe(ClassLoader parent) {
+            super(parent, null);
+        }
+
+        private Object lockFor(String className) {
+            return getClassLoadingLock(className);
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to