Merge branch 'stable-3.2' into stable-3.3

* stable-3.2:
  Document that authGroup must have Access Database

Change-Id: I6d7292dd7e604edbf4e2fd6b3c1615f43c1d1df4
diff --git a/BUILD b/BUILD
index ba4a21d..ee97660 100644
--- a/BUILD
+++ b/BUILD
@@ -14,9 +14,6 @@
         "Gerrit-SshModule: com.googlesource.gerrit.plugins.replication.SshModule",
     ],
     resources = glob(["src/main/resources/**/*"]),
-    deps = [
-        "//lib/commons:io",
-    ],
 )
 
 junit_tests(
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
index 67dae7e..bd34676 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -26,9 +26,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Lists;
-import com.google.gerrit.common.data.GroupReference;
+import com.google.common.io.Files;
 import com.google.gerrit.entities.AccountGroup;
 import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.exceptions.StorageException;
@@ -78,7 +79,6 @@
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
-import org.apache.commons.io.FilenameUtils;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
@@ -701,7 +701,7 @@
     } else if (remoteNameStyle.equals("underscore")) {
       name = name.replace("/", "_");
     } else if (remoteNameStyle.equals("basenameOnly")) {
-      name = FilenameUtils.getBaseName(name);
+      name = Files.getNameWithoutExtension(name);
     } else if (!remoteNameStyle.equals("slash")) {
       repLog.atFine().log("Unknown remoteNameStyle: %s, falling back to slash", remoteNameStyle);
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFilter.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFilter.java
index 5b4204e..28f2fba 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFilter.java
@@ -14,7 +14,7 @@
 
 package com.googlesource.gerrit.plugins.replication;
 
-import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.entities.AccessSection;
 import com.google.gerrit.entities.Project;
 import java.util.Collections;
 import java.util.List;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
index 6af4156..990e387 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -193,10 +193,6 @@
     try {
       replaying = true;
       for (ReplicationTasksStorage.ReplicateRefUpdate t : replicationTasksStorage.listWaiting()) {
-        if (t == null) {
-          repLog.atWarning().log("Encountered null replication event in ReplicationTasksStorage");
-          continue;
-        }
         try {
           fire(new URIish(t.uri), Project.nameKey(t.project), t.ref);
         } catch (URISyntaxException e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationTasksStorage.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationTasksStorage.java
index 35ecec6..3947ebc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationTasksStorage.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationTasksStorage.java
@@ -24,14 +24,15 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
+import java.nio.file.NotDirectoryException;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.transport.URIish;
 
@@ -61,6 +62,22 @@
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   public static class ReplicateRefUpdate {
+    public static Optional<ReplicateRefUpdate> createOptionally(Path file) {
+      try {
+        return Optional.ofNullable(create(file));
+      } catch (NoSuchFileException e) {
+        logger.atFine().log("File %s not found while reading task", file);
+      } catch (IOException e) {
+        logger.atSevere().withCause(e).log("Error while reading task %s", file);
+      }
+      return Optional.empty();
+    }
+
+    public static ReplicateRefUpdate create(Path file) throws IOException {
+      String json = new String(Files.readAllBytes(file), UTF_8);
+      return GSON.fromJson(json, ReplicateRefUpdate.class);
+    }
+
     public final String project;
     public final String ref;
     public final String uri;
@@ -139,34 +156,26 @@
     return list(createDir(runningUpdates));
   }
 
-  private List<ReplicateRefUpdate> list(Path tasks) {
-    List<ReplicateRefUpdate> results = new ArrayList<>();
-    try (DirectoryStream<Path> events = Files.newDirectoryStream(tasks)) {
-      for (Path path : events) {
-        if (Files.isRegularFile(path)) {
-          try {
-            String json = new String(Files.readAllBytes(path), UTF_8);
-            results.add(GSON.fromJson(json, ReplicateRefUpdate.class));
-          } catch (NoSuchFileException ex) {
-            logger.atFine().log(
-                "File %s not found while listing waiting tasks (likely in-flight or completed by another node)",
-                path);
-          } catch (IOException e) {
-            logger.atSevere().withCause(e).log("Error when firing pending event %s", path);
-          }
-        } else if (Files.isDirectory(path)) {
-          try {
-            results.addAll(list(path));
-          } catch (DirectoryIteratorException d) {
-            // iterating over the sub-directories is expected to have dirs disappear
-            Nfs.throwIfNotStaleFileHandle(d.getCause());
-          }
-        }
-      }
-    } catch (IOException e) {
-      logger.atSevere().withCause(e).log("Error while listing tasks");
+  private List<ReplicateRefUpdate> list(Path taskDir) {
+    return streamRecursive(taskDir).collect(Collectors.toList());
+  }
+
+  private Stream<ReplicateRefUpdate> streamRecursive(Path dir) {
+    return walkNonDirs(dir)
+        .map(path -> ReplicateRefUpdate.createOptionally(path))
+        .filter(Optional::isPresent)
+        .map(Optional::get);
+  }
+
+  private static Stream<Path> walkNonDirs(Path path) {
+    try {
+      return Files.list(path).flatMap(sub -> walkNonDirs(sub));
+    } catch (NotDirectoryException e) {
+      return Stream.of(path);
+    } catch (Exception e) {
+      logger.atSevere().withCause(e).log("Error while walking directory %s", path);
+      return Stream.empty();
     }
-    return results;
   }
 
   @SuppressWarnings("deprecation")
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationDaemon.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationDaemon.java
index 420cdf8..afe1d82 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationDaemon.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationDaemon.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.WaitUtil;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.config.SitePaths;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationFanoutIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationFanoutIT.java
index 3619add..536080e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationFanoutIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationFanoutIT.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.acceptance.PushOneCommit.Result;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.WaitUtil;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.googlesource.gerrit.plugins.replication.ReplicationTasksStorage.ReplicateRefUpdate;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
index fdea8d0..ba6f94d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationIT.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.acceptance.PushOneCommit.Result;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.WaitUtil;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.projects.BranchInput;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationStorageIT.java b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationStorageIT.java
index dd584ce..ff96b1c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationStorageIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/replication/ReplicationStorageIT.java
@@ -20,6 +20,7 @@
 
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.WaitUtil;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.projects.BranchInput;
 import com.googlesource.gerrit.plugins.replication.Destination.QueueInfo;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtil.java b/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtil.java
deleted file mode 100644
index 586b56c..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtil.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed 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 com.googlesource.gerrit.plugins.replication;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
-import com.google.common.base.Stopwatch;
-import java.time.Duration;
-import java.util.function.Supplier;
-
-public class WaitUtil {
-  public static void waitUntil(Supplier<Boolean> waitCondition, Duration timeout)
-      throws InterruptedException {
-    Stopwatch stopwatch = Stopwatch.createStarted();
-    while (!waitCondition.get()) {
-      if (stopwatch.elapsed().compareTo(timeout) > 0) {
-        throw new InterruptedException();
-      }
-      MILLISECONDS.sleep(50);
-    }
-  }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtilTest.java b/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtilTest.java
deleted file mode 100644
index 0ccb0af..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/replication/WaitUtilTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed 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 com.googlesource.gerrit.plugins.replication;
-
-import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import static com.googlesource.gerrit.plugins.replication.WaitUtil.waitUntil;
-
-import java.time.Duration;
-import org.junit.Test;
-
-public class WaitUtilTest {
-
-  @Test
-  public void shouldFailWhenConditionNotMetWithinTimeout() throws Exception {
-    assertThrows(
-        InterruptedException.class,
-        () -> waitUntil(() -> returnTrue() == false, Duration.ofSeconds(1)));
-  }
-
-  @Test
-  public void shouldNotFailWhenConditionIsMetWithinTimeout() throws Exception {
-    waitUntil(() -> returnTrue() == true, Duration.ofSeconds(1));
-  }
-
-  private static boolean returnTrue() {
-    return true;
-  }
-}