Merge branch 'stable-2.15' into master

* stable-2.15:
  GerritSshApi: Use Logger's string formatting
  Revert "Use proper HEAD ref value when creating project"
  Fix creating missing repository
  Fix project deletion logs
  Use logger built-in formatter
  Fix project creation logs

Change-Id: I0c2c6c0638275e91c15d7c2871cb1c3cffa259c6
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
index 55c7072..8b6b8fc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/AutoReloadConfigDecorator.java
@@ -75,11 +75,9 @@
           this.currentConfig = newConfig;
           this.currentConfigTs = lastModified;
           log.info(
-              "Configuration reloaded: "
-                  + currentConfig.getDestinations(FilterType.ALL).size()
-                  + " destinations, "
-                  + discarded
-                  + " replication events discarded");
+              "Configuration reloaded: {} destinations, {} replication events discarded",
+              currentConfig.getDestinations(FilterType.ALL).size(),
+              discarded);
         }
       }
     } catch (Exception e) {
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 193f196..b69bab6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -140,7 +140,7 @@
           builder.add(g.getUUID());
           addRecursiveParents(g.getUUID(), builder, groupIncludeCache);
         } else {
-          repLog.warn(String.format("Group \"%s\" not recognized, removing from authGroup", name));
+          repLog.warn("Group \"{}\" not recognized, removing from authGroup", name);
         }
       }
       remoteUser = replicationUserFactory.create(new ListGroupMembership(builder.build()));
@@ -217,7 +217,7 @@
   public int shutdown() {
     int cnt = 0;
     if (pool != null) {
-      repLog.warn(String.format("Cancelling replication events"));
+      repLog.warn("Cancelling replication events");
 
       cnt = pool.shutdownNow().size();
       pool = null;
@@ -576,8 +576,7 @@
         } else if (remoteNameStyle.equals("basenameOnly")) {
           name = FilenameUtils.getBaseName(name);
         } else if (!remoteNameStyle.equals("slash")) {
-          repLog.debug(
-              String.format("Unknown remoteNameStyle: %s, falling back to slash", remoteNameStyle));
+          repLog.debug("Unknown remoteNameStyle: {}, falling back to slash", remoteNameStyle);
         }
         String replacedPath =
             ReplicationQueue.replaceName(uri.getPath(), name, isSingleProjectMatch());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/GerritSshApi.java b/src/main/java/com/googlesource/gerrit/plugins/replication/GerritSshApi.java
index 98372d8..08dc296 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/GerritSshApi.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/GerritSshApi.java
@@ -80,10 +80,13 @@
       execute(uri, cmd, errStream);
     } catch (IOException e) {
       log.error(
-          String.format(
-              "Error updating HEAD of remote repository at %s to %s:\n"
-                  + "  Exception: %s\n  Command: %s\n  Output: %s",
-              uri, newHead, e, cmd, errStream),
+          "Error updating HEAD of remote repository at {} to {}:\n"
+              + "  Exception: {}\n  Command: {}\n  Output: {}",
+          uri,
+          newHead,
+          e,
+          cmd,
+          errStream,
           e);
       return false;
     }
@@ -111,7 +114,7 @@
       URIish sshUri = toSshUri(uri);
       return sshHelper.executeRemoteSsh(sshUri, cmd, errStream);
     } catch (URISyntaxException e) {
-      log.error(String.format("Cannot convert %s to SSH uri", uri), e);
+      log.error("Cannot convert {} to SSH uri", uri, e);
     }
     return SSH_COMMAND_FAILED;
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
index fac47e7..220ccad 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
@@ -21,9 +21,7 @@
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Sets;
-import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
-import com.google.gerrit.extensions.events.NewProjectCreatedListener;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.metrics.Timer1;
@@ -214,10 +212,10 @@
     if (ALL_REFS.equals(ref)) {
       delta.clear();
       pushAllRefs = true;
-      repLog.trace("Added all refs for replication to " + uri);
+      repLog.trace("Added all refs for replication to {}", uri);
     } else if (!pushAllRefs) {
       delta.add(ref);
-      repLog.trace("Added ref " + ref + " for replication to " + uri);
+      repLog.trace("Added ref {} for replication to {}", ref, uri);
     }
   }
 
@@ -301,13 +299,13 @@
     if (!pool.requestRunway(this)) {
       if (!canceled) {
         repLog.info(
-            "Rescheduling replication to " + uri + " to avoid collision with an in-flight push.");
+            "Rescheduling replication to {} to avoid collision with an in-flight push.", uri);
         pool.reschedule(this, Destination.RetryReason.COLLISION);
       }
       return;
     }
 
-    repLog.info("Replication to " + uri + " started...");
+    repLog.info("Replication to {} started...", uri);
     Timer1.Context context = metrics.start(config.getName());
     try {
       long startedAt = context.getStartTime();
@@ -317,15 +315,11 @@
       runImpl();
       long elapsed = NANOSECONDS.toMillis(context.stop());
       repLog.info(
-          "Replication to "
-              + uri
-              + " completed in "
-              + (elapsed)
-              + "ms, "
-              + (delay)
-              + "ms delay, "
-              + retryCount
-              + " retries");
+          "Replication to {} completed in {}ms, {}ms delay, {} retries",
+          uri,
+          elapsed,
+          delay,
+          retryCount);
     } catch (RepositoryNotFoundException e) {
       stateLog.error(
           "Cannot replicate " + projectName + "; Local repository error: " + e.getMessage(),
@@ -341,7 +335,7 @@
           || msg.contains("Git repository not found")) {
         createRepository();
       } else {
-        repLog.error("Cannot replicate " + projectName + "; Remote repository error: " + msg);
+        repLog.error("Cannot replicate {}; Remote repository error: {}", projectName, msg);
       }
 
     } catch (NoRemoteRepositoryException e) {
@@ -351,12 +345,12 @@
     } catch (TransportException e) {
       Throwable cause = e.getCause();
       if (cause instanceof JSchException && cause.getMessage().startsWith("UnknownHostKey:")) {
-        repLog.error("Cannot replicate to " + uri + ": " + cause.getMessage());
+        repLog.error("Cannot replicate to {}: {}", uri, cause.getMessage());
       } else if (e instanceof LockFailureException) {
         lockRetryCount++;
         // The LockFailureException message contains both URI and reason
         // for this failure.
-        repLog.error("Cannot replicate to " + e.getMessage());
+        repLog.error("Cannot replicate to {}: {}", uri, e.getMessage());
 
         // The remote push operation should be retried.
         if (lockRetryCount <= maxLockRetries) {
@@ -367,16 +361,16 @@
           }
         } else {
           repLog.error(
-              "Giving up after "
-                  + lockRetryCount
-                  + " of this error during replication to "
-                  + e.getMessage());
+              "Giving up after {} occurrences of this error: {} during replication to {}",
+              lockRetryCount,
+              e.getMessage(),
+              uri);
         }
       } else {
         if (canceledWhileRunning.get()) {
           logCanceledWhileRunningException(e);
         } else {
-          repLog.error("Cannot replicate to " + uri, e);
+          repLog.error("Cannot replicate to {}", uri, e);
           // The remote push operation should be retried.
           pool.reschedule(this, Destination.RetryReason.TRANSPORT_ERROR);
         }
@@ -394,40 +388,22 @@
   }
 
   private void logCanceledWhileRunningException(TransportException e) {
-    repLog.info("Cannot replicate to " + uri + ". It was canceled while running", e);
+    repLog.info("Cannot replicate to {}. It was canceled while running", uri, e);
   }
 
   private void createRepository() {
     if (pool.isCreateMissingRepos()) {
       try {
         Ref head = git.exactRef(Constants.HEAD);
-        if (replicationQueue.createProject(projectName, head != null ? head.getName() : null)) {
-          NewProjectCreatedListener.Event event =
-              new NewProjectCreatedListener.Event() {
-                @Override
-                public String getProjectName() {
-                  return projectName.get();
-                }
-
-                @Override
-                public String getHeadName() {
-                  return head != null ? head.getTarget().getName() : null;
-                }
-
-                @Override
-                public NotifyHandling getNotify() {
-                  return NotifyHandling.NONE;
-                }
-              };
-          replicationQueue.onNewProjectCreated(event);
-          repLog.warn("Missing repository created; retry replication to " + uri);
+        if (replicationQueue.createProject(projectName, head != null ? getName(head) : null)) {
+          repLog.warn("Missing repository created; retry replication to {}", uri);
           pool.reschedule(this, Destination.RetryReason.REPOSITORY_MISSING);
         } else {
           repLog.warn(
-              "Missing repository could not be created when replicating "
-                  + uri
-                  + ". You can only create missing repositories locally, over SSH or when "
-                  + "using adminUrl in replication.config. See documentation for more information.");
+              "Missing repository could not be created when replicating {}. "
+                  + "You can only create missing repositories locally, over SSH or when "
+                  + "using adminUrl in replication.config. See documentation for more information.",
+              uri);
         }
       } catch (IOException ioe) {
         stateLog.error(
@@ -440,6 +416,14 @@
     }
   }
 
+  private String getName(Ref ref) {
+    Ref target = ref;
+    while (target.isSymbolic()) {
+      target = target.getTarget();
+    }
+    return target.getName();
+  }
+
   private void runImpl() throws IOException, PermissionBackendException {
     PushResult res;
     try (Transport tn = Transport.open(git, uri)) {
@@ -462,7 +446,7 @@
       return new PushResult();
     }
 
-    repLog.info("Push to " + uri + " references: " + todo);
+    repLog.info("Push to {} references: {}", uri, todo);
 
     return tn.push(NullProgressMonitor.INSTANCE, todo);
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
index 82e68ed..bec4f20 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationFileBasedConfig.java
@@ -82,11 +82,11 @@
   private List<Destination> allDestinations(DestinationFactory destinationFactory)
       throws ConfigInvalidException, IOException {
     if (!config.getFile().exists()) {
-      log.warn("Config file " + config.getFile() + " does not exist; not replicating");
+      log.warn("Config file {} does not exist; not replicating", config.getFile());
       return Collections.emptyList();
     }
     if (config.getFile().length() == 0) {
-      log.info("Config file " + config.getFile() + " is empty; not replicating");
+      log.info("Config file {} is empty; not replicating", config.getFile());
       return Collections.emptyList();
     }
 
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 1c5d346..d73733a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationQueue.java
@@ -101,7 +101,7 @@
     running = false;
     int discarded = config.shutdown();
     if (discarded > 0) {
-      repLog.warn(String.format("Canceled %d replication events during shutdown", discarded));
+      repLog.warn("Canceled {} replication events during shutdown", discarded);
     }
   }
 
@@ -194,7 +194,7 @@
         try {
           uri = new URIish(url);
         } catch (URISyntaxException e) {
-          repLog.warn(String.format("adminURL '%s' is invalid: %s", url, e.getMessage()));
+          repLog.warn("adminURL '{}' is invalid: {}", url, e.getMessage());
           continue;
         }
 
@@ -202,13 +202,13 @@
           String path =
               replaceName(uri.getPath(), projectName.get(), config.isSingleProjectMatch());
           if (path == null) {
-            repLog.warn(String.format("adminURL %s does not contain ${name}", uri));
+            repLog.warn("adminURL {} does not contain ${name}", uri);
             continue;
           }
 
           uri = uri.setPath(path);
           if (!isSSH(uri)) {
-            repLog.warn(String.format("adminURL '%s' is invalid: only SSH is supported", uri));
+            repLog.warn("adminURL '{}' is invalid: only SSH is supported", uri);
             continue;
           }
         }
@@ -238,17 +238,14 @@
       gerritAdmin.createProject(replicateURI, projectName, head);
     } else if (!replicateURI.isRemote()) {
       createLocally(replicateURI, head);
-      repLog.info("Created local repository: " + replicateURI);
     } else if (isSSH(replicateURI)) {
       createRemoteSsh(replicateURI, head);
-      repLog.info("Created remote repository: " + replicateURI);
     } else {
       repLog.warn(
-          String.format(
-              "Cannot create new project on remote site %s."
-                  + " Only local paths and SSH URLs are supported"
-                  + " for remote repository creation",
-              replicateURI));
+          "Cannot create new project on remote site {}."
+              + " Only local paths and SSH URLs are supported"
+              + " for remote repository creation",
+          replicateURI);
       return false;
     }
     return true;
@@ -258,13 +255,14 @@
     try (Repository repo = new FileRepository(uri.getPath())) {
       repo.create(true /* bare */);
 
-      if (head != null) {
+      if (head != null && head.startsWith(Constants.R_REFS)) {
         RefUpdate u = repo.updateRef(Constants.HEAD);
         u.disableRefLog();
         u.link(head);
       }
+      repLog.info("Created local repository: {}", uri);
     } catch (IOException e) {
-      repLog.error(String.format("Error creating local repository %s:\n", uri.getPath()), e);
+      repLog.error("Error creating local repository {}:\n", uri.getPath(), e);
     }
   }
 
@@ -277,14 +275,17 @@
     OutputStream errStream = sshHelper.newErrorBufferStream();
     try {
       sshHelper.executeRemoteSsh(uri, cmd, errStream);
+      repLog.info("Created remote repository: {}", uri);
     } catch (IOException e) {
       repLog.error(
-          String.format(
-              "Error creating remote repository at %s:\n"
-                  + "  Exception: %s\n"
-                  + "  Command: %s\n"
-                  + "  Output: %s",
-              uri, e, cmd, errStream),
+          "Error creating remote repository at {}:\n"
+              + "  Exception: {}\n"
+              + "  Command: {}\n"
+              + "  Output: {}",
+          uri,
+          e,
+          cmd,
+          errStream,
           e);
     }
   }
@@ -295,25 +296,23 @@
       repLog.info("Deleted remote repository: " + replicateURI);
     } else if (!replicateURI.isRemote()) {
       deleteLocally(replicateURI);
-      repLog.info("Deleted local repository: " + replicateURI);
     } else if (isSSH(replicateURI)) {
       deleteRemoteSsh(replicateURI);
-      repLog.info("Deleted remote repository: " + replicateURI);
     } else {
       repLog.warn(
-          String.format(
-              "Cannot delete project on remote site %s."
-                  + " Only local paths and SSH URLs are supported"
-                  + " for remote repository deletion",
-              replicateURI));
+          "Cannot delete project on remote site {}. "
+              + "Only local paths and SSH URLs are supported"
+              + " for remote repository deletion",
+          replicateURI);
     }
   }
 
   private static void deleteLocally(URIish uri) {
     try {
       recursivelyDelete(new File(uri.getPath()));
+      repLog.info("Deleted local repository: {}", uri);
     } catch (IOException e) {
-      repLog.error(String.format("Error deleting local repository %s:\n", uri.getPath()), e);
+      repLog.error("Error deleting local repository {}:\n", uri.getPath(), e);
     }
   }
 
@@ -341,14 +340,17 @@
     OutputStream errStream = sshHelper.newErrorBufferStream();
     try {
       sshHelper.executeRemoteSsh(uri, cmd, errStream);
+      repLog.info("Deleted remote repository: {}", uri);
     } catch (IOException e) {
       repLog.error(
-          String.format(
-              "Error deleting remote repository at %s:\n"
-                  + "  Exception: %s\n"
-                  + "  Command: %s\n"
-                  + "  Output: %s",
-              uri, e, cmd, errStream),
+          "Error deleting remote repository at {}:\n"
+              + "  Exception: {}\n"
+              + "  Command: {}\n"
+              + "  Output: {}",
+          uri,
+          e,
+          cmd,
+          errStream,
           e);
     }
   }
@@ -362,11 +364,10 @@
       updateHeadRemoteSsh(replicateURI, newHead);
     } else {
       repLog.warn(
-          String.format(
-              "Cannot update HEAD of project on remote site %s."
-                  + " Only local paths and SSH URLs are supported"
-                  + " for remote HEAD update.",
-              replicateURI));
+          "Cannot update HEAD of project on remote site {}."
+              + " Only local paths and SSH URLs are supported"
+              + " for remote HEAD update.",
+          replicateURI);
     }
   }
 
@@ -379,12 +380,15 @@
       sshHelper.executeRemoteSsh(uri, cmd, errStream);
     } catch (IOException e) {
       repLog.error(
-          String.format(
-              "Error updating HEAD of remote repository at %s to %s:\n"
-                  + "  Exception: %s\n"
-                  + "  Command: %s\n"
-                  + "  Output: %s",
-              uri, newHead, e, cmd, errStream),
+          "Error updating HEAD of remote repository at {} to {}:\n"
+              + "  Exception: {}\n"
+              + "  Command: {}\n"
+              + "  Output: {}",
+          uri,
+          newHead,
+          e,
+          cmd,
+          errStream,
           e);
     }
   }
@@ -396,8 +400,7 @@
         u.link(newHead);
       }
     } catch (IOException e) {
-      repLog.error(
-          String.format("Failed to update HEAD of repository %s to %s", uri.getPath(), newHead), e);
+      repLog.error("Failed to update HEAD of repository {} to {}", uri.getPath(), newHead, e);
     }
   }