2 * Copyright (C) 2009, Google Inc.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org
.spearce
.jgit
.lib
;
40 import java
.io
.IOException
;
41 import java
.util
.Collection
;
42 import java
.util
.concurrent
.atomic
.AtomicReference
;
45 * Abstraction of arbitrary object storage.
47 * An object database stores one or more Git objects, indexed by their unique
48 * {@link ObjectId}. Optionally an object database can reference one or more
49 * alternates; other ObjectDatabase instances that are searched in addition to
50 * the current database.
52 * Databases are usually divided into two halves: a half that is considered to
53 * be fast to search, and a half that is considered to be slow to search. When
54 * alternates are present the fast half is fully searched (recursively through
55 * all alternates) before the slow half is considered.
57 public abstract class ObjectDatabase
{
58 /** Constant indicating no alternate databases exist. */
59 protected static final ObjectDatabase
[] NO_ALTERNATES
= {};
61 private final AtomicReference
<ObjectDatabase
[]> alternates
;
63 /** Initialize a new database instance for access. */
64 protected ObjectDatabase() {
65 alternates
= new AtomicReference
<ObjectDatabase
[]>();
69 * Does this database exist yet?
71 * @return true if this database is already created; false if the caller
72 * should invoke {@link #create()} to create this database location.
74 public boolean exists() {
79 * Initialize a new object database at this location.
82 * the database could not be created.
84 public void create() throws IOException
{
85 // Assume no action is required.
89 * Close any resources held by this database and its active alternates.
91 public final void close() {
97 * Close any resources held by this database only; ignoring alternates.
99 * To fully close this database and its referenced alternates, the caller
100 * should instead invoke {@link #close()}.
102 public void closeSelf() {
103 // Assume no action is required.
106 /** Fully close all loaded alternates and clear the alternate list. */
107 public final void closeAlternates() {
108 ObjectDatabase
[] alt
= alternates
.get();
110 alternates
.set(null);
111 for (final ObjectDatabase d
: alt
) {
118 * Does the requested object exist in this database?
120 * Alternates (if present) are searched automatically.
123 * identity of the object to test for existence of.
124 * @return true if the specified object is stored in this database, or any
125 * of the alternate databases.
127 public final boolean hasObject(final AnyObjectId objectId
) {
128 return hasObjectImpl1(objectId
) || hasObjectImpl2(objectId
.name());
131 private final boolean hasObjectImpl1(final AnyObjectId objectId
) {
132 if (hasObject1(objectId
)) {
135 for (final ObjectDatabase alt
: getAlternates()) {
136 if (alt
.hasObjectImpl1(objectId
)) {
140 return tryAgain1() && hasObject1(objectId
);
143 private final boolean hasObjectImpl2(final String objectId
) {
144 if (hasObject2(objectId
)) {
147 for (final ObjectDatabase alt
: getAlternates()) {
148 if (alt
.hasObjectImpl2(objectId
)) {
156 * Fast half of {@link #hasObject(AnyObjectId)}.
159 * identity of the object to test for existence of.
160 * @return true if the specified object is stored in this database.
162 protected abstract boolean hasObject1(AnyObjectId objectId
);
165 * Slow half of {@link #hasObject(AnyObjectId)}.
168 * identity of the object to test for existence of.
169 * @return true if the specified object is stored in this database.
171 protected boolean hasObject2(String objectName
) {
172 // Assume the search took place during hasObject1.
177 * Open an object from this database.
179 * Alternates (if present) are searched automatically.
182 * temporary working space associated with the calling thread.
184 * identity of the object to open.
185 * @return a {@link ObjectLoader} for accessing the data of the named
186 * object, or null if the object does not exist.
187 * @throws IOException
189 public final ObjectLoader
openObject(final WindowCursor curs
,
190 final AnyObjectId objectId
) throws IOException
{
193 ldr
= openObjectImpl1(curs
, objectId
);
198 ldr
= openObjectImpl2(curs
, objectId
.name(), objectId
);
205 private ObjectLoader
openObjectImpl1(final WindowCursor curs
,
206 final AnyObjectId objectId
) throws IOException
{
209 ldr
= openObject1(curs
, objectId
);
213 for (final ObjectDatabase alt
: getAlternates()) {
214 ldr
= alt
.openObjectImpl1(curs
, objectId
);
220 ldr
= openObject1(curs
, objectId
);
228 private ObjectLoader
openObjectImpl2(final WindowCursor curs
,
229 final String objectName
, final AnyObjectId objectId
)
233 ldr
= openObject2(curs
, objectName
, objectId
);
237 for (final ObjectDatabase alt
: getAlternates()) {
238 ldr
= alt
.openObjectImpl2(curs
, objectName
, objectId
);
247 * Fast half of {@link #openObject(WindowCursor, AnyObjectId)}.
250 * temporary working space associated with the calling thread.
252 * identity of the object to open.
253 * @return a {@link ObjectLoader} for accessing the data of the named
254 * object, or null if the object does not exist.
255 * @throws IOException
257 protected abstract ObjectLoader
openObject1(WindowCursor curs
,
258 AnyObjectId objectId
) throws IOException
;
261 * Slow half of {@link #openObject(WindowCursor, AnyObjectId)}.
264 * temporary working space associated with the calling thread.
266 * name of the object to open.
268 * identity of the object to open.
269 * @return a {@link ObjectLoader} for accessing the data of the named
270 * object, or null if the object does not exist.
271 * @throws IOException
273 protected ObjectLoader
openObject2(WindowCursor curs
, String objectName
,
274 AnyObjectId objectId
) throws IOException
{
275 // Assume the search took place during openObject1.
280 * Open the object from all packs containing it.
282 * If any alternates are present, their packs are also considered.
285 * result collection of loaders for this object, filled with
286 * loaders from all packs containing specified object
288 * temporary working space associated with the calling thread.
290 * id of object to search for
291 * @throws IOException
293 final void openObjectInAllPacks(final Collection
<PackedObjectLoader
> out
,
294 final WindowCursor curs
, final AnyObjectId objectId
)
296 openObjectInAllPacks1(out
, curs
, objectId
);
297 for (final ObjectDatabase alt
: getAlternates()) {
298 alt
.openObjectInAllPacks1(out
, curs
, objectId
);
303 * Open the object from all packs containing it.
306 * result collection of loaders for this object, filled with
307 * loaders from all packs containing specified object
309 * temporary working space associated with the calling thread.
311 * id of object to search for
312 * @throws IOException
314 void openObjectInAllPacks1(Collection
<PackedObjectLoader
> out
,
315 WindowCursor curs
, AnyObjectId objectId
) throws IOException
{
316 // Assume no pack support
320 * @return true if the fast-half search should be tried again.
322 protected boolean tryAgain1() {
327 * Get the alternate databases known to this database.
329 * @return the alternate list. Never null, but may be an empty array.
331 public final ObjectDatabase
[] getAlternates() {
332 ObjectDatabase
[] r
= alternates
.get();
334 synchronized (alternates
) {
335 r
= alternates
.get();
338 r
= loadAlternates();
339 } catch (IOException e
) {
350 * Load the list of alternate databases into memory.
352 * This method is invoked by {@link #getAlternates()} if the alternate list
353 * has not yet been populated, or if {@link #closeAlternates()} has been
354 * called on this instance and the alternate list is needed again.
356 * If the alternate array is empty, implementors should consider using the
357 * constant {@link #NO_ALTERNATES}.
359 * @return the alternate list for this database.
360 * @throws IOException
361 * the alternate list could not be accessed. The empty alternate
362 * array {@link #NO_ALTERNATES} will be assumed by the caller.
364 protected ObjectDatabase
[] loadAlternates() throws IOException
{
365 return NO_ALTERNATES
;