FANNG1 commented on code in PR #12541:
URL: https://github.com/apache/gravitino/pull/12541#discussion_r3977642732


##########
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/plugin/restcatalog/GravitinoLakehouseRESTDiscoveryDriverPlugin.java:
##########
@@ -0,0 +1,420 @@
+/*
+ * 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.spark.connector.plugin.restcatalog;
+
+import static org.apache.gravitino.spark.connector.ConnectorConstants.COMMA;
+import static 
org.apache.gravitino.spark.connector.utils.ConnectorUtil.removeDuplicateSparkExtensions;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.spark.connector.plugin.GravitinoSparkPlugin;
+import org.apache.spark.SparkConf;
+import org.apache.spark.SparkContext;
+import org.apache.spark.api.plugin.DriverPlugin;
+import org.apache.spark.api.plugin.PluginContext;
+import org.apache.spark.sql.catalyst.parser.CatalystSqlParser$;
+import org.apache.spark.sql.internal.StaticSQLConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.Tuple2;
+import scala.collection.Seq;
+
+class GravitinoLakehouseRESTDiscoveryDriverPlugin implements DriverPlugin {
+
+  @VisibleForTesting
+  static final String REGISTRATION_POLICY_CONFIG = 
"spark.sql.gravitino.REST.registrationPolicy";
+
+  private static final Logger LOG =
+      
LoggerFactory.getLogger(GravitinoLakehouseRESTDiscoveryDriverPlugin.class);
+  private static final String GRAVITINO_PREFIX = "spark.sql.gravitino.";
+  private static final String SPARK_CATALOG_PREFIX = "spark.sql.catalog.";
+  private static final String URI_SUFFIX = "REST.uri";
+  private static final String CATALOG_PROPERTIES_INFIX = 
"REST.catalogProperties.";
+  private static final Pattern PROVIDER_URI_PATTERN =
+      
Pattern.compile("^spark\\.sql\\.gravitino\\.([A-Za-z][A-Za-z0-9]*)REST\\.uri$");
+  private static final CatalogRegistrationPolicy DEFAULT_POLICY = (format, 
catalogName) -> true;
+
+  GravitinoLakehouseRESTDiscoveryDriverPlugin() {}
+
+  @Override
+  public Map<String, String> init(SparkContext sc, PluginContext 
pluginContext) {
+    initialize(sc.conf());
+    return Collections.emptyMap();
+  }
+
+  @VisibleForTesting
+  void initialize(SparkConf sparkConf) {
+    initialize(sparkConf, BuiltinRESTCatalogProviders.providerClassNames());
+  }
+
+  @VisibleForTesting
+  void initialize(SparkConf sparkConf, Map<String, String> providerClassNames) 
{
+    validatePluginOrder(sparkConf);
+    SparkConf userConf = sparkConf.clone();
+    Map<String, String> activeFormats = findActiveFormats(userConf);
+    if (activeFormats.isEmpty()) {
+      return;
+    }
+
+    ClassLoader classLoader = contextClassLoader();
+    CatalogRegistrationPolicy policy = loadRegistrationPolicy(userConf, 
classLoader);
+    List<CatalogRegistration> registrations = new ArrayList<>();
+    Set<String> registeredNames = new LinkedHashSet<>();
+    Set<String> extensions = new LinkedHashSet<>();
+
+    activeFormats.forEach(
+        (format, uri) -> {
+          LakehouseRESTCatalogProvider provider =
+              loadProvider(format, providerClassNames, classLoader);
+          validateProviderRuntime(provider, classLoader);
+
+          Map<String, String> globalProperties = 
extractCatalogProperties(userConf, format);
+          List<String> advertisedCatalogs =
+              provider.listCatalogs(uri, 
Collections.unmodifiableMap(globalProperties));
+          Preconditions.checkState(
+              advertisedCatalogs != null,
+              "Lakehouse REST catalog provider %s returned a null catalog 
list",
+              format);
+
+          List<String> sortedCatalogs = new ArrayList<>(advertisedCatalogs);
+          Collections.sort(sortedCatalogs);

Review Comment:
   Good point. Sorting is not required for registration, so I will remove it 
and preserve the provider response order.



##########
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/plugin/restcatalog/lance/LanceRESTCatalogProvider.java:
##########
@@ -0,0 +1,121 @@
+/*
+ * 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.spark.connector.plugin.restcatalog.lance;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import 
org.apache.gravitino.spark.connector.plugin.restcatalog.LakehouseRESTCatalogProvider;
+import org.lance.namespace.client.apache.ApiClient;
+import org.lance.namespace.client.apache.ApiException;
+import org.lance.namespace.client.apache.api.NamespaceApi;
+import org.lance.namespace.model.ListNamespacesResponse;
+
+/** Discovers and configures Lance REST catalogs. */
+public class LanceRESTCatalogProvider implements LakehouseRESTCatalogProvider {
+
+  static final String FORMAT = "lance";
+  static final String CATALOG_CLASS = 
"org.lance.spark.LanceNamespaceSparkCatalog";
+  static final String SPARK_EXTENSIONS = 
"org.lance.spark.extensions.LanceSparkSessionExtensions";
+
+  private static final String ROOT_NAMESPACE_ID = "$";
+  private static final String NAMESPACE_DELIMITER = "$";
+
+  @Override
+  public String format() {
+    return FORMAT;
+  }
+
+  @Override
+  public List<String> listCatalogs(String uri, Map<String, String> 
catalogProperties) {
+    List<String> catalogs = new ArrayList<>();
+    Set<String> seenPageTokens = new HashSet<>();
+    String pageToken = null;
+
+    ApiClient apiClient = new ApiClient().setBasePath(normalizeUri(uri));
+    try (Closeable httpClient = getHttpClient(apiClient)) {
+      NamespaceApi namespaceApi = new NamespaceApi(apiClient);
+      do {
+        ListNamespacesResponse response =
+            namespaceApi.listNamespaces(ROOT_NAMESPACE_ID, 
NAMESPACE_DELIMITER, pageToken, null);
+        Preconditions.checkState(response != null, "Lance REST server returned 
an empty response");
+        Preconditions.checkState(
+            response.getNamespaces() != null,
+            "Lance REST server returned a response without namespaces");
+        catalogs.addAll(response.getNamespaces());
+
+        pageToken = StringUtils.trimToNull(response.getPageToken());
+        Preconditions.checkState(
+            pageToken == null || seenPageTokens.add(pageToken),
+            "Lance REST server returned repeated page token: %s",
+            pageToken);
+      } while (pageToken != null);
+    } catch (ApiException | IOException e) {
+      throw new IllegalStateException("Failed to list catalogs from Lance REST 
server " + uri, e);
+    }
+
+    return catalogs;
+  }
+
+  @Override
+  public String catalogClassName() {
+    return CATALOG_CLASS;
+  }
+
+  @Override
+  public Map<String, String> generatedCatalogProperties(String uri, String 
advertisedCatalogName) {

Review Comment:
   It means the catalog name returned by the REST discovery endpoint, before 
the registration policy filters or renames it. I will rename it to 
`discoveredCatalogName` to distinguish it from the Spark registered name.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to