Merge branch 'stable-3.4' into stable-3.5

* stable-3.4:
  Set version to 3.4.5-SNAPSHOT
  Set version to 3.4.4
  Set version to 3.3.11-SNAPSHOT
  Set version to 3.3.10
  Ignore '--no-limit' query changes option for anonymous users
  Show HTTP(S) message in Missing Change ID Error message
  Update reload4j to 1.2.19 and slf4j to 1.7.36
  Move SLF4J to nongoogle.bzl
  Separate reviewer-recipients from project-watch-recipients
  Restore Java 11 binary bytecode for releases to Maven
  Update git submodules

Release-Notes: skip
Change-Id: I057b11ba549129dfbb1cc8a72cd9940d4fba9b9a
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 0a203cc..da3018b 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -77,8 +77,8 @@
 link:user-search.html#_search_operators[query string] must be provided
 by the `q` parameter. The `n` parameter can be used to limit the
 returned results. The `no-limit` parameter can be used remove the default
-limit on queries and return all results. This might not be supported by
-all index backends.
+limit on queries and return all results (does not apply to anonymous requests).
+This might not be supported by all index backends.
 
 As result a list of link:#change-info[ChangeInfo] entries is returned.
 The change output is sorted by the last update time, most recently
diff --git a/WORKSPACE b/WORKSPACE
index d267550..1a43bc0 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -239,32 +239,6 @@
     sha1 = "28c59f58f5adcc307604602e2aa89e2aca14c554",
 )
 
-SLF4J_VERS = "1.7.33"
-
-maven_jar(
-    name = "log-api",
-    artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
-    sha1 = "d375aa1b98d34d5ddf73a3f19eaad66e98975b12",
-)
-
-maven_jar(
-    name = "log-ext",
-    artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
-    sha1 = "00da03640ae1ad57f964dcaa542fb5d804dce8a6",
-)
-
-maven_jar(
-    name = "impl-log4j",
-    artifact = "org.slf4j:slf4j-reload4j:" + SLF4J_VERS,
-    sha1 = "ddc89144bfb56781936120b2334a70869b68db6d",
-)
-
-maven_jar(
-    name = "jcl-over-slf4j",
-    artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
-    sha1 = "28c441128bc81b6d95cc2857ae5bb46ae5bf658b",
-)
-
 maven_jar(
     name = "json-smart",
     artifact = "net.minidev:json-smart:1.1.1",
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 056407e..596efb5 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -265,6 +265,12 @@
         "multiple Change-Id lines in message footer";
     private static final String INVALID_CHANGE_ID_MSG =
         "invalid Change-Id line format in message footer";
+    private static final String HTTP_INSTALL_HOOK =
+        "f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\""
+            + " %stools/hooks/commit-msg ; chmod +x \"$f\"";
+    private static final String SSH_INSTALL_HOOK =
+        "gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg ${gitdir}/hooks/";
+    private static final String CHANGE_ID_MISSING_INSTALL_HOOKS = "  %s\nor, for http(s):\n  %s";
 
     @VisibleForTesting
     public static final String CHANGE_ID_MISMATCH_MSG =
@@ -371,14 +377,15 @@
       // If there are no SSH keys, the commit-msg hook must be installed via
       // HTTP(S)
       Optional<String> webUrl = urlFormatter.getWebUrl();
+
+      String httpHook = String.format(HTTP_INSTALL_HOOK, webUrl.get());
+
       if (hostKeys.isEmpty()) {
         checkState(webUrl.isPresent());
-        return String.format(
-            "  f=\"$(git rev-parse --git-dir)/hooks/commit-msg\"; curl -o \"$f\" %stools/hooks/commit-msg ; chmod +x \"$f\"",
-            webUrl.get());
+        return httpHook;
       }
 
-      // SSH keys exist, so the hook can be installed with scp.
+      // SSH keys exist, so the hook might be able to be installed with scp.
       String sshHost;
       int sshPort;
       String host = hostKeys.get(0).getHost();
@@ -396,9 +403,10 @@
         sshPort = 22;
       }
 
-      return String.format(
-          "  gitdir=$(git rev-parse --git-dir); scp -p -P %d %s@%s:hooks/commit-msg ${gitdir}/hooks/",
-          sshPort, user.getUserName().orElse("<USERNAME>"), sshHost);
+      String sshHook =
+          String.format(
+              SSH_INSTALL_HOOK, sshPort, user.getUserName().orElse("<USERNAME>"), sshHost);
+      return String.format(CHANGE_ID_MISSING_INSTALL_HOOKS, sshHook, httpHook);
     }
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 1a2e150..5c0132c 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -408,15 +408,25 @@
 
   @Override
   protected void add(RecipientType rt, Account.Id to) {
-    Optional<AccountState> accountState = args.accountCache.get(to);
-    if (!accountState.isPresent()) {
-      return;
-    }
-    if (emailOnlyAttentionSetIfEnabled
-        && accountState.get().generalPreferences().getEmailStrategy()
-            == EmailStrategy.ATTENTION_SET_ONLY
-        && !currentAttentionSet.contains(to)) {
-      return;
+    addRecipient(rt, to, /* isWatcher= */ false);
+  }
+
+  /** This bypasses the EmailStrategy.ATTENTION_SET_ONLY strategy when adding the recipient. */
+  @Override
+  protected void addWatcher(RecipientType rt, Account.Id to) {
+    addRecipient(rt, to, /* isWatcher= */ true);
+  }
+
+  private void addRecipient(RecipientType rt, Account.Id to, boolean isWatcher) {
+    if (!isWatcher) {
+      Optional<AccountState> accountState = args.accountCache.get(to);
+      if (emailOnlyAttentionSetIfEnabled
+          && accountState.isPresent()
+          && accountState.get().generalPreferences().getEmailStrategy()
+              == EmailStrategy.ATTENTION_SET_ONLY
+          && !currentAttentionSet.contains(to)) {
+        return;
+      }
     }
     if (emailOnlyAuthors && !authors.contains(to)) {
       return;
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index b78dc62..2ab73a8 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -60,7 +60,7 @@
       // TODO(hiesel): Remove special handling for owners
       StreamSupport.stream(matching.all().accounts.spliterator(), false)
           .filter(this::isOwnerOfProjectOrBranch)
-          .forEach(acc -> add(RecipientType.TO, acc));
+          .forEach(acc -> addWatcher(RecipientType.TO, acc));
       // Add everyone else. Owners added above will not be duplicated.
       add(RecipientType.TO, matching.to);
       add(RecipientType.CC, matching.cc);
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index 5ffd928..91310ce 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -82,13 +82,15 @@
   /** Add users or email addresses to the TO, CC, or BCC list. */
   protected void add(RecipientType type, Watchers.List list) {
     for (Account.Id user : list.accounts) {
-      add(type, user);
+      addWatcher(type, user);
     }
     for (Address addr : list.emails) {
       add(type, addr);
     }
   }
 
+  protected abstract void addWatcher(RecipientType type, Account.Id to);
+
   public String getSshHost() {
     String host = Iterables.getFirst(args.sshAddresses, null);
     if (host == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 91fa2f0..2c15bc9 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -27,6 +27,7 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryRequiresAuthException;
 import com.google.gerrit.index.query.QueryResult;
+import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.DynamicOptions;
 import com.google.gerrit.server.change.ChangeJson;
@@ -95,7 +96,9 @@
     this.start = start;
   }
 
-  @Option(name = "--no-limit", usage = "Return all results, overriding the default limit")
+  @Option(
+      name = "--no-limit",
+      usage = "Return all results, overriding the default limit. Ignored for anonymous users.")
   public void setNoLimit(boolean on) {
     this.noLimit = on;
   }
@@ -168,7 +171,7 @@
     if (start != null) {
       queryProcessor.setStart(start);
     }
-    if (noLimit != null) {
+    if (noLimit != null && !AnonymousUser.class.isAssignableFrom(userProvider.get().getClass())) {
       queryProcessor.setNoLimit(noLimit);
     }
     if (skipVisibility != null) {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 0d7210f..c7aac96 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -2884,7 +2884,7 @@
   }
 
   @Test
-  public void queryChangesNoLimit() throws Exception {
+  public void queryChangesNoLimitRegisteredUser() throws Exception {
     projectOperations
         .allProjectsForUpdate()
         .add(
@@ -2902,6 +2902,26 @@
   }
 
   @Test
+  public void queryChangesNoLimitIgnoredForAnonymousUser() throws Exception {
+    int limit = 2;
+    projectOperations
+        .allProjectsForUpdate()
+        .add(
+            allowCapability(GlobalCapability.QUERY_LIMIT)
+                .group(SystemGroupBackend.ANONYMOUS_USERS)
+                .range(0, limit))
+        .update();
+    for (int i = 0; i < 3; i++) {
+      createChange();
+    }
+    requestScopeOperations.setApiUserAnonymous();
+    List<ChangeInfo> resultsWithDefaultLimit = gApi.changes().query().get();
+    List<ChangeInfo> resultsWithNoLimit = gApi.changes().query().withNoLimit().get();
+    assertThat(resultsWithDefaultLimit).hasSize(limit);
+    assertThat(resultsWithNoLimit).hasSize(limit);
+  }
+
+  @Test
   public void queryChangesStart() throws Exception {
     PushOneCommit.Result r1 = createChange();
     createChange();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
index 0a11b15..576b5512 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AttentionSetIT.java
@@ -1658,11 +1658,7 @@
 
     // Add preference for the user such that they only receive an email on changes that require
     // their attention.
-    requestScopeOperations.setApiUser(user.id());
-    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
-    prefs.emailStrategy = EmailStrategy.ATTENTION_SET_ONLY;
-    gApi.accounts().self().setPreferences(prefs);
-    requestScopeOperations.setApiUser(admin.id());
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
 
     // Add user to attention set. They receive an email since they are in the attention set.
     change(r).addReviewer(user.id().toString());
@@ -1736,11 +1732,7 @@
   public void attentionSetWithEmailFilterImpactingOnlyChangeEmails() throws Exception {
     // Add preference for the user such that they only receive an email on changes that require
     // their attention.
-    requestScopeOperations.setApiUser(user.id());
-    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
-    prefs.emailStrategy = EmailStrategy.ATTENTION_SET_ONLY;
-    gApi.accounts().self().setPreferences(prefs);
-    requestScopeOperations.setApiUser(admin.id());
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
 
     // Ensure emails that don't relate to changes are still sent.
     gApi.accounts().id(user.id().get()).generateHttpPassword();
@@ -1993,6 +1985,27 @@
         .isEqualTo(Operation.REMOVE);
   }
 
+  @Test
+  public void outsideAttentionSet_watchProjectEmailReceived() throws Exception {
+    setEmailStrategyForUser(EmailStrategy.ATTENTION_SET_ONLY);
+
+    requestScopeOperations.setApiUser(user.id());
+    watch(project.get());
+
+    createChange();
+
+    assertThat(sender.getMessages()).isNotEmpty();
+    sender.clear();
+  }
+
+  private void setEmailStrategyForUser(EmailStrategy es) throws Exception {
+    requestScopeOperations.setApiUser(user.id());
+    GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
+    prefs.emailStrategy = es;
+    gApi.accounts().self().setPreferences(prefs);
+    requestScopeOperations.setApiUser(admin.id());
+  }
+
   private List<AttentionSetUpdate> getAttentionSetUpdatesForUser(
       PushOneCommit.Result r, TestAccount account) {
     return getAttentionSetUpdates(r.getChange().getId()).stream()
diff --git a/lib/nongoogle_test.sh b/lib/nongoogle_test.sh
index b319b77b..f6141dd 100755
--- a/lib/nongoogle_test.sh
+++ b/lib/nongoogle_test.sh
@@ -23,9 +23,13 @@
 guice-assistedinject
 guice-library
 guice-servlet
+impl-log4j
 j2objc
+jcl-over-slf4j
 jimfs
 jruby
+log-api
+log-ext
 log4j
 lucene-analyzers-common
 lucene-core
diff --git a/tools/maven/package.bzl b/tools/maven/package.bzl
index b25656d..eacb02b 100644
--- a/tools/maven/package.bzl
+++ b/tools/maven/package.bzl
@@ -40,7 +40,7 @@
         src = {},
         doc = {},
         war = {}):
-    build_cmd = ["bazel_cmd", "build"]
+    build_cmd = ["bazel_cmd", "build", "--java_toolchain=//tools:error_prone_warnings_toolchain_java11"]
     mvn_cmd = ["python", "tools/maven/mvn.py", "-v", version]
     api_cmd = mvn_cmd[:]
     api_targets = []
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index 3c5c22e..0377497 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -16,8 +16,34 @@
 
     maven_jar(
         name = "log4j",
-        artifact = "ch.qos.reload4j:reload4j:1.2.18.1",
-        sha1 = "7075022a11e18c1ad230de5be074e0c691fed17b",
+        artifact = "ch.qos.reload4j:reload4j:1.2.19",
+        sha1 = "4eae9978468c5e885a6fb44df7e2bbc07a20e6ce",
+    )
+
+    SLF4J_VERS = "1.7.36"
+
+    maven_jar(
+        name = "log-api",
+        artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
+        sha1 = "6c62681a2f655b49963a5983b8b0950a6120ae14",
+    )
+
+    maven_jar(
+        name = "log-ext",
+        artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
+        sha1 = "99f282aea4b6dbca04d00f0ade6e5ed61ee7091a",
+    )
+
+    maven_jar(
+        name = "impl-log4j",
+        artifact = "org.slf4j:slf4j-reload4j:" + SLF4J_VERS,
+        sha1 = "db708f7d959dee1857ac524636e85ecf2e1781c1",
+    )
+
+    maven_jar(
+        name = "jcl-over-slf4j",
+        artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
+        sha1 = "d877e195a05aca4a2f1ad2ff14bfec1393af4b5e",
     )
 
     maven_jar(