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