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

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 812a63c28f [Cherry-pick to branch-1.3] [#13192] 
improvement(jdbc-catalog): fail fast with a clear error when the JDBC driver is 
missing (#13193) (#13202)
812a63c28f is described below

commit 812a63c28f943be9ca196c17e48c86b2f21696de
Author: geyanggang <[email protected]>
AuthorDate: Wed Sep 16 15:13:20 2026 +0800

    [Cherry-pick to branch-1.3] [#13192] improvement(jdbc-catalog): fail fast 
with a clear error when the JDBC driver is missing (#13193) (#13202)
    
    Cherry-pick Information:
    
    Original commit:
    
https://github.com/apache/gravitino/commit/e210811550cee04737464debf694848b732176ca
    Target branch: branch-1.3
    Status:  The conflict markers are resolved
---
 .../catalog/jdbc/utils/DataSourceUtils.java        | 57 ++++++++++++++++++++++
 .../jdbc/utils/TestDataSourceUrlValidation.java    | 25 ++++++++++
 docs/jdbc-mysql-catalog.md                         | 13 ++++-
 docs/jdbc-oceanbase-catalog.md                     | 16 +++++-
 4 files changed, 108 insertions(+), 3 deletions(-)

diff --git 
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/DataSourceUtils.java
 
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/DataSourceUtils.java
index a2b8e12b80..d69055db03 100644
--- 
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/DataSourceUtils.java
+++ 
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/utils/DataSourceUtils.java
@@ -59,10 +59,45 @@ public class DataSourceUtils {
     try {
       return createDBCPDataSource(jdbcConfig);
     } catch (Exception exception) {
+      if (isDriverClassMissing(exception)) {
+        // Some JDBC drivers are not packaged with Gravitino and must be 
installed by the user.
+        // Surface a clear, actionable message naming the driver instead of a 
raw
+        // ClassNotFoundException.
+        throw new GravitinoRuntimeException(
+            exception,
+            "JDBC driver class '%s' was not found on the catalog classpath. 
Install the driver "
+                + "JAR in the catalog's libs directory and recreate the 
catalog.",
+            jdbcConfig.getJdbcDriver());
+      }
       throw new GravitinoRuntimeException(exception, "Error creating 
datasource");
     }
   }
 
+  /**
+   * Returns whether the given throwable chain indicates a missing JDBC driver 
class. DBCP2 reports
+   * an absent driver as a {@link ClassNotFoundException} (sometimes wrapped 
in a {@link
+   * SQLException} whose message is "Cannot load JDBC driver class ..."), so 
both the exception
+   * chain and that message are checked.
+   *
+   * @param throwable the throwable thrown while creating the data source
+   * @return {@code true} if the failure is due to a driver class that cannot 
be loaded
+   */
+  private static boolean isDriverClassMissing(Throwable throwable) {
+    for (Throwable current = throwable; current != null; current = 
current.getCause()) {
+      if (current instanceof ClassNotFoundException || current instanceof 
NoClassDefFoundError) {
+        return true;
+      }
+      String message = current.getMessage();
+      if (message != null && message.contains("Cannot load JDBC driver 
class")) {
+        return true;
+      }
+      if (current.getCause() == current) {
+        break;
+      }
+    }
+    return false;
+  }
+
   private static DataSource createDBCPDataSource(JdbcConfig jdbcConfig) throws 
Exception {
     JdbcUrlUtils.validateJdbcConfig(
         jdbcConfig.getJdbcDriver(), jdbcConfig.getJdbcUrl(), 
jdbcConfig.getAllConfig());
@@ -71,6 +106,11 @@ public class DataSourceUtils {
     String jdbcUrl = jdbcConfig.getJdbcUrl();
     basicDataSource.setUrl(jdbcUrl);
     String driverClassName = jdbcConfig.getJdbcDriver();
+    // DBCP2 loads the driver lazily on the first connection, so a missing 
driver would otherwise
+    // only surface much later (and as an opaque error). Verify the driver 
class is on the catalog
+    // classpath now, so catalog creation fails fast with a clear, actionable 
message. Loaded
+    // without initialization; the H2 driver is already rejected above.
+    verifyDriverPresent(driverClassName);
     basicDataSource.setDriverClassName(driverClassName);
     String userName = jdbcConfig.getUsername();
     basicDataSource.setUsername(userName);
@@ -86,6 +126,23 @@ public class DataSourceUtils {
     return basicDataSource;
   }
 
+  /**
+   * Verifies that the configured JDBC driver class is loadable from the 
catalog classpath. The
+   * class is resolved without initialization using the catalog's context 
class loader (falling back
+   * to this class's loader), so an absent driver fails catalog creation 
immediately rather than on
+   * the first connection.
+   *
+   * @param driverClassName the fully qualified JDBC driver class name
+   * @throws ClassNotFoundException if the driver class is not on the catalog 
classpath
+   */
+  private static void verifyDriverPresent(String driverClassName) throws 
ClassNotFoundException {
+    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+    if (classLoader == null) {
+      classLoader = DataSourceUtils.class.getClassLoader();
+    }
+    Class.forName(driverClassName, false, classLoader);
+  }
+
   private static Properties getProperties(JdbcConfig jdbcConfig) {
     Properties properties = new Properties();
     properties.putAll(jdbcConfig.getAllConfig());
diff --git 
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestDataSourceUrlValidation.java
 
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestDataSourceUrlValidation.java
index 21cdba0984..ae9548e987 100644
--- 
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestDataSourceUrlValidation.java
+++ 
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/utils/TestDataSourceUrlValidation.java
@@ -189,4 +189,29 @@ public class TestDataSourceUrlValidation {
     Assertions.assertEquals(
         "H2 JDBC driver is not allowed in catalog configuration", 
gre.getMessage());
   }
+
+  @Test
+  public void testMissingDriverGivesClearError() {
+    // A driver that is not on the classpath must fail fast at catalog 
creation with an actionable
+    // message naming the driver, not a raw ClassNotFoundException surfaced 
later on first use.
+    // Use a driver class that is guaranteed absent so the assertion does not 
depend on which
+    // vendor jars happen to be present.
+    String missingDriver = "com.example.NonExistentJdbcDriver";
+    HashMap<String, String> properties = Maps.newHashMap();
+    properties.put(JdbcConfig.JDBC_DRIVER.getKey(), missingDriver);
+    properties.put(JdbcConfig.JDBC_URL.getKey(), "jdbc:sqlite::memory:");
+    properties.put(JdbcConfig.USERNAME.getKey(), "test");
+    properties.put(JdbcConfig.PASSWORD.getKey(), "test");
+
+    GravitinoRuntimeException gre =
+        Assertions.assertThrows(
+            GravitinoRuntimeException.class, () -> 
DataSourceUtils.createDataSource(properties));
+    Assertions.assertTrue(
+        gre.getMessage().contains(missingDriver),
+        "message should name the missing driver: " + gre.getMessage());
+    Assertions.assertTrue(
+        gre.getMessage().contains("was not found"),
+        "message should state the driver was not found: " + gre.getMessage());
+    Assertions.assertInstanceOf(ClassNotFoundException.class, gre.getCause());
+  }
 }
diff --git a/docs/jdbc-mysql-catalog.md b/docs/jdbc-mysql-catalog.md
index 6e7c4172ce..bd2964861b 100644
--- a/docs/jdbc-mysql-catalog.md
+++ b/docs/jdbc-mysql-catalog.md
@@ -52,7 +52,18 @@ Besides the [common catalog 
properties](./gravitino-server-config.md#catalog-pro
 | `jdbc.pool.max-wait-ms` | The maximum Duration that the pool will wait for a 
connection to be returned. `30000` by default.      | `30000`       | No       |
 
 :::caution
-Download the corresponding JDBC driver to the `catalogs/jdbc-mysql/libs` 
directory.
+Gravitino does not package the MySQL JDBC driver, because MySQL Connector/J is
+GPLv2 and cannot be redistributed. You must supply it yourself.
+
+Download MySQL Connector/J (`com.mysql:mysql-connector-j`, 8.0.16 or later; the
+`com.mysql.cj.jdbc.Driver` class) from
+[Maven Central](https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/) 
and
+place the JAR in the `catalogs/jdbc-mysql/libs` directory.
+
+For container or Kubernetes deployments where you cannot copy into that
+directory directly, supply the driver through your deployment's mechanism for
+adding catalog libraries. The catalog fails fast at creation time with a clear
+message if the driver is absent.
 :::
 
 ### Driver Version Compatibility
diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md
index 319a2ad302..22ffc1c0ec 100644
--- a/docs/jdbc-oceanbase-catalog.md
+++ b/docs/jdbc-oceanbase-catalog.md
@@ -50,8 +50,20 @@ Besides the [common catalog 
properties](./gravitino-server-config.md#catalog-pro
 | `jdbc.pool.max-wait-ms` | The maximum Duration that the pool will wait for a 
connection to be returned. `30000` by default.                                  
   | `30000`       | No       |
 
 :::caution
-Before using the OceanBase Catalog, you must download the corresponding JDBC 
driver to the `catalogs/jdbc-oceanbase/libs` directory.
-Gravitino doesn't package the JDBC driver for OceanBase due to licensing 
issues.
+Gravitino does not package the OceanBase JDBC driver due to licensing, so you
+must supply it yourself. OceanBase speaks the MySQL wire protocol, so either
+driver works: OceanBase Connector/J (`com.oceanbase:oceanbase-client`, 2.4.18
+or later; the `com.oceanbase.jdbc.Driver` class) or MySQL Connector/J
+(`com.mysql:mysql-connector-j`, 8.0.16 or later; the `com.mysql.cj.jdbc.Driver`
+class). Download it from Maven Central
+([OceanBase](https://repo1.maven.org/maven2/com/oceanbase/oceanbase-client/),
+[MySQL](https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/)) and place
+the JAR in the `catalogs/jdbc-oceanbase/libs` directory.
+
+For container or Kubernetes deployments where you cannot copy into that
+directory directly, supply the driver through your deployment's mechanism for
+adding catalog libraries. The catalog fails fast at creation time with a clear
+message if the driver is absent.
 :::
 
 ### Driver Version Compatibility

Reply via email to