View Javadoc
1   /*
2    * Copyright (C) 2017, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.internal.storage.dfs;
45  
46  import java.io.IOException;
47  import java.util.Arrays;
48  import java.util.HashSet;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.Set;
52  import java.util.TreeSet;
53  import java.util.concurrent.locks.ReentrantLock;
54  
55  import org.eclipse.jgit.annotations.Nullable;
56  import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
57  import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
58  import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
59  import org.eclipse.jgit.lib.BatchRefUpdate;
60  import org.eclipse.jgit.lib.NullProgressMonitor;
61  import org.eclipse.jgit.lib.ObjectId;
62  import org.eclipse.jgit.lib.Ref;
63  import org.eclipse.jgit.revwalk.RevWalk;
64  import org.eclipse.jgit.transport.ReceiveCommand;
65  import org.eclipse.jgit.util.RefList;
66  import org.eclipse.jgit.util.RefMap;
67  
68  /**
69   * A {@link org.eclipse.jgit.internal.storage.dfs.DfsRefDatabase} that uses
70   * reftable for storage.
71   * <p>
72   * A {@code DfsRefDatabase} instance is thread-safe.
73   * <p>
74   * Implementors may wish to use
75   * {@link org.eclipse.jgit.internal.storage.dfs.DfsPackDescription#getMaxUpdateIndex()}
76   * as the primary key identifier for a
77   * {@link org.eclipse.jgit.internal.storage.pack.PackExt#REFTABLE} only pack
78   * description, ensuring that when there are competing transactions one wins,
79   * and one will fail.
80   */
81  public class DfsReftableDatabase extends DfsRefDatabase {
82  	final ReftableDatabase reftableDatabase;
83  
84  	private DfsReader ctx;
85  	private DfsReftableStack stack;
86  
87  	/**
88  	 * Initialize the reference database for a repository.
89  	 *
90  	 * @param repo
91  	 *            the repository this database instance manages references for.
92  	 */
93  	protected DfsReftableDatabase(DfsRepository repo) {
94  		super(repo);
95  		reftableDatabase = new ReftableDatabase() {
96  			@Override
97  			public MergedReftable openMergedReftable() throws IOException {
98  				DfsReftableDatabase.this.getLock().lock();
99  				try {
100 					return new MergedReftable(stack().readers());
101 				} finally {
102 					DfsReftableDatabase.this.getLock().unlock();
103 				}
104 			}
105 		};
106 		stack = null;
107 	}
108 
109 	/** {@inheritDoc} */
110 	@Override
111 	public boolean hasVersioning() {
112 		return true;
113 	}
114 
115 	/** {@inheritDoc} */
116 	@Override
117 	public boolean performsAtomicTransactions() {
118 		return true;
119 	}
120 
121 	/** {@inheritDoc} */
122 	@Override
123 	public BatchRefUpdate newBatchUpdate() {
124 		DfsObjDatabase odb = getRepository().getObjectDatabase();
125 		return new DfsReftableBatchRefUpdate(this, odb);
126 	}
127 
128 	/**
129 	 * Get configuration to write new reftables with.
130 	 *
131 	 * @return configuration to write new reftables with.
132 	 */
133 	public ReftableConfig getReftableConfig() {
134 		return new ReftableConfig(getRepository());
135 	}
136 
137 	/**
138 	 * Get the lock protecting this instance's state.
139 	 *
140 	 * @return the lock protecting this instance's state.
141 	 */
142 	protected ReentrantLock getLock() {
143 		return reftableDatabase.getLock();
144 	}
145 
146 	/**
147 	 * Whether to compact reftable instead of extending the stack depth.
148 	 *
149 	 * @return {@code true} if commit of a new small reftable should try to
150 	 *         replace a prior small reftable by performing a compaction,
151 	 *         instead of extending the stack depth.
152 	 */
153 	protected boolean compactDuringCommit() {
154 		return true;
155 	}
156 
157 
158 	/**
159 	 * Obtain a handle to the stack of reftables. Must hold lock.
160 	 *
161 	 * @return (possibly cached) handle to the stack.
162 	 * @throws java.io.IOException
163 	 *             if tables cannot be opened.
164 	 */
165 	protected DfsReftableStack stack() throws IOException {
166 		if (!getLock().isLocked()) {
167 			throw new IllegalStateException("most hold lock to access stack"); //$NON-NLS-1$
168 		}
169 		DfsObjDatabase odb = getRepository().getObjectDatabase();
170 
171 		if (ctx == null) {
172 			ctx = odb.newReader();
173 		}
174 		if (stack == null) {
175 			stack = DfsReftableStack.open(ctx, Arrays.asList(odb.getReftables()));
176 		}
177 		return stack;
178 	}
179 
180 	@Override
181 	public boolean isNameConflicting(String refName) throws IOException {
182 		return reftableDatabase.isNameConflicting(refName, new TreeSet<>(), new HashSet<>());
183 	}
184 
185 	/** {@inheritDoc} */
186 	@Override
187 	public Ref exactRef(String name) throws IOException {
188 		return reftableDatabase.exactRef(name);
189 	}
190 
191 	/** {@inheritDoc} */
192 	@Override
193 	public Map<String, Ref> getRefs(String prefix) throws IOException {
194 		List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
195 		RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
196 		for (Ref r : refs) {
197 			builder.add(r);
198 		}
199 		return new RefMap(prefix, builder.toRefList(), RefList.emptyList(),
200 			RefList.emptyList());
201 	}
202 
203 	/** {@inheritDoc} */
204 	@Override
205 	public List<Ref> getRefsByPrefix(String prefix) throws IOException {
206 
207 		return reftableDatabase.getRefsByPrefix(prefix);
208 	}
209 
210 	/** {@inheritDoc} */
211 	@Override
212 	public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
213 		if (!getReftableConfig().isIndexObjects()) {
214 			return super.getTipsWithSha1(id);
215 		}
216 		return reftableDatabase.getTipsWithSha1(id);
217 	}
218 
219 	/** {@inheritDoc} */
220 	@Override
221 	public boolean hasFastTipsWithSha1() throws IOException {
222 		return reftableDatabase.hasFastTipsWithSha1();
223 	}
224 
225 	/** {@inheritDoc} */
226 	@Override
227 	public Ref" href="../../../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref peel(Ref ref) throws IOException {
228 		Ref oldLeaf = ref.getLeaf();
229 		if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) {
230 			return ref;
231 		}
232 		return recreate(ref, doPeel(oldLeaf), hasVersioning());
233 	}
234 
235 	@Override
236 	boolean exists() throws IOException {
237 		DfsObjDatabase odb = getRepository().getObjectDatabase();
238 		return odb.getReftables().length > 0;
239 	}
240 
241 	@Override
242 	void clearCache() {
243 		getLock().lock();
244 		try {
245 			if (ctx != null) {
246 				ctx.close();
247 				ctx = null;
248 			}
249 			reftableDatabase.clearCache();
250 			if (stack != null) {
251 				stack.close();
252 				stack = null;
253 			}
254 		} finally {
255 			getLock().unlock();
256 		}
257 	}
258 
259 	/** {@inheritDoc} */
260 	@Override
261 	protected boolean compareAndPut(Ref/../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref oldRef, @Nullable Ref newRef)
262 			throws IOException {
263 		ReceiveCommand cmd = ReftableDatabase.toCommand(oldRef, newRef);
264 		try (RevWalkvwalk/RevWalk.html#RevWalk">RevWalk rw = new RevWalk(getRepository())) {
265 			rw.setRetainBody(false);
266 			newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
267 					.execute(rw, NullProgressMonitor.INSTANCE);
268 		}
269 		switch (cmd.getResult()) {
270 		case OK:
271 			return true;
272 		case REJECTED_OTHER_REASON:
273 			throw new IOException(cmd.getMessage());
274 		case LOCK_FAILURE:
275 		default:
276 			return false;
277 		}
278 	}
279 
280 	/** {@inheritDoc} */
281 	@Override
282 	protected boolean compareAndRemove(Ref oldRef) throws IOException {
283 		return compareAndPut(oldRef, null);
284 	}
285 
286 	/** {@inheritDoc} */
287 	@Override
288 	protected RefCache scanAllRefs() throws IOException {
289 		throw new UnsupportedOperationException();
290 	}
291 
292 	@Override
293 	void stored(Ref ref) {
294 		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
295 	}
296 
297 	@Override
298 	void removed(String refName) {
299 		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
300 	}
301 
302 	/** {@inheritDoc} */
303 	@Override
304 	protected void cachePeeledState(Refef="../../../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref oldLeaf, Ref newLeaf) {
305 		// Do not cache peeled state in reftable.
306 	}
307 
308 }