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

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new eb41cb38c6 [Cherry-pick to branch-1.3] [#12201] refactor(core): inject 
table hook dependencies (#12202) (#12204)
eb41cb38c6 is described below

commit eb41cb38c6e109f7beb57c1c3702dd3433730acf
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jul 27 14:16:22 2026 +0800

    [Cherry-pick to branch-1.3] [#12201] refactor(core): inject table hook 
dependencies (#12202) (#12204)
    
    **Cherry-pick Information:**
    - Original commit: 074a288d85806748c4e30fb40d2e0356da49de12
    - Target branch: `branch-1.3`
    - Status: ✅ Clean cherry-pick (no conflicts)
    
    Co-authored-by: Nevin Zheng <[email protected]>
---
 .../java/org/apache/gravitino/GravitinoEnv.java    |   3 +-
 .../apache/gravitino/hook/TableHookDispatcher.java |  25 +-
 .../authorization/TestAuthorizationUtils.java      |  80 ++++
 .../gravitino/hook/TestTableHookDispatcher.java    | 424 +++++++--------------
 4 files changed, 240 insertions(+), 292 deletions(-)

diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java 
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index 11c1291129..4f42989045 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -711,7 +711,8 @@ public class GravitinoEnv {
         new TableNormalizeDispatcher(internalTableOperationDispatcher, 
catalogManager);
     TableEventDispatcher tableEventDispatcher =
         new TableEventDispatcher(eventBus, tableNormalizeDispatcher);
-    this.tableDispatcher = new TableHookDispatcher(tableEventDispatcher);
+    this.tableDispatcher =
+        new TableHookDispatcher(tableEventDispatcher, this::ownerDispatcher, 
catalogManager);
 
     // TODO: We can install hooks when we need, we only supports ownership 
post hook,
     //  partition doesn't have ownership, so we don't need it now.
diff --git 
a/core/src/main/java/org/apache/gravitino/hook/TableHookDispatcher.java 
b/core/src/main/java/org/apache/gravitino/hook/TableHookDispatcher.java
index e898a549cb..bb923afdf2 100644
--- a/core/src/main/java/org/apache/gravitino/hook/TableHookDispatcher.java
+++ b/core/src/main/java/org/apache/gravitino/hook/TableHookDispatcher.java
@@ -20,14 +20,15 @@ package org.apache.gravitino.hook;
 
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 import org.apache.gravitino.Entity;
-import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.authorization.AuthorizationUtils;
 import org.apache.gravitino.authorization.Owner;
 import org.apache.gravitino.authorization.OwnerDispatcher;
 import org.apache.gravitino.catalog.CapabilityHelpers;
+import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.catalog.TableDispatcher;
 import org.apache.gravitino.connector.capability.Capability;
 import org.apache.gravitino.exceptions.NoSuchSchemaException;
@@ -50,9 +51,24 @@ import org.apache.gravitino.utils.PrincipalUtils;
  */
 public class TableHookDispatcher implements TableDispatcher {
   private final TableDispatcher dispatcher;
+  private final Supplier<OwnerDispatcher> ownerDispatcher;
+  private final CatalogManager catalogManager;
 
-  public TableHookDispatcher(TableDispatcher dispatcher) {
+  /**
+   * Creates a table hook dispatcher.
+   *
+   * @param dispatcher the underlying table dispatcher
+   * @param ownerDispatcher supplies the owner dispatcher, or {@code null} 
when authorization is
+   *     disabled
+   * @param catalogManager the catalog manager used to apply catalog 
capabilities
+   */
+  public TableHookDispatcher(
+      TableDispatcher dispatcher,
+      Supplier<OwnerDispatcher> ownerDispatcher,
+      CatalogManager catalogManager) {
     this.dispatcher = dispatcher;
+    this.ownerDispatcher = ownerDispatcher;
+    this.catalogManager = catalogManager;
   }
 
   @Override
@@ -81,15 +97,14 @@ public class TableHookDispatcher implements TableDispatcher 
{
             ident, columns, comment, properties, partitions, distribution, 
sortOrders, indexes);
 
     // Set the creator as the owner of the table.
-    OwnerDispatcher ownerManager = 
GravitinoEnv.getInstance().ownerDispatcher();
+    OwnerDispatcher ownerManager = ownerDispatcher.get();
     if (ownerManager != null) {
       // The inner NormalizeDispatcher case-folds the table name (and its 
schema namespace)
       // based on catalog capabilities, so the entity is stored under the 
normalized identifier.
       // Apply the same normalization here so the owner is attached to the 
same identifier the
       // manager sees.
       NameIdentifier normalizedIdent =
-          CapabilityHelpers.applyCapabilities(
-              ident, Capability.Scope.TABLE, 
GravitinoEnv.getInstance().catalogManager());
+          CapabilityHelpers.applyCapabilities(ident, Capability.Scope.TABLE, 
catalogManager);
       ownerManager.setOwner(
           normalizedIdent.namespace().level(0),
           NameIdentifierUtil.toMetadataObject(normalizedIdent, 
Entity.EntityType.TABLE),
diff --git 
a/core/src/test/java/org/apache/gravitino/authorization/TestAuthorizationUtils.java
 
b/core/src/test/java/org/apache/gravitino/authorization/TestAuthorizationUtils.java
index c0ae444eb0..e922449ce6 100644
--- 
a/core/src/test/java/org/apache/gravitino/authorization/TestAuthorizationUtils.java
+++ 
b/core/src/test/java/org/apache/gravitino/authorization/TestAuthorizationUtils.java
@@ -27,6 +27,7 @@ import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.Schema;
@@ -35,6 +36,7 @@ import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.catalog.SchemaDispatcher;
 import org.apache.gravitino.catalog.TableDispatcher;
 import org.apache.gravitino.connector.BaseCatalog;
+import org.apache.gravitino.connector.authorization.AuthorizationPlugin;
 import org.apache.gravitino.exceptions.IllegalNameIdentifierException;
 import org.apache.gravitino.exceptions.IllegalNamespaceException;
 import org.apache.gravitino.meta.AuditInfo;
@@ -42,6 +44,7 @@ import org.apache.gravitino.meta.RoleEntity;
 import org.apache.gravitino.rel.Table;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
@@ -321,4 +324,81 @@ class TestAuthorizationUtils {
           .handleEntityNameIdMappingChange("metalake", ident, 
Entity.EntityType.TABLE);
     }
   }
+
+  @Test
+  void 
testRenameTablePrivilegesNotifiesAuthorizationPluginWithExpectedChange() {
+    NameIdentifier ident = NameIdentifier.of("metalake", "catalog", "schema", 
"table");
+    NameIdentifier catalogIdent = NameIdentifier.of("metalake", "catalog");
+    List<String> locations = Lists.newArrayList("/warehouse/schema/table");
+
+    AccessControlDispatcher accessControlDispatcher = 
Mockito.mock(AccessControlDispatcher.class);
+    CatalogManager catalogManager = Mockito.mock(CatalogManager.class);
+    BaseCatalog<?> baseCatalog = Mockito.mock(BaseCatalog.class);
+    AuthorizationPlugin authorizationPlugin = 
Mockito.mock(AuthorizationPlugin.class);
+    
Mockito.when(catalogManager.loadCatalog(catalogIdent)).thenReturn(baseCatalog);
+    
Mockito.when(baseCatalog.getAuthorizationPlugin()).thenReturn(authorizationPlugin);
+
+    GravitinoEnv envMock = Mockito.mock(GravitinoEnv.class);
+    
Mockito.when(envMock.accessControlDispatcher()).thenReturn(accessControlDispatcher);
+    Mockito.when(envMock.catalogManager()).thenReturn(catalogManager);
+
+    try (MockedStatic<GravitinoEnv> envStatic = 
Mockito.mockStatic(GravitinoEnv.class)) {
+      envStatic.when(GravitinoEnv::getInstance).thenReturn(envMock);
+
+      AuthorizationUtils.authorizationPluginRenamePrivileges(
+          ident, Entity.EntityType.TABLE, "renamed_table", locations);
+    }
+
+    ArgumentCaptor<MetadataObjectChange[]> changesCaptor =
+        ArgumentCaptor.forClass(MetadataObjectChange[].class);
+    
Mockito.verify(authorizationPlugin).onMetadataUpdated(changesCaptor.capture());
+    Assertions.assertEquals(1, changesCaptor.getValue().length);
+
+    MetadataObjectChange.RenameMetadataObject renameChange =
+        Assertions.assertInstanceOf(
+            MetadataObjectChange.RenameMetadataObject.class, 
changesCaptor.getValue()[0]);
+    Assertions.assertEquals(MetadataObject.Type.TABLE, 
renameChange.metadataObject().type());
+    Assertions.assertEquals("catalog.schema.table", 
renameChange.metadataObject().fullName());
+    Assertions.assertEquals(MetadataObject.Type.TABLE, 
renameChange.newMetadataObject().type());
+    Assertions.assertEquals(
+        "catalog.schema.renamed_table", 
renameChange.newMetadataObject().fullName());
+    Assertions.assertEquals(locations, renameChange.locations());
+  }
+
+  @Test
+  void 
testRemoveTablePrivilegesNotifiesAuthorizationPluginWithExpectedChange() {
+    NameIdentifier ident = NameIdentifier.of("metalake", "catalog", "schema", 
"table");
+    NameIdentifier catalogIdent = NameIdentifier.of("metalake", "catalog");
+    List<String> locations = Lists.newArrayList("/warehouse/schema/table");
+
+    AccessControlDispatcher accessControlDispatcher = 
Mockito.mock(AccessControlDispatcher.class);
+    CatalogManager catalogManager = Mockito.mock(CatalogManager.class);
+    BaseCatalog<?> baseCatalog = Mockito.mock(BaseCatalog.class);
+    AuthorizationPlugin authorizationPlugin = 
Mockito.mock(AuthorizationPlugin.class);
+    
Mockito.when(catalogManager.loadCatalog(catalogIdent)).thenReturn(baseCatalog);
+    
Mockito.when(baseCatalog.getAuthorizationPlugin()).thenReturn(authorizationPlugin);
+
+    GravitinoEnv envMock = Mockito.mock(GravitinoEnv.class);
+    
Mockito.when(envMock.accessControlDispatcher()).thenReturn(accessControlDispatcher);
+    Mockito.when(envMock.catalogManager()).thenReturn(catalogManager);
+
+    try (MockedStatic<GravitinoEnv> envStatic = 
Mockito.mockStatic(GravitinoEnv.class)) {
+      envStatic.when(GravitinoEnv::getInstance).thenReturn(envMock);
+
+      AuthorizationUtils.authorizationPluginRemovePrivileges(
+          ident, Entity.EntityType.TABLE, locations);
+    }
+
+    ArgumentCaptor<MetadataObjectChange[]> changesCaptor =
+        ArgumentCaptor.forClass(MetadataObjectChange[].class);
+    
Mockito.verify(authorizationPlugin).onMetadataUpdated(changesCaptor.capture());
+    Assertions.assertEquals(1, changesCaptor.getValue().length);
+
+    MetadataObjectChange.RemoveMetadataObject removeChange =
+        Assertions.assertInstanceOf(
+            MetadataObjectChange.RemoveMetadataObject.class, 
changesCaptor.getValue()[0]);
+    Assertions.assertEquals(MetadataObject.Type.TABLE, 
removeChange.metadataObject().type());
+    Assertions.assertEquals("catalog.schema.table", 
removeChange.metadataObject().fullName());
+    Assertions.assertEquals(locations, removeChange.getLocations());
+  }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/hook/TestTableHookDispatcher.java 
b/core/src/test/java/org/apache/gravitino/hook/TestTableHookDispatcher.java
index 45f2cc4ca4..bf9431a504 100644
--- a/core/src/test/java/org/apache/gravitino/hook/TestTableHookDispatcher.java
+++ b/core/src/test/java/org/apache/gravitino/hook/TestTableHookDispatcher.java
@@ -18,334 +18,186 @@
  */
 package org.apache.gravitino.hook;
 
-import static org.apache.gravitino.Configs.CATALOG_CACHE_EVICTION_INTERVAL_MS;
-import static org.apache.gravitino.Configs.DEFAULT_ENTITY_RELATIONAL_STORE;
-import static 
org.apache.gravitino.Configs.ENTITY_CHANGE_LOG_CLEANUP_INTERVAL_SECS;
-import static 
org.apache.gravitino.Configs.ENTITY_CHANGE_LOG_POLL_INTERVAL_SECS;
-import static org.apache.gravitino.Configs.ENTITY_CHANGE_LOG_RETENTION_SECS;
-import static 
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_DRIVER;
-import static 
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_MAX_CONNECTIONS;
-import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_URL;
-import static 
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_WAIT_MILLISECONDS;
-import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_STORE;
-import static org.apache.gravitino.Configs.ENTITY_STORE;
-import static org.apache.gravitino.Configs.RELATIONAL_ENTITY_STORE;
-import static org.apache.gravitino.Configs.SERVICE_ADMINS;
-import static org.apache.gravitino.Configs.STORE_DELETE_AFTER_TIME;
-import static org.apache.gravitino.Configs.STORE_TRANSACTION_MAX_SKEW_TIME;
-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.apache.gravitino.Configs.VERSION_RETENTION_COUNT;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import java.util.Map;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.gravitino.Config;
-import org.apache.gravitino.GravitinoEnv;
+import java.util.List;
+import org.apache.gravitino.Entity;
 import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
-import org.apache.gravitino.Namespace;
-import org.apache.gravitino.TestColumn;
-import org.apache.gravitino.authorization.AccessControlManager;
+import org.apache.gravitino.authorization.AuthorizationUtils;
 import org.apache.gravitino.authorization.Owner;
 import org.apache.gravitino.authorization.OwnerDispatcher;
 import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.catalog.TableDispatcher;
-import org.apache.gravitino.catalog.TestOperationDispatcher;
-import org.apache.gravitino.catalog.TestTableOperationDispatcher;
-import org.apache.gravitino.connector.BaseCatalog;
-import org.apache.gravitino.connector.authorization.AuthorizationPlugin;
 import org.apache.gravitino.connector.capability.Capability;
 import org.apache.gravitino.connector.capability.CapabilityResult;
-import org.apache.gravitino.lock.LockManager;
 import org.apache.gravitino.rel.Column;
 import org.apache.gravitino.rel.Table;
 import org.apache.gravitino.rel.TableChange;
-import org.apache.gravitino.rel.expressions.NamedReference;
-import org.apache.gravitino.rel.expressions.distributions.Distribution;
 import org.apache.gravitino.rel.expressions.distributions.Distributions;
-import org.apache.gravitino.rel.expressions.distributions.Strategy;
-import org.apache.gravitino.rel.expressions.literals.Literals;
 import org.apache.gravitino.rel.expressions.sorts.SortOrder;
-import org.apache.gravitino.rel.expressions.sorts.SortOrders;
 import org.apache.gravitino.rel.expressions.transforms.Transform;
-import org.apache.gravitino.rel.expressions.transforms.Transforms;
 import org.apache.gravitino.rel.indexes.Index;
-import org.apache.gravitino.rel.indexes.Indexes;
-import org.apache.gravitino.rel.partitions.Partitions;
-import org.apache.gravitino.rel.partitions.RangePartition;
-import org.apache.gravitino.rel.types.Types;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
-public class TestTableHookDispatcher extends TestOperationDispatcher {
+public class TestTableHookDispatcher {
 
-  private static TableHookDispatcher tableHookDispatcher;
-  private static SchemaHookDispatcher schemaHookDispatcher;
-  private static AccessControlManager accessControlManager =
-      Mockito.mock(AccessControlManager.class);
-  private static AuthorizationPlugin authorizationPlugin;
-
-  @BeforeAll
-  public static void initialize() throws Exception {
-    TestTableOperationDispatcher.initialize();
-
-    tableHookDispatcher =
-        new 
TableHookDispatcher(TestTableOperationDispatcher.getTableOperationDispatcher());
-    schemaHookDispatcher =
-        new 
SchemaHookDispatcher(TestTableOperationDispatcher.getSchemaOperationDispatcher());
-
-    FieldUtils.writeField(
-        GravitinoEnv.getInstance(), "accessControlDispatcher", 
accessControlManager, true);
-    catalogManager = Mockito.mock(CatalogManager.class);
-    FieldUtils.writeField(GravitinoEnv.getInstance(), "catalogManager", 
catalogManager, true);
-    BaseCatalog catalog = Mockito.mock(BaseCatalog.class);
-    Mockito.when(catalog.capability()).thenReturn(Capability.DEFAULT);
-    CatalogManager.CatalogWrapper catalogWrapper =
-        Mockito.mock(CatalogManager.CatalogWrapper.class);
-    Mockito.when(catalogWrapper.catalog()).thenReturn(catalog);
-    Mockito.when(catalogWrapper.capabilities()).thenReturn(Capability.DEFAULT);
-
-    Mockito.when(catalogManager.loadCatalog(any())).thenReturn(catalog);
-    
Mockito.when(catalogManager.loadCatalogAndWrap(any())).thenReturn(catalogWrapper);
-    authorizationPlugin = Mockito.mock(AuthorizationPlugin.class);
-    
Mockito.when(catalog.getAuthorizationPlugin()).thenReturn(authorizationPlugin);
-  }
+  private static final String METALAKE = "metalake";
+  private static final String CATALOG = "catalog";
 
   @Test
   public void testDropAuthorizationPrivilege() {
-    Namespace tableNs = Namespace.of(metalake, catalog, "schema1123");
-    Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
-    schemaHookDispatcher.createSchema(NameIdentifier.of(tableNs.levels()), 
"comment", props);
-
-    NameIdentifier tableIdent = NameIdentifier.of(tableNs, "tableNAME");
-    Column[] columns =
-        new Column[] {
-          TestColumn.builder()
-              .withName("colNAME1")
-              .withPosition(0)
-              .withType(Types.StringType.get())
-              .build(),
-          TestColumn.builder()
-              .withName("colNAME2")
-              .withPosition(1)
-              .withType(Types.StringType.get())
-              .build()
-        };
-    RangePartition assignedPartition =
-        Partitions.range(
-            "partition_V1",
-            Literals.stringLiteral("value1"),
-            Literals.stringLiteral("value2"),
-            null);
-    Transform[] transforms =
-        new Transform[] {
-          Transforms.range(
-              new String[] {columns[0].name()}, new RangePartition[] 
{assignedPartition})
-        };
-    Distribution distribution =
-        Distributions.fields(Strategy.HASH, 5, new String[] 
{columns[0].name()});
-    SortOrder[] sortOrders =
-        new SortOrder[] 
{SortOrders.ascending(NamedReference.field(columns[0].name()))};
-    Index[] indexes = new Index[] {Indexes.primary("index1", new String[][] 
{{columns[0].name()}})};
-    tableHookDispatcher.createTable(
-        tableIdent, columns, "comment", props, transforms, distribution, 
sortOrders, indexes);
-
-    withMockedAuthorizationUtils(
-        () -> {
-          tableHookDispatcher.dropTable(tableIdent);
-          Config config = Mockito.mock(Config.class);
-          Mockito.when(config.get(SERVICE_ADMINS))
-              .thenReturn(Lists.newArrayList("admin1", "admin2"));
-          
Mockito.when(config.get(ENTITY_STORE)).thenReturn(RELATIONAL_ENTITY_STORE);
-          Mockito.when(config.get(ENTITY_RELATIONAL_STORE))
-              .thenReturn(DEFAULT_ENTITY_RELATIONAL_STORE);
-          Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_URL))
-              .thenReturn(
-                  
String.format("jdbc:h2:file:%s;DB_CLOSE_DELAY=-1;MODE=MYSQL", "/tmp/testdb"));
-          Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_DRIVER))
-              .thenReturn("org.h2.Driver");
-          
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_MAX_CONNECTIONS)).thenReturn(100);
-          
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_WAIT_MILLISECONDS))
-              .thenReturn(1000L);
-          
Mockito.when(config.get(STORE_TRANSACTION_MAX_SKEW_TIME)).thenReturn(1000L);
-          Mockito.when(config.get(STORE_DELETE_AFTER_TIME)).thenReturn(20 * 60 
* 1000L);
-          
Mockito.when(config.get(ENTITY_CHANGE_LOG_POLL_INTERVAL_SECS)).thenReturn(3L);
-          
Mockito.when(config.get(ENTITY_CHANGE_LOG_RETENTION_SECS)).thenReturn(24 * 60 * 
60L);
-          
Mockito.when(config.get(ENTITY_CHANGE_LOG_CLEANUP_INTERVAL_SECS)).thenReturn(60 
* 60L);
-          Mockito.when(config.get(VERSION_RETENTION_COUNT)).thenReturn(1L);
-          
Mockito.when(config.get(CATALOG_CACHE_EVICTION_INTERVAL_MS)).thenReturn(1000L);
-          
Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
-          
Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);
-          Mockito.doReturn(36000L).when(config).get(TREE_LOCK_CLEAN_INTERVAL);
-          try {
-            FieldUtils.writeField(
-                GravitinoEnv.getInstance(), "lockManager", new 
LockManager(config), true);
-          } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-          }
-          schemaHookDispatcher.dropSchema(NameIdentifier.of(tableNs.levels()), 
true);
-        });
+    TableDispatcher dispatcher = Mockito.mock(TableDispatcher.class);
+    TableHookDispatcher hook =
+        new TableHookDispatcher(dispatcher, () -> null, 
Mockito.mock(CatalogManager.class));
+    NameIdentifier ident = NameIdentifier.of(METALAKE, CATALOG, "schema", 
"table");
+    List<String> locations = ImmutableList.of("/test");
+    Mockito.when(dispatcher.dropTable(ident)).thenReturn(true);
+
+    try (MockedStatic<AuthorizationUtils> authorizationUtils =
+        Mockito.mockStatic(AuthorizationUtils.class)) {
+      authorizationUtils
+          .when(() -> AuthorizationUtils.getMetadataObjectLocation(ident, 
Entity.EntityType.TABLE))
+          .thenReturn(locations);
+
+      assertTrue(hook.dropTable(ident));
+
+      authorizationUtils.verify(
+          () ->
+              AuthorizationUtils.authorizationPluginRemovePrivileges(
+                  ident, Entity.EntityType.TABLE, locations));
+    }
   }
 
   @Test
   public void testCreateTableSetsOwnerWithNormalizedIdentifier() throws 
Exception {
-    // Self-contained: use a fresh hook with a directly-mocked TableDispatcher 
and a case-
-    // insensitive catalog so we can verify the helper passes a normalized 
ident to setOwner.
-    CatalogManager savedCatalogManager = 
GravitinoEnv.getInstance().catalogManager();
-    OwnerDispatcher savedOwnerDispatcher = 
GravitinoEnv.getInstance().ownerDispatcher();
-
-    CatalogManager mockCatalogManager = Mockito.mock(CatalogManager.class);
-    CatalogManager.CatalogWrapper mockWrapper = 
Mockito.mock(CatalogManager.CatalogWrapper.class);
-    Mockito.when(mockWrapper.capabilities()).thenReturn(new 
CaseInsensitiveCapability());
-    
Mockito.when(mockCatalogManager.loadCatalogAndWrap(any())).thenReturn(mockWrapper);
-
-    OwnerDispatcher mockOwnerDispatcher = Mockito.mock(OwnerDispatcher.class);
-    TableDispatcher mockTableDispatcher = Mockito.mock(TableDispatcher.class);
-    Mockito.when(
-            mockTableDispatcher.createTable(any(), any(), any(), any(), any(), 
any(), any(), any()))
-        .thenReturn(Mockito.mock(Table.class));
-
-    FieldUtils.writeField(GravitinoEnv.getInstance(), "catalogManager", 
mockCatalogManager, true);
-    FieldUtils.writeField(GravitinoEnv.getInstance(), "ownerDispatcher", 
mockOwnerDispatcher, true);
-
-    try {
-      TableHookDispatcher localHook = new 
TableHookDispatcher(mockTableDispatcher);
-      NameIdentifier ident = NameIdentifier.of(metalake, catalog, 
"SCHEMA_NORM", "MY_TABLE");
-      localHook.createTable(
-          ident,
-          new Column[0],
-          "comment",
-          ImmutableMap.of(),
-          new Transform[0],
-          Distributions.NONE,
-          new SortOrder[0],
-          new Index[0]);
-
-      ArgumentCaptor<MetadataObject> captor = 
ArgumentCaptor.forClass(MetadataObject.class);
-      Mockito.verify(mockOwnerDispatcher)
-          .setOwner(eq(metalake), captor.capture(), any(), 
eq(Owner.Type.USER));
-      Assertions.assertEquals(
-          "my_table",
-          captor.getValue().name(),
-          "Table name passed to setOwner must be lowercased by 
Capability.Scope.TABLE normalization");
-      Assertions.assertEquals(
-          catalog + ".schema_norm",
-          captor.getValue().parent(),
-          "Table parent (catalog.schema) must have its schema component 
lowercased by"
-              + " Capability.Scope.TABLE namespace normalization");
-    } finally {
-      FieldUtils.writeField(
-          GravitinoEnv.getInstance(), "catalogManager", savedCatalogManager, 
true);
-      FieldUtils.writeField(
-          GravitinoEnv.getInstance(), "ownerDispatcher", savedOwnerDispatcher, 
true);
-    }
+    CatalogManager catalogManager = Mockito.mock(CatalogManager.class);
+    CatalogManager.CatalogWrapper wrapper = 
Mockito.mock(CatalogManager.CatalogWrapper.class);
+    Mockito.when(wrapper.capabilities()).thenReturn(new 
CaseInsensitiveCapability());
+    Mockito.when(catalogManager.loadCatalogAndWrap(any())).thenReturn(wrapper);
+
+    OwnerDispatcher ownerDispatcher = Mockito.mock(OwnerDispatcher.class);
+    TableDispatcher dispatcher = Mockito.mock(TableDispatcher.class);
+    Table createdTable = Mockito.mock(Table.class);
+    Mockito.when(dispatcher.createTable(any(), any(), any(), any(), any(), 
any(), any(), any()))
+        .thenReturn(createdTable);
+    TableHookDispatcher hook =
+        new TableHookDispatcher(dispatcher, () -> ownerDispatcher, 
catalogManager);
+    NameIdentifier ident = NameIdentifier.of(METALAKE, CATALOG, "SCHEMA_NORM", 
"MY_TABLE");
+
+    assertSame(
+        createdTable,
+        hook.createTable(
+            ident,
+            new Column[0],
+            "comment",
+            ImmutableMap.of(),
+            new Transform[0],
+            Distributions.NONE,
+            new SortOrder[0],
+            new Index[0]));
+
+    ArgumentCaptor<MetadataObject> captor = 
ArgumentCaptor.forClass(MetadataObject.class);
+    Mockito.verify(ownerDispatcher)
+        .setOwner(eq(METALAKE), captor.capture(), any(), eq(Owner.Type.USER));
+    assertEquals("my_table", captor.getValue().name());
+    assertEquals(CATALOG + ".schema_norm", captor.getValue().parent());
   }
 
   @Test
-  public void testCreateTableThrowsWhenSetOwnerFails() throws 
IllegalAccessException {
-    // Save the original ownerDispatcher so we can restore it in the finally 
block instead of
-    // wiping it to null and leaking that into other tests in the suite.
-    OwnerDispatcher savedOwnerDispatcher = 
GravitinoEnv.getInstance().ownerDispatcher();
-
-    // Create the schema first with the existing (non-throwing) 
ownerDispatcher, then swap to the
-    // throwing mock only for the table create we actually want to exercise. 
Otherwise the throwing
-    // mock would fire during schema creation and we would never reach the 
table call.
-    Namespace tableNs = Namespace.of(metalake, catalog, "schema_owner_fail");
-    Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
-    schemaHookDispatcher.createSchema(NameIdentifier.of(tableNs.levels()), 
"comment", props);
+  public void testCreateTableSkipsOwnerWhenAuthorizationDisabled() {
+    CatalogManager catalogManager = Mockito.mock(CatalogManager.class);
+    TableDispatcher dispatcher = Mockito.mock(TableDispatcher.class);
+    Table createdTable = Mockito.mock(Table.class);
+    Mockito.when(dispatcher.createTable(any(), any(), any(), any(), any(), 
any(), any(), any()))
+        .thenReturn(createdTable);
+    TableHookDispatcher hook = new TableHookDispatcher(dispatcher, () -> null, 
catalogManager);
+
+    assertSame(
+        createdTable,
+        hook.createTable(
+            NameIdentifier.of(METALAKE, CATALOG, "schema", "table"),
+            new Column[0],
+            "comment",
+            ImmutableMap.of(),
+            new Transform[0],
+            Distributions.NONE,
+            new SortOrder[0],
+            new Index[0]));
+
+    Mockito.verifyNoInteractions(catalogManager);
+  }
 
-    OwnerDispatcher mockOwnerDispatcher = Mockito.mock(OwnerDispatcher.class);
+  @Test
+  public void testCreateTableThrowsWhenSetOwnerFails() throws Exception {
+    OwnerDispatcher ownerDispatcher = Mockito.mock(OwnerDispatcher.class);
     Mockito.doThrow(new RuntimeException("Set owner failed"))
-        .when(mockOwnerDispatcher)
+        .when(ownerDispatcher)
         .setOwner(any(), any(), any(), any());
-    FieldUtils.writeField(GravitinoEnv.getInstance(), "ownerDispatcher", 
mockOwnerDispatcher, true);
-
-    try {
-      NameIdentifier tableIdent = NameIdentifier.of(tableNs, 
"table_owner_fail");
-      Column[] columns =
-          new Column[] {
-            TestColumn.builder()
-                .withName("col1")
-                .withPosition(0)
-                .withType(Types.StringType.get())
-                .build()
-          };
-      RuntimeException thrown =
-          Assertions.assertThrows(
-              RuntimeException.class,
-              () ->
-                  tableHookDispatcher.createTable(
-                      tableIdent,
-                      columns,
-                      "comment",
-                      props,
-                      new Transform[0],
-                      Distributions.NONE,
-                      new SortOrder[0],
-                      new Index[0]));
-      Assertions.assertEquals("Set owner failed", thrown.getMessage());
-    } finally {
-      FieldUtils.writeField(
-          GravitinoEnv.getInstance(), "ownerDispatcher", savedOwnerDispatcher, 
true);
-    }
+    TableDispatcher dispatcher = Mockito.mock(TableDispatcher.class);
+    Mockito.when(dispatcher.createTable(any(), any(), any(), any(), any(), 
any(), any(), any()))
+        .thenReturn(Mockito.mock(Table.class));
+    CatalogManager catalogManager = Mockito.mock(CatalogManager.class);
+    CatalogManager.CatalogWrapper wrapper = 
Mockito.mock(CatalogManager.CatalogWrapper.class);
+    Mockito.when(wrapper.capabilities()).thenReturn(Capability.DEFAULT);
+    Mockito.when(catalogManager.loadCatalogAndWrap(any())).thenReturn(wrapper);
+    TableHookDispatcher hook =
+        new TableHookDispatcher(dispatcher, () -> ownerDispatcher, 
catalogManager);
+
+    RuntimeException thrown =
+        assertThrows(
+            RuntimeException.class,
+            () ->
+                hook.createTable(
+                    NameIdentifier.of(METALAKE, CATALOG, "schema", "table"),
+                    new Column[0],
+                    "comment",
+                    ImmutableMap.of(),
+                    new Transform[0],
+                    Distributions.NONE,
+                    new SortOrder[0],
+                    new Index[0]));
+
+    assertEquals("Set owner failed", thrown.getMessage());
   }
 
   @Test
   public void testRenameAuthorizationPrivilege() {
-    Namespace tableNs = Namespace.of(metalake, catalog, "schema1124");
-    Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
-    schemaHookDispatcher.createSchema(NameIdentifier.of(tableNs.levels()), 
"comment", props);
-
-    NameIdentifier tableIdent = NameIdentifier.of(tableNs, "tableNAME");
-    Column[] columns =
-        new Column[] {
-          TestColumn.builder()
-              .withName("colNAME1")
-              .withPosition(0)
-              .withType(Types.StringType.get())
-              .build(),
-          TestColumn.builder()
-              .withName("colNAME2")
-              .withPosition(1)
-              .withType(Types.StringType.get())
-              .build()
-        };
-    RangePartition assignedPartition =
-        Partitions.range(
-            "partition_V1",
-            Literals.stringLiteral("value1"),
-            Literals.stringLiteral("value2"),
-            null);
-    Transform[] transforms =
-        new Transform[] {
-          Transforms.range(
-              new String[] {columns[0].name()}, new RangePartition[] 
{assignedPartition})
-        };
-    Distribution distribution =
-        Distributions.fields(Strategy.HASH, 5, new String[] 
{columns[0].name()});
-    SortOrder[] sortOrders =
-        new SortOrder[] 
{SortOrders.ascending(NamedReference.field(columns[0].name()))};
-    Index[] indexes = new Index[] {Indexes.primary("index1", new String[][] 
{{columns[0].name()}})};
-    tableHookDispatcher.createTable(
-        tableIdent, columns, "comment", props, transforms, distribution, 
sortOrders, indexes);
-
-    Mockito.reset(authorizationPlugin);
-    TableChange setChange = TableChange.setProperty("k1", "v1");
-    tableHookDispatcher.alterTable(tableIdent, setChange);
-    Mockito.verify(authorizationPlugin, 
Mockito.never()).onMetadataUpdated(any());
-
-    Mockito.reset(authorizationPlugin);
+    TableDispatcher dispatcher = Mockito.mock(TableDispatcher.class);
+    TableHookDispatcher hook =
+        new TableHookDispatcher(dispatcher, () -> null, 
Mockito.mock(CatalogManager.class));
+    NameIdentifier ident = NameIdentifier.of(METALAKE, CATALOG, "schema", 
"table");
+    Table alteredTable = Mockito.mock(Table.class);
+    TableChange setChange = TableChange.setProperty("key", "value");
     TableChange renameChange = TableChange.rename("newName");
-    tableHookDispatcher.alterTable(tableIdent, renameChange);
-    Mockito.verify(authorizationPlugin).onMetadataUpdated(any());
+    List<String> locations = ImmutableList.of("/test");
+    Mockito.when(dispatcher.alterTable(ident, 
setChange)).thenReturn(alteredTable);
+    Mockito.when(dispatcher.alterTable(ident, 
renameChange)).thenReturn(alteredTable);
+
+    try (MockedStatic<AuthorizationUtils> authorizationUtils =
+        Mockito.mockStatic(AuthorizationUtils.class)) {
+      assertSame(alteredTable, hook.alterTable(ident, setChange));
+      authorizationUtils.verifyNoInteractions();
+
+      authorizationUtils
+          .when(() -> AuthorizationUtils.getMetadataObjectLocation(ident, 
Entity.EntityType.TABLE))
+          .thenReturn(locations);
+      assertSame(alteredTable, hook.alterTable(ident, renameChange));
+      authorizationUtils.verify(
+          () ->
+              AuthorizationUtils.authorizationPluginRenamePrivileges(
+                  ident, Entity.EntityType.TABLE, "newName", locations));
+    }
   }
 
   private static class CaseInsensitiveCapability implements Capability {


Reply via email to