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

fanng 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 1ec5b1152 [#4581]feat(iceberg): support Gravitino-based multi catalog 
support for Gravitino Iceberg REST server (#4598)
1ec5b1152 is described below

commit 1ec5b1152c30f53441f971066a818a5c6838a641
Author: theoryxu <[email protected]>
AuthorDate: Sun Aug 25 19:46:45 2024 +0800

    [#4581]feat(iceberg): support Gravitino-based multi catalog support for 
Gravitino Iceberg REST server (#4598)
    
    ### What changes were proposed in this pull request?
    
    support Gravitino-based multi catalog support for Gravitino Iceberg REST
    server
    
    ### Why are the changes needed?
    
    fixes: #4581
    
    ### Does this PR introduce _any_ user-facing change?
    
    add a property key
    
    ### How was this patch tested?
    
    1. add UT
    2. manual test
    
    ---------
    
    Co-authored-by: theoryxu <[email protected]>
---
 .../lakehouse/iceberg/IcebergConstants.java        |   7 ++
 docs/iceberg-rest-service.md                       |  21 +++-
 .../gravitino/iceberg/common/IcebergConfig.java    |  23 ++++
 iceberg/iceberg-rest-server/build.gradle.kts       |   4 +
 .../org/apache/gravitino/iceberg/RESTService.java  |   2 +-
 .../ConfigBasedIcebergTableOpsProvider.java        |   4 +-
 .../GravitinoBasedIcebergTableOpsProvider.java     |  98 +++++++++++++++++
 .../iceberg/service}/IcebergTableOpsManager.java   |  48 ++++++++-
 .../service/rest/IcebergNamespaceOperations.java   |   2 +-
 .../service/rest/IcebergTableOperations.java       |   2 +-
 .../service/rest/IcebergTableRenameOperations.java |   2 +-
 .../TestConfigBasedIcebergTableOpsProvider.java    |  15 +--
 .../TestGravitinoBasedIcebergTableOpsProvider.java | 120 +++++++++++++++++++++
 .../service}/TestIcebergTableOpsManager.java       |   9 +-
 .../ConfigBasedIcebergTableOpsProviderForTest.java |   2 +-
 .../iceberg/service/rest/IcebergRestTestUtil.java  |   2 +-
 16 files changed, 338 insertions(+), 23 deletions(-)

diff --git 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
index d07a6c487..21d5db3ff 100644
--- 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
+++ 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
@@ -65,7 +65,14 @@ public class IcebergConstants {
 
   public static final String GRAVITINO_ICEBERG_REST_SERVICE_NAME = 
"iceberg-rest";
 
+  public static final String ICEBERG_REST_CATALOG_CACHE_EVICTION_INTERVAL =
+      "catalog-cache-eviction-interval-ms";
+
   public static final String ICEBERG_REST_CATALOG_PROVIDER = 
"catalog-provider";
 
+  public static final String GRAVITINO_URI = "gravitino-uri";
+
+  public static final String GRAVITINO_METALAKE = "gravitino-metalake";
+
   public static final String GRAVITINO_DEFAULT_CATALOG = 
"__gravitino_default_catalog";
 }
diff --git a/docs/iceberg-rest-service.md b/docs/iceberg-rest-service.md
index d87649404..67fb96dd4 100644
--- a/docs/iceberg-rest-service.md
+++ b/docs/iceberg-rest-service.md
@@ -178,7 +178,9 @@ The Gravitino Iceberg REST server supports multiple 
catalogs and offers a config
 
|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|----------|---------------|
 | `gravitino.iceberg-rest.catalog-provider`    | Catalog provider class name, 
you can develop a class that implements `IcebergTableOpsProvider` and add the 
corresponding jar file to the Iceberg REST service classpath directory.   | 
`config-based-provider`     | No       | 0.7.0         |
 
-When using a config-based catalog provider, you can configure the default 
catalog with `gravitino.iceberg-rest.catalog.<param name>=<value>`. For 
specific catalogs, use the format `gravitino.iceberg-rest.catalog.<catalog 
name>.<param name>=<value>`.
+##### Configuration based catalog provider
+
+When using a configuration based catalog provider, you can configure the 
default catalog with `gravitino.iceberg-rest.catalog.<param name>=<value>`. For 
specific catalogs, use the format `gravitino.iceberg-rest.catalog.<catalog 
name>.<param name>=<value>`.
 
 For instance, you can configure three different catalogs, the default catalog 
and the specific `hive_backend` and `jdbc_backend` catalogs separately.
 
@@ -215,6 +217,23 @@ You can access different catalogs by setting the `prefix` 
to the specific catalo
 ...
 ```
 
+##### Gravitino server based catalog provider
+
+When using a Gravitino server based catalog provider, you can leverage 
Gravitino to support dynamic catalog management for the Iceberg REST server.
+
+| Configuration item                                           | Description   
                                                                                
                                                   | Default value | Required | 
Since Version |
+|--------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------|----------|---------------|
+| `gravitino.iceberg-rest.gravitino-uri`                       | The uri of 
Gravitino server address, only worked if `catalog-provider` is 
`gravitino-based-provider`.                                            | (none) 
       | No       | 0.7.0         |
+| `gravitino.iceberg-rest.gravitino-metalake`                  | The metalake 
name that `gravitino-based-provider` used to request to Gravitino, only worked 
if `catalog-provider` is `gravitino-based-provider`. | (none)        | No       
| 0.7.0         |
+| `gravitino.iceberg-rest.catalog-cache-eviction-interval-ms`  | Catalog cache 
eviction interval.                                                              
                                                   | 3600000       | No       | 
0.7.0         |
+
+```text
+gravitino.iceberg-rest.catalog-cache-eviction-interval-ms = 300000
+gravitino.iceberg-rest.catalog-provider = gravitino-based-provider
+gravitino.iceberg-rest.gravitino-uri = http://127.0.0.1:8090
+gravitino.iceberg-rest.gravitino-metalake = test
+```
+
 ### Other Apache Iceberg catalog properties
 
 You can add other properties defined in [Iceberg catalog 
properties](https://iceberg.apache.org/docs/1.5.2/configuration/#catalog-properties).
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
index bc1e47a06..e99d28508 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/IcebergConfig.java
@@ -166,6 +166,13 @@ public class IcebergConfig extends Config implements 
OverwriteDefaultConfig {
           .stringConf()
           .create();
 
+  public static final ConfigEntry<Long> 
ICEBERG_REST_CATALOG_CACHE_EVICTION_INTERVAL =
+      new 
ConfigBuilder(IcebergConstants.ICEBERG_REST_CATALOG_CACHE_EVICTION_INTERVAL)
+          .doc("Catalog cache eviction interval.")
+          .version(ConfigConstants.VERSION_0_7_0)
+          .longConf()
+          .createWithDefault(3600000L);
+
   public static final ConfigEntry<String> ICEBERG_REST_CATALOG_PROVIDER =
       new ConfigBuilder(IcebergConstants.ICEBERG_REST_CATALOG_PROVIDER)
           .doc(
@@ -174,6 +181,22 @@ public class IcebergConfig extends Config implements 
OverwriteDefaultConfig {
           .stringConf()
           .createWithDefault("config-based-provider");
 
+  public static final ConfigEntry<String> GRAVITINO_URI =
+      new ConfigBuilder(IcebergConstants.GRAVITINO_URI)
+          .doc(
+              "The uri of Gravitino server address, only worked if 
`catalog-provider` is `gravitino-based-provider`.")
+          .version(ConfigConstants.VERSION_0_7_0)
+          .stringConf()
+          .create();
+
+  public static final ConfigEntry<String> GRAVITINO_METALAKE =
+      new ConfigBuilder(IcebergConstants.GRAVITINO_METALAKE)
+          .doc(
+              "The metalake name that `gravitino-based-provider` used to 
request to Gravitino, only worked if `catalog-provider` is 
`gravitino-based-provider`.")
+          .version(ConfigConstants.VERSION_0_7_0)
+          .stringConf()
+          .create();
+
   public String getJdbcDriver() {
     return get(JDBC_DRIVER);
   }
diff --git a/iceberg/iceberg-rest-server/build.gradle.kts 
b/iceberg/iceberg-rest-server/build.gradle.kts
index 0fa77b850..0ee6b0ff6 100644
--- a/iceberg/iceberg-rest-server/build.gradle.kts
+++ b/iceberg/iceberg-rest-server/build.gradle.kts
@@ -31,7 +31,9 @@ val icebergVersion: String = libs.versions.iceberg.get()
 val scalaCollectionCompatVersion: String = 
libs.versions.scala.collection.compat.get()
 
 dependencies {
+  implementation(project(":api"))
   implementation(project(":catalogs:catalog-common"))
+  implementation(project(":clients:client-java"))
   implementation(project(":core"))
   implementation(project(":common"))
   implementation(project(":iceberg:iceberg-common"))
@@ -40,6 +42,8 @@ dependencies {
   implementation(libs.bundles.jetty)
   implementation(libs.bundles.jersey)
   implementation(libs.bundles.log4j)
+  implementation(libs.caffeine)
+  implementation(libs.commons.lang3)
   implementation(libs.guava)
   implementation(libs.jackson.annotations)
   implementation(libs.jackson.databind)
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
index dd3b9d9ab..d791a6a1c 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
@@ -23,9 +23,9 @@ import javax.servlet.Servlet;
 import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.auxiliary.GravitinoAuxiliaryService;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
-import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.IcebergExceptionMapper;
 import org.apache.gravitino.iceberg.service.IcebergObjectMapperProvider;
+import org.apache.gravitino.iceberg.service.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.apache.gravitino.metrics.MetricsSystem;
 import org.apache.gravitino.metrics.source.MetricsSource;
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/ConfigBasedIcebergTableOpsProvider.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergTableOpsProvider.java
similarity index 95%
rename from 
iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/ConfigBasedIcebergTableOpsProvider.java
rename to 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergTableOpsProvider.java
index 070e67ce1..2cbf2af2c 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/ConfigBasedIcebergTableOpsProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/ConfigBasedIcebergTableOpsProvider.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.gravitino.iceberg.common.ops;
+package org.apache.gravitino.iceberg.provider;
 
 import com.google.common.annotations.VisibleForTesting;
 import java.util.Map;
@@ -24,6 +24,8 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsProvider;
 import org.apache.gravitino.utils.MapUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergTableOpsProvider.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergTableOpsProvider.java
new file mode 100644
index 000000000..a09abbb1a
--- /dev/null
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/provider/GravitinoBasedIcebergTableOpsProvider.java
@@ -0,0 +1,98 @@
+/*
+ * 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.iceberg.provider;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
+import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergPropertiesUtils;
+import org.apache.gravitino.client.GravitinoAdminClient;
+import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This provider proxy Gravitino lakehouse-iceberg catalogs.
+ *
+ * <p>For example, there are one catalog named iceberg_catalog in metalake
+ *
+ * <p>The catalogName is iceberg_catalog
+ */
+public class GravitinoBasedIcebergTableOpsProvider
+    implements IcebergTableOpsProvider, AutoCloseable {
+  public static final Logger LOG =
+      LoggerFactory.getLogger(GravitinoBasedIcebergTableOpsProvider.class);
+
+  public static final String GRAVITINO_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME =
+      "gravitino-based-provider";
+
+  private String gravitinoMetalake;
+
+  private GravitinoAdminClient client;
+
+  @Override
+  public void initialize(Map<String, String> properties) {
+    String uri = properties.get(IcebergConstants.GRAVITINO_URI);
+    String metalake = properties.get(IcebergConstants.GRAVITINO_METALAKE);
+
+    Preconditions.checkArgument(
+        StringUtils.isNotBlank(uri), IcebergConstants.GRAVITINO_URI + " is 
blank");
+    Preconditions.checkArgument(
+        StringUtils.isNotBlank(metalake), IcebergConstants.GRAVITINO_METALAKE 
+ " is blank");
+
+    this.gravitinoMetalake = metalake;
+    this.client = GravitinoAdminClient.builder(uri).build();
+  }
+
+  @Override
+  public IcebergTableOps getIcebergTableOps(String catalogName) {
+    Preconditions.checkArgument(
+        StringUtils.isNotBlank(catalogName), "blank catalogName is illegal");
+    Preconditions.checkArgument(
+        !IcebergConstants.GRAVITINO_DEFAULT_CATALOG.equals(catalogName),
+        IcebergConstants.GRAVITINO_DEFAULT_CATALOG + " is illegal in 
gravitino-based-provider");
+
+    Catalog catalog = 
client.loadMetalake(gravitinoMetalake).loadCatalog(catalogName);
+
+    Preconditions.checkArgument(
+        "lakehouse-iceberg".equals(catalog.provider()),
+        String.format("%s.%s is not iceberg catalog", gravitinoMetalake, 
catalogName));
+
+    Map<String, String> properties =
+        
IcebergPropertiesUtils.toIcebergCatalogProperties(catalog.properties());
+    return new IcebergTableOps(new IcebergConfig(properties));
+  }
+
+  @VisibleForTesting
+  void setClient(GravitinoAdminClient client) {
+    this.client = client;
+  }
+
+  @Override
+  public void close() throws Exception {
+    if (client != null) {
+      client.close();
+    }
+  }
+}
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergTableOpsManager.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergTableOpsManager.java
similarity index 67%
rename from 
iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergTableOpsManager.java
rename to 
iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergTableOpsManager.java
index 57792b89f..906bb5b26 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergTableOpsManager.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergTableOpsManager.java
@@ -16,16 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.gravitino.iceberg.common.ops;
+package org.apache.gravitino.iceberg.service;
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.Scheduler;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.util.Map;
-import org.apache.commons.lang.StringUtils;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsProvider;
+import 
org.apache.gravitino.iceberg.provider.ConfigBasedIcebergTableOpsProvider;
+import 
org.apache.gravitino.iceberg.provider.GravitinoBasedIcebergTableOpsProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,16 +43,37 @@ public class IcebergTableOpsManager implements 
AutoCloseable {
   private static final ImmutableMap<String, String> 
ICEBERG_TABLE_OPS_PROVIDER_NAMES =
       ImmutableMap.of(
           
ConfigBasedIcebergTableOpsProvider.CONFIG_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME,
-          ConfigBasedIcebergTableOpsProvider.class.getCanonicalName());
+          ConfigBasedIcebergTableOpsProvider.class.getCanonicalName(),
+          
GravitinoBasedIcebergTableOpsProvider.GRAVITINO_BASE_ICEBERG_TABLE_OPS_PROVIDER_NAME,
+          GravitinoBasedIcebergTableOpsProvider.class.getCanonicalName());
 
   private final Cache<String, IcebergTableOps> icebergTableOpsCache;
 
   private final IcebergTableOpsProvider provider;
 
   public IcebergTableOpsManager(Map<String, String> properties) {
-    this.icebergTableOpsCache = Caffeine.newBuilder().build();
     this.provider = createProvider(properties);
     this.provider.initialize(properties);
+    this.icebergTableOpsCache =
+        Caffeine.newBuilder()
+            .expireAfterWrite(
+                (new IcebergConfig(properties))
+                    
.get(IcebergConfig.ICEBERG_REST_CATALOG_CACHE_EVICTION_INTERVAL),
+                TimeUnit.MILLISECONDS)
+            .removalListener(
+                (k, v, c) -> {
+                  LOG.info("Remove IcebergTableOps cache {}.", k);
+                  closeIcebergTableOps((IcebergTableOps) v);
+                })
+            .scheduler(
+                Scheduler.forScheduledExecutorService(
+                    new ScheduledThreadPoolExecutor(
+                        1,
+                        new ThreadFactoryBuilder()
+                            .setDaemon(true)
+                            .setNameFormat("table-ops-cleaner-%d")
+                            .build())))
+            .build();
   }
 
   /**
@@ -97,8 +126,19 @@ public class IcebergTableOpsManager implements 
AutoCloseable {
     }
   }
 
+  private void closeIcebergTableOps(IcebergTableOps ops) {
+    try {
+      ops.close();
+    } catch (Exception ex) {
+      LOG.warn("Close Iceberg table ops fail: {}, {}", ops, ex);
+    }
+  }
+
   @Override
   public void close() throws Exception {
     icebergTableOpsCache.invalidateAll();
+    if (provider instanceof AutoCloseable) {
+      ((AutoCloseable) provider).close();
+    }
   }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergNamespaceOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergNamespaceOperations.java
index 82b6250e6..c9fd7a5c0 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergNamespaceOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergNamespaceOperations.java
@@ -34,8 +34,8 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.IcebergRestUtils;
+import org.apache.gravitino.iceberg.service.IcebergTableOpsManager;
 import org.apache.gravitino.metrics.MetricNames;
 import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.rest.RESTUtil;
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
index f6f6042a5..d028c8d4a 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
@@ -37,9 +37,9 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.IcebergObjectMapper;
 import org.apache.gravitino.iceberg.service.IcebergRestUtils;
+import org.apache.gravitino.iceberg.service.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.apache.gravitino.metrics.MetricNames;
 import org.apache.iceberg.catalog.TableIdentifier;
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
index 441ce0551..4fc2ea832 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableRenameOperations.java
@@ -30,8 +30,8 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.IcebergRestUtils;
+import org.apache.gravitino.iceberg.service.IcebergTableOpsManager;
 import org.apache.gravitino.metrics.MetricNames;
 import org.apache.iceberg.rest.requests.RenameTableRequest;
 
diff --git 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestConfigBasedIcebergTableOpsProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergTableOpsProvider.java
similarity index 90%
rename from 
iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestConfigBasedIcebergTableOpsProvider.java
rename to 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergTableOpsProvider.java
index c3c8c3fe8..70d9a834f 100644
--- 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestConfigBasedIcebergTableOpsProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestConfigBasedIcebergTableOpsProvider.java
@@ -16,13 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.gravitino.iceberg.common.ops;
+package org.apache.gravitino.iceberg.provider;
 
 import com.google.common.collect.Maps;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
 import org.apache.iceberg.hive.HiveCatalog;
 import org.apache.iceberg.inmemory.InMemoryCatalog;
 import org.apache.iceberg.jdbc.JdbcCatalog;
@@ -94,13 +95,13 @@ public class TestConfigBasedIcebergTableOpsProvider {
     Assertions.assertEquals("memory", 
defaultIcebergConfig.get(IcebergConfig.CATALOG_BACKEND));
     Assertions.assertEquals("/tmp/", 
defaultIcebergConfig.get(IcebergConfig.CATALOG_WAREHOUSE));
 
-    Assertions.assertEquals(hiveCatalogName, hiveOps.catalog.name());
-    Assertions.assertEquals(jdbcCatalogName, jdbcOps.catalog.name());
-    Assertions.assertEquals(defaultCatalogName, defaultOps.catalog.name());
+    Assertions.assertEquals(hiveCatalogName, hiveOps.getCatalog().name());
+    Assertions.assertEquals(jdbcCatalogName, jdbcOps.getCatalog().name());
+    Assertions.assertEquals(defaultCatalogName, 
defaultOps.getCatalog().name());
 
-    Assertions.assertTrue(hiveOps.catalog instanceof HiveCatalog);
-    Assertions.assertTrue(jdbcOps.catalog instanceof JdbcCatalog);
-    Assertions.assertTrue(defaultOps.catalog instanceof InMemoryCatalog);
+    Assertions.assertTrue(hiveOps.getCatalog() instanceof HiveCatalog);
+    Assertions.assertTrue(jdbcOps.getCatalog() instanceof JdbcCatalog);
+    Assertions.assertTrue(defaultOps.getCatalog() instanceof InMemoryCatalog);
   }
 
   @ParameterizedTest
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergTableOpsProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergTableOpsProvider.java
new file mode 100644
index 000000000..b95a430f4
--- /dev/null
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/provider/TestGravitinoBasedIcebergTableOpsProvider.java
@@ -0,0 +1,120 @@
+/*
+ * 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.iceberg.provider;
+
+import java.util.HashMap;
+import java.util.UUID;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
+import org.apache.gravitino.client.GravitinoAdminClient;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
+import org.apache.iceberg.hive.HiveCatalog;
+import org.apache.iceberg.jdbc.JdbcCatalog;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class TestGravitinoBasedIcebergTableOpsProvider {
+  private static final String STORE_PATH =
+      "/tmp/gravitino_test_iceberg_jdbc_backend_" + 
UUID.randomUUID().toString().replace("-", "");
+
+  @Test
+  public void testValidIcebergTableOps() {
+    String hiveCatalogName = "hive_backend";
+    String jdbcCatalogName = "jdbc_backend";
+
+    Catalog hiveMockCatalog = Mockito.mock(Catalog.class);
+    Catalog jdbcMockCatalog = Mockito.mock(Catalog.class);
+
+    GravitinoMetalake gravitinoMetalake = 
Mockito.mock(GravitinoMetalake.class);
+    
Mockito.when(gravitinoMetalake.loadCatalog(hiveCatalogName)).thenReturn(hiveMockCatalog);
+    
Mockito.when(gravitinoMetalake.loadCatalog(jdbcCatalogName)).thenReturn(jdbcMockCatalog);
+
+    Mockito.when(hiveMockCatalog.provider()).thenReturn("lakehouse-iceberg");
+    Mockito.when(jdbcMockCatalog.provider()).thenReturn("lakehouse-iceberg");
+
+    Mockito.when(hiveMockCatalog.properties())
+        .thenReturn(
+            new HashMap<String, String>() {
+              {
+                put(IcebergConstants.CATALOG_BACKEND, "hive");
+                put(IcebergConstants.URI, "thrift://127.0.0.1:7004");
+                put(IcebergConstants.WAREHOUSE, "/tmp/usr/hive/warehouse");
+                put(IcebergConstants.CATALOG_BACKEND_NAME, hiveCatalogName);
+              }
+            });
+    Mockito.when(jdbcMockCatalog.properties())
+        .thenReturn(
+            new HashMap<String, String>() {
+              {
+                put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+                put(
+                    IcebergConstants.URI,
+                    String.format("jdbc:h2:%s;DB_CLOSE_DELAY=-1;MODE=MYSQL", 
STORE_PATH));
+                put(IcebergConstants.WAREHOUSE, 
"/tmp/user/hive/warehouse-jdbc");
+                put(IcebergConstants.GRAVITINO_JDBC_USER, "gravitino");
+                put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, "gravitino");
+                put(IcebergConstants.GRAVITINO_JDBC_DRIVER, "org.h2.Driver");
+                put(IcebergConstants.ICEBERG_JDBC_INITIALIZE, "true");
+                put(IcebergConstants.CATALOG_BACKEND_NAME, jdbcCatalogName);
+              }
+            });
+
+    GravitinoBasedIcebergTableOpsProvider provider = new 
GravitinoBasedIcebergTableOpsProvider();
+    GravitinoAdminClient client = Mockito.mock(GravitinoAdminClient.class);
+    
Mockito.when(client.loadMetalake(Mockito.any())).thenReturn(gravitinoMetalake);
+    provider.setClient(client);
+
+    IcebergTableOps hiveOps = provider.getIcebergTableOps(hiveCatalogName);
+    IcebergTableOps jdbcOps = provider.getIcebergTableOps(jdbcCatalogName);
+
+    Assertions.assertEquals(hiveCatalogName, hiveOps.getCatalog().name());
+    Assertions.assertEquals(jdbcCatalogName, jdbcOps.getCatalog().name());
+
+    Assertions.assertTrue(hiveOps.getCatalog() instanceof HiveCatalog);
+    Assertions.assertTrue(jdbcOps.getCatalog() instanceof JdbcCatalog);
+  }
+
+  @Test
+  public void testInvalidIcebergTableOps() {
+    String invalidCatalogName = "invalid_catalog";
+
+    Catalog invalidCatalog = Mockito.mock(Catalog.class);
+
+    GravitinoMetalake gravitinoMetalake = 
Mockito.mock(GravitinoMetalake.class);
+    
Mockito.when(gravitinoMetalake.loadCatalog(invalidCatalogName)).thenReturn(invalidCatalog);
+
+    Mockito.when(invalidCatalog.provider()).thenReturn("hive");
+
+    GravitinoAdminClient client = Mockito.mock(GravitinoAdminClient.class);
+    
Mockito.when(client.loadMetalake(Mockito.any())).thenReturn(gravitinoMetalake);
+
+    GravitinoBasedIcebergTableOpsProvider provider = new 
GravitinoBasedIcebergTableOpsProvider();
+    provider.setClient(client);
+
+    Assertions.assertThrowsExactly(
+        IllegalArgumentException.class, () -> 
provider.getIcebergTableOps(invalidCatalogName));
+    Assertions.assertThrowsExactly(
+        IllegalArgumentException.class, () -> provider.getIcebergTableOps(""));
+    Assertions.assertThrowsExactly(
+        IllegalArgumentException.class,
+        () -> 
provider.getIcebergTableOps(IcebergConstants.GRAVITINO_DEFAULT_CATALOG));
+  }
+}
diff --git 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergTableOpsManager.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergTableOpsManager.java
similarity index 88%
rename from 
iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergTableOpsManager.java
rename to 
iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergTableOpsManager.java
index 04ded71dd..68e03841f 100644
--- 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergTableOpsManager.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergTableOpsManager.java
@@ -16,11 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.gravitino.iceberg.common.ops;
+package org.apache.gravitino.iceberg.service;
 
 import com.google.common.collect.Maps;
 import java.util.Map;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
@@ -43,9 +44,9 @@ public class TestIcebergTableOpsManager {
     IcebergTableOps ops = manager.getOps(rawPrefix);
 
     if (StringUtils.isBlank(prefix)) {
-      Assertions.assertEquals(ops.catalog.name(), DEFAULT_CATALOG);
+      Assertions.assertEquals(ops.getCatalog().name(), DEFAULT_CATALOG);
     } else {
-      Assertions.assertEquals(ops.catalog.name(), prefix);
+      Assertions.assertEquals(ops.getCatalog().name(), prefix);
     }
   }
 
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergTableOpsProviderForTest.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergTableOpsProviderForTest.java
index 6a9375615..07ef96d20 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergTableOpsProviderForTest.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/ConfigBasedIcebergTableOpsProviderForTest.java
@@ -18,8 +18,8 @@
  */
 package org.apache.gravitino.iceberg.service.rest;
 
-import 
org.apache.gravitino.iceberg.common.ops.ConfigBasedIcebergTableOpsProvider;
 import org.apache.gravitino.iceberg.common.ops.IcebergTableOps;
+import 
org.apache.gravitino.iceberg.provider.ConfigBasedIcebergTableOpsProvider;
 
 public class ConfigBasedIcebergTableOpsProviderForTest extends 
ConfigBasedIcebergTableOpsProvider {
   @Override
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
index e7b19e0af..ab36d1924 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/IcebergRestTestUtil.java
@@ -25,9 +25,9 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
-import org.apache.gravitino.iceberg.common.ops.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.IcebergExceptionMapper;
 import org.apache.gravitino.iceberg.service.IcebergObjectMapperProvider;
+import org.apache.gravitino.iceberg.service.IcebergTableOpsManager;
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.jackson.JacksonFeature;


Reply via email to