Merge changes from topic "account-resolver-2"

* changes:
  Rename AccountResolver#byId to accountCache
  Update {account-id} documentation
  Rename AccountResolver2 to AccountResolver
  Remove now-unused old AccountResolver implementation
  Remove unnecessary explicit binding of AccountResolver
  Use AccountResolver2 to resolve strings to IdentifiedUsers
  AccountResolver2: Allow resolving "self" and "me"
  AccountResolver2: Expose more useful exception information
  AccountResolver: Mark more methods as private
  Implement new AccountResolver2#resolveByNameOrEmail
  Rewrite AccountResolver with consistent interface and semantics
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 71385e2..681b5ff 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -21,7 +21,7 @@
   [--verified <N>] [--code-review <N>]
   [--label Label-Name=<N>]
   [--tag TAG]
-  {COMMIT | CHANGEID,PATCHSET}...
+  {COMMIT | CHANGENUMBER,PATCHSET}...
 --
 
 == DESCRIPTION
@@ -144,19 +144,19 @@
 
 Approve the change with commit c0ff33 as "Verified +1"
 ----
-$ ssh -p 29418 review.example.com gerrit review --verified +1 c0ff33
+$ ssh -p 29418 review.example.com gerrit review --verified +1 8242,2
 ----
 
 Vote on the project specific label "mylabel":
 ----
-$ ssh -p 29418 review.example.com gerrit review --label mylabel=+1 c0ff33
+$ ssh -p 29418 review.example.com gerrit review --label mylabel=+1 8242,2
 ----
 
 Append the message "Build Successful". Notice two levels of quoting is
 required, one for the local shell, and another for the argument parser
 inside the Gerrit server:
 ----
-$ ssh -p 29418 review.example.com gerrit review -m '"Build Successful"' c0ff33
+$ ssh -p 29418 review.example.com gerrit review -m '"Build Successful"' 8242,2
 ----
 
 Mark the unmerged commits both "Verified +1" and "Code-Review +2" and
@@ -172,7 +172,7 @@
 
 Abandon an active change:
 ----
-$ ssh -p 29418 review.example.com gerrit review --abandon c0ff33
+$ ssh -p 29418 review.example.com gerrit review --abandon 8242,2
 ----
 
 == SEE ALSO
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 1712746..e6e0259 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -105,14 +105,12 @@
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.group.db.Groups;
 import com.google.gerrit.server.index.account.AccountIndex;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
 import com.google.gerrit.server.index.account.AccountIndexer;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.index.change.ChangeIndexer;
-import com.google.gerrit.server.index.group.GroupIndexer;
 import com.google.gerrit.server.notedb.AbstractChangeNotes;
 import com.google.gerrit.server.notedb.ChangeNoteUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -282,8 +280,6 @@
   @Inject private AccountIndexer accountIndexer;
   @Inject private ChangeIndexCollection changeIndexes;
   @Inject private EventRecorder.Factory eventRecorderFactory;
-  @Inject private GroupIndexer groupIndexer;
-  @Inject private Groups groups;
   @Inject private InProcessProtocol inProcessProtocol;
   @Inject private ProjectIndexCollection projectIndexes;
   @Inject private ProjectOperations projectOperations;
@@ -370,13 +366,6 @@
     accountIndexer.index(accountId);
   }
 
-  private void reindexAllGroups() throws IOException, ConfigInvalidException {
-    Iterable<GroupReference> allGroups = groups.getAllGroupReferences()::iterator;
-    for (GroupReference group : allGroups) {
-      groupIndexer.index(group.getUUID());
-    }
-  }
-
   protected static Config submitWholeTopicEnabledConfig() {
     Config cfg = new Config();
     cfg.setBoolean("change", null, "submitWholeTopic", true);
@@ -420,18 +409,6 @@
     Transport.register(inProcessProtocol);
     toClose = Collections.synchronizedList(new ArrayList<>());
 
-    // All groups which were added during the server start (e.g. in SchemaCreatorImpl) aren't
-    // contained in the instance of the group index which is available here and in tests. There are
-    // two reasons:
-    // 1) No group index is available in SchemaCreatorImpl when using an in-memory database.
-    // (This could be fixed by using the IndexManagerOnInit in InMemoryTestingDatabaseModule similar
-    // to how BaseInit uses it.)
-    // 2) During the on-init part of the server start, we use another instance of the index than
-    // later on. As test indexes are non-permanent, closing an instance and opening another one
-    // removes all indexed data.
-    // As a workaround, we simply reindex all available groups here.
-    reindexAllGroups();
-
     admin = accountCreator.admin();
     user = accountCreator.user();
 
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 570b59c..7571184 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -378,7 +378,9 @@
             site);
     daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
     daemon.setAuditEventModuleForTesting(new FakeGroupAuditService.Module());
-    daemon.setAdditionalSysModuleForTesting(testSysModule);
+    if (testSysModule != null) {
+      daemon.addAdditionalSysModuleForTesting(testSysModule);
+    }
     daemon.setEnableSshd(desc.useSsh());
 
     if (desc.memory()) {
@@ -419,6 +421,8 @@
                 bind(GerritRuntime.class).toInstance(GerritRuntime.DAEMON);
               }
             }));
+    daemon.addAdditionalSysModuleForTesting(
+        new ReindexProjectsAtStartup.Module(), new ReindexGroupsAtStartup.Module());
     daemon.start();
     return new GerritServer(desc, null, createTestInjector(daemon), daemon, null);
   }
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
new file mode 100644
index 0000000..84e798c
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -0,0 +1,79 @@
+// 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.google.gerrit.acceptance;
+
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.inject.Inject;
+import com.google.inject.Scopes;
+import java.io.IOException;
+import java.util.stream.Stream;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+
+/** Reindex all groups at Gerrit daemon startup. */
+public class ReindexGroupsAtStartup implements LifecycleListener {
+  private final GroupIndexer groupIndexer;
+  private final Groups groups;
+  private final Config cfg;
+
+  public static class Module extends LifecycleModule {
+    @Override
+    protected void configure() {
+      listener().to(ReindexGroupsAtStartup.class).in(Scopes.SINGLETON);
+    }
+  }
+
+  @Inject
+  public ReindexGroupsAtStartup(
+      GroupIndexer groupIndexer, Groups groups, @GerritServerConfig Config cfg) {
+    this.groupIndexer = groupIndexer;
+    this.groups = groups;
+    this.cfg = cfg;
+  }
+
+  @Override
+  public void start() {
+    // Gerrit slaves without a reindex
+    if (cfg.getBoolean("container", "slave", false)
+        && !cfg.getBoolean("index", "scheduledIndexer", "runOnStartup", true)) {
+      return;
+    }
+
+    Stream<GroupReference> allGroupReferences;
+    try {
+      allGroupReferences = groups.getAllGroupReferences();
+    } catch (ConfigInvalidException | IOException e) {
+      throw new IllegalStateException("Unable to reindex groups, tests may fail", e);
+    }
+
+    allGroupReferences.forEach(
+        group -> {
+          try {
+            groupIndexer.index(group.getUUID());
+          } catch (IOException e) {
+            throw new IllegalStateException(
+                String.format("Unable to index %s, tests may fail", group), e);
+          }
+        });
+  }
+
+  @Override
+  public void stop() {}
+}
diff --git a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
new file mode 100644
index 0000000..4893efa
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
@@ -0,0 +1,61 @@
+// 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.google.gerrit.acceptance;
+
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.index.project.ProjectIndexer;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.Scopes;
+import java.io.IOException;
+
+/** Reindex all projects at Gerrit daemon startup. */
+public class ReindexProjectsAtStartup implements LifecycleListener {
+  private final ProjectIndexer projectIndexer;
+  private final GitRepositoryManager repoMgr;
+
+  public static class Module extends LifecycleModule {
+    @Override
+    protected void configure() {
+      listener().to(ReindexProjectsAtStartup.class).in(Scopes.SINGLETON);
+    }
+  }
+
+  @Inject
+  public ReindexProjectsAtStartup(ProjectIndexer projectIndexer, GitRepositoryManager repoMgr) {
+    this.projectIndexer = projectIndexer;
+    this.repoMgr = repoMgr;
+  }
+
+  @Override
+  public void start() {
+    repoMgr
+        .list()
+        .stream()
+        .forEach(
+            projectName -> {
+              try {
+                projectIndexer.index(projectName);
+              } catch (IOException e) {
+                throw new IllegalStateException(
+                    String.format("Unable to index %s, tests may fail", projectName), e);
+              }
+            });
+  }
+
+  @Override
+  public void stop() {}
+}
diff --git a/java/com/google/gerrit/httpd/HttpLogoutServlet.java b/java/com/google/gerrit/httpd/HttpLogoutServlet.java
index ab7bfdf..1eaaba3 100644
--- a/java/com/google/gerrit/httpd/HttpLogoutServlet.java
+++ b/java/com/google/gerrit/httpd/HttpLogoutServlet.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.AuditEvent;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.group.GroupAuditService;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -38,14 +38,14 @@
   private final DynamicItem<WebSession> webSession;
   private final Provider<String> urlProvider;
   private final String logoutUrl;
-  private final AuditService audit;
+  private final GroupAuditService audit;
 
   @Inject
   protected HttpLogoutServlet(
       AuthConfig authConfig,
       DynamicItem<WebSession> webSession,
       @CanonicalWebUrl @Nullable Provider<String> urlProvider,
-      AuditService audit) {
+      GroupAuditService audit) {
     this.webSession = webSession;
     this.urlProvider = urlProvider;
     this.logoutUrl = authConfig.getLogoutURL();
diff --git a/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java b/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
index d25ff60..f468ecb 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
+++ b/java/com/google/gerrit/httpd/auth/oauth/OAuthLogoutServlet.java
@@ -18,9 +18,9 @@
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.httpd.HttpLogoutServlet;
 import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.group.GroupAuditService;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -39,7 +39,7 @@
       AuthConfig authConfig,
       DynamicItem<WebSession> webSession,
       @CanonicalWebUrl @Nullable Provider<String> urlProvider,
-      AuditService audit,
+      GroupAuditService audit,
       Provider<OAuthSession> oauthSession) {
     super(authConfig, webSession, urlProvider, audit);
     this.oauthSession = oauthSession;
diff --git a/java/com/google/gerrit/httpd/auth/openid/OAuthOverOpenIDLogoutServlet.java b/java/com/google/gerrit/httpd/auth/openid/OAuthOverOpenIDLogoutServlet.java
index 8299c16..d75805c 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OAuthOverOpenIDLogoutServlet.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OAuthOverOpenIDLogoutServlet.java
@@ -18,9 +18,9 @@
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.httpd.HttpLogoutServlet;
 import com.google.gerrit.httpd.WebSession;
-import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.group.GroupAuditService;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -39,7 +39,7 @@
       AuthConfig authConfig,
       DynamicItem<WebSession> webSession,
       @CanonicalWebUrl @Nullable Provider<String> urlProvider,
-      AuditService audit,
+      GroupAuditService audit,
       Provider<OAuthSessionOverOpenID> oauthSession) {
     super(authConfig, webSession, urlProvider, audit);
     this.oauthSession = oauthSession;
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 4057d54..2b00d7c 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -105,10 +105,10 @@
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.OptionUtil;
-import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.audit.ExtendedHttpAuditEvent;
 import com.google.gerrit.server.cache.PerThreadCache;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.GroupAuditService;
 import com.google.gerrit.server.logging.RequestId;
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.permissions.GlobalPermission;
@@ -225,7 +225,7 @@
     final DynamicItem<WebSession> webSession;
     final Provider<ParameterParser> paramParser;
     final PermissionBackend permissionBackend;
-    final AuditService auditService;
+    final GroupAuditService auditService;
     final RestApiMetrics metrics;
     final Pattern allowOrigin;
     final RestApiQuotaEnforcer quotaChecker;
@@ -236,7 +236,7 @@
         DynamicItem<WebSession> webSession,
         Provider<ParameterParser> paramParser,
         PermissionBackend permissionBackend,
-        AuditService auditService,
+        GroupAuditService auditService,
         RestApiMetrics metrics,
         RestApiQuotaEnforcer quotaChecker,
         @GerritServerConfig Config cfg) {
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index d9ca76d..ae36b48 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -30,6 +30,7 @@
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.FieldType;
 import com.google.gerrit.index.Index;
@@ -312,6 +313,14 @@
     return result;
   }
 
+  /**
+   * Trasform an index document into a target object type.
+   *
+   * @param doc index document
+   * @return target object, or null if the target object was not found or failed to load from the
+   *     underlying store.
+   */
+  @Nullable
   protected abstract V fromDocument(Document doc);
 
   void add(Document doc, Values<V> values) {
diff --git a/java/com/google/gerrit/lucene/LuceneProjectIndex.java b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
index 807c40a..02d8655 100644
--- a/java/com/google/gerrit/lucene/LuceneProjectIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -138,6 +139,7 @@
   @Override
   protected ProjectData fromDocument(Document doc) {
     Project.NameKey nameKey = new Project.NameKey(doc.getField(NAME.getName()).stringValue());
-    return projectCache.get().get(nameKey).toProjectData();
+    ProjectState projectState = projectCache.get().get(nameKey);
+    return projectState == null ? null : projectState.toProjectData();
   }
 }
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index c280a2d..3759636 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -118,6 +118,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.lib.Config;
@@ -182,7 +183,7 @@
   private boolean inMemoryTest;
   private AbstractModule luceneModule;
   private Module emailModule;
-  private Module testSysModule;
+  private List<Module> testSysModules = new ArrayList<>();
   private Module auditEventModule;
 
   private Runnable serverStarted;
@@ -309,8 +310,8 @@
   }
 
   @VisibleForTesting
-  public void setAdditionalSysModuleForTesting(@Nullable Module m) {
-    testSysModule = m;
+  public void addAdditionalSysModuleForTesting(@Nullable Module... modules) {
+    testSysModules.addAll(Arrays.asList(modules));
   }
 
   @VisibleForTesting
@@ -461,9 +462,7 @@
       modules.add(new AccountDeactivator.Module());
       modules.add(new ChangeCleanupRunner.Module());
     }
-    if (testSysModule != null) {
-      modules.add(testSysModule);
-    }
+    modules.addAll(testSysModules);
     modules.add(new LocalMergeSuperSetComputation.Module());
     modules.add(new DefaultProjectNameLockManager.Module());
     return cfgInjector.createChildInjector(
diff --git a/java/com/google/gerrit/sshd/SshLog.java b/java/com/google/gerrit/sshd/SshLog.java
index df3242c..5fb75c8 100644
--- a/java/com/google/gerrit/sshd/SshLog.java
+++ b/java/com/google/gerrit/sshd/SshLog.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PeerDaemonUser;
-import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.audit.SshAuditEvent;
 import com.google.gerrit.server.config.ConfigKey;
 import com.google.gerrit.server.config.ConfigUpdatedEvent;
@@ -29,6 +28,7 @@
 import com.google.gerrit.server.config.ConfigUpdatedEvent.UpdateResult;
 import com.google.gerrit.server.config.GerritConfigListener;
 import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.group.GroupAuditService;
 import com.google.gerrit.server.ioutil.HexFormat;
 import com.google.gerrit.server.util.SystemLog;
 import com.google.gerrit.server.util.time.TimeUtil;
@@ -57,7 +57,7 @@
   private final Provider<SshSession> session;
   private final Provider<Context> context;
   private volatile AsyncAppender async;
-  private final AuditService auditService;
+  private final GroupAuditService auditService;
   private final SystemLog systemLog;
 
   private final Object lock = new Object();
@@ -68,7 +68,7 @@
       final Provider<Context> context,
       SystemLog systemLog,
       @GerritServerConfig Config config,
-      AuditService auditService) {
+      GroupAuditService auditService) {
     this.session = session;
     this.context = context;
     this.auditService = auditService;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 65f1f40..09eff80 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1896,7 +1896,7 @@
       const query = [
         'status:open',
         '-change:' + changeNum,
-        'topic:' + topic,
+        `topic:"${topic}"`,
       ].join(' ');
       const params = {
         O: options,