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

mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 5abf8de434 [#11704] fix(core): add org.apache.gravitino.hive.* prefix 
to IsolatedClassLoader.isCatalogClass() (#11705)
5abf8de434 is described below

commit 5abf8de4347defbb8e46e37951d423ffbc4c2398
Author: weijiajun <[email protected]>
AuthorDate: Wed Jun 17 19:16:20 2026 +0800

    [#11704] fix(core): add org.apache.gravitino.hive.* prefix to 
IsolatedClassLoader.isCatalogClass() (#11705)
    
    ### What changes were proposed in this pull request?
    
    Add `org.apache.gravitino.hive.` as a recognized catalog class prefix in
    `IsolatedClassLoader.isCatalogClass()`, and add unit tests to cover the
    classification of all hive-related packages.
    
    ### Why are the changes needed?
    
    Fix: #11704
    
    PR #9460 refactored the Hive catalog and moved classes such as
    `HiveExceptionConverter` from `org.apache.gravitino.catalog.hive.*` to
    `org.apache.gravitino.hive.*`. However, `isCatalogClass()` was not
    updated,
    so classes under `org.apache.gravitino.hive.*` are incorrectly treated
    as
    shared classes and delegated to the server classloader.
    
    In certain classloader initialization timing windows, the server
    classloader
    becomes the defining classloader for `HiveExceptionConverter`. Its
    compiler-generated synthetic class `$1` (produced by `switch`-on-enum)
    is
    then requested from the server classloader, which cannot find it in the
    server
    classpath. **The JVM permanently caches this load failure for the
    process
    lifetime**, causing all subsequent calls to throw `NoClassDefFoundError`
    until
    the process is restarted.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. This is an internal classloader fix with no API or behavior change.
    
    ### How was this patch tested?
    
    Added `TestIsolatedClassLoader` with four test methods covering:
    - `org.apache.gravitino.hive.*` classes are recognized as catalog
    classes
    - `org.apache.gravitino.catalog.hive.*` classes continue to be
    recognized
    - Other catalog packages (`lakehouse`, `jdbc`, `kafka`, etc.) are
    unaffected
    - Server-side shared classes are correctly excluded
    
    ---------
    
    Co-authored-by: weijiajun <[email protected]>
---
 .../gravitino/utils/IsolatedClassLoader.java       | 26 ++++---
 .../gravitino/utils/TestIsolatedClassLoader.java   | 91 ++++++++++++++++++++++
 2 files changed, 107 insertions(+), 10 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/utils/IsolatedClassLoader.java 
b/core/src/main/java/org/apache/gravitino/utils/IsolatedClassLoader.java
index a7006aca57..18784e41c1 100644
--- a/core/src/main/java/org/apache/gravitino/utils/IsolatedClassLoader.java
+++ b/core/src/main/java/org/apache/gravitino/utils/IsolatedClassLoader.java
@@ -240,16 +240,22 @@ public class IsolatedClassLoader implements Closeable {
    * @return true if the class is a catalog class, false otherwise.
    */
   private boolean isCatalogClass(String name) {
-    return name.startsWith("org.apache.gravitino.catalog")
-        && (name.startsWith("org.apache.gravitino.catalog.hive.")
-            || name.startsWith("org.apache.gravitino.catalog.lakehouse.")
-            || name.startsWith("org.apache.gravitino.catalog.jdbc.")
-            || name.startsWith("org.apache.gravitino.catalog.mysql.")
-            || name.startsWith("org.apache.gravitino.catalog.postgresql.")
-            || name.startsWith("org.apache.gravitino.catalog.doris.")
-            || name.startsWith("org.apache.gravitino.catalog.fileset.")
-            || name.startsWith("org.apache.gravitino.catalog.model.")
-            || name.startsWith("org.apache.gravitino.catalog.kafka."));
+    // org.apache.gravitino.hive.* covers classes moved to the shared 
hive-metastore-common
+    // module by the HiveClient refactoring (e.g. HiveExceptionConverter). 
Without this prefix
+    // those classes are treated as shared and loaded by the server 
classloader; their
+    // compiler-generated synthetic classes (e.g. $1 from switch-on-enum) are 
then requested
+    // from the server classloader which cannot find them, causing a permanent
+    // NoClassDefFoundError that is cached by the JVM for the lifetime of the 
process.
+    return name.startsWith("org.apache.gravitino.hive.")
+        || name.startsWith("org.apache.gravitino.catalog.hive.")
+        || name.startsWith("org.apache.gravitino.catalog.lakehouse.")
+        || name.startsWith("org.apache.gravitino.catalog.jdbc.")
+        || name.startsWith("org.apache.gravitino.catalog.mysql.")
+        || name.startsWith("org.apache.gravitino.catalog.postgresql.")
+        || name.startsWith("org.apache.gravitino.catalog.doris.")
+        || name.startsWith("org.apache.gravitino.catalog.fileset.")
+        || name.startsWith("org.apache.gravitino.catalog.model.")
+        || name.startsWith("org.apache.gravitino.catalog.kafka.");
   }
 
   /**
diff --git 
a/core/src/test/java/org/apache/gravitino/utils/TestIsolatedClassLoader.java 
b/core/src/test/java/org/apache/gravitino/utils/TestIsolatedClassLoader.java
new file mode 100644
index 0000000000..c2145dad89
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/utils/TestIsolatedClassLoader.java
@@ -0,0 +1,91 @@
+/*
+ * 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.apache.gravitino.utils;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class TestIsolatedClassLoader {
+
+  private IsolatedClassLoader classLoader;
+  private Method isCatalogClassMethod;
+
+  @BeforeEach
+  public void setUp() throws Exception {
+    classLoader =
+        new IsolatedClassLoader(
+            Collections.emptyList(), Collections.emptyList(), 
Collections.emptyList());
+    isCatalogClassMethod =
+        IsolatedClassLoader.class.getDeclaredMethod("isCatalogClass", 
String.class);
+    isCatalogClassMethod.setAccessible(true);
+  }
+
+  private boolean isCatalogClass(String name) throws Exception {
+    return (boolean) isCatalogClassMethod.invoke(classLoader, name);
+  }
+
+  @Test
+  public void testHivePackageRecognizedAsCatalogClass() throws Exception {
+    // org.apache.gravitino.hive.* was moved from catalog.hive.* by HiveClient 
refactoring.
+    // These must be treated as catalog classes so they are loaded by the 
IsolatedClassLoader,
+    // not the server classloader. Otherwise their compiler-generated $1 
synthetic classes
+    // (from switch-on-enum) fail to load and the JVM permanently caches the 
failure.
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.hive.client.HiveExceptionConverter"));
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.hive.client.HiveExceptionConverter$1"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.hive.client.HiveClientPool"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.hive.HiveClientFactory"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.hive.SomeOtherClass"));
+  }
+
+  @Test
+  public void testCatalogHivePackageRecognizedAsCatalogClass() throws 
Exception {
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.catalog.hive.HiveCatalogCapability"));
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.catalog.hive.HiveCatalogCapability$1"));
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.catalog.hive.HiveCatalogOperations"));
+  }
+
+  @Test
+  public void testOtherCatalogPackagesRecognizedAsCatalogClass() throws 
Exception {
+    Assertions.assertTrue(
+        
isCatalogClass("org.apache.gravitino.catalog.lakehouse.iceberg.IcebergCatalog"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.catalog.jdbc.JdbcCatalog"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.catalog.kafka.KafkaCatalog"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.catalog.fileset.FilesetCatalog"));
+    
Assertions.assertTrue(isCatalogClass("org.apache.gravitino.catalog.model.ModelCatalog"));
+  }
+
+  @Test
+  public void testNonCatalogPackagesNotRecognizedAsCatalogClass() throws 
Exception {
+    // Server-side / shared classes must NOT be treated as catalog classes.
+    
Assertions.assertFalse(isCatalogClass("org.apache.gravitino.connector.BaseCatalog"));
+    
Assertions.assertFalse(isCatalogClass("org.apache.gravitino.NameIdentifier"));
+    
Assertions.assertFalse(isCatalogClass("org.apache.gravitino.catalog.SomeSharedClass"));
+    Assertions.assertFalse(isCatalogClass("java.lang.String"));
+    Assertions.assertFalse(isCatalogClass("org.slf4j.Logger"));
+  }
+}

Reply via email to