Class GroupNameNotes

java.lang.Object
com.google.gerrit.server.git.meta.VersionedMetaData
com.google.gerrit.server.group.db.GroupNameNotes

public class GroupNameNotes extends VersionedMetaData
An enforcer of unique names for groups in NoteDb.

The way groups are stored in NoteDb (see GroupConfig) doesn't enforce unique names, even though groups in Gerrit must not have duplicate names. The storage format doesn't allow to quickly look up whether a name has already been used either. That's why we additionally keep a map of name/UUID pairs and manage it with this class.

To claim the name for a new group, create an instance of GroupNameNotes via forNewGroup(Project.NameKey, Repository, AccountGroup.UUID, AccountGroup.NameKey) and call commit(MetaDataUpdate) on it. For renaming, call forRename(Project.NameKey, Repository, AccountGroup.UUID, AccountGroup.NameKey, AccountGroup.NameKey) and also commit the returned GroupNameNotes. Both times, the creation of the GroupNameNotes will fail if the (new) name is already used. Committing the GroupNameNotes is necessary to make the adjustments for real.

The map has an additional benefit: We can quickly iterate over all group name/UUID pairs without having to load all groups completely (which is costly).

Internal details

The map of names is represented by Git notes. They are stored on the branch RefNames.REFS_GROUPNAMES. Each commit on the branch reflects one moment in time of the complete map.

As key for the notes, we use the SHA-1 of the name. As data, they contain a text version of a JGit Config file. That config file has two entries:

  • the name of the group (as clear text)
  • the UUID of the group which currently has this name
  • Method Details

    • forRename

      public static GroupNameNotes forRename(Project.NameKey projectName, org.eclipse.jgit.lib.Repository repository, AccountGroup.UUID groupUuid, AccountGroup.NameKey oldName, AccountGroup.NameKey newName) throws IOException, org.eclipse.jgit.errors.ConfigInvalidException, com.google.gerrit.exceptions.DuplicateKeyException
      Creates an instance of GroupNameNotes for use when renaming a group.

      Note: The returned instance of GroupNameNotes has to be committed via commit(MetaDataUpdate) in order to claim the new name and free up the old one.

      Parameters:
      projectName - the name of the project which holds the commits of the notes
      repository - the repository which holds the commits of the notes
      groupUuid - the UUID of the group which is renamed
      oldName - the current name of the group
      newName - the new name of the group
      Returns:
      an instance of GroupNameNotes configured for a specific renaming of a group
      Throws:
      IOException - if the repository can't be accessed for some reason
      org.eclipse.jgit.errors.ConfigInvalidException - if the note for the specified group doesn't exist or is in an invalid state
      com.google.gerrit.exceptions.DuplicateKeyException - if a group with the new name already exists
    • forNewGroup

      public static GroupNameNotes forNewGroup(Project.NameKey projectName, org.eclipse.jgit.lib.Repository repository, AccountGroup.UUID groupUuid, AccountGroup.NameKey groupName) throws IOException, org.eclipse.jgit.errors.ConfigInvalidException, com.google.gerrit.exceptions.DuplicateKeyException
      Creates an instance of GroupNameNotes for use when creating a new group.

      Note: The returned instance of GroupNameNotes has to be committed via commit(MetaDataUpdate) in order to claim the new name.

      Parameters:
      projectName - the name of the project which holds the commits of the notes
      repository - the repository which holds the commits of the notes
      groupUuid - the UUID of the new group
      groupName - the name of the new group
      Returns:
      an instance of GroupNameNotes configured for a specific group creation
      Throws:
      IOException - if the repository can't be accessed for some reason
      org.eclipse.jgit.errors.ConfigInvalidException - in no case so far
      com.google.gerrit.exceptions.DuplicateKeyException - if a group with the new name already exists
    • loadGroup

      public static Optional<GroupReference> loadGroup(org.eclipse.jgit.lib.Repository repository, AccountGroup.NameKey groupName) throws IOException, org.eclipse.jgit.errors.ConfigInvalidException
      Loads the GroupReference (name/UUID pair) for the group with the specified name.
      Parameters:
      repository - the repository which holds the commits of the notes
      groupName - the name of the group
      Returns:
      the corresponding GroupReference if a group/note with the given name exists
      Throws:
      IOException - if the repository can't be accessed for some reason
      org.eclipse.jgit.errors.ConfigInvalidException - if the note for the specified group is in an invalid state
    • loadAllGroups

      public static com.google.common.collect.ImmutableList<GroupReference> loadAllGroups(org.eclipse.jgit.lib.Repository repository) throws IOException, org.eclipse.jgit.errors.ConfigInvalidException
      Loads the GroupReferences (name/UUID pairs) for all groups.

      Even though group UUIDs should be unique, this class doesn't enforce it. For this reason, it's technically possible that two of the GroupReferences have a duplicate UUID but a different name. In practice, this shouldn't occur unless we introduce a bug in the future.

      Parameters:
      repository - the repository which holds the commits of the notes
      Returns:
      the GroupReferences of all existing groups/notes
      Throws:
      IOException - if the repository can't be accessed for some reason
      org.eclipse.jgit.errors.ConfigInvalidException - if one of the notes is in an invalid state
    • updateAllGroups

      public static void updateAllGroups(org.eclipse.jgit.lib.Repository repository, org.eclipse.jgit.lib.ObjectInserter inserter, org.eclipse.jgit.lib.BatchRefUpdate bru, Collection<GroupReference> groupReferences, org.eclipse.jgit.lib.PersonIdent ident) throws IOException
      Replaces the map of name/UUID pairs with a new version which matches exactly the passed GroupReferences.

      All old entries are discarded and replaced by the new ones.

      This operation also works if the previous map has invalid entries or can't be read anymore.

      Note: This method doesn't flush the ObjectInserter. It doesn't execute the BatchRefUpdate either.

      Parameters:
      repository - the repository which holds the commits of the notes
      inserter - an ObjectInserter for that repository
      bru - a BatchRefUpdate to which this method adds commands
      groupReferences - all GroupReferences (name/UUID pairs) which should be contained in the map of name/UUID pairs
      ident - the PersonIdent which is used as author and committer for commits
      Throws:
      IOException - if the repository can't be accessed for some reason
    • getRefName

      protected String getRefName()
      Description copied from class: VersionedMetaData
      Returns name of the reference storing this configuration.
      Specified by:
      getRefName in class VersionedMetaData
    • onLoad

      protected void onLoad() throws IOException, org.eclipse.jgit.errors.ConfigInvalidException
      Description copied from class: VersionedMetaData
      Set up the metadata, parsing any state from the loaded revision.
      Specified by:
      onLoad in class VersionedMetaData
      Throws:
      IOException
      org.eclipse.jgit.errors.ConfigInvalidException
    • onSave

      protected boolean onSave(org.eclipse.jgit.lib.CommitBuilder commit) throws IOException, org.eclipse.jgit.errors.ConfigInvalidException
      Description copied from class: VersionedMetaData
      Save any changes to the metadata in a commit.
      Specified by:
      onSave in class VersionedMetaData
      Returns:
      true if the commit should proceed, false to abort.
      Throws:
      IOException
      org.eclipse.jgit.errors.ConfigInvalidException
    • getNoteKey

      public static org.eclipse.jgit.lib.ObjectId getNoteKey(AccountGroup.NameKey groupName)