Refactor: Add base class for dealing with versioned meta data on init

Init steps may want to do changes to versioned meta data of projects,
e.g. edit the project config of the All-Projects project.

Each class that offered to modify some of the project meta data was
doing the same things to load and save the meta data. Having this code
in a common base class reduces code duplication.

Change-Id: I043b6611385e4d39ca3ee8fc3804f66be7e8ce67
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
index 1c72600..6739ce0 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/VersionedAuthorizedKeysOnInit.java
@@ -19,48 +19,29 @@
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.gerrit.pgm.init.api.InitFlags;
+import com.google.gerrit.pgm.init.api.VersionedMetaDataOnInit;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.account.AuthorizedKeys;
 import com.google.gerrit.server.account.VersionedAuthorizedKeys;
 import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.VersionedMetaData;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.util.FS;
 
-import java.io.File;
 import java.io.IOException;
-import java.nio.file.Path;
 import java.util.List;
 
-public class VersionedAuthorizedKeysOnInit extends VersionedMetaData {
+public class VersionedAuthorizedKeysOnInit extends VersionedMetaDataOnInit {
   public interface Factory {
     VersionedAuthorizedKeysOnInit create(Account.Id accountId);
   }
 
   private final Account.Id accountId;
-  private final String ref;
-  private final String project;
-  private final SitePaths site;
-  private final InitFlags flags;
-
   private List<Optional<AccountSshKey>> keys;
-  private ObjectId revision;
 
   @Inject
   public VersionedAuthorizedKeysOnInit(
@@ -68,41 +49,19 @@
       SitePaths site,
       InitFlags flags,
       @Assisted Account.Id accountId) {
-
-    this.project = allUsers.get();
-    this.site = site;
-    this.flags = flags;
+    super(flags, site, allUsers.get(), RefNames.refsUsers(accountId));
     this.accountId = accountId;
-    this.ref = RefNames.refsUsers(accountId);
   }
 
   @Override
-  protected String getRefName() {
-    return ref;
-  }
-
   public VersionedAuthorizedKeysOnInit load()
       throws IOException, ConfigInvalidException {
-    File path = getPath();
-    if (path != null) {
-      try (Repository repo = new FileRepository(path)) {
-        load(repo);
-      }
-    }
+    super.load();
     return this;
   }
 
-  private File getPath() {
-    Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath"));
-    if (basePath == null) {
-      throw new IllegalStateException("gerrit.basePath must be configured");
-    }
-    return FileKey.resolve(basePath.resolve(project).toFile(), FS.DETECTED);
-  }
-
   @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
-    revision = getRevision();
     keys = AuthorizedKeys.parse(accountId, readUTF8(AuthorizedKeys.FILE_NAME));
   }
 
@@ -116,50 +75,6 @@
     return key;
   }
 
-  public void save(String message) throws IOException {
-    save(new PersonIdent("Gerrit Initialization", "init@gerrit"), message);
-  }
-
-  private void save(PersonIdent ident, String msg) throws IOException {
-    File path = getPath();
-    if (path == null) {
-      throw new IOException(project + " does not exist.");
-    }
-
-    try (Repository repo = new FileRepository(path);
-        ObjectInserter i = repo.newObjectInserter();
-        ObjectReader r = repo.newObjectReader();
-        RevWalk rw = new RevWalk(reader)) {
-      inserter = i;
-      reader = r;
-
-      RevTree srcTree = revision != null ? rw.parseTree(revision) : null;
-      newTree = readTree(srcTree);
-
-      CommitBuilder commit = new CommitBuilder();
-      commit.setAuthor(ident);
-      commit.setCommitter(ident);
-      commit.setMessage(msg);
-
-      onSave(commit);
-      ObjectId res = newTree.writeTree(inserter);
-      if (res.equals(srcTree)) {
-        return;
-      }
-
-      commit.setTreeId(res);
-      if (revision != null) {
-        commit.addParentId(revision);
-      }
-      ObjectId newRevision = inserter.insert(commit);
-      updateRef(repo, ident, newRevision, "commit: " + msg);
-      revision = newRevision;
-    } finally {
-      inserter = null;
-      reader = null;
-    }
-  }
-
   @Override
   protected boolean onSave(CommitBuilder commit) throws IOException {
     if (Strings.isNullOrEmpty(commit.getMessage())) {
@@ -169,30 +84,4 @@
     saveUTF8(AuthorizedKeys.FILE_NAME, AuthorizedKeys.serialize(keys));
     return true;
   }
-
-  private void updateRef(Repository repo, PersonIdent ident,
-      ObjectId newRevision, String refLogMsg) throws IOException {
-    RefUpdate ru = repo.updateRef(getRefName());
-    ru.setRefLogIdent(ident);
-    ru.setNewObjectId(newRevision);
-    ru.setExpectedOldObjectId(revision);
-    ru.setRefLogMessage(refLogMsg, false);
-    RefUpdate.Result r = ru.update();
-    switch(r) {
-      case FAST_FORWARD:
-      case NEW:
-      case NO_CHANGE:
-        break;
-      case FORCED:
-      case IO_FAILURE:
-      case LOCK_FAILURE:
-      case NOT_ATTEMPTED:
-      case REJECTED:
-      case REJECTED_CURRENT_BRANCH:
-      case RENAMED:
-      default:
-        throw new IOException("Failed to update " + getRefName() + " of "
-            + project + ": " + r.name());
-    }
-  }
 }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
index f7d9d4a..a7ebd33 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -18,73 +18,32 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GroupList;
 import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.git.VersionedMetaData;
 import com.google.inject.Inject;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.internal.storage.file.FileRepository;
 import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.util.FS;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
 import java.io.IOException;
-import java.nio.file.Path;
 
-public class AllProjectsConfig extends VersionedMetaData {
+public class AllProjectsConfig extends VersionedMetaDataOnInit {
 
   private static final Logger log = LoggerFactory.getLogger(AllProjectsConfig.class);
 
-  private final String project;
-  private final SitePaths site;
-  private final InitFlags flags;
-
   private Config cfg;
-  private ObjectId revision;
   private GroupList groupList;
 
   @Inject
   AllProjectsConfig(AllProjectsNameOnInitProvider allProjects, SitePaths site,
       InitFlags flags) {
-    this.project = allProjects.get();
-    this.site = site;
-    this.flags = flags;
+    super(flags, site, allProjects.get(), RefNames.REFS_CONFIG);
 
   }
 
-  @Override
-  protected String getRefName() {
-    return RefNames.REFS_CONFIG;
-  }
-
-  private File getPath() {
-    Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath"));
-    if (basePath == null) {
-      throw new IllegalStateException("gerrit.basePath must be configured");
-    }
-    return FileKey.resolve(basePath.resolve(project).toFile(), FS.DETECTED);
-  }
-
-  public AllProjectsConfig load() throws IOException, ConfigInvalidException {
-    File path = getPath();
-    if (path != null) {
-      try (Repository repo = new FileRepository(path)) {
-        load(repo);
-      }
-    }
-    return this;
-  }
-
   public Config getConfig() {
     return cfg;
   }
@@ -94,10 +53,16 @@
   }
 
   @Override
+  public AllProjectsConfig load()
+      throws IOException, ConfigInvalidException {
+    super.load();
+    return this;
+  }
+
+  @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
     groupList = readGroupList();
     cfg = readConfig(ProjectConfig.PROJECT_CONFIG);
-    revision = getRevision();
   }
 
   private GroupList readGroupList() throws IOException {
@@ -105,96 +70,31 @@
         GroupList.createLoggerSink(GroupList.FILE_NAME, log));
   }
 
-  @Override
-  protected boolean onSave(CommitBuilder commit) throws IOException,
-      ConfigInvalidException {
-    throw new UnsupportedOperationException();
-  }
-
-  public void save(String message) throws IOException {
-    save(new PersonIdent("Gerrit Initialization", "init@gerrit"), message);
-  }
-
-  public void save(String pluginName, String message) throws IOException {
+  public void save(String pluginName, String message)
+      throws IOException, ConfigInvalidException {
     save(new PersonIdent(pluginName, pluginName + "@gerrit"),
         "Update from plugin " + pluginName + ": " + message);
   }
 
-  private void save(PersonIdent ident, String msg) throws IOException {
-    File path = getPath();
-    if (path == null) {
-      throw new IOException("All-Projects does not exist.");
-    }
-
-    try (Repository repo = new FileRepository(path)) {
-      inserter = repo.newObjectInserter();
-      reader = repo.newObjectReader();
-      try (RevWalk rw = new RevWalk(reader)) {
-        RevTree srcTree = revision != null ? rw.parseTree(revision) : null;
-        newTree = readTree(srcTree);
-        saveConfig(ProjectConfig.PROJECT_CONFIG, cfg);
-        saveGroupList();
-        ObjectId res = newTree.writeTree(inserter);
-        if (res.equals(srcTree)) {
-          // If there are no changes to the content, don't create the commit.
-          return;
-        }
-
-        CommitBuilder commit = new CommitBuilder();
-        commit.setAuthor(ident);
-        commit.setCommitter(ident);
-        commit.setMessage(msg);
-        commit.setTreeId(res);
-        if (revision != null) {
-          commit.addParentId(revision);
-        }
-        ObjectId newRevision = inserter.insert(commit);
-        updateRef(repo, ident, newRevision, "commit: " + msg);
-        revision = newRevision;
-      } finally {
-        if (inserter != null) {
-          inserter.close();
-          inserter = null;
-        }
-        if (reader != null) {
-          reader.close();
-          reader = null;
-        }
-      }
-    }
+  @Override
+  protected void save(PersonIdent ident, String msg)
+      throws IOException, ConfigInvalidException {
+    super.save(ident, msg);
 
     // we need to invalidate the JGit cache if the group list is invalidated in
     // an unattended init step
     RepositoryCache.clear();
   }
 
-  private void saveGroupList() throws IOException {
-    saveUTF8(GroupList.FILE_NAME, groupList.asText());
+  @Override
+  protected boolean onSave(CommitBuilder commit) throws IOException,
+      ConfigInvalidException {
+    saveConfig(ProjectConfig.PROJECT_CONFIG, cfg);
+    saveGroupList();
+    return true;
   }
 
-  private void updateRef(Repository repo, PersonIdent ident,
-      ObjectId newRevision, String refLogMsg) throws IOException {
-    RefUpdate ru = repo.updateRef(getRefName());
-    ru.setRefLogIdent(ident);
-    ru.setNewObjectId(newRevision);
-    ru.setExpectedOldObjectId(revision);
-    ru.setRefLogMessage(refLogMsg, false);
-    RefUpdate.Result r = ru.update();
-    switch (r) {
-      case FAST_FORWARD:
-      case NEW:
-      case NO_CHANGE:
-        break;
-      case FORCED:
-      case IO_FAILURE:
-      case LOCK_FAILURE:
-      case NOT_ATTEMPTED:
-      case REJECTED:
-      case REJECTED_CURRENT_BRANCH:
-      case RENAMED:
-      default:
-        throw new IOException("Failed to update " + getRefName() + " of "
-            + project + ": " + r.name());
-    }
+  private void saveGroupList() throws IOException {
+    saveUTF8(GroupList.FILE_NAME, groupList.asText());
   }
 }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
new file mode 100644
index 0000000..b953a0b
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/VersionedMetaDataOnInit.java
@@ -0,0 +1,148 @@
+// Copyright (C) 2016 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.pgm.init.api;
+
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.VersionedMetaData;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.FS;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+
+public abstract class VersionedMetaDataOnInit extends VersionedMetaData {
+
+  private final InitFlags flags;
+  private final SitePaths site;
+  private final String project;
+  private final String ref;
+
+  protected VersionedMetaDataOnInit(InitFlags flags, SitePaths site,
+      String project, String ref) {
+    this.flags = flags;
+    this.site = site;
+    this.project = project;
+    this.ref = ref;
+  }
+
+  @Override
+  protected String getRefName() {
+    return ref;
+  }
+
+  public VersionedMetaDataOnInit load()
+      throws IOException, ConfigInvalidException {
+    File path = getPath();
+    if (path != null) {
+      try (Repository repo = new FileRepository(path)) {
+        load(repo);
+      }
+    }
+    return this;
+  }
+
+  public void save(String message) throws IOException, ConfigInvalidException {
+    save(new PersonIdent("Gerrit Initialization", "init@gerrit"), message);
+  }
+
+  protected void save(PersonIdent ident, String msg)
+      throws IOException, ConfigInvalidException {
+    File path = getPath();
+    if (path == null) {
+      throw new IOException(project + " does not exist.");
+    }
+
+    try (Repository repo = new FileRepository(path);
+        ObjectInserter i = repo.newObjectInserter();
+        ObjectReader r = repo.newObjectReader();
+        RevWalk rw = new RevWalk(r)) {
+      inserter = i;
+      reader = r;
+
+      RevTree srcTree = revision != null ? rw.parseTree(revision) : null;
+      newTree = readTree(srcTree);
+
+      CommitBuilder commit = new CommitBuilder();
+      commit.setAuthor(ident);
+      commit.setCommitter(ident);
+      commit.setMessage(msg);
+
+      onSave(commit);
+
+      ObjectId res = newTree.writeTree(inserter);
+      if (res.equals(srcTree)) {
+        return;
+      }
+      commit.setTreeId(res);
+
+      if (revision != null) {
+        commit.addParentId(revision);
+      }
+      ObjectId newRevision = inserter.insert(commit);
+      updateRef(repo, ident, newRevision, "commit: " + msg);
+      revision = rw.parseCommit(newRevision);
+    } finally {
+      inserter = null;
+      reader = null;
+    }
+  }
+
+  private void updateRef(Repository repo, PersonIdent ident,
+      ObjectId newRevision, String refLogMsg) throws IOException {
+    RefUpdate ru = repo.updateRef(getRefName());
+    ru.setRefLogIdent(ident);
+    ru.setNewObjectId(newRevision);
+    ru.setExpectedOldObjectId(revision);
+    ru.setRefLogMessage(refLogMsg, false);
+    RefUpdate.Result r = ru.update();
+    switch(r) {
+      case FAST_FORWARD:
+      case NEW:
+      case NO_CHANGE:
+        break;
+      case FORCED:
+      case IO_FAILURE:
+      case LOCK_FAILURE:
+      case NOT_ATTEMPTED:
+      case REJECTED:
+      case REJECTED_CURRENT_BRANCH:
+      case RENAMED:
+      default:
+        throw new IOException("Failed to update " + getRefName() + " of "
+            + project + ": " + r.name());
+    }
+  }
+
+  private File getPath() {
+    Path basePath = site.resolve(flags.cfg.getString("gerrit", null, "basePath"));
+    if (basePath == null) {
+      throw new IllegalStateException("gerrit.basePath must be configured");
+    }
+    return FileKey.resolve(basePath.resolve(project).toFile(), FS.DETECTED);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
index eb4d400..6334cd2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -78,7 +78,7 @@
     }
   }
 
-  private RevCommit revision;
+  protected RevCommit revision;
   protected ObjectReader reader;
   protected ObjectInserter inserter;
   protected DirCache newTree;