Merge branch 'stable-2.15'

* stable-2.15: (43 commits)
  Simplify client module
  Split reviewers plugin into GWT and non-GWT variants
  Update bazlets to latest revision on stable-2.15
  Add modify reviewers config plugin capability
  Fix classpath generation for Eclipse project
  Update bazlets to latest revision on stable-2.15
  ReviewersResolver: Log failure to list accounts for group as error
  ReviewersResolver: Don't log stack trace when group does not exist
  ReviewersResolver: Emit a warning to the log when account is inactive
  Update bazlets to latest stable-2.15 to use 2.15.1 API
  Update bazlets to latest stable-2.14 and switch to 2.14.8 release API
  Fix compilation warnings flagged by Eclipse
  Upgrade bazlets and use 2.15 release API
  Reviewers: Restrict index query to the given change
  Build with 2.15-SNAPSHOT API
  Update bazlets to latest on stable-2.15
  Add integration test for adding reviewers
  ReviewersConfig: Add unit test for merged project inheritance
  ReviewerFilterSection: Add equals and hashCode methods
  ReviewersResolver#resolveGroup: Simplify error handling
  Remove stale documentation how to build with maven
  Add reviewers resolver test
  ReviewersResolver#resolve: Extract logic in small methods
  Reviewers: Extract account resolver logic to it own class
  Add members of all matching groups
  ReviewersConfig: merge parent reviewers.config
  Update bazlets to latest stable-2.14 to use 2.14.8-SNAPSHOT API
  Update bazlets to the latest on stable-2.14 to use 2.14.7 release API
  Reviewers: Don't create ChangeData instances
  Reviewers: Actually use change index for filter query
  Refactor ReviewersConfig to also handle the global config
  Rename DefaultReviewers to AddReviewersByConfiguration
  Factor addition of reviewers into own Runnable class
  Reviewers: Don't add change uploader as reviewer
  Reviewers: Only consider active accounts
  Reviewers: Pass RevisionEvent to onEvent method
  Add project name & change Id to the logs
  Reviewers: Use log built-in string formatting
  Handle the QueryParseException when querying a group that does not exist
  Reviewers#suggestReviewers: Add null check on changeId
  Add support for reviewer suggestion
  ChangeEventListener: Remove unnecessary final modifiers
  ChangeEventListener: Don't unnecessarily open the repository

Change-Id: I74e11c3f770ecf923b78a1e792a59365e8a062a5
diff --git a/BUILD b/BUILD
index c50df23..fc5a471 100644
--- a/BUILD
+++ b/BUILD
@@ -1,9 +1,9 @@
 load("//tools/bzl:junit.bzl", "junit_tests")
 load(
     "//tools/bzl:plugin.bzl",
-    "gerrit_plugin",
     "PLUGIN_DEPS",
     "PLUGIN_TEST_DEPS",
+    "gerrit_plugin",
 )
 
 SRC = "src/main/java/com/googlesource/gerrit/plugins/reviewers/"
diff --git a/WORKSPACE b/WORKSPACE
index ea097a8..0d61018 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,30 +3,30 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "ad9df4d323c1df5c733d379c8891bde13a00a285",
+    commit = "3a53199198db3f49a43b7708c2c3352670393717",
     #    local_path = "/home/<user>/projects/bazlets",
 )
 
 # Release Plugin API
-load(
-    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
-    "gerrit_api",
-)
+#load(
+#    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
+#    "gerrit_api",
+#)
 
 # Snapshot Plugin API
-#load(
-#    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-#    "gerrit_api_maven_local",
-#)
+load(
+    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
+    "gerrit_api_maven_local",
+)
 load(
     "@com_googlesource_gerrit_bazlets//:gerrit_gwt.bzl",
     "gerrit_gwt",
 )
 
 # Load release Plugin API
-gerrit_api()
+#gerrit_api()
 
 # Load snapshot Plugin API
-#gerrit_api_maven_local()
+gerrit_api_maven_local()
 
 gerrit_gwt()
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
index b90b350..1026fbb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/PutReviewers.java
@@ -27,11 +27,13 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.git.MetaDataUpdate;
-import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -83,7 +85,7 @@
 
   @Override
   public List<ReviewerFilterSection> apply(ProjectResource rsrc, Input input)
-      throws RestApiException {
+      throws RestApiException, PermissionBackendException {
     Project.NameKey projectName = rsrc.getNameKey();
     ReviewersConfig.ForProject cfg = config.forProject(projectName);
     if (cfg == null) {
@@ -91,7 +93,7 @@
     }
 
     PermissionBackend.WithUser userPermission = permissionBackend.user(rsrc.getUser());
-    if (!rsrc.getControl().isOwner()
+    if (!userPermission.project(rsrc.getNameKey()).testOrFalse(ProjectPermission.WRITE_CONFIG)
         && !userPermission.testOrFalse(new PluginPermission(pluginName, MODIFY_REVIEWERS_CONFIG))) {
       throw new AuthException("not allowed to modify reviewers config");
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
index a12c45f..a70fd6f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfig.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.PluginConfigFactory;
-import com.google.gerrit.server.git.VersionedMetaData;
+import com.google.gerrit.server.git.meta.VersionedMetaData;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
index 6f5c202..82c3139 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersResolver.java
@@ -19,7 +19,6 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.NoSuchGroupException;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -27,8 +26,8 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.GroupMembers;
-import com.google.gerrit.server.group.GroupsCollection;
 import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -46,18 +45,18 @@
 
   private final AccountResolver accountResolver;
   private final Provider<GroupsCollection> groupsCollection;
-  private final GroupMembers.Factory groupMembersFactory;
+  private final GroupMembers groupMembers;
   private final IdentifiedUser.GenericFactory identifiedUserFactory;
 
   @Inject
   ReviewersResolver(
       AccountResolver accountResolver,
       Provider<GroupsCollection> groupsCollection,
-      GroupMembers.Factory groupMembersFactory,
+      GroupMembers groupMembers,
       IdentifiedUser.GenericFactory identifiedUserFactory) {
     this.accountResolver = accountResolver;
     this.groupsCollection = groupsCollection;
-    this.groupMembersFactory = groupMembersFactory;
+    this.groupMembers = groupMembers;
     this.identifiedUserFactory = identifiedUserFactory;
   }
 
@@ -78,26 +77,12 @@
       int changeNumber,
       @Nullable AccountInfo uploader) {
     Set<Account.Id> reviewers = Sets.newHashSetWithExpectedSize(names.size());
-    GroupMembers groupMembers = null;
     for (String name : names) {
       if (resolveAccount(project, changeNumber, uploader, reviewers, name)) {
         continue;
       }
 
-      if (groupMembers == null && uploader != null) {
-        groupMembers = createGroupMembers(project, changeNumber, uploader, name);
-      }
-
-      if (groupMembers != null) {
-        resolveGroup(project, changeNumber, reviewers, groupMembers, name);
-      } else {
-        log.warn(
-            "For the change {} of project {}: failed to list accounts for group {}; cannot retrieve uploader account for {}.",
-            changeNumber,
-            project,
-            name,
-            uploader.email);
-      }
+      resolveGroup(project, changeNumber, reviewers, groupMembers, name);
     }
     return reviewers;
   }
@@ -152,13 +137,13 @@
               .map(Account::getId)
               .collect(toSet());
       reviewers.addAll(accounts);
-    } catch (UnprocessableEntityException | NoSuchGroupException e) {
+    } catch (UnprocessableEntityException e) {
       log.warn(
           "For the change {} of project {}: reviewer {} is neither an account nor a group.",
           changeNumber,
           project,
           group);
-    } catch (NoSuchProjectException | IOException | OrmException e) {
+    } catch (NoSuchProjectException | IOException e) {
       log.error(
           "For the change {} of project {}: failed to list accounts for group {}.",
           changeNumber,
@@ -167,26 +152,4 @@
           e);
     }
   }
-
-  private GroupMembers createGroupMembers(
-      Project.NameKey project, int changeNumber, AccountInfo uploader, String group) {
-    // email is not unique to one account, try to locate the account using
-    // "Full name <email>" to increase chance of finding only one.
-    String uploaderNameEmail = String.format("%s <%s>", uploader.name, uploader.email);
-    try {
-      Account uploaderAccount = accountResolver.findByNameOrEmail(uploaderNameEmail);
-      if (uploaderAccount != null) {
-        return groupMembersFactory.create(identifiedUserFactory.create(uploaderAccount.getId()));
-      }
-    } catch (OrmException | IOException e) {
-      log.warn(
-          "For the change {} of project {}: failed to list accounts for group {}, cannot retrieve uploader account {}.",
-          changeNumber,
-          project,
-          group,
-          uploaderNameEmail,
-          e);
-    }
-    return null;
-  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
index b8b076e..34419fb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/server/SuggestProjectReviewers.java
@@ -21,13 +21,14 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ReviewersUtil;
-import com.google.gerrit.server.ReviewersUtil.VisibilityControl;
-import com.google.gerrit.server.change.SuggestReviewers;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.change.ReviewersUtil;
+import com.google.gerrit.server.restapi.change.ReviewersUtil.VisibilityControl;
+import com.google.gerrit.server.restapi.change.SuggestReviewers;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -54,7 +55,8 @@
 
   @Override
   public List<SuggestedReviewerInfo> apply(ProjectResource rsrc)
-      throws BadRequestException, OrmException, IOException, ConfigInvalidException {
+      throws BadRequestException, OrmException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     return reviewersUtil.suggestReviewers(
         null, this, rsrc.getProjectState(), getVisibility(rsrc), true);
   }
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index e570e2c..1aac8d5 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -1,7 +1,7 @@
 Build
 =====
 
-This plugin can be built with Bazel.
+This plugin is built with Bazel.
 
 Two build modes are supported: Standalone and in Gerrit tree.
 The standalone build mode is recommended, as this mode doesn't require
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
index c966a6f..32b2243 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersConfigIT.java
@@ -41,9 +41,7 @@
   private static final String JOHN_DOE = "john.doe@example.com";
 
   @Before
-  @Override
   public void setUp() throws Exception {
-    super.setUp();
     fetch(testRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
     testRepo.reset("refs/heads/config");
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
index 6036865..c445e81 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/reviewers/server/ReviewersIT.java
@@ -42,9 +42,7 @@
 @TestPlugin(name = "reviewers", sysModule = "com.googlesource.gerrit.plugins.reviewers.Module")
 public class ReviewersIT extends LightweightPluginDaemonTest {
   @Before
-  @Override
   public void setUp() throws Exception {
-    super.setUp();
     fetch(testRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
     testRepo.reset("refs/heads/config");
   }