LCOV - code coverage report
Current view: top level - server/notedb - AbstractChangeNotes.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 56 67 83.6 %
Date: 2022-11-19 15:00:39 Functions: 14 16 87.5 %

          Line data    Source code
       1             : // Copyright (C) 2014 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.notedb;
      16             : 
      17             : import static java.util.Objects.requireNonNull;
      18             : 
      19             : import com.google.common.annotations.VisibleForTesting;
      20             : import com.google.common.collect.ImmutableSet;
      21             : import com.google.gerrit.common.Nullable;
      22             : import com.google.gerrit.common.UsedAt;
      23             : import com.google.gerrit.entities.Change;
      24             : import com.google.gerrit.entities.Project;
      25             : import com.google.gerrit.exceptions.StorageException;
      26             : import com.google.gerrit.metrics.Timer0;
      27             : import com.google.gerrit.server.config.AllUsersName;
      28             : import com.google.gerrit.server.config.GerritImportedServerIds;
      29             : import com.google.gerrit.server.config.GerritServerId;
      30             : import com.google.gerrit.server.git.GitRepositoryManager;
      31             : import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
      32             : import com.google.gerrit.server.project.NoSuchChangeException;
      33             : import com.google.inject.Inject;
      34             : import com.google.inject.Provider;
      35             : import com.google.inject.Singleton;
      36             : import java.io.IOException;
      37             : import java.util.concurrent.atomic.AtomicBoolean;
      38             : import org.eclipse.jgit.errors.ConfigInvalidException;
      39             : import org.eclipse.jgit.lib.ObjectId;
      40             : import org.eclipse.jgit.lib.Ref;
      41             : import org.eclipse.jgit.lib.Repository;
      42             : 
      43             : /** View of contents at a single ref related to some change. * */
      44             : public abstract class AbstractChangeNotes<T> {
      45             :   @VisibleForTesting
      46             :   @Singleton
      47             :   @UsedAt(UsedAt.Project.PLUGIN_CHECKS)
      48             :   public static class Args {
      49             :     // TODO(dborowitz): Some less smelly way of disabling NoteDb in tests.
      50             :     public final AtomicBoolean failOnLoadForTest;
      51             :     public final ChangeNoteJson changeNoteJson;
      52             :     public final GitRepositoryManager repoManager;
      53             :     public final AllUsersName allUsers;
      54             :     public final NoteDbMetrics metrics;
      55             :     public final String serverId;
      56             :     public final ImmutableSet<String> importedServerIds;
      57             : 
      58             :     // Providers required to avoid dependency cycles.
      59             : 
      60             :     // ChangeNoteCache -> Args
      61             :     public final Provider<ChangeNotesCache> cache;
      62             : 
      63             :     @Inject
      64             :     Args(
      65             :         GitRepositoryManager repoManager,
      66             :         AllUsersName allUsers,
      67             :         ChangeNoteJson changeNoteJson,
      68             :         NoteDbMetrics metrics,
      69             :         Provider<ChangeNotesCache> cache,
      70             :         @GerritServerId String serverId,
      71         152 :         @GerritImportedServerIds ImmutableSet<String> importedServerIds) {
      72         152 :       this.failOnLoadForTest = new AtomicBoolean();
      73         152 :       this.repoManager = repoManager;
      74         152 :       this.allUsers = allUsers;
      75         152 :       this.changeNoteJson = changeNoteJson;
      76         152 :       this.metrics = metrics;
      77         152 :       this.cache = cache;
      78         152 :       this.serverId = serverId;
      79         152 :       this.importedServerIds = importedServerIds;
      80         152 :     }
      81             :   }
      82             : 
      83             :   /** An {@link AutoCloseable} for parsing a single commit into ChangeNotesCommits. */
      84             :   public static class LoadHandle implements AutoCloseable {
      85             :     private final Repository repo;
      86             :     private final ObjectId id;
      87             :     private ChangeNotesRevWalk rw;
      88             : 
      89         103 :     private LoadHandle(Repository repo, @Nullable ObjectId id) {
      90         103 :       this.repo = requireNonNull(repo);
      91             : 
      92         103 :       if (ObjectId.zeroId().equals(id)) {
      93           0 :         id = null;
      94         103 :       } else if (id != null) {
      95         103 :         id = id.copy();
      96             :       }
      97         103 :       this.id = id;
      98         103 :     }
      99             : 
     100             :     public ChangeNotesRevWalk walk() {
     101         103 :       if (rw == null) {
     102         103 :         rw = ChangeNotesCommit.newRevWalk(repo);
     103             :       }
     104         103 :       return rw;
     105             :     }
     106             : 
     107             :     @Nullable
     108             :     public ObjectId id() {
     109         103 :       return id;
     110             :     }
     111             : 
     112             :     @Override
     113             :     public void close() {
     114         103 :       if (rw != null) {
     115         103 :         rw.close();
     116             :       }
     117         103 :     }
     118             :   }
     119             : 
     120             :   protected final Args args;
     121             :   private final Change.Id changeId;
     122             : 
     123             :   private ObjectId revision;
     124             :   private boolean loaded;
     125             : 
     126         103 :   protected AbstractChangeNotes(Args args, Change.Id changeId, @Nullable ObjectId metaSha1) {
     127         103 :     this.args = requireNonNull(args);
     128         103 :     this.changeId = requireNonNull(changeId);
     129         103 :     this.revision = metaSha1;
     130         103 :   }
     131             : 
     132             :   protected AbstractChangeNotes(Args args, Change.Id changeId) {
     133           0 :     this(args, changeId, null);
     134           0 :   }
     135             : 
     136             :   public Change.Id getChangeId() {
     137         103 :     return changeId;
     138             :   }
     139             : 
     140             :   /** Returns revision of the metadata that was loaded. */
     141             :   public ObjectId getRevision() {
     142          19 :     return revision;
     143             :   }
     144             : 
     145             :   public T load() {
     146         103 :     try (Repository repo = args.repoManager.openRepository(getProjectName())) {
     147         103 :       load(repo);
     148         103 :       return self();
     149           1 :     } catch (IOException e) {
     150           1 :       throw new StorageException(e);
     151             :     }
     152             :   }
     153             : 
     154             :   public T load(Repository repo) {
     155         103 :     if (loaded) {
     156         103 :       return self();
     157             :     }
     158             : 
     159         103 :     if (args.failOnLoadForTest.get()) {
     160           0 :       throw new StorageException("Reading from NoteDb is disabled");
     161             :     }
     162         103 :     try (Timer0.Context timer = args.metrics.readLatency.start();
     163             :         // Call openHandle even if reading is disabled, to trigger
     164             :         // auto-rebuilding before this object may get passed to a ChangeUpdate.
     165         103 :         LoadHandle handle = openHandle(repo, revision)) {
     166         103 :       revision = handle.id();
     167         103 :       onLoad(handle);
     168         103 :       loaded = true;
     169           1 :     } catch (ConfigInvalidException | IOException e) {
     170           1 :       throw new StorageException(e);
     171         103 :     }
     172         103 :     return self();
     173             :   }
     174             : 
     175             :   @Nullable
     176             :   protected ObjectId readRef(Repository repo) throws IOException {
     177         103 :     Ref ref = repo.getRefDatabase().exactRef(getRefName());
     178         103 :     return ref != null ? ref.getObjectId() : null;
     179             :   }
     180             : 
     181             :   /**
     182             :    * Open a handle for reading this entity from a repository.
     183             :    *
     184             :    * <p>Implementations may override this method to provide auto-rebuilding behavior.
     185             :    *
     186             :    * @param repo open repository.
     187             :    * @param id SHA1 of the entity to read from the repository. The SHA1 is not sanity checked and is
     188             :    *     assumed to be valid. If null, lookup SHA1 from the /meta ref.
     189             :    * @return handle for reading the entity.
     190             :    * @throws NoSuchChangeException change does not exist.
     191             :    * @throws IOException a repo-level error occurred.
     192             :    */
     193             :   protected LoadHandle openHandle(Repository repo, @Nullable ObjectId id)
     194             :       throws NoSuchChangeException, IOException {
     195         103 :     if (id == null) {
     196         103 :       id = readRef(repo);
     197             :     }
     198             : 
     199         103 :     return new LoadHandle(repo, id);
     200             :   }
     201             : 
     202             :   public T reload() {
     203           0 :     loaded = false;
     204           0 :     return load();
     205             :   }
     206             : 
     207             :   @Nullable
     208             :   public ObjectId loadRevision() {
     209           8 :     if (loaded) {
     210           8 :       return getRevision();
     211             :     }
     212           0 :     try (Repository repo = args.repoManager.openRepository(getProjectName())) {
     213           0 :       Ref ref = repo.getRefDatabase().exactRef(getRefName());
     214           0 :       return ref != null ? ref.getObjectId() : null;
     215           0 :     } catch (IOException e) {
     216           0 :       throw new StorageException(e);
     217             :     }
     218             :   }
     219             : 
     220             :   /** Load default values for any instance variables when NoteDb is disabled. */
     221             :   protected abstract void loadDefaults();
     222             : 
     223             :   /**
     224             :    * Returns the NameKey for the project where the notes should be stored, which is not necessarily
     225             :    * the same as the change's project.
     226             :    */
     227             :   public abstract Project.NameKey getProjectName();
     228             : 
     229             :   /** Returns name of the reference storing this configuration. */
     230             :   protected abstract String getRefName();
     231             : 
     232             :   /** Set up the metadata, parsing any state from the loaded revision. */
     233             :   protected abstract void onLoad(LoadHandle handle)
     234             :       throws NoSuchChangeException, IOException, ConfigInvalidException;
     235             : 
     236             :   @SuppressWarnings("unchecked")
     237             :   protected final T self() {
     238         103 :     return (T) this;
     239             :   }
     240             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750