Filter out non-existent changes from advertised refs

The project may contain refs that are not associated to
any change, possibly because of corruption issues or errors
in migrating them to NoteDb.

Consider any non-existent changes as non-existent refs and
avoid serving them in the advertised refs list.

Change-Id: I8df20d299f753fdbf206503f8ca02bff99d8b7e6
diff --git a/README.md b/README.md
index 0c5c8b6..e8c373c 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,12 @@
 By default the capability isn't assigned to any user or group, thus the module installation
 has no side effects.
 
+Filtering a closed change refs has the following meaning:
+- Merged changes and all their patch-sets
+- Abandoned changes and all their patch-sets
+- Corrupted changes and all their patch-sets
+- All '/meta' refs of all changes
+
 To enable a group of users of getting a "filtered list" of refs (e.g. CI jobs):
 - Define a new group of users (e.g. Builders)
 - Add a user to that group (e.g. Add 'jenkins' to the Builders group)
diff --git a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
index 426519e..7d9df89 100644
--- a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
+++ b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.modules.gitrefsfilter;
 
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
@@ -24,6 +25,7 @@
 import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
 import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
 import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Collection;
@@ -33,6 +35,8 @@
 import org.eclipse.jgit.lib.Repository;
 
 public class ForProjectWrapper extends ForProject {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   private final ForProject defaultForProject;
   private final Project.NameKey project;
   private final ChangeNotes.Factory changeNotesFactory;
@@ -89,9 +93,15 @@
   }
 
   private boolean isOpen(String refName) {
-    Change.Id changeId = Change.Id.fromRef(refName);
-    ChangeNotes changeNotes = changeNotesFactory.create(project, changeId);
-    return changeNotes.getChange().getStatus().isOpen();
+    try {
+      Change.Id changeId = Change.Id.fromRef(refName);
+      ChangeNotes changeNotes = changeNotesFactory.create(project, changeId);
+      return changeNotes.getChange().getStatus().isOpen();
+    } catch (NoSuchChangeException e) {
+      logger.atWarning().withCause(e).log(
+          "Ref %s refers to a non-existent change: hiding from the advertised refs", refName);
+      return false;
+    }
   }
 
   @Override