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

jinsongzhou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/amoro.git


The following commit(s) were added to refs/heads/master by this push:
     new 8b9050e69 [AMORO-3189] Support loading mixed catalog without ams url 
on Spark (#3190)
8b9050e69 is described below

commit 8b9050e69f3e1e182f3d22945a50ac398a37d92a
Author: ZhouJinsong <[email protected]>
AuthorDate: Thu Sep 19 17:32:37 2024 +0800

    [AMORO-3189] Support loading mixed catalog without ams url on Spark (#3190)
    
    * Spark support load mixed catalog without ams url
    
    * Add docs and unit tests
    
    * Fix checkstyle errors
    
    * Fix a mixed format spark catalog unit test error
    
    * Fix mixed spark 3.2 runtime jar includes issue
    
    * Fix a doc error
---
 .../org/apache/amoro/CommonUnifiedCatalog.java     |  64 +++++++----
 .../org/apache/amoro/table/TableMetaStore.java     | 124 ++++++++++-----------
 .../java/org/apache/amoro/mixed/CatalogLoader.java |  68 ++---------
 .../java/org/apache/amoro/TestUnifiedCatalog.java  |  29 ++++-
 .../apache/amoro/catalog/TestCatalogLoader.java    |  18 ---
 .../amoro/spark/SparkUnifiedCatalogBase.java       |  57 +++++++---
 .../amoro/spark/mixed/MixedSparkCatalogBase.java   |  49 ++++----
 .../apache/amoro/spark/SparkUnifiedCatalog.java    |   1 -
 .../v3.2/amoro-mixed-spark-runtime-3.2/pom.xml     |  10 +-
 .../apache/amoro/spark/SparkUnifiedCatalog.java    |   1 -
 .../v3.3/amoro-mixed-spark-runtime-3.3/pom.xml     |  11 +-
 docs/engines/spark/spark-conf.md                   |  13 ++-
 12 files changed, 234 insertions(+), 211 deletions(-)

diff --git 
a/amoro-common/src/main/java/org/apache/amoro/CommonUnifiedCatalog.java 
b/amoro-common/src/main/java/org/apache/amoro/CommonUnifiedCatalog.java
index 84e855972..79b6e55be 100644
--- a/amoro-common/src/main/java/org/apache/amoro/CommonUnifiedCatalog.java
+++ b/amoro-common/src/main/java/org/apache/amoro/CommonUnifiedCatalog.java
@@ -35,27 +35,48 @@ import java.util.stream.Stream;
 
 public class CommonUnifiedCatalog implements UnifiedCatalog {
 
+  private final String catalogName;
+  private final String metaStoreType;
   private final Supplier<CatalogMeta> metaSupplier;
+  // Client side catalog properties
+  private final Map<String, String> clientProperties;
+  // Catalog properties after merging the server and client
+  private Map<String, String> catalogProperties;
   private CatalogMeta meta;
-  private Map<TableFormat, FormatCatalog> formatCatalogs = Maps.newHashMap();
-  private final Map<String, String> properties = Maps.newHashMap();
-
   private TableMetaStore tableMetaStore;
+  private Map<TableFormat, FormatCatalog> formatCatalogs = Maps.newHashMap();
 
   public CommonUnifiedCatalog(
       Supplier<CatalogMeta> catalogMetaSupplier, Map<String, String> 
properties) {
     CatalogMeta catalogMeta = catalogMetaSupplier.get();
     CatalogUtil.mergeCatalogProperties(catalogMeta, properties);
     this.meta = catalogMeta;
+    this.catalogName = catalogMeta.getCatalogName();
+    this.metaStoreType = catalogMeta.getCatalogType();
     this.tableMetaStore = CatalogUtil.buildMetaStore(catalogMeta);
-    this.properties.putAll(properties);
+    this.clientProperties = properties;
+    this.catalogProperties = catalogMeta.getCatalogProperties();
     this.metaSupplier = catalogMetaSupplier;
     initializeFormatCatalogs();
   }
 
+  public CommonUnifiedCatalog(
+      String catalogName,
+      String metaStoreType,
+      Map<String, String> properties,
+      TableMetaStore tableMetaStore) {
+    this.catalogName = catalogName;
+    this.metaStoreType = metaStoreType;
+    this.tableMetaStore = tableMetaStore;
+    this.clientProperties = properties;
+    this.catalogProperties = properties;
+    this.metaSupplier = null;
+    initializeFormatCatalogs();
+  }
+
   @Override
   public String metastoreType() {
-    return meta.getCatalogType();
+    return metaStoreType;
   }
 
   @Override
@@ -126,7 +147,7 @@ public class CommonUnifiedCatalog implements UnifiedCatalog 
{
 
   @Override
   public String name() {
-    return this.meta.getCatalogName();
+    return catalogName;
   }
 
   @Override
@@ -158,7 +179,7 @@ public class CommonUnifiedCatalog implements UnifiedCatalog 
{
             tableName -> {
               TableFormat format = tableNameToFormat.get(tableName);
               return TableIDWithFormat.of(
-                  TableIdentifier.of(this.meta.getCatalogName(), database, 
tableName), format);
+                  TableIdentifier.of(catalogName, database, tableName), 
format);
             })
         .collect(Collectors.toList());
   }
@@ -175,33 +196,34 @@ public class CommonUnifiedCatalog implements 
UnifiedCatalog {
 
   @Override
   public synchronized void refresh() {
-    CatalogMeta newMeta = metaSupplier.get();
-    CatalogUtil.mergeCatalogProperties(meta, properties);
-    if (newMeta.equals(this.meta)) {
-      return;
+    if (metaSupplier != null) {
+      CatalogMeta newMeta = metaSupplier.get();
+      CatalogUtil.mergeCatalogProperties(meta, clientProperties);
+      if (newMeta.equals(this.meta)) {
+        return;
+      }
+      this.catalogProperties = newMeta.getCatalogProperties();
+      this.tableMetaStore = CatalogUtil.buildMetaStore(newMeta);
+      this.meta = newMeta;
+      this.initializeFormatCatalogs();
     }
-    this.tableMetaStore = CatalogUtil.buildMetaStore(newMeta);
-    this.meta = newMeta;
-    this.initializeFormatCatalogs();
   }
 
   @Override
   public Map<String, String> properties() {
-    return this.meta.getCatalogProperties();
+    return catalogProperties;
   }
 
   protected void initializeFormatCatalogs() {
     ServiceLoader<FormatCatalogFactory> loader = 
ServiceLoader.load(FormatCatalogFactory.class);
-    Set<TableFormat> formats = CatalogUtil.tableFormats(this.meta);
-    TableMetaStore store = CatalogUtil.buildMetaStore(this.meta);
+    Set<TableFormat> formats = CatalogUtil.tableFormats(metaStoreType, 
catalogProperties);
     Map<TableFormat, FormatCatalog> formatCatalogs = Maps.newConcurrentMap();
     for (FormatCatalogFactory factory : loader) {
       if (formats.contains(factory.format())) {
-        Map<String, String> catalogProperties =
-            factory.convertCatalogProperties(
-                name(), meta.getCatalogType(), meta.getCatalogProperties());
+        Map<String, String> formatCatalogProperties =
+            factory.convertCatalogProperties(name(), metaStoreType, 
this.catalogProperties);
         FormatCatalog catalog =
-            factory.create(name(), meta.getCatalogType(), catalogProperties, 
store);
+            factory.create(name(), metaStoreType, formatCatalogProperties, 
tableMetaStore);
         formatCatalogs.put(factory.format(), catalog);
       }
     }
diff --git 
a/amoro-common/src/main/java/org/apache/amoro/table/TableMetaStore.java 
b/amoro-common/src/main/java/org/apache/amoro/table/TableMetaStore.java
index 9983b159c..db6827bca 100644
--- a/amoro-common/src/main/java/org/apache/amoro/table/TableMetaStore.java
+++ b/amoro-common/src/main/java/org/apache/amoro/table/TableMetaStore.java
@@ -19,7 +19,6 @@
 package org.apache.amoro.table;
 
 import org.apache.amoro.properties.CatalogMetaProperties;
-import 
org.apache.amoro.shade.guava32.com.google.common.annotations.VisibleForTesting;
 import org.apache.amoro.shade.guava32.com.google.common.base.Charsets;
 import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
 import org.apache.amoro.shade.guava32.com.google.common.base.Strings;
@@ -39,7 +38,6 @@ import org.slf4j.LoggerFactory;
 import sun.security.krb5.KrbException;
 
 import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.Serializable;
@@ -48,6 +46,7 @@ import java.lang.management.ManagementFactory;
 import java.lang.reflect.Field;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.PrivilegedAction;
@@ -70,7 +69,7 @@ public class TableMetaStore implements Serializable {
       new ConcurrentHashMap<>();
 
   public static final TableMetaStore EMPTY =
-      TableMetaStore.builder().withConfiguration(new 
Configuration()).buildForTest();
+      TableMetaStore.builder().withConfiguration(new Configuration()).build();
 
   public static final String HADOOP_CONF_DIR = "conf.hadoop.dir";
   public static final String HIVE_SITE = "hive-site";
@@ -126,14 +125,12 @@ public class TableMetaStore implements Serializable {
   private final byte[] krbConf;
   private final String krbPrincipal;
   private final boolean disableAuth;
+  private final String accessKey;
+  private final String secretKey;
 
   private transient RuntimeContext runtimeContext;
   private transient String authInformation;
 
-  private String accessKey;
-
-  private String secretKey;
-
   public static Builder builder() {
     return new Builder();
   }
@@ -170,6 +167,21 @@ public class TableMetaStore implements Serializable {
     this.secretKey = secretKey;
   }
 
+  private TableMetaStore(Configuration configuration) {
+    this.disableAuth = true;
+    this.metaStoreSite = null;
+    this.hdfsSite = null;
+    this.coreSite = null;
+    this.authMethod = null;
+    this.hadoopUsername = null;
+    this.krbKeyTab = null;
+    this.krbConf = null;
+    this.krbPrincipal = null;
+    this.accessKey = null;
+    this.secretKey = null;
+    getRuntimeContext().setConfiguration(configuration);
+  }
+
   public byte[] getMetaStoreSite() {
     return metaStoreSite;
   }
@@ -218,7 +230,7 @@ public class TableMetaStore implements Serializable {
     return getRuntimeContext().getConfiguration();
   }
 
-  public synchronized UserGroupInformation getUGI() {
+  private synchronized UserGroupInformation getUGI() {
     return getRuntimeContext().getUGI();
   }
 
@@ -567,7 +579,6 @@ public class TableMetaStore implements Serializable {
     private byte[] krbKeyTab;
     private byte[] krbConf;
     private String krbPrincipal;
-
     private String accessKey;
     private String secretKey;
     private boolean disableAuth = true;
@@ -710,7 +721,7 @@ public class TableMetaStore implements Serializable {
 
     private byte[] readBytesFromFile(String filePath) {
       try {
-        return IOUtils.toByteArray(new FileInputStream(filePath));
+        return IOUtils.toByteArray(Files.newInputStream(Paths.get(filePath)));
       } catch (IOException e) {
         throw new UncheckedIOException("Read config failed:" + filePath, e);
       }
@@ -751,61 +762,46 @@ public class TableMetaStore implements Serializable {
     }
 
     public TableMetaStore build() {
-      readProperties();
-      if (!disableAuth & !AUTH_METHOD_AK_SK.equals(authMethod)) {
-        Preconditions.checkNotNull(hdfsSite);
-        Preconditions.checkNotNull(coreSite);
-      }
-      if (AUTH_METHOD_SIMPLE.equals(authMethod)) {
-        Preconditions.checkNotNull(hadoopUsername);
-      } else if (AUTH_METHOD_KERBEROS.equals(authMethod)) {
-        Preconditions.checkNotNull(krbConf);
-        Preconditions.checkNotNull(krbKeyTab);
-        Preconditions.checkNotNull(krbPrincipal);
-      } else if (AUTH_METHOD_AK_SK.equals(authMethod)) {
-        Preconditions.checkNotNull(accessKey);
-        Preconditions.checkNotNull(secretKey);
-      } else if (authMethod != null) {
-        throw new IllegalArgumentException("Unsupported auth method:" + 
authMethod);
-      }
+      if (disableAuth && configuration != null) {
+        LOG.info("Build table meta store with local configuration:" + 
configuration);
+        return new TableMetaStore(configuration);
+      } else {
+        readProperties();
+        if (!AUTH_METHOD_AK_SK.equals(authMethod)) {
+          Preconditions.checkNotNull(hdfsSite);
+          Preconditions.checkNotNull(coreSite);
+        }
+        if (AUTH_METHOD_SIMPLE.equals(authMethod)) {
+          Preconditions.checkNotNull(hadoopUsername);
+        } else if (AUTH_METHOD_KERBEROS.equals(authMethod)) {
+          Preconditions.checkNotNull(krbConf);
+          Preconditions.checkNotNull(krbKeyTab);
+          Preconditions.checkNotNull(krbPrincipal);
+        } else if (AUTH_METHOD_AK_SK.equals(authMethod)) {
+          Preconditions.checkNotNull(accessKey);
+          Preconditions.checkNotNull(secretKey);
+        } else if (authMethod != null) {
+          throw new IllegalArgumentException("Unsupported auth method:" + 
authMethod);
+        }
 
-      LOG.info(
-          "Construct TableMetaStore with authMethod:{}, hadoopUsername:{}, 
krbPrincipal:{}",
-          authMethod,
-          hadoopUsername,
-          krbPrincipal);
-      return new TableMetaStore(
-          metaStoreSite,
-          hdfsSite,
-          coreSite,
-          authMethod,
-          hadoopUsername,
-          krbKeyTab,
-          krbConf,
-          krbPrincipal,
-          accessKey,
-          secretKey,
-          disableAuth);
-    }
-
-    @VisibleForTesting
-    public TableMetaStore buildForTest() {
-      readProperties();
-      TableMetaStore tableMetaStore =
-          new TableMetaStore(
-              metaStoreSite,
-              hdfsSite,
-              coreSite,
-              authMethod,
-              hadoopUsername,
-              krbKeyTab,
-              krbConf,
-              krbPrincipal,
-              accessKey,
-              secretKey,
-              disableAuth);
-      tableMetaStore.getRuntimeContext().setConfiguration(configuration);
-      return tableMetaStore;
+        LOG.info(
+            "Build table meta store with configurations: authMethod:{}, 
hadoopUsername:{}, krbPrincipal:{}",
+            authMethod,
+            hadoopUsername,
+            krbPrincipal);
+        return new TableMetaStore(
+            metaStoreSite,
+            hdfsSite,
+            coreSite,
+            authMethod,
+            hadoopUsername,
+            krbKeyTab,
+            krbConf,
+            krbPrincipal,
+            accessKey,
+            secretKey,
+            disableAuth);
+      }
     }
   }
 }
diff --git 
a/amoro-format-iceberg/src/main/java/org/apache/amoro/mixed/CatalogLoader.java 
b/amoro-format-iceberg/src/main/java/org/apache/amoro/mixed/CatalogLoader.java
index 2b534937a..3842e8e6a 100644
--- 
a/amoro-format-iceberg/src/main/java/org/apache/amoro/mixed/CatalogLoader.java
+++ 
b/amoro-format-iceberg/src/main/java/org/apache/amoro/mixed/CatalogLoader.java
@@ -18,28 +18,24 @@
 
 package org.apache.amoro.mixed;
 
+import static org.apache.iceberg.CatalogUtil.ICEBERG_CATALOG_TYPE;
+
 import org.apache.amoro.AmsClient;
 import org.apache.amoro.Constants;
 import org.apache.amoro.PooledAmsClient;
 import org.apache.amoro.TableFormat;
-import org.apache.amoro.api.AmoroTableMetastore;
 import org.apache.amoro.api.CatalogMeta;
 import org.apache.amoro.api.NoSuchObjectException;
-import org.apache.amoro.client.AmsClientPools;
 import org.apache.amoro.client.AmsThriftUrl;
 import org.apache.amoro.properties.CatalogMetaProperties;
-import 
org.apache.amoro.shade.guava32.com.google.common.annotations.VisibleForTesting;
-import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
 import org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
 import org.apache.amoro.shade.thrift.org.apache.thrift.TException;
 import org.apache.amoro.table.TableMetaStore;
 import org.apache.amoro.utils.MixedFormatCatalogUtil;
 import org.apache.iceberg.common.DynConstructors;
 
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /** Catalogs, create mixed-format catalog from metastore thrift url. */
 public class CatalogLoader {
@@ -82,40 +78,24 @@ public class CatalogLoader {
    * @return class name for catalog
    */
   private static String catalogImpl(String metastoreType, Map<String, String> 
catalogProperties) {
-    Set<TableFormat> tableFormats =
-        MixedFormatCatalogUtil.tableFormats(metastoreType, catalogProperties);
-    Preconditions.checkArgument(
-        tableFormats.size() == 1, "Catalog support only one table format 
now.");
-    TableFormat tableFormat = tableFormats.iterator().next();
-    Preconditions.checkArgument(
-        TableFormat.MIXED_HIVE == tableFormat || TableFormat.MIXED_ICEBERG == 
tableFormat,
-        "MixedCatalogLoader only support mixed-format, format: %s",
-        tableFormat.name());
-
     String catalogImpl;
     switch (metastoreType) {
       case CatalogMetaProperties.CATALOG_TYPE_HADOOP:
       case CatalogMetaProperties.CATALOG_TYPE_GLUE:
       case CatalogMetaProperties.CATALOG_TYPE_CUSTOM:
-        Preconditions.checkArgument(
-            TableFormat.MIXED_ICEBERG == tableFormat,
-            "%s catalog support mixed-iceberg table only.",
-            metastoreType);
         catalogImpl = MIXED_ICEBERG_CATALOG_IMP;
         break;
       case CatalogMetaProperties.CATALOG_TYPE_HIVE:
-        if (TableFormat.MIXED_HIVE == tableFormat) {
+        Set<TableFormat> tableFormats =
+            MixedFormatCatalogUtil.tableFormats(metastoreType, 
catalogProperties);
+        if (tableFormats.contains(TableFormat.MIXED_HIVE)) {
           catalogImpl = HIVE_CATALOG_IMPL;
         } else {
           catalogImpl = MIXED_ICEBERG_CATALOG_IMP;
         }
         break;
       case CatalogMetaProperties.CATALOG_TYPE_AMS:
-        if (TableFormat.MIXED_ICEBERG == tableFormat) {
-          catalogImpl = INTERNAL_CATALOG_IMPL;
-        } else {
-          throw new IllegalArgumentException("Internal Catalog mixed-iceberg 
table only");
-        }
+        catalogImpl = INTERNAL_CATALOG_IMPL;
         break;
       default:
         throw new IllegalStateException("unsupported metastore type:" + 
metastoreType);
@@ -142,21 +122,6 @@ public class CatalogLoader {
     }
   }
 
-  /**
-   * Show catalog list in metastore.
-   *
-   * @param metastoreUrl url of ams
-   * @return catalog name list
-   */
-  public static List<String> catalogs(String metastoreUrl) {
-    try {
-      return ((AmoroTableMetastore.Iface) 
AmsClientPools.getClientPool(metastoreUrl).iface())
-          
.getCatalogs().stream().map(CatalogMeta::getCatalogName).collect(Collectors.toList());
-    } catch (TException e) {
-      throw new IllegalStateException("failed when load catalogs", e);
-    }
-  }
-
   /**
    * Entrypoint for loading catalog
    *
@@ -200,25 +165,10 @@ public class CatalogLoader {
       Map<String, String> properties,
       TableMetaStore metaStore) {
     String catalogImpl = catalogImpl(metastoreType, properties);
-    properties =
-        MixedFormatCatalogUtil.withIcebergCatalogInitializeProperties(
-            catalogName, metastoreType, properties);
-    MixedFormatCatalog catalog = buildCatalog(catalogImpl);
-    catalog.initialize(catalogName, properties, metaStore);
-    return catalog;
-  }
-
-  @VisibleForTesting
-  public static MixedFormatCatalog createCatalog(
-      String catalogName,
-      String catalogImpl,
-      String metastoreType,
-      Map<String, String> properties,
-      TableMetaStore metaStore) {
-    properties =
-        MixedFormatCatalogUtil.withIcebergCatalogInitializeProperties(
-            catalogName, metastoreType, properties);
     MixedFormatCatalog catalog = buildCatalog(catalogImpl);
+    if (!properties.containsKey(ICEBERG_CATALOG_TYPE)) {
+      properties.put(ICEBERG_CATALOG_TYPE, metastoreType);
+    }
     catalog.initialize(catalogName, properties, metaStore);
     return catalog;
   }
diff --git 
a/amoro-format-iceberg/src/test/java/org/apache/amoro/TestUnifiedCatalog.java 
b/amoro-format-iceberg/src/test/java/org/apache/amoro/TestUnifiedCatalog.java
index 5b0bca78c..489eb8b47 100644
--- 
a/amoro-format-iceberg/src/test/java/org/apache/amoro/TestUnifiedCatalog.java
+++ 
b/amoro-format-iceberg/src/test/java/org/apache/amoro/TestUnifiedCatalog.java
@@ -20,7 +20,9 @@ package org.apache.amoro;
 
 import org.apache.amoro.api.CatalogMeta;
 import org.apache.amoro.catalog.CatalogTestHelper;
+import org.apache.amoro.shade.guava32.com.google.common.collect.Lists;
 import org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
+import org.apache.amoro.table.TableMetaStore;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
@@ -60,11 +62,32 @@ public class TestUnifiedCatalog {
 
   @Test
   public void testCatalogLoader() {
-    UnifiedCatalog catalog =
+    UnifiedCatalog unifiedCatalog =
         UnifiedCatalogLoader.loadUnifiedCatalog(
             testAms.getServerUrl(), meta.getCatalogName(), Maps.newHashMap());
+    validateUnifiedCatalog(unifiedCatalog);
+  }
+
+  @Test
+  public void testCreateUnifiedCatalog() {
+    UnifiedCatalog unifiedCatalog =
+        new CommonUnifiedCatalog(
+            meta.getCatalogName(),
+            meta.getCatalogType(),
+            meta.getCatalogProperties(),
+            TableMetaStore.EMPTY);
+    validateUnifiedCatalog(unifiedCatalog);
+  }
+
+  private void validateUnifiedCatalog(UnifiedCatalog unifiedCatalog) {
+    Assert.assertNotNull(unifiedCatalog);
+    Assert.assertEquals(CommonUnifiedCatalog.class.getName(), 
unifiedCatalog.getClass().getName());
 
-    Assert.assertNotNull(catalog);
-    Assert.assertEquals(CommonUnifiedCatalog.class.getName(), 
catalog.getClass().getName());
+    unifiedCatalog.createDatabase(TableTestHelper.TEST_DB_NAME);
+    Assert.assertEquals(
+        Lists.newArrayList(TableTestHelper.TEST_DB_NAME), 
unifiedCatalog.listDatabases());
+    Assert.assertEquals(0, 
unifiedCatalog.listTables(TableTestHelper.TEST_DB_NAME).size());
+    unifiedCatalog.dropDatabase(TableTestHelper.TEST_DB_NAME);
+    unifiedCatalog.refresh();
   }
 }
diff --git 
a/amoro-format-iceberg/src/test/java/org/apache/amoro/catalog/TestCatalogLoader.java
 
b/amoro-format-iceberg/src/test/java/org/apache/amoro/catalog/TestCatalogLoader.java
index 5ebb8cda3..b47149233 100644
--- 
a/amoro-format-iceberg/src/test/java/org/apache/amoro/catalog/TestCatalogLoader.java
+++ 
b/amoro-format-iceberg/src/test/java/org/apache/amoro/catalog/TestCatalogLoader.java
@@ -62,24 +62,6 @@ public class TestCatalogLoader {
         () -> CatalogLoader.load(getCatalogUrl(TEST_CATALOG_NAME)));
   }
 
-  @Test
-  public void testLoadCatalogWithErrorFormat() {
-    Map<String, String> properties = Maps.newHashMap();
-    CatalogMeta catalogMeta =
-        CatalogTestHelpers.buildCatalogMeta(
-            TEST_CATALOG_NAME,
-            CatalogMetaProperties.CATALOG_TYPE_HADOOP,
-            properties,
-            TableFormat.MIXED_ICEBERG);
-    TEST_AMS.getAmsHandler().createCatalog(catalogMeta);
-    // lack warehouse
-    Assert.assertThrows(
-        "failed when load catalog test",
-        IllegalStateException.class,
-        () -> CatalogLoader.load(getCatalogUrl(TEST_CATALOG_NAME)));
-    TEST_AMS.getAmsHandler().dropCatalog(TEST_CATALOG_NAME);
-  }
-
   private String getCatalogUrl(String catalogName) {
     return TEST_AMS.getServerUrl() + "/" + catalogName;
   }
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalogBase.java
 
b/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalogBase.java
index e4662d9e9..aed47f5b8 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalogBase.java
+++ 
b/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalogBase.java
@@ -18,7 +18,10 @@
 
 package org.apache.amoro.spark;
 
+import static org.apache.iceberg.CatalogUtil.ICEBERG_CATALOG_TYPE;
+
 import org.apache.amoro.AmoroTable;
+import org.apache.amoro.CommonUnifiedCatalog;
 import org.apache.amoro.Constants;
 import org.apache.amoro.FormatCatalogFactory;
 import org.apache.amoro.TableFormat;
@@ -29,7 +32,11 @@ import org.apache.amoro.client.AmsThriftUrl;
 import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
 import org.apache.amoro.shade.guava32.com.google.common.collect.ImmutableMap;
 import org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
+import org.apache.amoro.table.TableMetaStore;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.iceberg.spark.SparkUtil;
+import org.apache.spark.sql.SparkSession;
 import org.apache.spark.sql.catalyst.analysis.NoSuchNamespaceException;
 import org.apache.spark.sql.catalyst.analysis.NoSuchProcedureException;
 import org.apache.spark.sql.catalyst.analysis.NoSuchTableException;
@@ -60,6 +67,8 @@ import java.util.ServiceLoader;
 public class SparkUnifiedCatalogBase implements TableCatalog, 
SupportsNamespaces, ProcedureCatalog {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(SparkUnifiedCatalogBase.class);
+
+  public static final String CATALOG_REGISTER_NAME = "register-name";
   private static final Map<TableFormat, String> defaultTableCatalogImplMap =
       ImmutableMap.of(
           TableFormat.ICEBERG, "org.apache.iceberg.spark.SparkCatalog",
@@ -74,28 +83,40 @@ public class SparkUnifiedCatalogBase implements 
TableCatalog, SupportsNamespaces
 
   @Override
   public void initialize(String name, CaseInsensitiveStringMap options) {
+    this.name = name;
     Map<String, String> properties = Maps.newHashMap(options);
     String uri = options.get(SparkUnifiedCatalogProperties.URI);
     properties.remove(SparkUnifiedCatalogProperties.URI);
-    Preconditions.checkNotNull(uri, "lack required option: %s", 
SparkUnifiedCatalogProperties.URI);
-
-    AmsThriftUrl catalogUri = AmsThriftUrl.parse(uri, 
Constants.THRIFT_TABLE_SERVICE_NAME);
-    String registerCatalogName = catalogUri.catalogName();
-
-    if (StringUtils.isBlank(registerCatalogName)) {
-      registerCatalogName = name;
-      if 
(CatalogManager.SESSION_CATALOG_NAME().equalsIgnoreCase(registerCatalogName)) {
-        LOG.warn(
-            "Catalog name is not exists in catalog uri, using spark catalog as 
register catalog name, but "
-                + "current name "
-                + registerCatalogName
-                + " is spark session catalog name.");
+    if (StringUtils.isNotEmpty(uri)) {
+      AmsThriftUrl catalogUri = AmsThriftUrl.parse(uri, 
Constants.THRIFT_TABLE_SERVICE_NAME);
+      String registerCatalogName = catalogUri.catalogName();
+
+      if (StringUtils.isBlank(registerCatalogName)) {
+        registerCatalogName = name;
+        if 
(CatalogManager.SESSION_CATALOG_NAME().equalsIgnoreCase(registerCatalogName)) {
+          LOG.warn(
+              "Catalog name is not exists in catalog uri, using spark catalog 
as register catalog name, but "
+                  + "current name "
+                  + registerCatalogName
+                  + " is spark session catalog name.");
+        }
       }
+
+      this.unifiedCatalog =
+          UnifiedCatalogLoader.loadUnifiedCatalog(
+              catalogUri.serverUrl(), registerCatalogName, properties);
+    } else {
+      String metastoreType = properties.get(ICEBERG_CATALOG_TYPE);
+      Preconditions.checkArgument(
+          StringUtils.isNotEmpty(metastoreType),
+          "Lack required property: type when initializing unified spark 
catalog");
+      Configuration localConfiguration =
+          SparkUtil.hadoopConfCatalogOverrides(SparkSession.active(), name);
+      TableMetaStore tableMetaStore =
+          
TableMetaStore.builder().withConfiguration(localConfiguration).build();
+      this.unifiedCatalog =
+          new CommonUnifiedCatalog(name, metastoreType, properties, 
tableMetaStore);
     }
-    this.name = name;
-    this.unifiedCatalog =
-        UnifiedCatalogLoader.loadUnifiedCatalog(
-            catalogUri.serverUrl(), registerCatalogName, properties);
     ServiceLoader<SparkTableFormat> sparkTableFormats = 
ServiceLoader.load(SparkTableFormat.class);
     for (SparkTableFormat format : sparkTableFormats) {
       tableFormats.put(format.format(), format);
@@ -317,7 +338,7 @@ public class SparkUnifiedCatalogBase implements 
TableCatalog, SupportsNamespaces
       if (tableCatalog instanceof SupportAuthentication) {
         ((SupportAuthentication) tableCatalog)
             .setAuthenticationContext(unifiedCatalog.authenticationContext());
-        tableCatalogInitializeProperties.put("register-name", 
unifiedCatalog.name());
+        tableCatalogInitializeProperties.put(CATALOG_REGISTER_NAME, 
unifiedCatalog.name());
       }
       tableCatalog.initialize(name, new 
CaseInsensitiveStringMap(tableCatalogInitializeProperties));
       return tableCatalog;
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/mixed/MixedSparkCatalogBase.java
 
b/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/mixed/MixedSparkCatalogBase.java
index 8276a6a75..1ae7e78e2 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/mixed/MixedSparkCatalogBase.java
+++ 
b/amoro-format-mixed/amoro-mixed-spark/amoro-mixed-spark-3-common/src/main/java/org/apache/amoro/spark/mixed/MixedSparkCatalogBase.java
@@ -18,20 +18,23 @@
 
 package org.apache.amoro.spark.mixed;
 
+import static 
org.apache.amoro.spark.SparkUnifiedCatalogBase.CATALOG_REGISTER_NAME;
 import static 
org.apache.amoro.spark.mixed.SparkSQLProperties.REFRESH_CATALOG_BEFORE_USAGE;
 import static 
org.apache.amoro.spark.mixed.SparkSQLProperties.REFRESH_CATALOG_BEFORE_USAGE_DEFAULT;
 import static org.apache.iceberg.CatalogUtil.ICEBERG_CATALOG_TYPE;
 
 import org.apache.amoro.mixed.CatalogLoader;
 import org.apache.amoro.mixed.MixedFormatCatalog;
-import org.apache.amoro.properties.CatalogMetaProperties;
 import org.apache.amoro.shade.guava32.com.google.common.base.Joiner;
 import org.apache.amoro.shade.guava32.com.google.common.base.Preconditions;
 import org.apache.amoro.shade.guava32.com.google.common.collect.Lists;
+import org.apache.amoro.shade.guava32.com.google.common.collect.Maps;
 import org.apache.amoro.spark.SupportAuthentication;
 import org.apache.amoro.table.TableIdentifier;
 import org.apache.amoro.table.TableMetaStore;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.iceberg.spark.SparkUtil;
 import org.apache.spark.sql.SparkSession;
 import org.apache.spark.sql.catalyst.analysis.NoSuchNamespaceException;
 import org.apache.spark.sql.connector.catalog.Identifier;
@@ -65,30 +68,34 @@ public abstract class MixedSparkCatalogBase
   @Override
   public final void initialize(String name, CaseInsensitiveStringMap options) {
     this.catalogName = name;
-    String catalogUrl = options.get(CatalogMetaProperties.AMS_URI);
-    if (StringUtils.isNotBlank(catalogUrl)) {
-      // initialize for unified catalog.
-      String metastoreType = options.get(ICEBERG_CATALOG_TYPE);
-      String registerName = options.get("register-name");
-      Preconditions.checkArgument(
-          StringUtils.isNotEmpty(metastoreType),
-          "Lack required property: type when initialized by unified catalog.");
-      Preconditions.checkNotNull(
-          tableMetaStore,
-          "Authentication context must be set when initialized by unified 
catalog.");
-      Preconditions.checkArgument(
-          StringUtils.isNotEmpty(registerName),
-          "Lack required property: register-name when initialized by unified 
catalog");
-      catalog = CatalogLoader.createCatalog(registerName, metastoreType, 
options, tableMetaStore);
-    } else {
-      catalogUrl = options.get("url");
+    Map<String, String> properties = Maps.newHashMap(options);
+    if (tableMetaStore == null) {
+      String catalogUrl = options.get("url");
       if (StringUtils.isBlank(catalogUrl)) {
         catalogUrl = options.get("uri");
       }
+      if (catalogUrl != null) {
+        catalog = CatalogLoader.load(catalogUrl, properties);
+      } else {
+        Configuration localConfiguration =
+            SparkUtil.hadoopConfCatalogOverrides(SparkSession.active(), name);
+        tableMetaStore = 
TableMetaStore.builder().withConfiguration(localConfiguration).build();
+      }
+    }
+    if (catalog == null) {
+      String metastoreType = options.get(ICEBERG_CATALOG_TYPE);
+      String registerName = options.get(CATALOG_REGISTER_NAME);
       Preconditions.checkArgument(
-          StringUtils.isNotBlank(catalogUrl), "lack required properties: url");
-
-      catalog = CatalogLoader.load(catalogUrl, options);
+          StringUtils.isNotEmpty(metastoreType),
+          "Lack required property: type when initializing mixed spark 
catalog");
+      Preconditions.checkNotNull(
+          tableMetaStore, "Lack authentication context when initializing mixed 
spark catalog");
+      catalog =
+          CatalogLoader.createCatalog(
+              registerName == null ? catalogName : registerName,
+              metastoreType,
+              properties,
+              tableMetaStore);
     }
     this.options = options;
   }
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-3.2/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
 
b/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-3.2/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
index 6a9e840bd..4dd464d55 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-3.2/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
+++ 
b/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-3.2/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
@@ -28,7 +28,6 @@ import org.apache.spark.sql.connector.catalog.TableCatalog;
 import org.apache.spark.sql.connector.catalog.functions.UnboundFunction;
 import org.apache.spark.sql.connector.iceberg.catalog.ProcedureCatalog;
 
-/** @Auth: hzwangtao6 @Time: 2024/5/24 14:27 @Description: */
 public class SparkUnifiedCatalog extends SparkUnifiedCatalogBase
     implements TableCatalog, SupportsNamespaces, ProcedureCatalog, 
FunctionCatalog {
   /**
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-runtime-3.2/pom.xml
 
b/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-runtime-3.2/pom.xml
index 642010ae9..acce293f8 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-runtime-3.2/pom.xml
+++ 
b/amoro-format-mixed/amoro-mixed-spark/v3.2/amoro-mixed-spark-runtime-3.2/pom.xml
@@ -60,7 +60,9 @@
                                 <includes>
                                     
<include>org.apache.amoro:amoro-format-mixed-spark-3-common</include>
                                     
<include>org.apache.amoro:amoro-format-mixed-spark-3.2</include>
-                                    
<include>org.apache.amoro:amoro-core</include>
+                                    
<include>org.apache.amoro:amoro-common</include>
+                                    
<include>org.apache.amoro:amoro-format-iceberg</include>
+                                    
<include>org.apache.amoro:amoro-mixed-hive</include>
                                     
<include>org.apache.amoro:amoro-shade-guava-32</include>
                                     
<include>org.apache.amoro:amoro-shade-jackson-2</include>
                                     
<include>org.apache.amoro:amoro-shade-thrift</include>
@@ -110,6 +112,12 @@
                                         
<exclude>org/apache/iceberg/spark/extensions/**</exclude>
                                     </excludes>
                                 </filter>
+                                <filter combine.children="append">
+                                    
<artifact>org.apache.iceberg:iceberg-spark-3.2_2.12</artifact>
+                                    <excludes>
+                                        
<exclude>META-INF/services/org.apache.spark.sql.sources.DataSourceRegister</exclude>
+                                    </excludes>
+                                </filter>
                             </filters>
                             <relocations>
                                 <!-- relocation amoro spark packages -->
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-3.3/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
 
b/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-3.3/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
index 44c813300..09fbfeb5a 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-3.3/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
+++ 
b/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-3.3/src/main/java/org/apache/amoro/spark/SparkUnifiedCatalog.java
@@ -31,7 +31,6 @@ import org.apache.spark.sql.connector.catalog.TableCatalog;
 import org.apache.spark.sql.connector.catalog.functions.UnboundFunction;
 import org.apache.spark.sql.connector.iceberg.catalog.ProcedureCatalog;
 
-/** @Auth: hzwangtao6 @Time: 2024/5/24 14:27 @Description: */
 public class SparkUnifiedCatalog extends SparkUnifiedCatalogBase
     implements TableCatalog, SupportsNamespaces, ProcedureCatalog, 
FunctionCatalog {
 
diff --git 
a/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-runtime-3.3/pom.xml
 
b/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-runtime-3.3/pom.xml
index 33ebd3440..3af0cf6de 100644
--- 
a/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-runtime-3.3/pom.xml
+++ 
b/amoro-format-mixed/amoro-mixed-spark/v3.3/amoro-mixed-spark-runtime-3.3/pom.xml
@@ -60,8 +60,9 @@
                                 <includes>
                                     
<include>org.apache.amoro:amoro-format-mixed-spark-3-common</include>
                                     
<include>org.apache.amoro:amoro-format-mixed-spark-3.3</include>
-                                    
<include>org.apache.amoro:amoro-core</include>
-                                    
<include>org.apache.amoro:amoro-format-mixed-hive</include>
+                                    
<include>org.apache.amoro:amoro-common</include>
+                                    
<include>org.apache.amoro:amoro-format-iceberg</include>
+                                    
<include>org.apache.amoro:amoro-mixed-hive</include>
                                     
<include>org.apache.amoro:amoro-shade-guava-32</include>
                                     
<include>org.apache.amoro:amoro-shade-jackson-2</include>
                                     
<include>org.apache.amoro:amoro-shade-thrift</include>
@@ -115,6 +116,12 @@
                                         
<exclude>org/apache/iceberg/spark/extensions/**</exclude>
                                     </excludes>
                                 </filter>
+                                <filter combine.children="append">
+                                    
<artifact>org.apache.iceberg:iceberg-spark-3.3_2.12</artifact>
+                                    <excludes>
+                                        
<exclude>META-INF/services/org.apache.spark.sql.sources.DataSourceRegister</exclude>
+                                    </excludes>
+                                </filter>
                             </filters>
                             <relocations>
                                 <!-- relocation amoro spark packages -->
diff --git a/docs/engines/spark/spark-conf.md b/docs/engines/spark/spark-conf.md
index fd5d97854..6afdb65bc 100644
--- a/docs/engines/spark/spark-conf.md
+++ b/docs/engines/spark/spark-conf.md
@@ -31,13 +31,22 @@ menu:
 ### Using Mixed-Format in a standalone catalog
 
 Starting from version 3.x, Spark supports configuring an independent Catalog.
-If you want to use a Mixed-Format table in a standalone Catalog, you can 
configure it as follows:
+If you want to use a Mixed-Format table in a standalone Catalog, you can 
create a mixed_catalog and load catalog 
+metadata from AMS with following properties:
 
 ```properties
 spark.sql.catalog.mixed_catalog=org.apache.amoro.spark.MixedFormatSparkCatalog
 
spark.sql.catalog.mixed_catalog.url=thrift://${AMS_HOST}:${AMS_PORT}/${AMS_CATALOG_NAME_HIVE}
 ```
 
+Or create a mixed_catalog with local configurations with following properties:
+```properties
+spark.sql.catalog.mixed_catalog=org.apache.amoro.spark.MixedFormatSparkCatalog
+# Configure mixed catalog type as you needed
+spark.sql.catalog.mixed_catalog.type=hadoop
+spark.sql.catalog.mixed_catalog.warehouse=/warehouse/hadoop_mixed_catalog
+```
+
 Then, execute the following SQL in the Spark SQL Client to switch to the 
corresponding catalog.
 
 ```sql
@@ -47,7 +56,7 @@ use mixed_catalog;
 Of course, you can also access Mixed-Format tables by directly using the 
triplet
 `mixed_catalog.{db_name}.{table_name}`.
 
-You can also set Spark's default Catalog to your configured Catalog using the 
following properties.
+You can also set Spark's default catalog to your configured catalog using the 
following properties.
 In this way, you don't need to use the `use {catalog}` command to switch the 
default catalog.
 
 ```properties


Reply via email to