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

jshao 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 155c42968 [#5154] refactor(drop-metalake): re-define drop metalake 
(#5155)
155c42968 is described below

commit 155c4296899b1a7d3aebce81d2d08bf7b8b845bf
Author: mchades <[email protected]>
AuthorDate: Mon Oct 21 17:54:09 2024 +0800

    [#5154] refactor(drop-metalake): re-define drop metalake (#5155)
    
    ### What changes were proposed in this pull request?
    
    - Add an in-use property to the metalake with the default value of true.
    - Only empty metalake with in-use=false can be dropped when
    `force=false`
     - User can use `dorce` option to drop metalake
    - More drop metalake limitations please see the JavaDoc of
    `dropMetalake` in API module
    
    ### Why are the changes needed?
    
    Fix: #5154
    
    ### Does this PR introduce _any_ user-facing change?
    
    yes, users now can not drop an in used metalake
    
    ### How was this patch tested?
    
    tests added
---
 .../main/java/org/apache/gravitino/Metalake.java   |   3 +
 .../org/apache/gravitino/SupportsMetalakes.java    |  74 +++++++-
 .../exceptions/MetalakeInUseException.java         |  36 ++++
 .../exceptions/MetalakeNotInUseException.java      |  36 ++++
 .../ranger/integration/test/RangerHiveE2EIT.java   |   6 +-
 .../hadoop/integration/test/HadoopCatalogIT.java   |   7 +-
 .../test/HadoopUserAuthenticationIT.java           |   2 +-
 .../test/HadoopUserImpersonationIT.java            |   5 +-
 .../hive/integration/test/CatalogHiveIT.java       |  11 +-
 .../hive/integration/test/ProxyCatalogHiveIT.java  |   5 +-
 .../doris/integration/test/CatalogDorisIT.java     |   7 +-
 .../integration/test/AuditCatalogMysqlIT.java      |   7 +-
 .../mysql/integration/test/CatalogMysqlIT.java     |   6 +-
 .../integration/test/CatalogPostgreSqlIT.java      |   6 +-
 .../kafka/integration/test/CatalogKafkaIT.java     |   6 +-
 .../integration/test/CatalogIcebergBaseIT.java     |   6 +-
 .../integration/test/CatalogPaimonBaseIT.java      |   6 +-
 .../org/apache/gravitino/client/ErrorHandlers.java |  56 ++++++
 .../gravitino/client/GravitinoAdminClient.java     |  69 +++++++-
 .../gravitino/client/integration/test/AuditIT.java |   1 +
 .../client/integration/test/CatalogIT.java         |   2 +-
 .../client/integration/test/MetalakeIT.java        |   5 +-
 .../gravitino/client/integration/test/TagIT.java   |   2 +-
 .../authorization/AccessControlNotAllowIT.java     |   3 +-
 .../test/authorization/CheckCurrentUserIT.java     |   2 +-
 .../integration/test/authorization/OwnerIT.java    |  10 +-
 .../gravitino/client/gravitino_admin_client.py     |  44 ++++-
 .../gravitino/dto/requests/metalake_set_request.py |  41 +++++
 clients/client-python/gravitino/exceptions/base.py |   8 +
 .../exceptions/handlers/metalake_error_handler.py  |   9 +
 .../tests/integration/auth/test_auth_common.py     |   4 +-
 .../tests/integration/test_catalog.py              |   4 +-
 .../tests/integration/test_fileset_catalog.py      |   4 +-
 .../tests/integration/test_metalake.py             |   6 +-
 .../client-python/tests/integration/test_schema.py |   4 +-
 .../test/GravitinoVirtualFileSystemIT.java         |   2 +-
 .../gravitino/dto/requests/MetalakeSetRequest.java |  57 +++++++
 .../authorization/AuthorizationUtils.java          |  22 ---
 .../gravitino/authorization/RoleManager.java       |  17 +-
 .../gravitino/authorization/UserGroupManager.java  |  19 ++-
 .../apache/gravitino/catalog/CatalogManager.java   |  68 ++++----
 .../gravitino/catalog/OperationDispatcher.java     |  11 +-
 .../gravitino/connector/PropertiesMetadata.java    |   2 +-
 .../gravitino/hook/MetalakeHookDispatcher.java     |  17 +-
 .../gravitino/listener/CatalogEventDispatcher.java |   2 +-
 .../listener/MetalakeEventDispatcher.java          |  19 ++-
 .../org/apache/gravitino/meta/BaseMetalake.java    |  16 +-
 .../apache/gravitino/metalake/MetalakeManager.java | 188 +++++++++++++++++----
 .../metalake/MetalakeNormalizeDispatcher.java      |  73 +++++++-
 .../metalake/MetalakePropertiesMetadata.java       |  50 ++++++
 .../gravitino/metalake/SupportsMetalakes.java      |  65 ++++++-
 .../apache/gravitino/proto/BaseMetalakeSerDe.java  |   3 +-
 .../java/org/apache/gravitino/tag/TagManager.java  |  28 +--
 .../listener/api/event/TestMetalakeEvent.java      |   3 +-
 .../gravitino/metalake/TestMetalakeManager.java    |   1 +
 .../gravitino/storage/TestEntityStorage.java       |  10 +-
 .../server/web/rest/ExceptionHandlers.java         |  50 +++++-
 .../server/web/rest/MetalakeOperations.java        |  48 +++++-
 .../server/web/rest/TestMetalakeOperations.java    |   9 +-
 .../integration/test/TrinoQueryITBase.java         |   2 +-
 web/web/src/lib/api/metalakes/index.js             |   2 +-
 61 files changed, 1043 insertions(+), 244 deletions(-)

diff --git a/api/src/main/java/org/apache/gravitino/Metalake.java 
b/api/src/main/java/org/apache/gravitino/Metalake.java
index fb6fdbee0..4f95a8a23 100644
--- a/api/src/main/java/org/apache/gravitino/Metalake.java
+++ b/api/src/main/java/org/apache/gravitino/Metalake.java
@@ -29,6 +29,9 @@ import org.apache.gravitino.authorization.SupportsRoles;
 @Evolving
 public interface Metalake extends Auditable {
 
+  /** The property indicating the metalake is in use. */
+  String PROPERTY_IN_USE = "in-use";
+
   /**
    * The name of the metalake.
    *
diff --git a/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java 
b/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
index 47cce7d3a..515b2d836 100644
--- a/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
+++ b/api/src/main/java/org/apache/gravitino/SupportsMetalakes.java
@@ -21,7 +21,10 @@ package org.apache.gravitino;
 import java.util.Map;
 import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 
 /**
  * Client interface for supporting metalakes. It includes methods for listing, 
loading, creating,
@@ -38,7 +41,7 @@ public interface SupportsMetalakes {
   Metalake[] listMetalakes();
 
   /**
-   * Load a metalake by its identifier.
+   * Load a metalake by its name.
    *
    * @param name the name of the metalake.
    * @return The metalake.
@@ -62,7 +65,7 @@ public interface SupportsMetalakes {
   }
 
   /**
-   * Create a metalake with specified identifier.
+   * Create a metalake with specified name, comment and properties.
    *
    * @param name The name of the metalake.
    * @param comment The comment of the metalake.
@@ -74,7 +77,7 @@ public interface SupportsMetalakes {
       throws MetalakeAlreadyExistsException;
 
   /**
-   * Alter a metalake with specified identifier.
+   * Alter a metalake with specified metalake name and changes.
    *
    * @param name The name of the metalake.
    * @param changes The changes to apply.
@@ -86,10 +89,69 @@ public interface SupportsMetalakes {
       throws NoSuchMetalakeException, IllegalArgumentException;
 
   /**
-   * Drop a metalake with specified identifier.
+   * Drop a metalake with specified name. Please make sure:
    *
-   * @param name The identifier of the metalake.
+   * <ul>
+   *   <li>There is no catalog in the metalake. Otherwise, a {@link 
NonEmptyEntityException} will be
+   *       thrown.
+   *   <li>The method {@link #disableMetalake(String)} has been called before 
dropping the metalake.
+   *       Otherwise, a {@link MetalakeInUseException} will be thrown.
+   * </ul>
+   *
+   * It is equivalent to calling {@code dropMetalake(name, false)}.
+   *
+   * @param name The name of the metalake.
    * @return True if the metalake was dropped, false if the metalake does not 
exist.
+   * @throws NonEmptyEntityException If the metalake is not empty.
+   * @throws MetalakeInUseException If the metalake is in use.
+   */
+  default boolean dropMetalake(String name) throws NonEmptyEntityException, 
MetalakeInUseException {
+    return dropMetalake(name, false);
+  }
+
+  /**
+   * Drop a metalake with specified name. If the force flag is true, it will:
+   *
+   * <ul>
+   *   <li>Cascade drop all sub-entities (tags, catalogs, schemas, tables, 
etc.) of the metalake in
+   *       Gravitino store.
+   *   <li>Drop the metalake even if it is in use.
+   *   <li>External resources (e.g. database, table, etc.) associated with 
sub-entities will not be
+   *       deleted unless it is managed (such as managed fileset).
+   * </ul>
+   *
+   * If the force flag is false, it is equivalent to calling {@link 
#dropMetalake(String)}.
+   *
+   * @param name The name of the metalake.
+   * @param force Whether to force the drop.
+   * @return True if the metalake was dropped, false if the metalake does not 
exist.
+   * @throws NonEmptyEntityException If the metalake is not empty and force is 
false.
+   * @throws MetalakeInUseException If the metalake is in use and force is 
false.
+   */
+  boolean dropMetalake(String name, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException;
+
+  /**
+   * Enable a metalake. If the metalake is already in use, this method does 
nothing.
+   *
+   * @param name The name of the metalake.
+   * @throws NoSuchMetalakeException If the metalake does not exist.
+   */
+  void enableMetalake(String name) throws NoSuchMetalakeException;
+
+  /**
+   * Disable a metalake. If the metalake is already disabled, this method does 
nothing. Once a
+   * metalake is disable:
+   *
+   * <ul>
+   *   <li>It can only be listed, loaded, dropped, or enable.
+   *   <li>Any other operations on the metalake will throw an {@link 
MetalakeNotInUseException}.
+   *   <li>Any operation on the sub-entities (catalogs, schemas, tables, etc.) 
will throw an {@link
+   *       MetalakeNotInUseException}.
+   * </ul>
+   *
+   * @param name The name of the metalake.
+   * @throws NoSuchMetalakeException If the metalake does not exist.
    */
-  boolean dropMetalake(String name);
+  void disableMetalake(String name) throws NoSuchMetalakeException;
 }
diff --git 
a/api/src/main/java/org/apache/gravitino/exceptions/MetalakeInUseException.java 
b/api/src/main/java/org/apache/gravitino/exceptions/MetalakeInUseException.java
new file mode 100644
index 000000000..5184116d9
--- /dev/null
+++ 
b/api/src/main/java/org/apache/gravitino/exceptions/MetalakeInUseException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.exceptions;
+
+import com.google.errorprone.annotations.FormatMethod;
+import com.google.errorprone.annotations.FormatString;
+
+/** Exception thrown when a metalake is in use and cannot be deleted. */
+public class MetalakeInUseException extends InUseException {
+  /**
+   * Constructs a new exception with the specified detail message.
+   *
+   * @param message the detail message.
+   * @param args the arguments to the message.
+   */
+  @FormatMethod
+  public MetalakeInUseException(@FormatString String message, Object... args) {
+    super(message, args);
+  }
+}
diff --git 
a/api/src/main/java/org/apache/gravitino/exceptions/MetalakeNotInUseException.java
 
b/api/src/main/java/org/apache/gravitino/exceptions/MetalakeNotInUseException.java
new file mode 100644
index 000000000..4ad6cc33a
--- /dev/null
+++ 
b/api/src/main/java/org/apache/gravitino/exceptions/MetalakeNotInUseException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.exceptions;
+
+import com.google.errorprone.annotations.FormatMethod;
+import com.google.errorprone.annotations.FormatString;
+
+/** An exception thrown when operating on a metalake that is not in use. */
+public class MetalakeNotInUseException extends NotInUseException {
+  /**
+   * Constructs a new exception with the specified detail message.
+   *
+   * @param message the detail message.
+   * @param args the arguments to the message.
+   */
+  @FormatMethod
+  public MetalakeNotInUseException(@FormatString String message, Object... 
args) {
+    super(message, args);
+  }
+}
diff --git 
a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java
 
b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java
index 208e3d64e..91597ce8e 100644
--- 
a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java
+++ 
b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java
@@ -188,6 +188,7 @@ public class RangerHiveE2EIT extends BaseIT {
               }));
       Arrays.stream(metalake.listCatalogs())
           .forEach((catalogName -> metalake.dropCatalog(catalogName, true)));
+      client.disableMetalake(metalakeName);
       client.dropMetalake(metalakeName);
     }
     if (sparkSession != null) {
@@ -267,10 +268,9 @@ public class RangerHiveE2EIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopCatalogIT.java
 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopCatalogIT.java
index ef8f37187..49bd29b2e 100644
--- 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopCatalogIT.java
+++ 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopCatalogIT.java
@@ -88,7 +88,7 @@ public class HadoopCatalogIT extends BaseIT {
     Catalog catalog = metalake.loadCatalog(catalogName);
     catalog.asSchemas().dropSchema(schemaName, true);
     metalake.dropCatalog(catalogName, true);
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
     if (fileSystem != null) {
       fileSystem.close();
     }
@@ -104,10 +104,9 @@ public class HadoopCatalogIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
index d0de29727..d074709bd 100644
--- 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
+++ 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserAuthenticationIT.java
@@ -654,6 +654,6 @@ public class HadoopUserAuthenticationIT extends BaseIT {
     catalog.asFilesetCatalog().dropFileset(NameIdentifier.of(SCHEMA_NAME, 
filesetName));
     catalog.asSchemas().dropSchema(SCHEMA_NAME, true);
     gravitinoMetalake.dropCatalog(catalogName, true);
-    adminClient.dropMetalake(metalakeName);
+    adminClient.dropMetalake(metalakeName, true);
   }
 }
diff --git 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserImpersonationIT.java
 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserImpersonationIT.java
index 9515b45b5..808aafbae 100644
--- 
a/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserImpersonationIT.java
+++ 
b/catalogs/catalog-hadoop/src/test/java/org/apache/gravitino/catalog/hadoop/integration/test/HadoopUserImpersonationIT.java
@@ -258,10 +258,9 @@ public class HadoopUserImpersonationIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHiveIT.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHiveIT.java
index 31493d54b..903f3fe8a 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHiveIT.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/CatalogHiveIT.java
@@ -229,7 +229,7 @@ public class CatalogHiveIT extends BaseIT {
               }));
       Arrays.stream(metalake.listCatalogs())
           .forEach((catalogName -> metalake.dropCatalog(catalogName, true)));
-      client.dropMetalake(metalakeName);
+      client.dropMetalake(metalakeName, true);
     }
     if (hiveClientPool != null) {
       hiveClientPool.close();
@@ -264,10 +264,9 @@ public class CatalogHiveIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
@@ -1429,8 +1428,8 @@ public class CatalogHiveIT extends BaseIT {
     client.createMetalake(metalakeName1, "comment", Collections.emptyMap());
     client.createMetalake(metalakeName2, "comment", Collections.emptyMap());
 
-    client.dropMetalake(metalakeName1);
-    client.dropMetalake(metalakeName2);
+    client.dropMetalake(metalakeName1, true);
+    client.dropMetalake(metalakeName2, true);
 
     client.createMetalake(metalakeName1, "comment", Collections.emptyMap());
 
diff --git 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
index d328a44dc..b7d61582e 100644
--- 
a/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
+++ 
b/catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/integration/test/ProxyCatalogHiveIT.java
@@ -389,10 +389,9 @@ public class ProxyCatalogHiveIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(METALAKE_NAME, "comment", 
Collections.emptyMap());
+    client.createMetalake(METALAKE_NAME, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(METALAKE_NAME);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(METALAKE_NAME, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
 
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
index b08aa4916..96b92b696 100644
--- 
a/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
+++ 
b/catalogs/catalog-jdbc-doris/src/test/java/org/apache/gravitino/catalog/doris/integration/test/CatalogDorisIT.java
@@ -126,7 +126,7 @@ public class CatalogDorisIT extends BaseIT {
   public void stop() {
     clearTableAndSchema();
     metalake.dropCatalog(catalogName, true);
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
   }
 
   @AfterEach
@@ -143,10 +143,9 @@ public class CatalogDorisIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetaLakes = client.listMetalakes();
     assertEquals(0, gravitinoMetaLakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    assertEquals(createdMetalake, loadMetalake);
+    assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
index 784361d64..b65d82c21 100644
--- 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
+++ 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/AuditCatalogMysqlIT.java
@@ -77,7 +77,7 @@ public class AuditCatalogMysqlIT extends BaseIT {
 
   @AfterAll
   public void stopIntegrationTest() throws IOException, InterruptedException {
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
     mysqlService.close();
     super.stopIntegrationTest();
   }
@@ -169,10 +169,9 @@ public class AuditCatalogMysqlIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
     metalake = loadMetalake;
   }
 }
diff --git 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
index 60af07011..f6b91b00e 100644
--- 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
+++ 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
@@ -144,6 +144,7 @@ public class CatalogMysqlIT extends BaseIT {
     clearTableAndSchema();
     metalake.disableCatalog(catalogName);
     metalake.dropCatalog(catalogName);
+    client.disableMetalake(metalakeName);
     client.dropMetalake(metalakeName);
     mysqlService.close();
   }
@@ -167,10 +168,9 @@ public class CatalogMysqlIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/integration/test/CatalogPostgreSqlIT.java
 
b/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/integration/test/CatalogPostgreSqlIT.java
index 0d1292c67..f22073c78 100644
--- 
a/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/integration/test/CatalogPostgreSqlIT.java
+++ 
b/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/integration/test/CatalogPostgreSqlIT.java
@@ -130,6 +130,7 @@ public class CatalogPostgreSqlIT extends BaseIT {
     }
     metalake.disableCatalog(catalogName);
     metalake.dropCatalog(catalogName);
+    client.disableMetalake(metalakeName);
     client.dropMetalake(metalakeName);
     postgreSqlService.close();
   }
@@ -153,10 +154,9 @@ public class CatalogPostgreSqlIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/integration/test/CatalogKafkaIT.java
 
b/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/integration/test/CatalogKafkaIT.java
index 907b00733..dc91a3dda 100644
--- 
a/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/integration/test/CatalogKafkaIT.java
+++ 
b/catalogs/catalog-kafka/src/test/java/org/apache/gravitino/catalog/kafka/integration/test/CatalogKafkaIT.java
@@ -126,6 +126,7 @@ public class CatalogKafkaIT extends BaseIT {
               metalake.disableCatalog(catalogName);
               metalake.dropCatalog(catalogName);
             }));
+    client.disableMetalake(METALAKE_NAME);
     client.dropMetalake(METALAKE_NAME);
     if (adminClient != null) {
       adminClient.close();
@@ -554,10 +555,9 @@ public class CatalogKafkaIT extends BaseIT {
   }
 
   private void createMetalake() {
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(METALAKE_NAME, "comment", 
Collections.emptyMap());
+    client.createMetalake(METALAKE_NAME, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(METALAKE_NAME);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(METALAKE_NAME, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java
index 9fcc93451..7c5d93362 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java
@@ -140,6 +140,7 @@ public abstract class CatalogIcebergBaseIT extends BaseIT {
       clearTableAndSchema();
       metalake.disableCatalog(catalogName);
       metalake.dropCatalog(catalogName);
+      client.disableMetalake(metalakeName);
       client.dropMetalake(metalakeName);
     } finally {
       if (spark != null) {
@@ -197,10 +198,9 @@ public abstract class CatalogIcebergBaseIT extends BaseIT {
     GravitinoMetalake[] gravitinoMetalakes = client.listMetalakes();
     Assertions.assertEquals(0, gravitinoMetalakes.length);
 
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonBaseIT.java
 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonBaseIT.java
index 19bbde331..668cd404e 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonBaseIT.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonBaseIT.java
@@ -139,6 +139,7 @@ public abstract class CatalogPaimonBaseIT extends BaseIT {
     clearTableAndSchema();
     metalake.disableCatalog(catalogName);
     metalake.dropCatalog(catalogName);
+    client.disableMetalake(metalakeName);
     client.dropMetalake(metalakeName);
     if (spark != null) {
       spark.close();
@@ -885,10 +886,9 @@ public abstract class CatalogPaimonBaseIT extends BaseIT {
   }
 
   private void createMetalake() {
-    GravitinoMetalake createdMetalake =
-        client.createMetalake(metalakeName, "comment", Collections.emptyMap());
+    client.createMetalake(metalakeName, "comment", Collections.emptyMap());
     GravitinoMetalake loadMetalake = client.loadMetalake(metalakeName);
-    Assertions.assertEquals(createdMetalake, loadMetalake);
+    Assertions.assertEquals(metalakeName, loadMetalake.name());
 
     metalake = loadMetalake;
   }
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
index db45b6436..3cc8df1d2 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
@@ -39,6 +39,8 @@ import 
org.apache.gravitino.exceptions.IllegalPrivilegeException;
 import org.apache.gravitino.exceptions.IllegalRoleException;
 import org.apache.gravitino.exceptions.InUseException;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.exceptions.NoSuchFilesetException;
 import org.apache.gravitino.exceptions.NoSuchGroupException;
@@ -281,6 +283,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -329,6 +336,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -377,6 +389,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -428,6 +445,9 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogInUseException.class.getSimpleName())) {
             throw new CatalogInUseException(errorMessage);
 
+          } else if 
(errorResponse.getType().equals(MetalakeInUseException.class.getSimpleName())) {
+            throw new MetalakeInUseException(errorMessage);
+
           } else {
             throw new InUseException(errorMessage);
           }
@@ -436,6 +456,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -468,6 +493,9 @@ public class ErrorHandlers {
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
+        case ErrorConstants.IN_USE_CODE:
+          throw new MetalakeInUseException(errorMessage);
+
         default:
           super.accept(errorResponse);
       }
@@ -562,6 +590,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -608,6 +641,11 @@ public class ErrorHandlers {
           if 
(errorResponse.getType().equals(CatalogNotInUseException.class.getSimpleName()))
 {
             throw new CatalogNotInUseException(errorMessage);
 
+          } else if (errorResponse
+              .getType()
+              .equals(MetalakeNotInUseException.class.getSimpleName())) {
+            throw new MetalakeNotInUseException(errorMessage);
+
           } else {
             throw new NotInUseException(errorMessage);
           }
@@ -647,6 +685,9 @@ public class ErrorHandlers {
         case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
           throw new UnsupportedOperationException(errorMessage);
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
@@ -685,6 +726,9 @@ public class ErrorHandlers {
         case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
           throw new UnsupportedOperationException(errorMessage);
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
@@ -738,6 +782,9 @@ public class ErrorHandlers {
         case ErrorConstants.FORBIDDEN_CODE:
           throw new ForbiddenException(errorMessage);
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
@@ -788,6 +835,9 @@ public class ErrorHandlers {
         case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
           throw new UnsupportedOperationException(errorMessage);
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
@@ -831,6 +881,9 @@ public class ErrorHandlers {
             throw new AlreadyExistsException(errorMessage);
           }
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
@@ -864,6 +917,9 @@ public class ErrorHandlers {
         case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
           throw new UnsupportedOperationException(errorMessage);
 
+        case ErrorConstants.NOT_IN_USE_CODE:
+          throw new MetalakeNotInUseException(errorMessage);
+
         case ErrorConstants.INTERNAL_ERROR_CODE:
           throw new RuntimeException(errorMessage);
 
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java
index 936edcf3c..7a4530235 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoAdminClient.java
@@ -22,19 +22,24 @@ package org.apache.gravitino.client;
 import com.google.common.base.Preconditions;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.gravitino.MetalakeChange;
 import org.apache.gravitino.SupportsMetalakes;
 import org.apache.gravitino.dto.requests.MetalakeCreateRequest;
+import org.apache.gravitino.dto.requests.MetalakeSetRequest;
 import org.apache.gravitino.dto.requests.MetalakeUpdateRequest;
 import org.apache.gravitino.dto.requests.MetalakeUpdatesRequest;
 import org.apache.gravitino.dto.responses.DropResponse;
+import org.apache.gravitino.dto.responses.ErrorResponse;
 import org.apache.gravitino.dto.responses.MetalakeListResponse;
 import org.apache.gravitino.dto.responses.MetalakeResponse;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 
 /**
  * Apache Gravitino Client for the administrator to interact with the 
Gravitino API, allowing the
@@ -145,17 +150,35 @@ public class GravitinoAdminClient extends 
GravitinoClientBase implements Support
   }
 
   /**
-   * Drops a specific Metalake using the Gravitino API.
+   * Drop a metalake with specified name. If the force flag is true, it will:
    *
-   * @param name The name of the Metalake to be dropped.
-   * @return True if the Metalake was successfully dropped, false if the 
Metalake does not exist.
+   * <ul>
+   *   <li>Cascade drop all sub-entities (tags, catalogs, schemas, tables, 
etc.) of the metalake in
+   *       Gravitino store.
+   *   <li>Drop the metalake even if it is in use.
+   *   <li>External resources (e.g. database, table, etc.) associated with 
sub-entities will not be
+   *       deleted unless it is managed (such as managed fileset).
+   * </ul>
+   *
+   * If the force flag is false, it is equivalent to calling {@link 
#dropMetalake(String)}.
+   *
+   * @param name The name of the metalake.
+   * @param force Whether to force the drop.
+   * @return True if the metalake was dropped, false if the metalake does not 
exist.
+   * @throws NonEmptyEntityException If the metalake is not empty and force is 
false.
+   * @throws MetalakeInUseException If the metalake is in use and force is 
false.
    */
   @Override
-  public boolean dropMetalake(String name) {
+  public boolean dropMetalake(String name, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException {
     checkMetalakeName(name);
+    Map<String, String> params = new HashMap<>();
+    params.put("force", String.valueOf(force));
+
     DropResponse resp =
         restClient.delete(
             API_METALAKES_IDENTIFIER_PATH + name,
+            params,
             DropResponse.class,
             Collections.emptyMap(),
             ErrorHandlers.metalakeErrorHandler());
@@ -163,6 +186,44 @@ public class GravitinoAdminClient extends 
GravitinoClientBase implements Support
     return resp.dropped();
   }
 
+  @Override
+  public void enableMetalake(String name) throws NoSuchMetalakeException {
+    MetalakeSetRequest req = new MetalakeSetRequest(true);
+
+    ErrorResponse resp =
+        restClient.patch(
+            API_METALAKES_IDENTIFIER_PATH + name,
+            req,
+            ErrorResponse.class,
+            Collections.emptyMap(),
+            ErrorHandlers.metalakeErrorHandler());
+
+    if (resp.getCode() == 0) {
+      return;
+    }
+
+    ErrorHandlers.metalakeErrorHandler().accept(resp);
+  }
+
+  @Override
+  public void disableMetalake(String name) throws NoSuchMetalakeException {
+    MetalakeSetRequest req = new MetalakeSetRequest(false);
+
+    ErrorResponse resp =
+        restClient.patch(
+            API_METALAKES_IDENTIFIER_PATH + name,
+            req,
+            ErrorResponse.class,
+            Collections.emptyMap(),
+            ErrorHandlers.metalakeErrorHandler());
+
+    if (resp.getCode() == 0) {
+      return;
+    }
+
+    ErrorHandlers.metalakeErrorHandler().accept(resp);
+  }
+
   /**
    * Creates a new builder for constructing a GravitinoClient.
    *
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/AuditIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/AuditIT.java
index c438e0fca..fd1fac54f 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/AuditIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/AuditIT.java
@@ -60,6 +60,7 @@ public class AuditIT extends BaseIT {
     metaLake = client.alterMetalake(metalakeAuditName, changes);
     Assertions.assertEquals(expectUser, metaLake.auditInfo().creator());
     Assertions.assertEquals(expectUser, metaLake.auditInfo().lastModifier());
+    Assertions.assertDoesNotThrow(() -> client.disableMetalake(newName));
     Assertions.assertTrue(client.dropMetalake(newName), "metaLake should be 
dropped");
     Assertions.assertFalse(client.dropMetalake(newName), "metalake should be 
non-existent");
   }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/CatalogIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/CatalogIT.java
index a29ef7320..5360f9f78 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/CatalogIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/CatalogIT.java
@@ -81,7 +81,7 @@ public class CatalogIT extends BaseIT {
 
   @AfterAll
   public void tearDown() {
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
 
     if (client != null) {
       client.close();
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
index fb9efd2ca..2922154f3 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/MetalakeIT.java
@@ -18,6 +18,7 @@
  */
 package org.apache.gravitino.client.integration.test;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -182,6 +183,7 @@ public class MetalakeIT extends BaseIT {
   public void testDropMetalakes() {
     GravitinoMetalake metalakeA =
         client.createMetalake(metalakeNameA, "metalake A comment", 
Collections.emptyMap());
+    assertDoesNotThrow(() -> client.disableMetalake(metalakeA.name()));
     assertTrue(client.dropMetalake(metalakeA.name()), "metaLake should be 
dropped");
     NameIdentifier id = NameIdentifier.of(metalakeNameA);
     assertThrows(
@@ -205,12 +207,13 @@ public class MetalakeIT extends BaseIT {
         new MetalakeChange[] {MetalakeChange.updateComment("new metalake 
comment")};
     GravitinoMetalake updatedMetalake = client.alterMetalake(metalakeNameA, 
changes);
     assertEquals("new metalake comment", updatedMetalake.comment());
-    client.dropMetalake(metalakeNameA);
+    assertTrue(client.dropMetalake(metalakeNameA, true));
   }
 
   public void dropMetalakes() {
     GravitinoMetalake[] metaLakes = client.listMetalakes();
     for (GravitinoMetalake metalake : metaLakes) {
+      assertDoesNotThrow(() -> client.disableMetalake(metalake.name()));
       assertTrue(client.dropMetalake(metalake.name()));
     }
 
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/TagIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/TagIT.java
index f5b5e1dab..dc82dfd67 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/TagIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/TagIT.java
@@ -112,7 +112,7 @@ public class TagIT extends BaseIT {
     
relationalCatalog.asTableCatalog().dropTable(NameIdentifier.of(schema.name(), 
table.name()));
     relationalCatalog.asSchemas().dropSchema(schema.name(), true);
     metalake.dropCatalog(relationalCatalog.name(), true);
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
 
     if (client != null) {
       client.close();
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlNotAllowIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlNotAllowIT.java
index a6817b274..1dcdd27a3 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlNotAllowIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/AccessControlNotAllowIT.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.gravitino.integration.test.authorization;
+package org.apache.gravitino.client.integration.test.authorization;
 
 import com.google.common.collect.Lists;
 import java.util.Collections;
@@ -148,6 +148,7 @@ public class AccessControlNotAllowIT extends BaseIT {
     Assertions.assertTrue(
         e.getMessage().contains("You should set 
'gravitino.authorization.enable'"));
 
+    client.disableMetalake(metalakeTestName);
     client.dropMetalake(metalakeTestName);
   }
 }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/CheckCurrentUserIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/CheckCurrentUserIT.java
index f5615beae..a7339ba0d 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/CheckCurrentUserIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/CheckCurrentUserIT.java
@@ -98,7 +98,7 @@ public class CheckCurrentUserIT extends BaseIT {
   @AfterAll
   public void tearDown() {
     if (client != null) {
-      client.dropMetalake(metalakeName);
+      client.dropMetalake(metalakeName, true);
       client.close();
       client = null;
     }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
index 99f1e8306..daac8002a 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerIT.java
@@ -169,7 +169,7 @@ public class OwnerIT extends BaseIT {
     catalog.asFilesetCatalog().dropFileset(fileIdent);
     catalog.asSchemas().dropSchema("schema_owner", true);
     metalake.dropCatalog(catalogNameA, true);
-    client.dropMetalake(metalakeNameA);
+    client.dropMetalake(metalakeNameA, true);
   }
 
   @Test
@@ -220,7 +220,7 @@ public class OwnerIT extends BaseIT {
     // Clean up
     catalogB.asTopicCatalog().dropTopic(topicIdent);
     metalake.dropCatalog(catalogNameB, true);
-    client.dropMetalake(metalakeNameB);
+    client.dropMetalake(metalakeNameB, true);
   }
 
   @Test
@@ -255,7 +255,7 @@ public class OwnerIT extends BaseIT {
 
     // Clean up
     metalake.deleteRole("role_owner");
-    client.dropMetalake(metalakeNameC);
+    client.dropMetalake(metalakeNameC, true);
   }
 
   @Test
@@ -321,7 +321,7 @@ public class OwnerIT extends BaseIT {
     catalog.asTableCatalog().dropTable(tableIdent);
     catalog.asSchemas().dropSchema("schema_owner", true);
     metalake.dropCatalog(catalogNameD, true);
-    client.dropMetalake(metalakeNameD);
+    client.dropMetalake(metalakeNameD, true);
   }
 
   @Test
@@ -352,6 +352,6 @@ public class OwnerIT extends BaseIT {
         () -> metalake.setOwner(metalakeObject, "not-existed", 
Owner.Type.USER));
 
     // Cleanup
-    client.dropMetalake(metalakeNameE);
+    client.dropMetalake(metalakeNameE, true);
   }
 }
diff --git a/clients/client-python/gravitino/client/gravitino_admin_client.py 
b/clients/client-python/gravitino/client/gravitino_admin_client.py
index b730a765b..85d9ff2f0 100644
--- a/clients/client-python/gravitino/client/gravitino_admin_client.py
+++ b/clients/client-python/gravitino/client/gravitino_admin_client.py
@@ -22,6 +22,7 @@ from gravitino.client.gravitino_client_base import 
GravitinoClientBase
 from gravitino.client.gravitino_metalake import GravitinoMetalake
 from gravitino.dto.dto_converters import DTOConverters
 from gravitino.dto.requests.metalake_create_request import 
MetalakeCreateRequest
+from gravitino.dto.requests.metalake_set_request import MetalakeSetRequest
 from gravitino.dto.requests.metalake_updates_request import 
MetalakeUpdatesRequest
 from gravitino.dto.responses.drop_response import DropResponse
 from gravitino.dto.responses.metalake_list_response import MetalakeListResponse
@@ -112,20 +113,59 @@ class GravitinoAdminClient(GravitinoClientBase):
 
         return GravitinoMetalake(metalake, self._rest_client)
 
-    def drop_metalake(self, name: str) -> bool:
+    def drop_metalake(self, name: str, force: bool = False) -> bool:
         """Drops a specific Metalake using the Gravitino API.
 
         Args:
             name: The name of the Metalake to be dropped.
+            force: Whether to force the drop operation.
 
         Returns:
-            True if the Metalake was successfully dropped, false otherwise.
+            True if the Metalake was successfully dropped, false if the 
Metalake does not exist.
         """
 
+        params = {"force": str(force)}
         resp = self._rest_client.delete(
             self.API_METALAKES_IDENTIFIER_PATH + name,
+            params=params,
             error_handler=METALAKE_ERROR_HANDLER,
         )
         drop_response = DropResponse.from_json(resp.body, infer_missing=True)
 
         return drop_response.dropped()
+
+    def enable_metalake(self, name: str):
+        """Enable the metalake with specified name. If the metalake is already 
in use, this method does nothing.
+
+        Args:
+            name: the name of the metalake.
+
+        Raises:
+            NoSuchMetalakeException if the metalake with specified name does 
not exist.
+        """
+
+        metalake_enable_request = MetalakeSetRequest(in_use=True)
+        metalake_enable_request.validate()
+
+        url = self.API_METALAKES_IDENTIFIER_PATH + name
+        self._rest_client.patch(
+            url, json=metalake_enable_request, 
error_handler=METALAKE_ERROR_HANDLER
+        )
+
+    def disable_metalake(self, name: str):
+        """Disable the metalake with specified name. If the metalake is 
already disabled, does nothing.
+
+        Args:
+            name: the name of the metalake.
+
+        Raises:
+            NoSuchMetalakeException if the metalake with specified name does 
not exist.
+        """
+
+        metalake_disable_request = MetalakeSetRequest(in_use=False)
+        metalake_disable_request.validate()
+
+        url = self.API_METALAKES_IDENTIFIER_PATH + name
+        self._rest_client.patch(
+            url, json=metalake_disable_request, 
error_handler=METALAKE_ERROR_HANDLER
+        )
diff --git 
a/clients/client-python/gravitino/dto/requests/metalake_set_request.py 
b/clients/client-python/gravitino/dto/requests/metalake_set_request.py
new file mode 100644
index 000000000..a7663b0ed
--- /dev/null
+++ b/clients/client-python/gravitino/dto/requests/metalake_set_request.py
@@ -0,0 +1,41 @@
+# 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.
+
+from dataclasses import dataclass, field
+
+from dataclasses_json import config
+
+from gravitino.rest.rest_message import RESTRequest
+
+
+@dataclass
+class MetalakeSetRequest(RESTRequest):
+    """Represents a request to set a metalake in use status."""
+
+    _in_use: bool = field(metadata=config(field_name="inUse"))
+
+    def __init__(self, in_use: bool):
+        self._in_use = in_use
+
+    def validate(self):
+        """Validates the fields of the request.
+
+        Raises:
+            IllegalArgumentException if in_use is not set.
+        """
+        if self._in_use is None:
+            raise ValueError('"in_use" field is required and cannot be empty')
diff --git a/clients/client-python/gravitino/exceptions/base.py 
b/clients/client-python/gravitino/exceptions/base.py
index 2dff76ac4..cd71de236 100644
--- a/clients/client-python/gravitino/exceptions/base.py
+++ b/clients/client-python/gravitino/exceptions/base.py
@@ -97,6 +97,10 @@ class InUseException(GravitinoRuntimeException):
     """Base class for all exceptions thrown when an entity is in use and 
cannot be deleted."""
 
 
+class MetalakeInUseException(InUseException):
+    """An exception thrown when a metalake is in use and cannot be deleted."""
+
+
 class CatalogInUseException(InUseException):
     """An Exception thrown when a catalog is in use and cannot be deleted."""
 
@@ -105,6 +109,10 @@ class NotInUseException(GravitinoRuntimeException):
     """Base class for all exceptions thrown when an entity is not in use."""
 
 
+class MetalakeNotInUseException(NotInUseException):
+    """An exception thrown when operating on not in use metalake."""
+
+
 class CatalogNotInUseException(NotInUseException):
     """An exception thrown when operating on not in use catalog."""
 
diff --git 
a/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py 
b/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
index 86dc57570..60a7dd0ac 100644
--- 
a/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
+++ 
b/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
@@ -21,6 +21,8 @@ from gravitino.exceptions.handlers.rest_error_handler import 
RestErrorHandler
 from gravitino.exceptions.base import (
     NoSuchMetalakeException,
     MetalakeAlreadyExistsException,
+    MetalakeInUseException,
+    MetalakeNotInUseException,
 )
 
 
@@ -33,9 +35,16 @@ class MetalakeErrorHandler(RestErrorHandler):
 
         if code == ErrorConstants.NOT_FOUND_CODE:
             raise NoSuchMetalakeException(error_message)
+
         if code == ErrorConstants.ALREADY_EXISTS_CODE:
             raise MetalakeAlreadyExistsException(error_message)
 
+        if code == ErrorConstants.IN_USE_CODE:
+            raise MetalakeInUseException(error_message)
+
+        if code == ErrorConstants.NOT_IN_USE_CODE:
+            raise MetalakeNotInUseException(error_message)
+
         super().handle(error_response)
 
 
diff --git a/clients/client-python/tests/integration/auth/test_auth_common.py 
b/clients/client-python/tests/integration/auth/test_auth_common.py
index 2f1506319..ede3a4e2a 100644
--- a/clients/client-python/tests/integration/auth/test_auth_common.py
+++ b/clients/client-python/tests/integration/auth/test_auth_common.py
@@ -101,7 +101,9 @@ class TestCommonAuth:
             logger.info(
                 "Drop metalake %s[%s]",
                 self.metalake_name,
-                self.gravitino_admin_client.drop_metalake(self.metalake_name),
+                self.gravitino_admin_client.drop_metalake(
+                    self.metalake_name, force=True
+                ),
             )
         except GravitinoRuntimeException:
             logger.warning("Failed to drop metalake %s", self.metalake_name)
diff --git a/clients/client-python/tests/integration/test_catalog.py 
b/clients/client-python/tests/integration/test_catalog.py
index 58580b8c5..755b295b0 100644
--- a/clients/client-python/tests/integration/test_catalog.py
+++ b/clients/client-python/tests/integration/test_catalog.py
@@ -92,7 +92,9 @@ class TestCatalog(IntegrationTestEnv):
             logger.info(
                 "Drop metalake %s[%s]",
                 self.metalake_name,
-                self.gravitino_admin_client.drop_metalake(self.metalake_name),
+                self.gravitino_admin_client.drop_metalake(
+                    self.metalake_name, force=True
+                ),
             )
         except GravitinoRuntimeException:
             logger.warning("Failed to drop metalake %s", self.metalake_name)
diff --git a/clients/client-python/tests/integration/test_fileset_catalog.py 
b/clients/client-python/tests/integration/test_fileset_catalog.py
index 2696c170a..754735b16 100644
--- a/clients/client-python/tests/integration/test_fileset_catalog.py
+++ b/clients/client-python/tests/integration/test_fileset_catalog.py
@@ -128,7 +128,9 @@ class TestFilesetCatalog(IntegrationTestEnv):
             logger.info(
                 "Drop metalake %s[%s]",
                 self.metalake_name,
-                self.gravitino_admin_client.drop_metalake(self.metalake_name),
+                self.gravitino_admin_client.drop_metalake(
+                    self.metalake_name, force=True
+                ),
             )
         except GravitinoRuntimeException:
             logger.warning("Failed to drop metalake %s", self.metalake_name)
diff --git a/clients/client-python/tests/integration/test_metalake.py 
b/clients/client-python/tests/integration/test_metalake.py
index 794cb894d..75d3a06f2 100644
--- a/clients/client-python/tests/integration/test_metalake.py
+++ b/clients/client-python/tests/integration/test_metalake.py
@@ -118,7 +118,7 @@ class TestMetalake(IntegrationTestEnv):
         self.assertTrue(self.metalake_properties_key1 not in 
metalake.properties())
 
     def drop_metalake(self, metalake_name: str) -> bool:
-        return self.gravitino_admin_client.drop_metalake(metalake_name)
+        return self.gravitino_admin_client.drop_metalake(metalake_name, True)
 
     def test_drop_metalake(self):
         self.create_metalake(self.metalake_name)
@@ -152,7 +152,9 @@ class TestMetalake(IntegrationTestEnv):
         self.assertIsNotNone(metalake)
         self.assertEqual(metalake.name(), self.metalake_name)
         self.assertEqual(metalake.comment(), self.metalake_comment)
-        self.assertEqual(metalake.properties(), self.metalake_properties)
+        self.assertEqual(
+            metalake.properties(), {**self.metalake_properties, "in-use": 
"true"}
+        )
         self.assertEqual(metalake.audit_info().creator(), "anonymous")
 
     def test_failed_load_metalakes(self):
diff --git a/clients/client-python/tests/integration/test_schema.py 
b/clients/client-python/tests/integration/test_schema.py
index 269693dcf..c8a6b270b 100644
--- a/clients/client-python/tests/integration/test_schema.py
+++ b/clients/client-python/tests/integration/test_schema.py
@@ -128,7 +128,9 @@ class TestSchema(IntegrationTestEnv):
             logger.info(
                 "Drop metalake %s[%s]",
                 self.metalake_name,
-                self.gravitino_admin_client.drop_metalake(self.metalake_name),
+                self.gravitino_admin_client.drop_metalake(
+                    self.metalake_name, force=True
+                ),
             )
         except GravitinoRuntimeException:
             logger.warning("Failed to drop metalake %s", self.metalake_name)
diff --git 
a/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/integration/test/GravitinoVirtualFileSystemIT.java
 
b/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/integration/test/GravitinoVirtualFileSystemIT.java
index ef41637d6..064643b79 100644
--- 
a/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/integration/test/GravitinoVirtualFileSystemIT.java
+++ 
b/clients/filesystem-hadoop3/src/test/java/org/apache/gravitino/filesystem/hadoop/integration/test/GravitinoVirtualFileSystemIT.java
@@ -92,7 +92,7 @@ public class GravitinoVirtualFileSystemIT extends BaseIT {
     Catalog catalog = metalake.loadCatalog(catalogName);
     catalog.asSchemas().dropSchema(schemaName, true);
     metalake.dropCatalog(catalogName, true);
-    client.dropMetalake(metalakeName);
+    client.dropMetalake(metalakeName, true);
 
     if (client != null) {
       client.close();
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/requests/MetalakeSetRequest.java
 
b/common/src/main/java/org/apache/gravitino/dto/requests/MetalakeSetRequest.java
new file mode 100644
index 000000000..be75b2d4a
--- /dev/null
+++ 
b/common/src/main/java/org/apache/gravitino/dto/requests/MetalakeSetRequest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dto.requests;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.apache.gravitino.rest.RESTRequest;
+
+/** Represents a request to set a Metalake in use. */
+@Getter
+@EqualsAndHashCode
+@ToString
+public class MetalakeSetRequest implements RESTRequest {
+
+  private final boolean inUse;
+
+  /** Default constructor for MetalakeSetRequest. */
+  public MetalakeSetRequest() {
+    this(false);
+  }
+
+  /**
+   * Constructor for MetalakeSetRequest.
+   *
+   * @param inUse The in use status to set.
+   */
+  public MetalakeSetRequest(boolean inUse) {
+    this.inUse = inUse;
+  }
+
+  /**
+   * Validates the request. No validation needed.
+   *
+   * @throws IllegalArgumentException If the request is invalid.
+   */
+  @Override
+  public void validate() throws IllegalArgumentException {
+    // No validation needed
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java 
b/core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java
index 81447abfd..1d5a2acf0 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/AuthorizationUtils.java
@@ -19,14 +19,12 @@
 package org.apache.gravitino.authorization;
 
 import com.google.common.collect.Sets;
-import java.io.IOException;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Entity;
-import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
@@ -40,12 +38,9 @@ import org.apache.gravitino.exceptions.ForbiddenException;
 import org.apache.gravitino.exceptions.IllegalPrivilegeException;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
-import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchUserException;
 import org.apache.gravitino.utils.MetadataObjectUtil;
 import org.apache.gravitino.utils.NameIdentifierUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /* The utilization class of authorization module*/
 public class AuthorizationUtils {
@@ -53,8 +48,6 @@ public class AuthorizationUtils {
   static final String USER_DOES_NOT_EXIST_MSG = "User %s does not exist in th 
metalake %s";
   static final String GROUP_DOES_NOT_EXIST_MSG = "Group %s does not exist in 
th metalake %s";
   static final String ROLE_DOES_NOT_EXIST_MSG = "Role %s does not exist in th 
metalake %s";
-  private static final Logger LOG = 
LoggerFactory.getLogger(AuthorizationUtils.class);
-  private static final String METALAKE_DOES_NOT_EXIST_MSG = "Metalake %s does 
not exist";
 
   private static final Set<Privilege.Name> FILESET_PRIVILEGES =
       Sets.immutableEnumSet(
@@ -68,21 +61,6 @@ public class AuthorizationUtils {
 
   private AuthorizationUtils() {}
 
-  static void checkMetalakeExists(String metalake) throws 
NoSuchMetalakeException {
-    try {
-      EntityStore store = GravitinoEnv.getInstance().entityStore();
-
-      NameIdentifier metalakeIdent = NameIdentifier.of(metalake);
-      if (!store.exists(metalakeIdent, Entity.EntityType.METALAKE)) {
-        LOG.warn("Metalake {} does not exist", metalakeIdent);
-        throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, 
metalakeIdent);
-      }
-    } catch (IOException e) {
-      LOG.error("Failed to do storage operation", e);
-      throw new RuntimeException(e);
-    }
-  }
-
   public static void checkCurrentUser(String metalake, String user) {
     try {
       AccessControlDispatcher dispatcher = 
GravitinoEnv.getInstance().accessControlDispatcher();
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java 
b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
index dc675fdce..11c24102b 100644
--- a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
+++ b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
@@ -19,6 +19,8 @@
 
 package org.apache.gravitino.authorization;
 
+import static org.apache.gravitino.metalake.MetalakeManager.checkMetalake;
+
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.time.Instant;
@@ -33,7 +35,6 @@ import org.apache.gravitino.Namespace;
 import org.apache.gravitino.SupportsRelationOperations;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
-import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchRoleException;
 import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 import org.apache.gravitino.meta.AuditInfo;
@@ -52,7 +53,6 @@ import org.slf4j.LoggerFactory;
 class RoleManager {
 
   private static final Logger LOG = LoggerFactory.getLogger(RoleManager.class);
-  private static final String METALAKE_DOES_NOT_EXIST_MSG = "Metalake %s does 
not exist";
   private final EntityStore store;
   private final IdGenerator idGenerator;
 
@@ -67,7 +67,7 @@ class RoleManager {
       Map<String, String> properties,
       List<SecurableObject> securableObjects)
       throws RoleAlreadyExistsException {
-    AuthorizationUtils.checkMetalakeExists(metalake);
+    checkMetalake(NameIdentifier.of(metalake), store);
     RoleEntity roleEntity =
         RoleEntity.builder()
             .withId(idGenerator.nextId())
@@ -104,7 +104,7 @@ class RoleManager {
 
   RoleEntity getRole(String metalake, String role) throws NoSuchRoleException {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       return getRoleEntity(AuthorizationUtils.ofRole(metalake, role));
     } catch (NoSuchEntityException e) {
       LOG.warn("Role {} does not exist in the metalake {}", role, metalake, e);
@@ -114,7 +114,7 @@ class RoleManager {
 
   boolean deleteRole(String metalake, String role) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       NameIdentifier ident = AuthorizationUtils.ofRole(metalake, role);
 
       try {
@@ -138,14 +138,11 @@ class RoleManager {
 
   String[] listRoleNames(String metalake) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       Namespace namespace = AuthorizationUtils.ofRoleNamespace(metalake);
       return store.list(namespace, RoleEntity.class, 
Entity.EntityType.ROLE).stream()
           .map(Role::name)
           .toArray(String[]::new);
-    } catch (NoSuchEntityException e) {
-      LOG.warn("Metalake {} does not exist", metalake, e);
-      throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, metalake);
     } catch (IOException ioe) {
       LOG.error("Listing user under metalake {} failed due to storage issues", 
metalake, ioe);
       throw new RuntimeException(ioe);
@@ -154,7 +151,7 @@ class RoleManager {
 
   String[] listRoleNamesByObject(String metalake, MetadataObject object) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
 
       return store.relationOperations()
           .listEntitiesByRelation(
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java 
b/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
index cd852ab66..905b100a6 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
@@ -18,6 +18,8 @@
  */
 package org.apache.gravitino.authorization;
 
+import static org.apache.gravitino.metalake.MetalakeManager.checkMetalake;
+
 import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.time.Instant;
@@ -27,6 +29,7 @@ import org.apache.gravitino.Entity;
 import org.apache.gravitino.Entity.EntityType;
 import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -62,7 +65,7 @@ class UserGroupManager {
 
   User addUser(String metalake, String name) throws UserAlreadyExistsException 
{
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       UserEntity userEntity =
           UserEntity.builder()
               .withId(idGenerator.nextId())
@@ -90,7 +93,7 @@ class UserGroupManager {
 
   boolean removeUser(String metalake, String user) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       return store.delete(AuthorizationUtils.ofUser(metalake, user), 
Entity.EntityType.USER);
     } catch (IOException ioe) {
       LOG.error(
@@ -101,7 +104,7 @@ class UserGroupManager {
 
   User getUser(String metalake, String user) throws NoSuchUserException {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       return store.get(
           AuthorizationUtils.ofUser(metalake, user), Entity.EntityType.USER, 
UserEntity.class);
 
@@ -127,7 +130,7 @@ class UserGroupManager {
 
   Group addGroup(String metalake, String group) throws 
GroupAlreadyExistsException {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       GroupEntity groupEntity =
           GroupEntity.builder()
               .withId(idGenerator.nextId())
@@ -155,7 +158,7 @@ class UserGroupManager {
 
   boolean removeGroup(String metalake, String group) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       return store.delete(AuthorizationUtils.ofGroup(metalake, group), 
Entity.EntityType.GROUP);
     } catch (IOException ioe) {
       LOG.error(
@@ -169,7 +172,7 @@ class UserGroupManager {
 
   Group getGroup(String metalake, String group) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
 
       return store.get(
           AuthorizationUtils.ofGroup(metalake, group), 
Entity.EntityType.GROUP, GroupEntity.class);
@@ -194,7 +197,7 @@ class UserGroupManager {
 
   private User[] listUsersInternal(String metalake, boolean allFields) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
 
       Namespace namespace = AuthorizationUtils.ofUserNamespace(metalake);
       return store
@@ -211,7 +214,7 @@ class UserGroupManager {
 
   private Group[] listGroupInternal(String metalake, boolean allFields) {
     try {
-      AuthorizationUtils.checkMetalakeExists(metalake);
+      checkMetalake(NameIdentifier.of(metalake), store);
       Namespace namespace = AuthorizationUtils.ofGroupNamespace(metalake);
       return store
           .list(namespace, GroupEntity.class, EntityType.GROUP, allFields)
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java 
b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
index f03b500bb..6af759c61 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
@@ -23,6 +23,8 @@ import static org.apache.gravitino.StringIdentifier.DUMMY_ID;
 import static 
org.apache.gravitino.catalog.PropertiesMetadataHelpers.validatePropertyForAlter;
 import static 
org.apache.gravitino.catalog.PropertiesMetadataHelpers.validatePropertyForCreate;
 import static 
org.apache.gravitino.connector.BaseCatalogPropertiesMetadata.BASIC_CATALOG_PROPERTIES_METADATA;
+import static org.apache.gravitino.metalake.MetalakeManager.checkMetalake;
+import static org.apache.gravitino.metalake.MetalakeManager.metalakeInUse;
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
@@ -81,6 +83,7 @@ import 
org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
 import org.apache.gravitino.exceptions.CatalogInUseException;
 import org.apache.gravitino.exceptions.CatalogNotInUseException;
 import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
@@ -104,14 +107,18 @@ import org.slf4j.LoggerFactory;
 public class CatalogManager implements CatalogDispatcher, Closeable {
 
   private static final String CATALOG_DOES_NOT_EXIST_MSG = "Catalog %s does 
not exist";
-  private static final String METALAKE_DOES_NOT_EXIST_MSG = "Metalake %s does 
not exist";
 
   private static final Logger LOG = 
LoggerFactory.getLogger(CatalogManager.class);
 
-  public static boolean catalogInUse(EntityStore store, NameIdentifier ident)
-      throws NoSuchMetalakeException, NoSuchCatalogException {
-    // todo: check if the metalake is in use
-    return getInUseValue(store, ident);
+  public static void checkCatalogInUse(EntityStore store, NameIdentifier ident)
+      throws NoSuchMetalakeException, NoSuchCatalogException, 
CatalogNotInUseException,
+          MetalakeNotInUseException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    checkMetalake(metalakeIdent, store);
+
+    if (!getCatalogInUseValue(store, ident)) {
+      throw new CatalogNotInUseException("Catalog %s is not in use, please 
enable it first", ident);
+    }
   }
 
   /** Wrapper class for a catalog instance and its class loader. */
@@ -284,7 +291,7 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
   @Override
   public NameIdentifier[] listCatalogs(Namespace namespace) throws 
NoSuchMetalakeException {
     NameIdentifier metalakeIdent = NameIdentifier.of(namespace.levels());
-    checkMetalakeExists(metalakeIdent);
+    checkMetalake(NameIdentifier.of(namespace.level(0)), store);
 
     try {
       return store.list(namespace, CatalogEntity.class, 
EntityType.CATALOG).stream()
@@ -300,7 +307,7 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
   @Override
   public Catalog[] listCatalogsInfo(Namespace namespace) throws 
NoSuchMetalakeException {
     NameIdentifier metalakeIdent = NameIdentifier.of(namespace.levels());
-    checkMetalakeExists(metalakeIdent);
+    checkMetalake(metalakeIdent, store);
 
     try {
       List<CatalogEntity> catalogEntities =
@@ -325,6 +332,9 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
    */
   @Override
   public Catalog loadCatalog(NameIdentifier ident) throws 
NoSuchCatalogException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    checkMetalake(metalakeIdent, store);
+
     return loadCatalogAndWrap(ident).catalog;
   }
 
@@ -348,6 +358,9 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
       String comment,
       Map<String, String> properties)
       throws NoSuchMetalakeException, CatalogAlreadyExistsException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    checkMetalake(metalakeIdent, store);
+
     Map<String, String> mergedConfig = buildCatalogConf(provider, properties);
 
     long uid = idGenerator.nextId();
@@ -374,12 +387,6 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
 
     boolean needClean = true;
     try {
-      NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
-      if (!store.exists(metalakeIdent, EntityType.METALAKE)) {
-        LOG.warn("Metalake {} does not exist", metalakeIdent);
-        throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, 
metalakeIdent);
-      }
-
       store.put(e, false /* overwrite */);
       CatalogWrapper wrapper = catalogCache.get(ident, id -> 
createCatalogWrapper(e, mergedConfig));
 
@@ -433,11 +440,9 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
       String comment,
       Map<String, String> properties) {
     NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
-    try {
-      if (!store.exists(metalakeIdent, EntityType.METALAKE)) {
-        throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, 
metalakeIdent);
-      }
+    checkMetalake(metalakeIdent, store);
 
+    try {
       if (store.exists(ident, EntityType.CATALOG)) {
         throw new CatalogAlreadyExistsException("Catalog %s already exists", 
ident);
       }
@@ -483,6 +488,9 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
   @Override
   public void enableCatalog(NameIdentifier ident)
       throws NoSuchCatalogException, CatalogNotInUseException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    checkMetalake(metalakeIdent, store);
+
     try {
       if (catalogInUse(store, ident)) {
         return;
@@ -511,6 +519,9 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
 
   @Override
   public void disableCatalog(NameIdentifier ident) throws 
NoSuchCatalogException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    checkMetalake(metalakeIdent, store);
+
     try {
       if (!catalogInUse(store, ident)) {
         return;
@@ -550,9 +561,7 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
   @Override
   public Catalog alterCatalog(NameIdentifier ident, CatalogChange... changes)
       throws NoSuchCatalogException, IllegalArgumentException {
-    if (!catalogInUse(store, ident)) {
-      throw new CatalogNotInUseException("Catalog %s is not in use, please 
enable it first", ident);
-    }
+    checkCatalogInUse(store, ident);
 
     // There could be a race issue that someone is using the catalog from 
cache while we are
     // updating it.
@@ -665,7 +674,13 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
     return catalogCache.get(ident, this::loadCatalogInternal);
   }
 
-  private static boolean getInUseValue(EntityStore store, NameIdentifier 
catalogIdent) {
+  private static boolean catalogInUse(EntityStore store, NameIdentifier ident)
+      throws NoSuchMetalakeException, NoSuchCatalogException {
+    NameIdentifier metalakeIdent = 
NameIdentifier.of(ident.namespace().levels());
+    return metalakeInUse(store, metalakeIdent) && getCatalogInUseValue(store, 
ident);
+  }
+
+  private static boolean getCatalogInUseValue(EntityStore store, 
NameIdentifier catalogIdent) {
     try {
       CatalogEntity catalogEntity =
           store.get(catalogIdent, EntityType.CATALOG, CatalogEntity.class);
@@ -730,17 +745,6 @@ public class CatalogManager implements CatalogDispatcher, 
Closeable {
     return Pair.of(upserts, deletes);
   }
 
-  private void checkMetalakeExists(NameIdentifier ident) throws 
NoSuchMetalakeException {
-    try {
-      if (!store.exists(ident, EntityType.METALAKE)) {
-        throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, ident);
-      }
-    } catch (IOException e) {
-      LOG.error("Failed to do storage operation", e);
-      throw new RuntimeException(e);
-    }
-  }
-
   private CatalogWrapper loadCatalogInternal(NameIdentifier ident) throws 
NoSuchCatalogException {
     try {
       CatalogEntity entity = store.get(ident, EntityType.CATALOG, 
CatalogEntity.class);
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/OperationDispatcher.java 
b/core/src/main/java/org/apache/gravitino/catalog/OperationDispatcher.java
index 88add6248..3e2ed6c1b 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/OperationDispatcher.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/OperationDispatcher.java
@@ -18,7 +18,7 @@
  */
 package org.apache.gravitino.catalog;
 
-import static org.apache.gravitino.catalog.CatalogManager.catalogInUse;
+import static org.apache.gravitino.catalog.CatalogManager.checkCatalogInUse;
 import static 
org.apache.gravitino.catalog.PropertiesMetadataHelpers.validatePropertyForAlter;
 import static 
org.apache.gravitino.utils.NameIdentifierUtil.getCatalogIdentifier;
 
@@ -35,7 +35,6 @@ import org.apache.gravitino.StringIdentifier;
 import org.apache.gravitino.connector.HasPropertyMetadata;
 import org.apache.gravitino.connector.PropertiesMetadata;
 import org.apache.gravitino.connector.capability.Capability;
-import org.apache.gravitino.exceptions.CatalogNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.file.FilesetChange;
 import org.apache.gravitino.messaging.TopicChange;
@@ -94,9 +93,7 @@ public abstract class OperationDispatcher {
   protected <R, E extends Throwable> R doWithCatalog(
       NameIdentifier ident, ThrowableFunction<CatalogManager.CatalogWrapper, 
R> fn, Class<E> ex)
       throws E {
-    if (!catalogInUse(store, ident)) {
-      throw new CatalogNotInUseException("Catalog %s is not in use, please 
enable it first", ident);
-    }
+    checkCatalogInUse(store, ident);
 
     try {
       CatalogManager.CatalogWrapper c = 
catalogManager.loadCatalogAndWrap(ident);
@@ -118,9 +115,7 @@ public abstract class OperationDispatcher {
       Class<E1> ex1,
       Class<E2> ex2)
       throws E1, E2 {
-    if (!catalogInUse(store, ident)) {
-      throw new CatalogNotInUseException("Catalog %s is not in use, please 
enable it first", ident);
-    }
+    checkCatalogInUse(store, ident);
 
     try {
       CatalogManager.CatalogWrapper c = 
catalogManager.loadCatalogAndWrap(ident);
diff --git 
a/core/src/main/java/org/apache/gravitino/connector/PropertiesMetadata.java 
b/core/src/main/java/org/apache/gravitino/connector/PropertiesMetadata.java
index 830a94b4f..d4778b2ff 100644
--- a/core/src/main/java/org/apache/gravitino/connector/PropertiesMetadata.java
+++ b/core/src/main/java/org/apache/gravitino/connector/PropertiesMetadata.java
@@ -89,7 +89,7 @@ public interface PropertiesMetadata {
       throw new IllegalArgumentException("Property is not defined: " + 
propertyName);
     }
 
-    if (properties.containsKey(propertyName)) {
+    if (properties != null && properties.containsKey(propertyName)) {
       return 
propertyEntries().get(propertyName).decode(properties.get(propertyName));
     }
     return propertyEntries().get(propertyName).getDefaultValue();
diff --git 
a/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java 
b/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java
index 3c242bd56..95554857a 100644
--- a/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java
+++ b/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java
@@ -28,7 +28,9 @@ import 
org.apache.gravitino.authorization.AccessControlDispatcher;
 import org.apache.gravitino.authorization.Owner;
 import org.apache.gravitino.authorization.OwnerManager;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 import org.apache.gravitino.metalake.MetalakeDispatcher;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.PrincipalUtils;
@@ -87,8 +89,19 @@ public class MetalakeHookDispatcher implements 
MetalakeDispatcher {
   }
 
   @Override
-  public boolean dropMetalake(NameIdentifier ident) {
-    return dispatcher.dropMetalake(ident);
+  public boolean dropMetalake(NameIdentifier ident, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException {
+    return dispatcher.dropMetalake(ident, force);
+  }
+
+  @Override
+  public void enableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    dispatcher.enableMetalake(ident);
+  }
+
+  @Override
+  public void disableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    dispatcher.disableMetalake(ident);
   }
 
   @Override
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/CatalogEventDispatcher.java 
b/core/src/main/java/org/apache/gravitino/listener/CatalogEventDispatcher.java
index 04a2600d8..ff70d4cff 100644
--- 
a/core/src/main/java/org/apache/gravitino/listener/CatalogEventDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/listener/CatalogEventDispatcher.java
@@ -177,7 +177,7 @@ public class CatalogEventDispatcher implements 
CatalogDispatcher {
   @Override
   public void enableCatalog(NameIdentifier ident)
       throws NoSuchCatalogException, CatalogNotInUseException {
-    // todo: support activate catalog event
+    // todo: support enable catalog event
     dispatcher.enableCatalog(ident);
   }
 
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/MetalakeEventDispatcher.java 
b/core/src/main/java/org/apache/gravitino/listener/MetalakeEventDispatcher.java
index 33005893f..535d337b3 100644
--- 
a/core/src/main/java/org/apache/gravitino/listener/MetalakeEventDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/listener/MetalakeEventDispatcher.java
@@ -24,7 +24,9 @@ import org.apache.gravitino.Metalake;
 import org.apache.gravitino.MetalakeChange;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 import org.apache.gravitino.listener.api.event.AlterMetalakeEvent;
 import org.apache.gravitino.listener.api.event.AlterMetalakeFailureEvent;
 import org.apache.gravitino.listener.api.event.CreateMetalakeEvent;
@@ -129,9 +131,10 @@ public class MetalakeEventDispatcher implements 
MetalakeDispatcher {
   }
 
   @Override
-  public boolean dropMetalake(NameIdentifier ident) {
+  public boolean dropMetalake(NameIdentifier ident, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException {
     try {
-      boolean isExists = dispatcher.dropMetalake(ident);
+      boolean isExists = dispatcher.dropMetalake(ident, force);
       eventBus.dispatchEvent(
           new DropMetalakeEvent(PrincipalUtils.getCurrentUserName(), ident, 
isExists));
       return isExists;
@@ -141,4 +144,16 @@ public class MetalakeEventDispatcher implements 
MetalakeDispatcher {
       throw e;
     }
   }
+
+  @Override
+  public void enableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    // todo: support enable metalake event
+    dispatcher.enableMetalake(ident);
+  }
+
+  @Override
+  public void disableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    // todo: support disable metalake event
+    dispatcher.disableMetalake(ident);
+  }
 }
diff --git a/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java 
b/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
index bab042d37..4e2ca045f 100644
--- a/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
+++ b/core/src/main/java/org/apache/gravitino/meta/BaseMetalake.java
@@ -25,13 +25,13 @@ import javax.annotation.Nullable;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.ToString;
-import org.apache.gravitino.Audit;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Metalake;
-import org.apache.gravitino.StringIdentifier;
+import org.apache.gravitino.connector.PropertiesMetadata;
+import org.apache.gravitino.metalake.MetalakePropertiesMetadata;
 
 /** Base implementation of a Metalake entity. */
 @EqualsAndHashCode
@@ -52,6 +52,8 @@ public class BaseMetalake implements Metalake, Entity, 
Auditable, HasIdentifier
   public static final Field SCHEMA_VERSION =
       Field.required("version", SchemaVersion.class, "The version of the 
schema for the metalake");
 
+  public static final PropertiesMetadata PROPERTIES_METADATA = new 
MetalakePropertiesMetadata();
+
   private Long id;
 
   private String name;
@@ -87,10 +89,10 @@ public class BaseMetalake implements Metalake, Entity, 
Auditable, HasIdentifier
   /**
    * The audit information of the metalake.
    *
-   * @return The audit information as an {@link Audit} instance.
+   * @return The audit information as an {@link AuditInfo} instance.
    */
   @Override
-  public Audit auditInfo() {
+  public AuditInfo auditInfo() {
     return auditInfo;
   }
 
@@ -141,7 +143,11 @@ public class BaseMetalake implements Metalake, Entity, 
Auditable, HasIdentifier
    */
   @Override
   public Map<String, String> properties() {
-    return StringIdentifier.newPropertiesWithoutId(properties);
+    return properties;
+  }
+
+  public PropertiesMetadata propertiesMetadata() {
+    return PROPERTIES_METADATA;
   }
 
   /** Builder class for creating instances of {@link BaseMetalake}. */
diff --git 
a/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
index 54298c0ef..33498665a 100644
--- a/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
+++ b/core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java
@@ -18,9 +18,12 @@
  */
 package org.apache.gravitino.metalake;
 
+import static org.apache.gravitino.Metalake.PROPERTY_IN_USE;
+
 import com.google.common.collect.Maps;
 import java.io.IOException;
 import java.time.Instant;
+import java.util.List;
 import java.util.Map;
 import org.apache.gravitino.Entity.EntityType;
 import org.apache.gravitino.EntityAlreadyExistsException;
@@ -28,13 +31,16 @@ import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.MetalakeChange;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
-import org.apache.gravitino.StringIdentifier;
 import org.apache.gravitino.exceptions.AlreadyExistsException;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.meta.BaseMetalake;
+import org.apache.gravitino.meta.CatalogEntity;
 import org.apache.gravitino.meta.SchemaVersion;
 import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.utils.PrincipalUtils;
@@ -63,6 +69,48 @@ public class MetalakeManager implements MetalakeDispatcher {
     this.idGenerator = idGenerator;
   }
 
+  /**
+   * Check whether the metalake is available
+   *
+   * @param ident The identifier of the Metalake to check.
+   * @param store The EntityStore to use for managing Metalakes.
+   * @throws NoSuchMetalakeException If the Metalake with the given identifier 
does not exist.
+   * @throws MetalakeNotInUseException If the Metalake is not in use.
+   */
+  public static void checkMetalake(NameIdentifier ident, EntityStore store)
+      throws NoSuchMetalakeException, MetalakeNotInUseException {
+    boolean metalakeInUse = metalakeInUse(store, ident);
+    if (!metalakeInUse) {
+      throw new MetalakeNotInUseException(
+          "Metalake %s is not in use, please enable it first", ident);
+    }
+  }
+
+  /**
+   * Return true if the metalake is in used, false otherwise.
+   *
+   * @param store The EntityStore to use for managing Metalakes.
+   * @param ident The identifier of the Metalake to check.
+   * @return True if the metalake is in use, false otherwise.
+   * @throws NoSuchMetalakeException If the Metalake with the given identifier 
does not exist.
+   */
+  public static boolean metalakeInUse(EntityStore store, NameIdentifier ident)
+      throws NoSuchMetalakeException {
+    try {
+      BaseMetalake metalake = store.get(ident, EntityType.METALAKE, 
BaseMetalake.class);
+      return (boolean)
+          metalake.propertiesMetadata().getOrDefault(metalake.properties(), 
PROPERTY_IN_USE);
+
+    } catch (NoSuchEntityException e) {
+      LOG.warn("Metalake {} does not exist", ident, e);
+      throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, ident);
+
+    } catch (IOException e) {
+      LOG.error("Failed to do store operation", e);
+      throw new RuntimeException(e);
+    }
+  }
+
   /**
    * Lists all available Metalakes.
    *
@@ -72,8 +120,9 @@ public class MetalakeManager implements MetalakeDispatcher {
   @Override
   public BaseMetalake[] listMetalakes() {
     try {
-      return store.list(Namespace.empty(), BaseMetalake.class, 
EntityType.METALAKE).stream()
-          .toArray(BaseMetalake[]::new);
+      return store
+          .list(Namespace.empty(), BaseMetalake.class, EntityType.METALAKE)
+          .toArray(new BaseMetalake[0]);
     } catch (IOException ioe) {
       LOG.error("Listing Metalakes failed due to storage issues.", ioe);
       throw new RuntimeException(ioe);
@@ -116,14 +165,13 @@ public class MetalakeManager implements 
MetalakeDispatcher {
       NameIdentifier ident, String comment, Map<String, String> properties)
       throws MetalakeAlreadyExistsException {
     long uid = idGenerator.nextId();
-    StringIdentifier stringId = StringIdentifier.fromId(uid);
 
     BaseMetalake metalake =
         BaseMetalake.builder()
             .withId(uid)
             .withName(ident.name())
             .withComment(comment)
-            .withProperties(StringIdentifier.newPropertiesWithId(stringId, 
properties))
+            .withProperties(properties)
             .withVersion(SchemaVersion.V_0_1)
             .withAuditInfo(
                 AuditInfo.builder()
@@ -158,28 +206,17 @@ public class MetalakeManager implements 
MetalakeDispatcher {
   public BaseMetalake alterMetalake(NameIdentifier ident, MetalakeChange... 
changes)
       throws NoSuchMetalakeException, IllegalArgumentException {
     try {
+      if (!metalakeInUse(store, ident)) {
+        throw new MetalakeNotInUseException(
+            "Metalake %s is not in use, please enable it first", ident);
+      }
+
       return store.update(
           ident,
           BaseMetalake.class,
           EntityType.METALAKE,
           metalake -> {
-            BaseMetalake.Builder builder =
-                BaseMetalake.builder()
-                    .withId(metalake.id())
-                    .withName(metalake.name())
-                    .withComment(metalake.comment())
-                    .withProperties(metalake.properties())
-                    .withVersion(metalake.getVersion());
-
-            AuditInfo newInfo =
-                AuditInfo.builder()
-                    .withCreator(metalake.auditInfo().creator())
-                    .withCreateTime(metalake.auditInfo().createTime())
-                    .withLastModifier(
-                        metalake.auditInfo().creator()) /*TODO: Use real user 
later on.  */
-                    .withLastModifiedTime(Instant.now())
-                    .build();
-            builder.withAuditInfo(newInfo);
+            BaseMetalake.Builder builder = newMetalakeBuilder(metalake);
 
             Map<String, String> newProps =
                 metalake.properties() == null
@@ -204,23 +241,106 @@ public class MetalakeManager implements 
MetalakeDispatcher {
     }
   }
 
-  /**
-   * Deletes a Metalake.
-   *
-   * @param ident The identifier of the Metalake to be deleted.
-   * @return `true` if the Metalake was successfully deleted, `false` 
otherwise.
-   * @throws RuntimeException If deleting the Metalake encounters storage 
issues.
-   */
   @Override
-  public boolean dropMetalake(NameIdentifier ident) {
+  public boolean dropMetalake(NameIdentifier ident, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException {
     try {
-      return store.delete(ident, EntityType.METALAKE);
-    } catch (IOException ioe) {
-      LOG.error("Deleting metalake {} failed due to storage issues", ident, 
ioe);
-      throw new RuntimeException(ioe);
+      boolean inUse = metalakeInUse(store, ident);
+      if (inUse && !force) {
+        throw new MetalakeInUseException(
+            "Metalake %s is in use, please disable it first or use force 
option", ident);
+      }
+
+      List<CatalogEntity> catalogEntities =
+          store.list(Namespace.of(ident.name()), CatalogEntity.class, 
EntityType.CATALOG);
+      if (!catalogEntities.isEmpty() && !force) {
+        throw new NonEmptyEntityException(
+            "Metalake %s has catalogs, please drop them first or use force 
option", ident);
+      }
+
+      return store.delete(ident, EntityType.METALAKE, true);
+    } catch (NoSuchMetalakeException e) {
+      return false;
+
+    } catch (IOException e) {
+      throw new RuntimeException(e);
     }
   }
 
+  @Override
+  public void enableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    try {
+
+      boolean inUse = metalakeInUse(store, ident);
+      if (!inUse) {
+        store.update(
+            ident,
+            BaseMetalake.class,
+            EntityType.METALAKE,
+            metalake -> {
+              BaseMetalake.Builder builder = newMetalakeBuilder(metalake);
+
+              Map<String, String> newProps =
+                  metalake.properties() == null
+                      ? Maps.newHashMap()
+                      : Maps.newHashMap(metalake.properties());
+              newProps.put(PROPERTY_IN_USE, "true");
+              builder.withProperties(newProps);
+
+              return builder.build();
+            });
+      }
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public void disableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    try {
+      boolean inUse = metalakeInUse(store, ident);
+      if (inUse) {
+        store.update(
+            ident,
+            BaseMetalake.class,
+            EntityType.METALAKE,
+            metalake -> {
+              BaseMetalake.Builder builder = newMetalakeBuilder(metalake);
+
+              Map<String, String> newProps =
+                  metalake.properties() == null
+                      ? Maps.newHashMap()
+                      : Maps.newHashMap(metalake.properties());
+              newProps.put(PROPERTY_IN_USE, "false");
+              builder.withProperties(newProps);
+
+              return builder.build();
+            });
+      }
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private BaseMetalake.Builder newMetalakeBuilder(BaseMetalake metalake) {
+    BaseMetalake.Builder builder =
+        BaseMetalake.builder()
+            .withId(metalake.id())
+            .withName(metalake.name())
+            .withComment(metalake.comment())
+            .withProperties(metalake.properties())
+            .withVersion(metalake.getVersion());
+
+    AuditInfo newInfo =
+        AuditInfo.builder()
+            .withCreator(metalake.auditInfo().creator())
+            .withCreateTime(metalake.auditInfo().createTime())
+            .withLastModifier(metalake.auditInfo().creator()) /*TODO: Use real 
user later on.  */
+            .withLastModifiedTime(Instant.now())
+            .build();
+    return builder.withAuditInfo(newInfo);
+  }
+
   /**
    * Updates an entity with the provided changes.
    *
diff --git 
a/core/src/main/java/org/apache/gravitino/metalake/MetalakeNormalizeDispatcher.java
 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakeNormalizeDispatcher.java
index b0c8cbf17..dbc9d6bdc 100644
--- 
a/core/src/main/java/org/apache/gravitino/metalake/MetalakeNormalizeDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakeNormalizeDispatcher.java
@@ -19,16 +19,25 @@
 package org.apache.gravitino.metalake;
 
 import static org.apache.gravitino.Entity.SYSTEM_METALAKE_RESERVED_NAME;
+import static org.apache.gravitino.Metalake.PROPERTY_IN_USE;
+import static 
org.apache.gravitino.catalog.PropertiesMetadataHelpers.validatePropertyForAlter;
+import static 
org.apache.gravitino.catalog.PropertiesMetadataHelpers.validatePropertyForCreate;
+import static org.apache.gravitino.meta.BaseMetalake.PROPERTIES_METADATA;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.gravitino.Metalake;
 import org.apache.gravitino.MetalakeChange;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
+import org.apache.gravitino.meta.BaseMetalake;
 
 public class MetalakeNormalizeDispatcher implements MetalakeDispatcher {
   private static final Set<String> RESERVED_WORDS = 
ImmutableSet.of(SYSTEM_METALAKE_RESERVED_NAME);
@@ -57,7 +66,7 @@ public class MetalakeNormalizeDispatcher implements 
MetalakeDispatcher {
 
   @Override
   public Metalake loadMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
-    return dispatcher.loadMetalake(ident);
+    return newMetalakeWithResolvedProperties((BaseMetalake) 
dispatcher.loadMetalake(ident));
   }
 
   @Override
@@ -70,6 +79,7 @@ public class MetalakeNormalizeDispatcher implements 
MetalakeDispatcher {
       NameIdentifier ident, String comment, Map<String, String> properties)
       throws MetalakeAlreadyExistsException {
     validateMetalakeName(ident.name());
+    validatePropertyForCreate(PROPERTIES_METADATA, properties);
     return dispatcher.createMetalake(ident, comment, properties);
   }
 
@@ -83,6 +93,10 @@ public class MetalakeNormalizeDispatcher implements 
MetalakeDispatcher {
                 validateMetalakeName(((MetalakeChange.RenameMetalake) 
change).getNewName());
               }
             });
+    Pair<Map<String, String>, Map<String, String>> alterProperty =
+        getMetalakeAlterProperty(changes);
+    validatePropertyForAlter(
+        PROPERTIES_METADATA, alterProperty.getLeft(), 
alterProperty.getRight());
     return dispatcher.alterMetalake(ident, changes);
   }
 
@@ -93,6 +107,22 @@ public class MetalakeNormalizeDispatcher implements 
MetalakeDispatcher {
     return dispatcher.dropMetalake(ident);
   }
 
+  @Override
+  public boolean dropMetalake(NameIdentifier ident, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException {
+    return dispatcher.dropMetalake(ident, force);
+  }
+
+  @Override
+  public void enableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    dispatcher.enableMetalake(ident);
+  }
+
+  @Override
+  public void disableMetalake(NameIdentifier ident) throws 
NoSuchMetalakeException {
+    dispatcher.disableMetalake(ident);
+  }
+
   private void validateMetalakeName(String name) {
     if (RESERVED_WORDS.contains(name)) {
       throw new IllegalArgumentException("The metalake name '" + name + "' is 
reserved.");
@@ -101,4 +131,45 @@ public class MetalakeNormalizeDispatcher implements 
MetalakeDispatcher {
       throw new IllegalArgumentException("The metalake name '" + name + "' is 
illegal.");
     }
   }
+
+  private BaseMetalake newMetalakeWithResolvedProperties(BaseMetalake 
metalakeEntity) {
+    Map<String, String> newProps = 
Maps.newHashMap(metalakeEntity.properties());
+    newProps
+        .entrySet()
+        .removeIf(e -> 
metalakeEntity.propertiesMetadata().isHiddenProperty(e.getKey()));
+    newProps.putIfAbsent(
+        PROPERTY_IN_USE,
+        
metalakeEntity.propertiesMetadata().getDefaultValue(PROPERTY_IN_USE).toString());
+
+    return BaseMetalake.builder()
+        .withId(metalakeEntity.id())
+        .withName(metalakeEntity.name())
+        .withComment(metalakeEntity.comment())
+        .withProperties(newProps)
+        .withVersion(metalakeEntity.getVersion())
+        .withAuditInfo(metalakeEntity.auditInfo())
+        .build();
+  }
+
+  private Pair<Map<String, String>, Map<String, String>> 
getMetalakeAlterProperty(
+      MetalakeChange... metalakeChanges) {
+    Map<String, String> upserts = Maps.newHashMap();
+    Map<String, String> deletes = Maps.newHashMap();
+
+    Arrays.stream(metalakeChanges)
+        .forEach(
+            metalakeChange -> {
+              if (metalakeChange instanceof MetalakeChange.SetProperty) {
+                MetalakeChange.SetProperty setProperty =
+                    (MetalakeChange.SetProperty) metalakeChange;
+                upserts.put(setProperty.getProperty(), setProperty.getValue());
+              } else if (metalakeChange instanceof 
MetalakeChange.RemoveProperty) {
+                MetalakeChange.RemoveProperty removeProperty =
+                    (MetalakeChange.RemoveProperty) metalakeChange;
+                deletes.put(removeProperty.getProperty(), 
removeProperty.getProperty());
+              }
+            });
+
+    return Pair.of(upserts, deletes);
+  }
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/metalake/MetalakePropertiesMetadata.java
 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakePropertiesMetadata.java
new file mode 100644
index 000000000..7a39b0b63
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/metalake/MetalakePropertiesMetadata.java
@@ -0,0 +1,50 @@
+/*
+ * 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.metalake;
+
+import static org.apache.gravitino.Metalake.PROPERTY_IN_USE;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.connector.PropertiesMetadata;
+import org.apache.gravitino.connector.PropertyEntry;
+
+public class MetalakePropertiesMetadata implements PropertiesMetadata {
+
+  private static final Map<String, PropertyEntry<?>> PROPERTY_ENTRIES;
+
+  static {
+    List<PropertyEntry<?>> propertyEntries =
+        ImmutableList.of(
+            PropertyEntry.booleanReservedPropertyEntry(
+                PROPERTY_IN_USE,
+                "The property indicating the catalog is in use",
+                true /* default value */,
+                false /* hidden */));
+
+    PROPERTY_ENTRIES = Maps.uniqueIndex(propertyEntries, 
PropertyEntry::getName);
+  }
+
+  @Override
+  public Map<String, PropertyEntry<?>> propertyEntries() {
+    return PROPERTY_ENTRIES;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/metalake/SupportsMetalakes.java 
b/core/src/main/java/org/apache/gravitino/metalake/SupportsMetalakes.java
index bd00c7ff5..481bfe844 100644
--- a/core/src/main/java/org/apache/gravitino/metalake/SupportsMetalakes.java
+++ b/core/src/main/java/org/apache/gravitino/metalake/SupportsMetalakes.java
@@ -24,7 +24,10 @@ import org.apache.gravitino.MetalakeChange;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NonEmptyEntityException;
 
 /**
  * Interface for supporting metalakes. It includes methods for listing, 
loading, creating, altering
@@ -89,10 +92,68 @@ public interface SupportsMetalakes {
       throws NoSuchMetalakeException, IllegalArgumentException;
 
   /**
-   * Drop a metalake with specified identifier.
+   * Drop a metalake with specified identifier. Please make sure:
+   *
+   * <ul>
+   *   <li>There is no catalog in the metalake. Otherwise, a {@link 
NonEmptyEntityException} will be
+   *       thrown.
+   *   <li>The method {@link #disableMetalake(NameIdentifier)} has been called 
before dropping the
+   *       metalake. Otherwise, a {@link MetalakeInUseException} will be 
thrown.
+   * </ul>
+   *
+   * It is equivalent to calling {@code dropMetalake(ident, false)}.
+   *
+   * @param ident The identifier of the metalake.
+   * @return True if the metalake was dropped, false if the metalake does not 
exist.
+   * @throws NonEmptyEntityException If the metalake is not empty.
+   * @throws MetalakeInUseException If the metalake is in use.
+   */
+  default boolean dropMetalake(NameIdentifier ident)
+      throws NonEmptyEntityException, MetalakeInUseException {
+    return dropMetalake(ident, false);
+  }
+
+  /**
+   * Drop a metalake with specified identifier. If the force flag is true, it 
will:
+   *
+   * <ul>
+   *   <li>Cascade drop all sub-entities (tags, catalogs, schemas, tables, 
etc.) of the metalake in
+   *       Gravitino store.
+   *   <li>Drop the metalake even if it is in use.
+   *   <li>External resources (e.g. database, table, etc.) associated with 
sub-entities will not be
+   *       deleted unless it is managed (such as managed fileset).
+   * </ul>
    *
    * @param ident The identifier of the metalake.
+   * @param force Whether to force the drop.
    * @return True if the metalake was dropped, false if the metalake does not 
exist.
+   * @throws NonEmptyEntityException If the metalake is not empty and force is 
false.
+   * @throws MetalakeInUseException If the metalake is in use and force is 
false.
+   */
+  boolean dropMetalake(NameIdentifier ident, boolean force)
+      throws NonEmptyEntityException, MetalakeInUseException;
+
+  /**
+   * Enable a metalake. If the metalake is already in use, this method does 
nothing.
+   *
+   * @param ident The identifier of the metalake.
+   * @throws NoSuchMetalakeException If the metalake does not exist.
+   */
+  void enableMetalake(NameIdentifier ident) throws NoSuchMetalakeException;
+
+  /**
+   * Disable a metalake. If the metalake is already disabled, this method does 
nothing. Once a
+   * metalake is disable:
+   *
+   * <ul>
+   *   <li>It can only be listed, loaded, dropped, or enable.
+   *   <li>Any other operations on the metalake will throw an {@link 
MetalakeNotInUseException}.
+   *   <li>Any operation on the sub-entities (catalogs, schemas, tables, etc.) 
will throw an {@link
+   *       MetalakeNotInUseException}.
+   * </ul>
+   *
+   * @param ident The identifier of the metalake.
+   * @throws NoSuchMetalakeException If the metalake does not exist.
    */
-  boolean dropMetalake(NameIdentifier ident);
+  void disableMetalake(NameIdentifier ident) throws NoSuchMetalakeException;
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/proto/BaseMetalakeSerDe.java 
b/core/src/main/java/org/apache/gravitino/proto/BaseMetalakeSerDe.java
index d8536fffb..e18b280e6 100644
--- a/core/src/main/java/org/apache/gravitino/proto/BaseMetalakeSerDe.java
+++ b/core/src/main/java/org/apache/gravitino/proto/BaseMetalakeSerDe.java
@@ -20,7 +20,6 @@
 package org.apache.gravitino.proto;
 
 import org.apache.gravitino.Namespace;
-import org.apache.gravitino.meta.AuditInfo;
 
 /** A class for serializing and deserializing BaseMetalake objects. */
 class BaseMetalakeSerDe implements 
ProtoSerDe<org.apache.gravitino.meta.BaseMetalake, Metalake> {
@@ -38,7 +37,7 @@ class BaseMetalakeSerDe implements 
ProtoSerDe<org.apache.gravitino.meta.BaseMeta
         Metalake.newBuilder()
             .setId(baseMetalake.id())
             .setName(baseMetalake.name())
-            .setAuditInfo(new AuditInfoSerDe().serialize((AuditInfo) 
baseMetalake.auditInfo()));
+            .setAuditInfo(new 
AuditInfoSerDe().serialize(baseMetalake.auditInfo()));
 
     if (baseMetalake.comment() != null) {
       builder.setComment(baseMetalake.comment());
diff --git a/core/src/main/java/org/apache/gravitino/tag/TagManager.java 
b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
index aaffd35b5..1b1626de0 100644
--- a/core/src/main/java/org/apache/gravitino/tag/TagManager.java
+++ b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
@@ -18,6 +18,8 @@
  */
 package org.apache.gravitino.tag;
 
+import static org.apache.gravitino.metalake.MetalakeManager.checkMetalake;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -35,7 +37,6 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
-import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchTagException;
 import org.apache.gravitino.exceptions.NotFoundException;
 import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
@@ -93,7 +94,7 @@ public class TagManager {
         NameIdentifier.of(ofTagNamespace(metalake).levels()),
         LockType.READ,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           try {
             return entityStore
@@ -114,7 +115,7 @@ public class TagManager {
         NameIdentifier.of(ofTagNamespace(metalake).levels()),
         LockType.WRITE,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           TagEntity tagEntity =
               TagEntity.builder()
@@ -148,7 +149,7 @@ public class TagManager {
         ofTagIdent(metalake, name),
         LockType.READ,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           try {
             return entityStore.get(
@@ -169,7 +170,7 @@ public class TagManager {
         NameIdentifier.of(ofTagNamespace(metalake).levels()),
         LockType.WRITE,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           try {
             return entityStore.update(
@@ -195,7 +196,7 @@ public class TagManager {
         NameIdentifier.of(ofTagNamespace(metalake).levels()),
         LockType.WRITE,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           try {
             return entityStore.delete(ofTagIdent(metalake, name), 
Entity.EntityType.TAG);
@@ -213,7 +214,7 @@ public class TagManager {
         tagId,
         LockType.READ,
         () -> {
-          checkMetalakeExists(metalake, entityStore);
+          checkMetalake(NameIdentifier.of(metalake), entityStore);
 
           try {
             if (!entityStore.exists(tagId, Entity.EntityType.TAG)) {
@@ -356,19 +357,6 @@ public class TagManager {
                 }));
   }
 
-  private static void checkMetalakeExists(String metalake, EntityStore 
entityStore) {
-    try {
-      NameIdentifier metalakeIdent = NameIdentifier.of(metalake);
-      if (!entityStore.exists(metalakeIdent, Entity.EntityType.METALAKE)) {
-        LOG.warn("Metalake {} does not exist", metalakeIdent);
-        throw new NoSuchMetalakeException("Metalake %s does not exist", 
metalakeIdent);
-      }
-    } catch (IOException ioe) {
-      LOG.error("Failed to check if metalake exists", ioe);
-      throw new RuntimeException(ioe);
-    }
-  }
-
   public static Namespace ofTagNamespace(String metalake) {
     return Namespace.of(metalake, Entity.SYSTEM_CATALOG_RESERVED_NAME, 
Entity.TAG_SCHEMA_NAME);
   }
diff --git 
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestMetalakeEvent.java
 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestMetalakeEvent.java
index 319ac641f..2b5377f75 100644
--- 
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestMetalakeEvent.java
+++ 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestMetalakeEvent.java
@@ -19,6 +19,7 @@
 
 package org.apache.gravitino.listener.api.event;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -205,7 +206,7 @@ public class TestMetalakeEvent {
     when(dispatcher.createMetalake(any(NameIdentifier.class), 
any(String.class), any(Map.class)))
         .thenReturn(metalake);
     
when(dispatcher.loadMetalake(any(NameIdentifier.class))).thenReturn(metalake);
-    when(dispatcher.dropMetalake(any(NameIdentifier.class))).thenReturn(true);
+    when(dispatcher.dropMetalake(any(NameIdentifier.class), 
anyBoolean())).thenReturn(true);
     when(dispatcher.listMetalakes()).thenReturn(null);
     when(dispatcher.alterMetalake(any(NameIdentifier.class), 
any(MetalakeChange.class)))
         .thenReturn(metalake);
diff --git 
a/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java 
b/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
index 96bc1ebdd..3b0f796e9 100644
--- a/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
+++ b/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
@@ -173,6 +173,7 @@ public class TestMetalakeManager {
     Assertions.assertEquals("comment", metalake.comment());
     testProperties(props, metalake.properties());
 
+    metalakeManager.disableMetalake(ident);
     boolean dropped = metalakeManager.dropMetalake(ident);
     Assertions.assertTrue(dropped, "metalake should be dropped");
 
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java 
b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
index cf860a579..01d919676 100644
--- a/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
+++ b/core/src/test/java/org/apache/gravitino/storage/TestEntityStorage.java
@@ -695,9 +695,7 @@ public class TestEntityStorage {
       // metalake
       BaseMetalake metalakeNew =
           createBaseMakeLake(
-              RandomIdGenerator.INSTANCE.nextId(),
-              metalake.name(),
-              (AuditInfo) metalake.auditInfo());
+              RandomIdGenerator.INSTANCE.nextId(), metalake.name(), 
metalake.auditInfo());
       store.put(metalakeNew);
       // catalog
       CatalogEntity catalogNew =
@@ -976,7 +974,7 @@ public class TestEntityStorage {
           NameIdentifier.of("metalake1"),
           BaseMetalake.class,
           Entity.EntityType.METALAKE,
-          e -> createBaseMakeLake(metalake1New.id(), "metalake2", (AuditInfo) 
e.auditInfo()));
+          e -> createBaseMakeLake(metalake1New.id(), "metalake2", 
e.auditInfo()));
 
       // Rename metalake3 --> metalake1
       BaseMetalake metalake3New1 =
@@ -986,7 +984,7 @@ public class TestEntityStorage {
           NameIdentifier.of("metalake3"),
           BaseMetalake.class,
           Entity.EntityType.METALAKE,
-          e -> createBaseMakeLake(metalake3New1.id(), "metalake1", (AuditInfo) 
e.auditInfo()));
+          e -> createBaseMakeLake(metalake3New1.id(), "metalake1", 
e.auditInfo()));
 
       // Rename metalake3 --> metalake2
       BaseMetalake metalake3New2 =
@@ -998,7 +996,7 @@ public class TestEntityStorage {
           NameIdentifier.of("metalake3"),
           BaseMetalake.class,
           Entity.EntityType.METALAKE,
-          e -> createBaseMakeLake(metalake3New2.id(), "metalake2", (AuditInfo) 
e.auditInfo()));
+          e -> createBaseMakeLake(metalake3New2.id(), "metalake2", 
e.auditInfo()));
 
       // Finally, only metalake2 and metalake1 are left.
       Assertions.assertDoesNotThrow(
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
index 2fe844d8c..284a07b84 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
@@ -24,16 +24,18 @@ import javax.ws.rs.core.Response;
 import org.apache.gravitino.dto.responses.ErrorResponse;
 import org.apache.gravitino.exceptions.AlreadyExistsException;
 import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
-import org.apache.gravitino.exceptions.CatalogInUseException;
-import org.apache.gravitino.exceptions.CatalogNotInUseException;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
 import org.apache.gravitino.exceptions.FilesetAlreadyExistsException;
 import org.apache.gravitino.exceptions.ForbiddenException;
 import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
+import org.apache.gravitino.exceptions.InUseException;
 import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
+import org.apache.gravitino.exceptions.MetalakeInUseException;
+import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NonEmptySchemaException;
 import org.apache.gravitino.exceptions.NotFoundException;
+import org.apache.gravitino.exceptions.NotInUseException;
 import org.apache.gravitino.exceptions.PartitionAlreadyExistsException;
 import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
@@ -131,6 +133,9 @@ public class ExceptionHandlers {
     } else if (e instanceof AlreadyExistsException) {
       response = ErrorResponse.alreadyExists(e.getClass().getSimpleName(), 
e.getMessage(), e);
 
+    } else if (e instanceof NotInUseException) {
+      response = ErrorResponse.notInUse(e.getClass().getSimpleName(), 
e.getMessage(), e);
+
     } else {
       return Utils.internalError(e.getMessage(), e);
     }
@@ -180,7 +185,7 @@ public class ExceptionHandlers {
       } else if (e instanceof UnsupportedOperationException) {
         return Utils.unsupportedOperation(errorMsg, e);
 
-      } else if (e instanceof CatalogNotInUseException) {
+      } else if (e instanceof NotInUseException) {
         return Utils.notInUse(errorMsg, e);
 
       } else {
@@ -221,7 +226,7 @@ public class ExceptionHandlers {
       } else if (e instanceof ForbiddenException) {
         return Utils.forbidden(errorMsg, e);
 
-      } else if (e instanceof CatalogNotInUseException) {
+      } else if (e instanceof NotInUseException) {
         return Utils.notInUse(errorMsg, e);
 
       } else {
@@ -265,7 +270,7 @@ public class ExceptionHandlers {
       } else if (e instanceof ForbiddenException) {
         return Utils.forbidden(errorMsg, e);
 
-      } else if (e instanceof CatalogNotInUseException) {
+      } else if (e instanceof NotInUseException) {
         return Utils.notInUse(errorMsg, e);
 
       } else {
@@ -306,10 +311,10 @@ public class ExceptionHandlers {
       } else if (e instanceof CatalogAlreadyExistsException) {
         return Utils.alreadyExists(errorMsg, e);
 
-      } else if (e instanceof CatalogNotInUseException) {
+      } else if (e instanceof NotInUseException) {
         return Utils.notInUse(errorMsg, e);
 
-      } else if (e instanceof CatalogInUseException) {
+      } else if (e instanceof InUseException) {
         return Utils.inUse(errorMsg, e);
 
       } else {
@@ -343,6 +348,12 @@ public class ExceptionHandlers {
       } else if (e instanceof NoSuchMetalakeException) {
         return Utils.notFound(errorMsg, e);
 
+      } else if (e instanceof MetalakeNotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
+      } else if (e instanceof MetalakeInUseException) {
+        return Utils.inUse(errorMsg, e);
+
       } else {
         return super.handle(op, metalake, parent, e);
       }
@@ -377,7 +388,7 @@ public class ExceptionHandlers {
       } else if (e instanceof ForbiddenException) {
         return Utils.forbidden(errorMsg, e);
 
-      } else if (e instanceof CatalogNotInUseException) {
+      } else if (e instanceof NotInUseException) {
         return Utils.notInUse(errorMsg, e);
 
       } else {
@@ -418,6 +429,9 @@ public class ExceptionHandlers {
       } else if (e instanceof UserAlreadyExistsException) {
         return Utils.alreadyExists(errorMsg, e);
 
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, user, metalake, e);
       }
@@ -450,6 +464,9 @@ public class ExceptionHandlers {
       } else if (e instanceof GroupAlreadyExistsException) {
         return Utils.alreadyExists(errorMsg, e);
 
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, group, metalake, e);
       }
@@ -485,6 +502,9 @@ public class ExceptionHandlers {
       } else if (e instanceof ForbiddenException) {
         return Utils.forbidden(errorMsg, e);
 
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, role, metalake, e);
       }
@@ -518,6 +538,10 @@ public class ExceptionHandlers {
 
       } else if (e instanceof ForbiddenException) {
         return Utils.forbidden(errorMsg, e);
+
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, topic, schema, e);
       }
@@ -581,6 +605,9 @@ public class ExceptionHandlers {
       } else if (e instanceof NotFoundException) {
         return Utils.notFound(errorMsg, e);
 
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, roles, parent, e);
       }
@@ -616,6 +643,9 @@ public class ExceptionHandlers {
       } else if (e instanceof TagAlreadyAssociatedException) {
         return Utils.alreadyExists(errorMsg, e);
 
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, tag, parent, e);
       }
@@ -643,6 +673,10 @@ public class ExceptionHandlers {
 
       } else if (e instanceof NotFoundException) {
         return Utils.notFound(errorMsg, e);
+
+      } else if (e instanceof NotInUseException) {
+        return Utils.notInUse(errorMsg, e);
+
       } else {
         return super.handle(op, name, parent, e);
       }
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
index fff86cf2e..e28f28f15 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
@@ -25,12 +25,15 @@ import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
+import javax.ws.rs.PATCH;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -40,8 +43,10 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.dto.MetalakeDTO;
 import org.apache.gravitino.dto.requests.MetalakeCreateRequest;
+import org.apache.gravitino.dto.requests.MetalakeSetRequest;
 import org.apache.gravitino.dto.requests.MetalakeUpdateRequest;
 import org.apache.gravitino.dto.requests.MetalakeUpdatesRequest;
+import org.apache.gravitino.dto.responses.BaseResponse;
 import org.apache.gravitino.dto.responses.DropResponse;
 import org.apache.gravitino.dto.responses.MetalakeListResponse;
 import org.apache.gravitino.dto.responses.MetalakeResponse;
@@ -149,6 +154,43 @@ public class MetalakeOperations {
     }
   }
 
+  @PATCH
+  @Path("{name}")
+  @Produces("application/vnd.gravitino.v1+json")
+  @Timed(name = "set-metalake." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
+  @ResponseMetered(name = "set-metalake", absolute = true)
+  public Response setMetalake(@PathParam("name") String metalakeName, 
MetalakeSetRequest request) {
+    LOG.info("Received set request for metalake: {}", metalakeName);
+    try {
+      return Utils.doAs(
+          httpRequest,
+          () -> {
+            NameIdentifier identifier = 
NameIdentifierUtil.ofMetalake(metalakeName);
+            TreeLockUtils.doWithTreeLock(
+                identifier,
+                LockType.WRITE,
+                () -> {
+                  if (request.isInUse()) {
+                    metalakeDispatcher.enableMetalake(identifier);
+                  } else {
+                    metalakeDispatcher.disableMetalake(identifier);
+                  }
+                  return null;
+                });
+            Response response = Utils.ok(new BaseResponse());
+            LOG.info(
+                "Successfully {} metalake: {}",
+                request.isInUse() ? "enable" : "disable",
+                metalakeName);
+            return response;
+          });
+
+    } catch (Exception e) {
+      LOG.info("Failed to {} metalake: {}", request.isInUse() ? "enable" : 
"disable", metalakeName);
+      return ExceptionHandlers.handleMetalakeException(OperationType.LOAD, 
metalakeName, e);
+    }
+  }
+
   @PUT
   @Path("{name}")
   @Produces("application/vnd.gravitino.v1+json")
@@ -186,7 +228,9 @@ public class MetalakeOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "drop-metalake." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "drop-metalake", absolute = true)
-  public Response dropMetalake(@PathParam("name") String metalakeName) {
+  public Response dropMetalake(
+      @PathParam("name") String metalakeName,
+      @DefaultValue("false") @QueryParam("force") boolean force) {
     LOG.info("Received drop metalake request for metalake: {}", metalakeName);
     try {
       return Utils.doAs(
@@ -195,7 +239,7 @@ public class MetalakeOperations {
             NameIdentifier identifier = 
NameIdentifierUtil.ofMetalake(metalakeName);
             boolean dropped =
                 TreeLockUtils.doWithRootTreeLock(
-                    LockType.WRITE, () -> 
metalakeDispatcher.dropMetalake(identifier));
+                    LockType.WRITE, () -> 
metalakeDispatcher.dropMetalake(identifier, force));
             if (!dropped) {
               LOG.warn("Failed to drop metalake by name {}", metalakeName);
             }
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
index 3b73cf035..9b265e016 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
@@ -22,6 +22,7 @@ import static 
org.apache.gravitino.Configs.TREE_LOCK_CLEAN_INTERVAL;
 import static org.apache.gravitino.Configs.TREE_LOCK_MAX_NODE_IN_MEMORY;
 import static org.apache.gravitino.Configs.TREE_LOCK_MIN_NODE_IN_MEMORY;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -381,7 +382,7 @@ public class TestMetalakeOperations extends JerseyTest {
 
   @Test
   public void testDropMetalake() {
-    when(metalakeManager.dropMetalake(any())).thenReturn(true);
+    when(metalakeManager.dropMetalake(any(), anyBoolean())).thenReturn(true);
     Response resp =
         target("/metalakes/test")
             .request(MediaType.APPLICATION_JSON_TYPE)
@@ -396,7 +397,7 @@ public class TestMetalakeOperations extends JerseyTest {
     Assertions.assertTrue(dropped);
 
     // Test when failed to drop metalake
-    when(metalakeManager.dropMetalake(any())).thenReturn(false);
+    when(metalakeManager.dropMetalake(any(), anyBoolean())).thenReturn(false);
     Response resp2 =
         target("/metalakes/test")
             .request(MediaType.APPLICATION_JSON_TYPE)
@@ -408,7 +409,9 @@ public class TestMetalakeOperations extends JerseyTest {
     Assertions.assertFalse(dropResponse2.dropped());
 
     // Test throw an exception when deleting tenant.
-    doThrow(new RuntimeException("Internal 
error")).when(metalakeManager).dropMetalake(any());
+    doThrow(new RuntimeException("Internal error"))
+        .when(metalakeManager)
+        .dropMetalake(any(), anyBoolean());
 
     Response resp1 =
         target("/metalakes/test")
diff --git 
a/trino-connector/integration-test/src/test/java/org/apache/gravitino/trino/connector/integration/test/TrinoQueryITBase.java
 
b/trino-connector/integration-test/src/test/java/org/apache/gravitino/trino/connector/integration/test/TrinoQueryITBase.java
index eabd77321..5271197d2 100644
--- 
a/trino-connector/integration-test/src/test/java/org/apache/gravitino/trino/connector/integration/test/TrinoQueryITBase.java
+++ 
b/trino-connector/integration-test/src/test/java/org/apache/gravitino/trino/connector/integration/test/TrinoQueryITBase.java
@@ -156,7 +156,7 @@ public class TrinoQueryITBase {
     if (!exists) {
       return;
     }
-    gravitinoClient.dropMetalake(metalakeName);
+    gravitinoClient.dropMetalake(metalakeName, true);
   }
 
   private static void createCatalog(
diff --git a/web/web/src/lib/api/metalakes/index.js 
b/web/web/src/lib/api/metalakes/index.js
index a34fad5a4..5b7b14228 100644
--- a/web/web/src/lib/api/metalakes/index.js
+++ b/web/web/src/lib/api/metalakes/index.js
@@ -47,7 +47,7 @@ export const createMetalakeApi = data => {
 
 export const deleteMetalakeApi = name => {
   return defHttp.delete({
-    url: `${Apis.DELETE}/${name}`
+    url: `${Apis.DELETE}/${name}?force=true`
   })
 }
 


Reply via email to