Fix issue with ref deletion and global-refdb

Do not reuse `com.google.gerrit.server.restapi.project.DeleteRef` class
to delete ref because it operates with an high-level repository manager.

Using the high-level repository manager may be an issue in come cases
where it is wrapped, like in the multi-site setup (see [1]), with a global-refdb
and therefore may not be able to complete the REST-API successfully.

[1] https://gerrit.googlesource.com/plugins/multi-site/

Original-Author: maczech@gmail.com
Bug: Issue 16128
Change-Id: I2034a48fe04489e2827de7c44bdb0e67de3f4665
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
index 0c5e016..2a3a79d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
@@ -19,41 +19,52 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.restapi.project.DeleteRef;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.replication.pull.Context;
 import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
 import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger;
 import com.googlesource.gerrit.plugins.replication.pull.ReplicationState;
+import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject;
+import com.googlesource.gerrit.plugins.replication.pull.fetch.RefUpdateState;
 import java.io.IOException;
 import java.util.Optional;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
 
 public class DeleteRefCommand {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private final PullReplicationStateLogger fetchStateLog;
-  private final DeleteRef deleteRef;
+  private final ApplyObject applyObject;
   private final DynamicItem<EventDispatcher> eventDispatcher;
   private final ProjectCache projectCache;
+  private final PermissionBackend permissionBackend;
+  private final LocalDiskRepositoryManager gitManager;
 
   @Inject
   public DeleteRefCommand(
       PullReplicationStateLogger fetchStateLog,
       ProjectCache projectCache,
-      DeleteRef deleteRef,
-      DynamicItem<EventDispatcher> eventDispatcher) {
+      ApplyObject applyObject,
+      PermissionBackend permissionBackend,
+      DynamicItem<EventDispatcher> eventDispatcher,
+      LocalDiskRepositoryManager gitManager) {
     this.fetchStateLog = fetchStateLog;
     this.projectCache = projectCache;
-    this.deleteRef = deleteRef;
+    this.applyObject = applyObject;
     this.eventDispatcher = eventDispatcher;
+    this.permissionBackend = permissionBackend;
+    this.gitManager = gitManager;
   }
 
   public void deleteRef(Project.NameKey name, String refName, String sourceLabel)
@@ -66,8 +77,15 @@
       }
 
       try {
+        projectState.get().checkStatePermitsWrite();
+        permissionBackend
+            .currentUser()
+            .project(projectState.get().getNameKey())
+            .ref(refName)
+            .check(RefPermission.DELETE);
+
         Context.setLocalEvent(true);
-        deleteRef.deleteSingleRef(projectState.get(), refName);
+        deleteRef(name, refName);
 
         eventDispatcher
             .get()
@@ -83,7 +101,7 @@
             "Unexpected error while trying to delete ref '%s' on project %s and notifying it",
             refName, name);
         throw RestApiException.wrap(e.getMessage(), e);
-      } catch (ResourceConflictException e) {
+      } catch (IOException e) {
         eventDispatcher
             .get()
             .postEvent(
@@ -110,4 +128,18 @@
       throw RestApiException.wrap(e.getMessage(), e);
     }
   }
+
+  private RefUpdateState deleteRef(Project.NameKey name, String refName) throws IOException {
+
+    try (Repository repository = gitManager.openRepository(name)) {
+      RefUpdate.Result result;
+      RefUpdate u = repository.updateRef(refName);
+      u.setExpectedOldObjectId(repository.exactRef(refName).getObjectId());
+      u.setNewObjectId(ObjectId.zeroId());
+      u.setForceUpdate(true);
+
+      result = u.delete();
+      return new RefUpdateState(refName, result);
+    }
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
index daf2001..4415a4b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommandTest.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -24,12 +25,22 @@
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.EventDispatcher;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackend.ForProject;
+import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
+import com.google.gerrit.server.permissions.PermissionBackend.WithUser;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.restapi.project.DeleteRef;
 import com.googlesource.gerrit.plugins.replication.pull.FetchRefReplicatedEvent;
 import com.googlesource.gerrit.plugins.replication.pull.PullReplicationStateLogger;
+import com.googlesource.gerrit.plugins.replication.pull.fetch.ApplyObject;
 import java.util.Optional;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,19 +59,42 @@
   @Mock private DynamicItem<EventDispatcher> eventDispatcherDataItem;
   @Mock private EventDispatcher eventDispatcher;
   @Mock private ProjectCache projectCache;
-  @Mock private DeleteRef deleteRef;
+  @Mock private ApplyObject applyObject;
   @Mock private ProjectState projectState;
+  @Mock private PermissionBackend permissionBackend;
+  @Mock private WithUser currentUser;
+  @Mock private ForProject forProject;
+  @Mock private ForRef forRef;
+  @Mock private LocalDiskRepositoryManager gitManager;
+  @Mock private RefUpdate refUpdate;
+  @Mock private Repository repository;
+  @Mock private Ref currentRef;
+  @Mock private RefDatabase refDb;
   @Captor ArgumentCaptor<Event> eventCaptor;
 
   private DeleteRefCommand objectUnderTest;
 
   @Before
-  public void setup() {
+  public void setup() throws Exception {
     when(eventDispatcherDataItem.get()).thenReturn(eventDispatcher);
     when(projectCache.get(any())).thenReturn(Optional.of(projectState));
+    when(permissionBackend.currentUser()).thenReturn(currentUser);
+    when(currentUser.project(any())).thenReturn(forProject);
+    when(forProject.ref(any())).thenReturn(forRef);
+    when(gitManager.openRepository(any())).thenReturn(repository);
+    when(repository.updateRef(any())).thenReturn(refUpdate);
+    when(repository.getRefDatabase()).thenReturn(refDb);
+    when(refDb.exactRef(anyString())).thenReturn(currentRef);
+    when(refUpdate.delete()).thenReturn(Result.FORCED);
 
     objectUnderTest =
-        new DeleteRefCommand(fetchStateLog, projectCache, deleteRef, eventDispatcherDataItem);
+        new DeleteRefCommand(
+            fetchStateLog,
+            projectCache,
+            applyObject,
+            permissionBackend,
+            eventDispatcherDataItem,
+            gitManager);
   }
 
   @Test