com.google.gerrit.server.account.AccountsUpdate.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.server.account.AccountsUpdate.java

Source

// Copyright (C) 2017 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.server.account;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.sql.Timestamp;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;

/** Updates accounts. */
@Singleton
public class AccountsUpdate {
    /**
     * Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
     *
     * <p>The Gerrit server identity will be used as author and committer for all commits that update
     * the accounts.
     */
    @Singleton
    public static class Server {
        private final GitRepositoryManager repoManager;
        private final AllUsersName allUsersName;
        private final Provider<PersonIdent> serverIdent;

        @Inject
        public Server(GitRepositoryManager repoManager, AllUsersName allUsersName,
                @GerritPersonIdent Provider<PersonIdent> serverIdent) {
            this.repoManager = repoManager;
            this.allUsersName = allUsersName;
            this.serverIdent = serverIdent;
        }

        public AccountsUpdate create() {
            PersonIdent i = serverIdent.get();
            return new AccountsUpdate(repoManager, allUsersName, i, i);
        }
    }

    /**
     * Factory to create an AccountsUpdate instance for updating accounts by the current user.
     *
     * <p>The identity of the current user will be used as author for all commits that update the
     * accounts. The Gerrit server identity will be used as committer.
     */
    @Singleton
    public static class User {
        private final GitRepositoryManager repoManager;
        private final AllUsersName allUsersName;
        private final Provider<PersonIdent> serverIdent;
        private final Provider<IdentifiedUser> identifiedUser;

        @Inject
        public User(GitRepositoryManager repoManager, AllUsersName allUsersName,
                @GerritPersonIdent Provider<PersonIdent> serverIdent, Provider<IdentifiedUser> identifiedUser) {
            this.repoManager = repoManager;
            this.allUsersName = allUsersName;
            this.serverIdent = serverIdent;
            this.identifiedUser = identifiedUser;
        }

        public AccountsUpdate create() {
            PersonIdent i = serverIdent.get();
            return new AccountsUpdate(repoManager, allUsersName, createPersonIdent(i, identifiedUser.get()), i);
        }

        private PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
            return user.newCommitterIdent(ident.getWhen(), ident.getTimeZone());
        }
    }

    private final GitRepositoryManager repoManager;
    private final AllUsersName allUsersName;
    private final PersonIdent committerIdent;
    private final PersonIdent authorIdent;

    private AccountsUpdate(GitRepositoryManager repoManager, AllUsersName allUsersName, PersonIdent committerIdent,
            PersonIdent authorIdent) {
        this.repoManager = checkNotNull(repoManager, "repoManager");
        this.allUsersName = checkNotNull(allUsersName, "allUsersName");
        this.committerIdent = checkNotNull(committerIdent, "committerIdent");
        this.authorIdent = checkNotNull(authorIdent, "authorIdent");
    }

    /**
     * Inserts a new account.
     *
     * @throws OrmDuplicateKeyException if the account already exists
     * @throws IOException if updating the user branch fails
     */
    public void insert(ReviewDb db, Account account) throws OrmException, IOException {
        db.accounts().insert(ImmutableSet.of(account));
        createUserBranch(account);
    }

    /**
     * Inserts or updates an account.
     *
     * <p>If the account already exists, it is overwritten, otherwise it is inserted.
     */
    public void upsert(ReviewDb db, Account account) throws OrmException, IOException {
        db.accounts().upsert(ImmutableSet.of(account));
        createUserBranchIfNeeded(account);
    }

    /** Deletes the account. */
    public void delete(ReviewDb db, Account account) throws OrmException, IOException {
        db.accounts().delete(ImmutableSet.of(account));
        deleteUserBranch(account.getId());
    }

    /** Deletes the account. */
    public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
        db.accounts().deleteKeys(ImmutableSet.of(accountId));
        deleteUserBranch(accountId);
    }

    private void createUserBranch(Account account) throws IOException {
        try (Repository repo = repoManager.openRepository(allUsersName);
                ObjectInserter oi = repo.newObjectInserter()) {
            String refName = RefNames.refsUsers(account.getId());
            if (repo.exactRef(refName) != null) {
                throw new IOException(String.format("User branch %s for newly created account %s already exists.",
                        refName, account.getId().get()));
            }
            createUserBranch(repo, oi, committerIdent, authorIdent, account);
        }
    }

    private void createUserBranchIfNeeded(Account account) throws IOException {
        try (Repository repo = repoManager.openRepository(allUsersName);
                ObjectInserter oi = repo.newObjectInserter()) {
            if (repo.exactRef(RefNames.refsUsers(account.getId())) == null) {
                createUserBranch(repo, oi, committerIdent, authorIdent, account);
            }
        }
    }

    public static void createUserBranch(Repository repo, ObjectInserter oi, PersonIdent committerIdent,
            PersonIdent authorIdent, Account account) throws IOException {
        ObjectId id = createInitialEmptyCommit(oi, committerIdent, authorIdent, account.getRegisteredOn());

        String refName = RefNames.refsUsers(account.getId());
        RefUpdate ru = repo.updateRef(refName);
        ru.setExpectedOldObjectId(ObjectId.zeroId());
        ru.setNewObjectId(id);
        ru.setForceUpdate(true);
        ru.setRefLogIdent(committerIdent);
        ru.setRefLogMessage("Create Account", true);
        Result result = ru.update();
        if (result != Result.NEW) {
            throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
        }
    }

    private static ObjectId createInitialEmptyCommit(ObjectInserter oi, PersonIdent committerIdent,
            PersonIdent authorIdent, Timestamp registrationDate) throws IOException {
        CommitBuilder cb = new CommitBuilder();
        cb.setTreeId(emptyTree(oi));
        cb.setCommitter(new PersonIdent(committerIdent, registrationDate));
        cb.setAuthor(new PersonIdent(authorIdent, registrationDate));
        cb.setMessage("Create Account");
        ObjectId id = oi.insert(cb);
        oi.flush();
        return id;
    }

    private static ObjectId emptyTree(ObjectInserter oi) throws IOException {
        return oi.insert(Constants.OBJ_TREE, new byte[] {});
    }

    private void deleteUserBranch(Account.Id accountId) throws IOException {
        try (Repository repo = repoManager.openRepository(allUsersName)) {
            deleteUserBranch(repo, committerIdent, accountId);
        }
    }

    public static void deleteUserBranch(Repository repo, PersonIdent refLogIdent, Account.Id accountId)
            throws IOException {
        String refName = RefNames.refsUsers(accountId);
        Ref ref = repo.exactRef(refName);
        if (ref == null) {
            return;
        }

        RefUpdate ru = repo.updateRef(refName);
        ru.setExpectedOldObjectId(ref.getObjectId());
        ru.setNewObjectId(ObjectId.zeroId());
        ru.setForceUpdate(true);
        ru.setRefLogIdent(refLogIdent);
        ru.setRefLogMessage("Delete Account", true);
        Result result = ru.delete();
        if (result != Result.FORCED) {
            throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
        }
    }
}