Line data Source code
1 : // Copyright (C) 2018 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.nio.charset.StandardCharsets.UTF_8;
18 : import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
19 :
20 : import com.google.auto.value.AutoValue;
21 : import com.google.common.annotations.VisibleForTesting;
22 : import com.google.common.base.CharMatcher;
23 : import com.google.common.flogger.FluentLogger;
24 : import com.google.common.primitives.Ints;
25 : import com.google.gerrit.common.Nullable;
26 : import com.google.gerrit.entities.Project;
27 : import com.google.gerrit.exceptions.StorageException;
28 : import com.google.gerrit.git.RefUpdateUtil;
29 : import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
30 : import java.io.IOException;
31 : import java.util.Optional;
32 : import org.eclipse.jgit.errors.IncorrectObjectTypeException;
33 : import org.eclipse.jgit.lib.AnyObjectId;
34 : import org.eclipse.jgit.lib.ObjectId;
35 : import org.eclipse.jgit.lib.ObjectInserter;
36 : import org.eclipse.jgit.lib.ObjectLoader;
37 : import org.eclipse.jgit.lib.ObjectReader;
38 : import org.eclipse.jgit.lib.Ref;
39 : import org.eclipse.jgit.lib.RefUpdate;
40 : import org.eclipse.jgit.lib.Repository;
41 : import org.eclipse.jgit.revwalk.RevWalk;
42 :
43 : /** An object blob in a Git repository that stores a single integer value. */
44 : @AutoValue
45 151 : public abstract class IntBlob {
46 151 : private static final FluentLogger logger = FluentLogger.forEnclosingClass();
47 :
48 : public static Optional<IntBlob> parse(Repository repo, String refName) throws IOException {
49 139 : try (ObjectReader or = repo.newObjectReader()) {
50 139 : return parse(repo, refName, or);
51 : }
52 : }
53 :
54 : public static Optional<IntBlob> parse(Repository repo, String refName, RevWalk rw)
55 : throws IOException {
56 151 : return parse(repo, refName, rw.getObjectReader());
57 : }
58 :
59 : private static Optional<IntBlob> parse(Repository repo, String refName, ObjectReader or)
60 : throws IOException {
61 151 : Ref ref = repo.exactRef(refName);
62 151 : if (ref == null) {
63 151 : return Optional.empty();
64 : }
65 151 : ObjectId id = ref.getObjectId();
66 151 : ObjectLoader ol = or.open(id, OBJ_BLOB);
67 151 : if (ol.getType() != OBJ_BLOB) {
68 : // In theory this should be thrown by open but not all implementations may do it properly
69 : // (certainly InMemoryRepository doesn't).
70 0 : throw new IncorrectObjectTypeException(id, OBJ_BLOB);
71 : }
72 151 : String str = CharMatcher.whitespace().trimFrom(new String(ol.getCachedBytes(), UTF_8));
73 151 : Integer value = Ints.tryParse(str);
74 151 : if (value == null) {
75 1 : throw new StorageException("invalid value in " + refName + " blob at " + id.name());
76 : }
77 151 : return Optional.of(IntBlob.create(id, value));
78 : }
79 :
80 : public static RefUpdate tryStore(
81 : Repository repo,
82 : RevWalk rw,
83 : Project.NameKey projectName,
84 : String refName,
85 : @Nullable ObjectId oldId,
86 : int val,
87 : GitReferenceUpdated gitRefUpdated)
88 : throws IOException {
89 : ObjectId newId;
90 151 : try (ObjectInserter ins = repo.newObjectInserter()) {
91 151 : logger.atFine().log(
92 : "storing value %d on %s in %s (oldId: %s)",
93 151 : val, refName, projectName, oldId == null ? "null" : oldId.name());
94 151 : newId = ins.insert(OBJ_BLOB, Integer.toString(val).getBytes(UTF_8));
95 151 : ins.flush();
96 151 : logger.atFine().log(
97 151 : "successfully stored %d on %s as %s in %s", val, refName, newId.name(), projectName);
98 : }
99 151 : RefUpdate ru = repo.updateRef(refName);
100 151 : if (oldId != null) {
101 151 : ru.setExpectedOldObjectId(oldId);
102 : }
103 151 : ru.disableRefLog();
104 151 : ru.setNewObjectId(newId);
105 151 : ru.setForceUpdate(true); // Required for non-commitish updates.
106 151 : RefUpdate.Result result = ru.update(rw);
107 151 : if (refUpdated(result)) {
108 151 : gitRefUpdated.fire(projectName, ru, null);
109 : }
110 151 : return ru;
111 : }
112 :
113 : public static void store(
114 : Repository repo,
115 : RevWalk rw,
116 : Project.NameKey projectName,
117 : String refName,
118 : @Nullable ObjectId oldId,
119 : int val,
120 : GitReferenceUpdated gitRefUpdated)
121 : throws IOException {
122 151 : RefUpdateUtil.checkResult(tryStore(repo, rw, projectName, refName, oldId, val, gitRefUpdated));
123 151 : }
124 :
125 : private static boolean refUpdated(RefUpdate.Result result) {
126 151 : return result == RefUpdate.Result.NEW || result == RefUpdate.Result.FORCED;
127 : }
128 :
129 : @VisibleForTesting
130 : static IntBlob create(AnyObjectId id, int value) {
131 151 : return new AutoValue_IntBlob(id.copy(), value);
132 : }
133 :
134 : public abstract ObjectId id();
135 :
136 : public abstract int value();
137 : }
|