This is an automated email from the ASF dual-hosted git repository.
diqiu50 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 441a717073 [#13211] improvement(trino-connector): Add
CatalogConnectorAdapterProvider SPI for external adapters (#13212)
441a717073 is described below
commit 441a717073cec92bb4fbaa3c8ab8d799053a0b0e
Author: Yuhui <[email protected]>
AuthorDate: Thu Sep 17 09:26:21 2026 +0800
[#13211] improvement(trino-connector): Add CatalogConnectorAdapterProvider
SPI for external adapters (#13212)
### What changes were proposed in this pull request?
- Add `CatalogConnectorAdapterProvider`, a `ServiceLoader` SPI that maps
a Gravitino catalog provider to a `CatalogConnectorAdapter`.
- `DefaultCatalogConnectorFactory` registers discovered providers after
the built-in adapters, ignores a provider whose name is already
registered, and skips one that fails to load.
- `ContainerSuite` mounts every jar under
`GRAVITINO_TRINO_CONNECTOR_EXTRA_LIBS` into the Trino container's
Gravitino plugin directory, so an integration test can load an extension
jar next to the connector.
### Why are the changes needed?
Adapters shipped outside the connector jar can be registered without
changing the connector.
Fix: #13211
### Does this PR introduce _any_ user-facing change?
New SPI
`org.apache.gravitino.trino.connector.catalog.CatalogConnectorAdapterProvider`.
Existing behavior is unchanged when no provider is present.
### How was this patch tested?
Added `TestDefaultCatalogConnectorFactory` with fake, duplicate and
broken providers registered via `META-INF/services`; existing
trino-connector unit tests pass.
---
.../integration/test/container/ContainerSuite.java | 29 +++++++--
.../catalog/CatalogConnectorAdapterProvider.java | 44 ++++++++++++++
.../catalog/DefaultCatalogConnectorFactory.java | 40 +++++++++++++
.../BrokenCatalogConnectorAdapterProvider.java | 37 ++++++++++++
.../DuplicateCatalogConnectorAdapterProvider.java | 35 +++++++++++
.../FailingCatalogConnectorAdapterProvider.java | 37 ++++++++++++
.../FakeCatalogConnectorAdapterProvider.java | 38 ++++++++++++
.../TestDefaultCatalogConnectorFactory.java | 68 ++++++++++++++++++++++
...nnector.catalog.CatalogConnectorAdapterProvider | 27 +++++++++
9 files changed, 349 insertions(+), 6 deletions(-)
diff --git
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/ContainerSuite.java
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/ContainerSuite.java
index 990a6b6f9e..9adff2730d 100644
---
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/ContainerSuite.java
+++
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/container/ContainerSuite.java
@@ -27,6 +27,7 @@ import com.github.dockerjava.api.model.Network.Ipam.Config;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
@@ -290,6 +291,27 @@ public class ContainerSuite implements Closeable {
}
}
+ /**
+ * Files copied into the Gravitino plugin directory of the Trino container:
the connector lib
+ * directory, plus every jar in the directory named by {@code
+ * GRAVITINO_TRINO_CONNECTOR_EXTRA_LIBS}, which lets a build add plugin jars
that are not part of
+ * the connector itself.
+ */
+ private static Map<String, String> trinoPluginFilesToMount(String
trinoConnectorLibDir) {
+ ImmutableMap.Builder<String, String> files = ImmutableMap.builder();
+ files.put(TrinoContainer.TRINO_CONTAINER_PLUGIN_GRAVITINO_DIR,
trinoConnectorLibDir);
+ String extraLibDir = System.getenv("GRAVITINO_TRINO_CONNECTOR_EXTRA_LIBS");
+ if (extraLibDir != null && !extraLibDir.isEmpty()) {
+ File[] jars = new File(extraLibDir).listFiles((dir, name) ->
name.endsWith(".jar"));
+ for (File jar : jars == null ? new File[0] : jars) {
+ files.put(
+ TrinoContainer.TRINO_CONTAINER_PLUGIN_GRAVITINO_DIR + "/" +
jar.getName(),
+ jar.getAbsolutePath());
+ }
+ }
+ return files.build();
+ }
+
public void startTrinoContainer(
String trinoConfDir,
String trinoConnectorLibDir,
@@ -317,12 +339,7 @@ public class ContainerSuite implements Closeable {
.put("host.docker.internal", "host-gateway")
.put(HiveContainer.HOST_NAME, hiveContainerIp)
.build())
- .withFilesToMount(
- ImmutableMap.<String, String>builder()
- .put(
-
TrinoContainer.TRINO_CONTAINER_PLUGIN_GRAVITINO_DIR,
- trinoConnectorLibDir)
- .build())
+
.withFilesToMount(trinoPluginFilesToMount(trinoConnectorLibDir))
.withExposePorts(ImmutableSet.of(TrinoContainer.TRINO_PORT))
.withTrinoConfDir(trinoConfDir)
.withMetalakeName(metalakeName)
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorAdapterProvider.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorAdapterProvider.java
new file mode 100644
index 0000000000..ed91617f23
--- /dev/null
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorAdapterProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.trino.connector.catalog;
+
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+
+/**
+ * Service provider interface that lets a jar in the plugin directory
contribute a {@link
+ * CatalogConnectorAdapter} for a Gravitino catalog provider. Implementations
are discovered through
+ * {@link java.util.ServiceLoader} when the connector starts.
+ */
+public interface CatalogConnectorAdapterProvider {
+
+ /**
+ * The Gravitino catalog provider this adapter serves, for example {@code
jdbc-oracle}.
+ *
+ * @return the provider name
+ */
+ String provider();
+
+ /**
+ * Creates the adapter for the provider.
+ *
+ * @param config the Gravitino connector configuration
+ * @return a new adapter
+ */
+ CatalogConnectorAdapter createAdapter(GravitinoConfig config);
+}
diff --git
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/DefaultCatalogConnectorFactory.java
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/DefaultCatalogConnectorFactory.java
index 319fe1a9f3..ca6dfd9f2e 100644
---
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/DefaultCatalogConnectorFactory.java
+++
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/DefaultCatalogConnectorFactory.java
@@ -21,6 +21,9 @@ package org.apache.gravitino.trino.connector.catalog;
import io.airlift.log.Logger;
import io.trino.spi.TrinoException;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
import java.util.Set;
import org.apache.gravitino.trino.connector.GravitinoConfig;
import org.apache.gravitino.trino.connector.GravitinoErrorCode;
@@ -81,9 +84,46 @@ public class DefaultCatalogConnectorFactory implements
CatalogConnectorFactory {
catalogBuilders.put(
TRINO_CLUSTER_CONNECTOR_PROVIDER_NAME,
new CatalogConnectorContext.Builder(new
TrinoClusterConnectorAdapter()));
+ registerAdapterProviders(config);
LOG.info("Start the DefaultCatalogConnectorFactory");
}
+ /**
+ * Adds the adapters contributed through {@link
CatalogConnectorAdapterProvider}. A provider for a
+ * catalog provider name that is already registered is ignored.
+ */
+ private void registerAdapterProviders(GravitinoConfig config) {
+ Iterator<CatalogConnectorAdapterProvider> iterator =
+ ServiceLoader.load(
+ CatalogConnectorAdapterProvider.class,
+ DefaultCatalogConnectorFactory.class.getClassLoader())
+ .iterator();
+ while (true) {
+ try {
+ if (!iterator.hasNext()) {
+ return;
+ }
+ CatalogConnectorAdapterProvider provider = iterator.next();
+ String providerName = provider.provider();
+ if (catalogBuilders.containsKey(providerName)) {
+ LOG.warn(
+ "Ignore catalog connector adapter provider %s for %s: already
registered.",
+ provider.getClass().getName(), providerName);
+ continue;
+ }
+ catalogBuilders.put(
+ providerName, new
CatalogConnectorContext.Builder(provider.createAdapter(config)));
+ LOG.info("Registered catalog connector adapter for %s", providerName);
+ } catch (ServiceConfigurationError | LinkageError | RuntimeException e) {
+ // ServiceLoader reports a missing class or a failing constructor as
+ // ServiceConfigurationError; a provider built against another Trino
version fails with a
+ // LinkageError once its methods run, and a misconfigured one may
throw from provider() or
+ // createAdapter(). Skip that entry and keep the rest.
+ LOG.warn(e, "Skip a catalog connector adapter provider that cannot be
loaded.");
+ }
+ }
+ }
+
/**
* Get supported catalog providers
*
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/BrokenCatalogConnectorAdapterProvider.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/BrokenCatalogConnectorAdapterProvider.java
new file mode 100644
index 0000000000..9cdcbabe7d
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/BrokenCatalogConnectorAdapterProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.trino.connector.catalog;
+
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+
+/** Fails to link once its methods run, like a provider built against another
Trino version. */
+public class BrokenCatalogConnectorAdapterProvider implements
CatalogConnectorAdapterProvider {
+
+ public static final String PROVIDER = "jdbc-broken";
+
+ @Override
+ public String provider() {
+ throw new NoClassDefFoundError("io/trino/spi/Missing");
+ }
+
+ @Override
+ public CatalogConnectorAdapter createAdapter(GravitinoConfig config) {
+ return null;
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/DuplicateCatalogConnectorAdapterProvider.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/DuplicateCatalogConnectorAdapterProvider.java
new file mode 100644
index 0000000000..d45cd0cec4
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/DuplicateCatalogConnectorAdapterProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.trino.connector.catalog;
+
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+
+/** Claims a built-in provider name; the factory must keep the built-in
adapter. */
+public class DuplicateCatalogConnectorAdapterProvider implements
CatalogConnectorAdapterProvider {
+
+ @Override
+ public String provider() {
+ return "jdbc-mysql";
+ }
+
+ @Override
+ public CatalogConnectorAdapter createAdapter(GravitinoConfig config) {
+ throw new AssertionError("A duplicate provider must not create an
adapter");
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FailingCatalogConnectorAdapterProvider.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FailingCatalogConnectorAdapterProvider.java
new file mode 100644
index 0000000000..7dcd030912
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FailingCatalogConnectorAdapterProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.trino.connector.catalog;
+
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+
+/** Throws while creating its adapter, like a provider that rejects the
connector configuration. */
+public class FailingCatalogConnectorAdapterProvider implements
CatalogConnectorAdapterProvider {
+
+ public static final String PROVIDER = "jdbc-failing";
+
+ @Override
+ public String provider() {
+ return PROVIDER;
+ }
+
+ @Override
+ public CatalogConnectorAdapter createAdapter(GravitinoConfig config) {
+ throw new IllegalStateException("adapter creation failed");
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FakeCatalogConnectorAdapterProvider.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FakeCatalogConnectorAdapterProvider.java
new file mode 100644
index 0000000000..7f1c1a4762
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/FakeCatalogConnectorAdapterProvider.java
@@ -0,0 +1,38 @@
+/*
+ * 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.trino.connector.catalog;
+
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+import
org.apache.gravitino.trino.connector.catalog.jdbc.mysql.MySQLConnectorAdapter;
+
+/** Registered through META-INF/services in the test resources. */
+public class FakeCatalogConnectorAdapterProvider implements
CatalogConnectorAdapterProvider {
+
+ public static final String PROVIDER = "jdbc-testdb";
+
+ @Override
+ public String provider() {
+ return PROVIDER;
+ }
+
+ @Override
+ public CatalogConnectorAdapter createAdapter(GravitinoConfig config) {
+ return new MySQLConnectorAdapter();
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestDefaultCatalogConnectorFactory.java
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestDefaultCatalogConnectorFactory.java
new file mode 100644
index 0000000000..71129ce206
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestDefaultCatalogConnectorFactory.java
@@ -0,0 +1,68 @@
+/*
+ * 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.trino.connector.catalog;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.gravitino.trino.connector.GravitinoConfig;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestDefaultCatalogConnectorFactory {
+
+ @Test
+ void testBuiltInProviders() {
+ DefaultCatalogConnectorFactory factory = new
DefaultCatalogConnectorFactory(config());
+
Assertions.assertTrue(factory.getSupportedCatalogProviders().contains("hive"));
+
Assertions.assertTrue(factory.getSupportedCatalogProviders().contains("jdbc-mysql"));
+
Assertions.assertTrue(factory.getSupportedCatalogProviders().contains("jdbc-postgresql"));
+ }
+
+ @Test
+ void testAdapterProviderIsRegistered() {
+ DefaultCatalogConnectorFactory factory = new
DefaultCatalogConnectorFactory(config());
+ Assertions.assertTrue(
+ factory
+ .getSupportedCatalogProviders()
+ .contains(FakeCatalogConnectorAdapterProvider.PROVIDER));
+ }
+
+ @Test
+ void testBuiltInProviderWinsOverAdapterProvider() {
+ // DuplicateCatalogConnectorAdapterProvider throws if asked for an adapter.
+ Assertions.assertDoesNotThrow(() -> new
DefaultCatalogConnectorFactory(config()));
+ }
+
+ @Test
+ void testUnloadableAdapterProviderIsSkipped() {
+ DefaultCatalogConnectorFactory factory = new
DefaultCatalogConnectorFactory(config());
+ Assertions.assertFalse(
+ factory
+ .getSupportedCatalogProviders()
+ .contains(BrokenCatalogConnectorAdapterProvider.PROVIDER));
+ Assertions.assertFalse(
+ factory
+ .getSupportedCatalogProviders()
+ .contains(FailingCatalogConnectorAdapterProvider.PROVIDER));
+ }
+
+ private static GravitinoConfig config() {
+ return new GravitinoConfig(
+ ImmutableMap.of("gravitino.uri", "http://localhost:8090",
"gravitino.metalake", "test"));
+ }
+}
diff --git
a/trino-connector/trino-connector/src/test/resources/META-INF/services/org.apache.gravitino.trino.connector.catalog.CatalogConnectorAdapterProvider
b/trino-connector/trino-connector/src/test/resources/META-INF/services/org.apache.gravitino.trino.connector.catalog.CatalogConnectorAdapterProvider
new file mode 100644
index 0000000000..455bc27733
--- /dev/null
+++
b/trino-connector/trino-connector/src/test/resources/META-INF/services/org.apache.gravitino.trino.connector.catalog.CatalogConnectorAdapterProvider
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+# A missing class exercises the ServiceConfigurationError skip path, a
provider that fails to link
+# the LinkageError skip path and one that throws from createAdapter the
RuntimeException skip
+# path; the fake below must still be registered.
+org.apache.gravitino.trino.connector.catalog.MissingCatalogConnectorAdapterProvider
+org.apache.gravitino.trino.connector.catalog.BrokenCatalogConnectorAdapterProvider
+org.apache.gravitino.trino.connector.catalog.FailingCatalogConnectorAdapterProvider
+org.apache.gravitino.trino.connector.catalog.FakeCatalogConnectorAdapterProvider
+org.apache.gravitino.trino.connector.catalog.DuplicateCatalogConnectorAdapterProvider