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"));
+ }
+}