All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.account.ExternalIdsUpdate Maven / Gradle / Ivy

There is a newer version: 3.10.0-rc4
Show newest version
// 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.server.account;

import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.account.ExternalId.Key.toAccountExternalIdKeys;
import static com.google.gerrit.server.account.ExternalId.toAccountExternalIds;
import static java.util.stream.Collectors.toSet;

import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.LockFailureException;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

// Updates externalIds in ReviewDb.
public class ExternalIdsUpdate {
  /**
   * Factory to create an ExternalIdsUpdate instance for updating external IDs by the Gerrit server.
   */
  @Singleton
  public static class Server {
    private final AccountCache accountCache;

    @Inject
    public Server(AccountCache accountCache) {
      this.accountCache = accountCache;
    }

    public ExternalIdsUpdate create() {
      return new ExternalIdsUpdate(accountCache);
    }
  }

  @Singleton
  public static class User {
    private final AccountCache accountCache;

    @Inject
    public User(AccountCache accountCache) {
      this.accountCache = accountCache;
    }

    public ExternalIdsUpdate create() {
      return new ExternalIdsUpdate(accountCache);
    }
  }

  @VisibleForTesting
  public static RetryerBuilder retryerBuilder() {
    return RetryerBuilder.newBuilder()
        .retryIfException(e -> e instanceof LockFailureException)
        .withWaitStrategy(
            WaitStrategies.join(
                WaitStrategies.exponentialWait(2, TimeUnit.SECONDS),
                WaitStrategies.randomWait(50, TimeUnit.MILLISECONDS)))
        .withStopStrategy(StopStrategies.stopAfterDelay(10, TimeUnit.SECONDS));
  }

  private final AccountCache accountCache;

  @VisibleForTesting
  public ExternalIdsUpdate(AccountCache accountCache) {
    this.accountCache = accountCache;
  }

  /**
   * Inserts a new external ID.
   *
   * 

If the external ID already exists, the insert fails with {@link OrmDuplicateKeyException}. */ public void insert(ReviewDb db, ExternalId extId) throws IOException, OrmException { insert(db, Collections.singleton(extId)); } /** * Inserts new external IDs. * *

If any of the external ID already exists, the insert fails with {@link * OrmDuplicateKeyException}. */ public void insert(ReviewDb db, Collection extIds) throws IOException, OrmException { db.accountExternalIds().insert(toAccountExternalIds(extIds)); evictAccounts(extIds); } /** * Inserts or updates an external ID. * *

If the external ID already exists, it is overwritten, otherwise it is inserted. */ public void upsert(ReviewDb db, ExternalId extId) throws IOException, OrmException { upsert(db, Collections.singleton(extId)); } /** * Inserts or updates external IDs. * *

If any of the external IDs already exists, it is overwritten. New external IDs are inserted. */ public void upsert(ReviewDb db, Collection extIds) throws IOException, OrmException { db.accountExternalIds().upsert(toAccountExternalIds(extIds)); evictAccounts(extIds); } /** * Deletes an external ID. * *

The deletion fails with {@link IllegalStateException} if there is an existing external ID * that has the same key, but otherwise doesn't match the specified external ID. */ public void delete(ReviewDb db, ExternalId extId) throws IOException, OrmException { delete(db, Collections.singleton(extId)); } /** * Deletes external IDs. * *

The deletion fails with {@link IllegalStateException} if there is an existing external ID * that has the same key as any of the external IDs that should be deleted, but otherwise doesn't * match the that external ID. */ public void delete(ReviewDb db, Collection extIds) throws IOException, OrmException { db.accountExternalIds().delete(toAccountExternalIds(extIds)); evictAccounts(extIds); } /** * Delete an external ID by key. * *

The external ID is only deleted if it belongs to the specified account. If it belongs to * another account the deletion fails with {@link IllegalStateException}. */ public void delete(ReviewDb db, Account.Id accountId, ExternalId.Key extIdKey) throws IOException, OrmException { delete(db, accountId, Collections.singleton(extIdKey)); } /** * Delete external IDs by external ID key. * *

The external IDs are only deleted if they belongs to the specified account. If any of the * external IDs belongs to another account the deletion fails with {@link IllegalStateException}. */ public void delete(ReviewDb db, Account.Id accountId, Collection extIdKeys) throws IOException, OrmException { db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(extIdKeys)); accountCache.evict(accountId); } /** Deletes all external IDs of the specified account. */ public void deleteAll(ReviewDb db, Account.Id accountId) throws IOException, OrmException { delete(db, ExternalId.from(db.accountExternalIds().byAccount(accountId).toList())); } /** * Replaces external IDs for an account by external ID keys. * *

Deletion of external IDs is done before adding the new external IDs. This means if an * external ID key is specified for deletion and an external ID with the same key is specified to * be added, the old external ID with that key is deleted first and then the new external ID is * added (so the external ID for that key is replaced). * *

If any of the specified external IDs belongs to another account the replacement fails with * {@link IllegalStateException}. */ public void replace( ReviewDb db, Account.Id accountId, Collection toDelete, Collection toAdd) throws IOException, OrmException { checkSameAccount(toAdd, accountId); db.accountExternalIds().deleteKeys(toAccountExternalIdKeys(toDelete)); db.accountExternalIds().insert(toAccountExternalIds(toAdd)); accountCache.evict(accountId); } /** * Replaces an external ID. * *

If the specified external IDs belongs to different accounts the replacement fails with * {@link IllegalStateException}. */ public void replace(ReviewDb db, ExternalId toDelete, ExternalId toAdd) throws IOException, OrmException { replace(db, Collections.singleton(toDelete), Collections.singleton(toAdd)); } /** * Replaces external IDs. * *

Deletion of external IDs is done before adding the new external IDs. This means if an * external ID is specified for deletion and an external ID with the same key is specified to be * added, the old external ID with that key is deleted first and then the new external ID is added * (so the external ID for that key is replaced). * *

If the specified external IDs belong to different accounts the replacement fails with {@link * IllegalStateException}. */ public void replace(ReviewDb db, Collection toDelete, Collection toAdd) throws IOException, OrmException { Account.Id accountId = checkSameAccount(Iterables.concat(toDelete, toAdd)); if (accountId == null) { // toDelete and toAdd are empty -> nothing to do return; } replace(db, accountId, toDelete.stream().map(e -> e.key()).collect(toSet()), toAdd); } /** * Checks that all specified external IDs belong to the same account. * * @return the ID of the account to which all specified external IDs belong. */ public static Account.Id checkSameAccount(Iterable extIds) { return checkSameAccount(extIds, null); } /** * Checks that all specified external IDs belong to specified account. If no account is specified * it is checked that all specified external IDs belong to the same account. * * @return the ID of the account to which all specified external IDs belong. */ public static Account.Id checkSameAccount( Iterable extIds, @Nullable Account.Id accountId) { for (ExternalId extId : extIds) { if (accountId == null) { accountId = extId.accountId(); continue; } checkState( accountId.equals(extId.accountId()), "external id %s belongs to account %s, expected account %s", extId.key().get(), extId.accountId().get(), accountId.get()); } return accountId; } private void evictAccounts(Collection extIds) throws IOException { for (Account.Id id : extIds.stream().map(ExternalId::accountId).collect(toSet())) { accountCache.evict(id); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy