From 133c987f4db0788043b79e33582d0c0d4d6ce09a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 18 Jun 2010 20:23:13 -0700 Subject: [PATCH] Refactor alternate object databases below ObjectDirectory Not every object storage system will have the concept of alternate object databases to search, and even if they do, they may not have the notion of fast-access / slow-access split like we do within the ObjectDirectory code for pack files and loose objects. Push all of that down below the generic API so that it is a hidden detail of the ObjectDirectory and its related supporting classes. Change-Id: I54bc1ca5ff2ac94dfffad1f9a9dad7af202b9523 Signed-off-by: Shawn O. Pearce --- .../jgit/lib/AlternateRepositoryDatabase.java | 147 --------- .../jgit/lib/CachedObjectDatabase.java | 137 --------- .../jgit/lib/CachedObjectDirectory.java | 111 +++++-- .../eclipse/jgit/lib/FileObjectDatabase.java | 199 +++++++++++++ .../org/eclipse/jgit/lib/FileRepository.java | 8 +- .../org/eclipse/jgit/lib/ObjectDatabase.java | 278 +----------------- .../org/eclipse/jgit/lib/ObjectDirectory.java | 125 +++++--- 7 files changed, 387 insertions(+), 618 deletions(-) delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java deleted file mode 100644 index 64b1254cc..000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2010, Constantine Plotnikov - * Copyright (C) 2009, Google Inc. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.lib; - -import java.io.IOException; -import java.util.Collection; - -/** - * An ObjectDatabase of another {@link Repository}. - *

- * This {@code ObjectDatabase} wraps around another {@code Repository}'s object - * database, providing its contents to the caller, and closing the Repository - * when this database is closed. The primary user of this class is - * {@link ObjectDirectory}, when the {@code info/alternates} file points at the - * {@code objects/} directory of another repository. - */ -public final class AlternateRepositoryDatabase extends ObjectDatabase { - private final Repository repository; - - private final ObjectDatabase odb; - - /** - * @param alt - * the alternate repository to wrap and export. - */ - public AlternateRepositoryDatabase(final Repository alt) { - repository = alt; - odb = repository.getObjectDatabase(); - } - - /** @return the alternate repository objects are borrowed from. */ - public Repository getRepository() { - return repository; - } - - @Override - public void closeSelf() { - repository.close(); - } - - @Override - public void create() throws IOException { - repository.create(); - } - - @Override - public ObjectInserter newInserter() { - return odb.newInserter(); - } - - @Override - public boolean exists() { - return odb.exists(); - } - - @Override - protected boolean hasObject1(final AnyObjectId objectId) { - return odb.hasObject1(objectId); - } - - @Override - protected boolean tryAgain1() { - return odb.tryAgain1(); - } - - @Override - protected boolean hasObject2(final String objectName) { - return odb.hasObject2(objectName); - } - - @Override - protected ObjectLoader openObject1(final WindowCursor curs, - final AnyObjectId objectId) throws IOException { - return odb.openObject1(curs, objectId); - } - - @Override - protected ObjectLoader openObject2(final WindowCursor curs, - final String objectName, final AnyObjectId objectId) - throws IOException { - return odb.openObject2(curs, objectName, objectId); - } - - @Override - void openObjectInAllPacks1(final Collection out, - final WindowCursor curs, final AnyObjectId objectId) - throws IOException { - odb.openObjectInAllPacks1(out, curs, objectId); - } - - @Override - protected ObjectDatabase[] loadAlternates() throws IOException { - return odb.getAlternates(); - } - - @Override - protected void closeAlternates(final ObjectDatabase[] alt) { - // Do nothing; these belong to odb to close, not us. - } - - @Override - public ObjectDatabase newCachedDatabase() { - return odb.newCachedDatabase(); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java deleted file mode 100644 index f593bfca4..000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2010, Constantine Plotnikov - * Copyright (C) 2010, JetBrains s.r.o. - * and other copyright owners as documented in the project's IP log. - * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.eclipse.jgit.lib; - -import java.io.IOException; -import java.util.Collection; - -/** - * {@link ObjectDatabase} wrapper providing temporary lookup caching. - *

- * The base class for {@code ObjectDatabase}s that wrap other database instances - * and optimize querying for objects by caching some database dependent - * information. Instances of this class (or any of its subclasses) can be - * returned from the method {@link ObjectDatabase#newCachedDatabase()}. This - * class can be used in scenarios where the database does not change, or when - * changes in the database while some operation is in progress is an acceptable - * risk. - *

- * The default implementation delegates all requests to the wrapped database. - * The instance might be indirectly invalidated if the wrapped instance is - * closed. Closing the delegating instance does not implies closing the wrapped - * instance. For alternative databases, cached instances are used as well. - */ -public class CachedObjectDatabase extends ObjectDatabase { - /** - * The wrapped database instance - */ - protected final ObjectDatabase wrapped; - - /** - * Create the delegating database instance - * - * @param wrapped - * the wrapped object database - */ - public CachedObjectDatabase(ObjectDatabase wrapped) { - this.wrapped = wrapped; - } - - @Override - protected boolean hasObject1(AnyObjectId objectId) { - return wrapped.hasObject1(objectId); - } - - @Override - protected ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId) - throws IOException { - return wrapped.openObject1(curs, objectId); - } - - @Override - protected boolean hasObject2(String objectName) { - return wrapped.hasObject2(objectName); - } - - @Override - protected ObjectDatabase[] loadAlternates() throws IOException { - ObjectDatabase[] loaded = wrapped.getAlternates(); - ObjectDatabase[] result = new ObjectDatabase[loaded.length]; - for (int i = 0; i < loaded.length; i++) { - result[i] = loaded[i].newCachedDatabase(); - } - return result; - } - - @Override - protected ObjectLoader openObject2(WindowCursor curs, String objectName, - AnyObjectId objectId) throws IOException { - return wrapped.openObject2(curs, objectName, objectId); - } - - @Override - void openObjectInAllPacks1(Collection out, - WindowCursor curs, AnyObjectId objectId) throws IOException { - wrapped.openObjectInAllPacks1(out, curs, objectId); - } - - @Override - protected boolean tryAgain1() { - return wrapped.tryAgain1(); - } - - @Override - public ObjectDatabase newCachedDatabase() { - // Note that "this" is not returned since subclasses might actually do something, - // on closeSelf() (for example closing database connections or open repositories). - // The situation might become even more tricky if we will consider alternates. - return wrapped.newCachedDatabase(); - } - - @Override - public ObjectInserter newInserter() { - return wrapped.newInserter(); - } -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java index 3724f8446..37a96d74f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java @@ -46,6 +46,7 @@ import java.io.File; import java.io.IOException; +import java.util.Collection; /** * The cached instance of an {@link ObjectDirectory}. @@ -53,21 +54,26 @@ * This class caches the list of loose objects in memory, so the file system is * not queried with stat calls. */ -public class CachedObjectDirectory extends CachedObjectDatabase { +class CachedObjectDirectory extends FileObjectDatabase { /** * The set that contains unpacked objects identifiers, it is created when * the cached instance is created. */ private final ObjectIdSubclassMap unpackedObjects = new ObjectIdSubclassMap(); + private final ObjectDirectory wrapped; + + private AlternateHandle[] alts; + /** * The constructor * * @param wrapped * the wrapped database */ - public CachedObjectDirectory(ObjectDirectory wrapped) { - super(wrapped); + CachedObjectDirectory(ObjectDirectory wrapped) { + this.wrapped = wrapped; + File objects = wrapped.getDirectory(); String[] fanout = objects.list(); if (fanout == null) @@ -91,22 +97,89 @@ public CachedObjectDirectory(ObjectDirectory wrapped) { } @Override - protected ObjectLoader openObject2(WindowCursor curs, String objectName, + public void close() { + // Don't close anything. + } + + @Override + public ObjectInserter newInserter() { + return wrapped.newInserter(); + } + + @Override + public ObjectDatabase newCachedDatabase() { + return this; + } + + @Override + FileObjectDatabase newCachedFileObjectDatabase() { + return this; + } + + @Override + void openObjectInAllPacks(Collection out, + WindowCursor curs, AnyObjectId objectId) throws IOException { + wrapped.openObjectInAllPacks(out, curs, objectId); + } + + @Override + File getDirectory() { + return wrapped.getDirectory(); + } + + @Override + AlternateHandle[] myAlternates() { + if (alts == null) { + AlternateHandle[] src = wrapped.myAlternates(); + alts = new AlternateHandle[src.length]; + for (int i = 0; i < alts.length; i++) { + FileObjectDatabase s = src[i].db; + alts[i] = new AlternateHandle(s.newCachedFileObjectDatabase()); + } + } + return alts; + } + + @Override + boolean tryAgain1() { + return wrapped.tryAgain1(); + } + + @Override + public boolean hasObject(final AnyObjectId objectId) { + return hasObjectImpl1(objectId); + } + + @Override + boolean hasObject1(AnyObjectId objectId) { + return unpackedObjects.contains(objectId) + || wrapped.hasObject1(objectId); + } + + @Override + public ObjectLoader openObject(final WindowCursor curs, + final AnyObjectId objectId) throws IOException { + return openObjectImpl1(curs, objectId); + } + + @Override + ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId) + throws IOException { + if (unpackedObjects.contains(objectId)) + return wrapped.openObject2(curs, objectId.name(), objectId); + return wrapped.openObject1(curs, objectId); + } + + @Override + boolean hasObject2(String objectId) { + // This method should never be invoked. + throw new UnsupportedOperationException(); + } + + @Override + ObjectLoader openObject2(WindowCursor curs, String objectName, AnyObjectId objectId) throws IOException { - if (unpackedObjects.get(objectId) == null) - return null; - return super.openObject2(curs, objectName, objectId); - } - - @Override - protected boolean hasObject1(AnyObjectId objectId) { - if (unpackedObjects.get(objectId) != null) - return true; // known to be loose - return super.hasObject1(objectId); - } - - @Override - protected boolean hasObject2(String name) { - return false; // loose objects were tested by hasObject1 + // This method should never be invoked. + throw new UnsupportedOperationException(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java new file mode 100644 index 000000000..a7bf60370 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.lib; + +import java.io.File; +import java.io.IOException; + +abstract class FileObjectDatabase extends ObjectDatabase { + /** + * Does the requested object exist in this database? + *

+ * Alternates (if present) are searched automatically. + * + * @param objectId + * identity of the object to test for existence of. + * @return true if the specified object is stored in this database, or any + * of the alternate databases. + */ + public boolean hasObject(final AnyObjectId objectId) { + return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name()); + } + + final boolean hasObjectImpl1(final AnyObjectId objectId) { + if (hasObject1(objectId)) + return true; + + for (final AlternateHandle alt : myAlternates()) { + if (alt.db.hasObjectImpl1(objectId)) + return true; + } + + return tryAgain1() && hasObject1(objectId); + } + + final boolean hasObjectImpl2(final String objectId) { + if (hasObject2(objectId)) + return true; + + for (final AlternateHandle alt : myAlternates()) { + if (alt.db.hasObjectImpl2(objectId)) + return true; + } + + return false; + } + + /** + * Open an object from this database. + *

+ * Alternates (if present) are searched automatically. + * + * @param curs + * temporary working space associated with the calling thread. + * @param objectId + * identity of the object to open. + * @return a {@link ObjectLoader} for accessing the data of the named + * object, or null if the object does not exist. + * @throws IOException + */ + public ObjectLoader openObject(final WindowCursor curs, + final AnyObjectId objectId) throws IOException { + ObjectLoader ldr; + + ldr = openObjectImpl1(curs, objectId); + if (ldr != null) + return ldr; + + ldr = openObjectImpl2(curs, objectId.name(), objectId); + if (ldr != null) + return ldr; + + return null; + } + + final ObjectLoader openObjectImpl1(final WindowCursor curs, + final AnyObjectId objectId) throws IOException { + ObjectLoader ldr; + + ldr = openObject1(curs, objectId); + if (ldr != null) + return ldr; + + for (final AlternateHandle alt : myAlternates()) { + ldr = alt.db.openObjectImpl1(curs, objectId); + if (ldr != null) + return ldr; + } + + if (tryAgain1()) { + ldr = openObject1(curs, objectId); + if (ldr != null) + return ldr; + } + + return null; + } + + final ObjectLoader openObjectImpl2(final WindowCursor curs, + final String objectName, final AnyObjectId objectId) + throws IOException { + ObjectLoader ldr; + + ldr = openObject2(curs, objectName, objectId); + if (ldr != null) + return ldr; + + for (final AlternateHandle alt : myAlternates()) { + ldr = alt.db.openObjectImpl2(curs, objectName, objectId); + if (ldr != null) + return ldr; + } + + return null; + } + + abstract File getDirectory(); + + abstract AlternateHandle[] myAlternates(); + + abstract boolean tryAgain1(); + + abstract boolean hasObject1(AnyObjectId objectId); + + abstract boolean hasObject2(String objectId); + + abstract ObjectLoader openObject1(WindowCursor curs, AnyObjectId objectId) + throws IOException; + + abstract ObjectLoader openObject2(WindowCursor curs, String objectName, + AnyObjectId objectId) throws IOException; + + abstract FileObjectDatabase newCachedFileObjectDatabase(); + + static class AlternateHandle { + final FileObjectDatabase db; + + AlternateHandle(FileObjectDatabase db) { + this.db = db; + } + + void close() { + db.close(); + } + } + + static class AlternateRepository extends AlternateHandle { + final FileRepository repository; + + AlternateRepository(FileRepository r) { + super(r.getObjectDatabase()); + repository = r; + } + + void close() { + repository.close(); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java index 0ad558b94..12248fb42 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java @@ -56,6 +56,8 @@ import org.eclipse.jgit.JGitText; import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.FileObjectDatabase.AlternateHandle; +import org.eclipse.jgit.lib.FileObjectDatabase.AlternateRepository; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; @@ -443,11 +445,11 @@ void openObjectInAllPacks(final AnyObjectId objectId, */ public Set getAdditionalHaves() { HashSet r = new HashSet(); - for (ObjectDatabase d : getObjectDatabase().getAlternates()) { - if (d instanceof AlternateRepositoryDatabase) { + for (AlternateHandle d : objectDatabase. myAlternates()) { + if (d instanceof AlternateRepository) { Repository repo; - repo = ((AlternateRepositoryDatabase) d).getRepository(); + repo = ((AlternateRepository) d).repository; for (Ref ref : repo.getAllRefs().values()) r.add(ref.getObjectId()); r.addAll(repo.getAdditionalHaves()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java index df52ae02f..c44a7ac6f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java @@ -45,30 +45,17 @@ import java.io.IOException; import java.util.Collection; -import java.util.concurrent.atomic.AtomicReference; /** * Abstraction of arbitrary object storage. *

* An object database stores one or more Git objects, indexed by their unique - * {@link ObjectId}. Optionally an object database can reference one or more - * alternates; other ObjectDatabase instances that are searched in addition to - * the current database. - *

- * Databases are usually divided into two halves: a half that is considered to - * be fast to search, and a half that is considered to be slow to search. When - * alternates are present the fast half is fully searched (recursively through - * all alternates) before the slow half is considered. + * {@link ObjectId}. */ public abstract class ObjectDatabase { - /** Constant indicating no alternate databases exist. */ - protected static final ObjectDatabase[] NO_ALTERNATES = {}; - - private final AtomicReference alternates; - /** Initialize a new database instance for access. */ protected ObjectDatabase() { - alternates = new AtomicReference(); + // Protected to force extension. } /** @@ -103,95 +90,21 @@ public void create() throws IOException { public abstract ObjectInserter newInserter(); /** - * Close any resources held by this database and its active alternates. + * Close any resources held by this database. */ - public final void close() { - closeSelf(); - closeAlternates(); - } - - /** - * Close any resources held by this database only; ignoring alternates. - *

- * To fully close this database and its referenced alternates, the caller - * should instead invoke {@link #close()}. - */ - public void closeSelf() { - // Assume no action is required. - } - - /** Fully close all loaded alternates and clear the alternate list. */ - public final void closeAlternates() { - ObjectDatabase[] alt = alternates.get(); - if (alt != null) { - alternates.set(null); - closeAlternates(alt); - } - } + public abstract void close(); /** * Does the requested object exist in this database? - *

- * Alternates (if present) are searched automatically. - * - * @param objectId - * identity of the object to test for existence of. - * @return true if the specified object is stored in this database, or any - * of the alternate databases. - */ - public final boolean hasObject(final AnyObjectId objectId) { - return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.name()); - } - - private final boolean hasObjectImpl1(final AnyObjectId objectId) { - if (hasObject1(objectId)) { - return true; - } - for (final ObjectDatabase alt : getAlternates()) { - if (alt.hasObjectImpl1(objectId)) { - return true; - } - } - return tryAgain1() && hasObject1(objectId); - } - - private final boolean hasObjectImpl2(final String objectId) { - if (hasObject2(objectId)) { - return true; - } - for (final ObjectDatabase alt : getAlternates()) { - if (alt.hasObjectImpl2(objectId)) { - return true; - } - } - return false; - } - - /** - * Fast half of {@link #hasObject(AnyObjectId)}. * * @param objectId * identity of the object to test for existence of. * @return true if the specified object is stored in this database. */ - protected abstract boolean hasObject1(AnyObjectId objectId); - - /** - * Slow half of {@link #hasObject(AnyObjectId)}. - * - * @param objectName - * identity of the object to test for existence of. - * @return true if the specified object is stored in this database. - */ - protected boolean hasObject2(String objectName) { - // Assume the search took place during hasObject1. - return false; - } + public abstract boolean hasObject(AnyObjectId objectId); /** * Open an object from this database. - *

- * Alternates (if present) are searched automatically. * * @param curs * temporary working space associated with the calling thread. @@ -201,100 +114,11 @@ protected boolean hasObject2(String objectName) { * object, or null if the object does not exist. * @throws IOException */ - public final ObjectLoader openObject(final WindowCursor curs, - final AnyObjectId objectId) throws IOException { - ObjectLoader ldr; - - ldr = openObjectImpl1(curs, objectId); - if (ldr != null) { - return ldr; - } - - ldr = openObjectImpl2(curs, objectId.name(), objectId); - if (ldr != null) { - return ldr; - } - return null; - } - - private ObjectLoader openObjectImpl1(final WindowCursor curs, - final AnyObjectId objectId) throws IOException { - ObjectLoader ldr; - - ldr = openObject1(curs, objectId); - if (ldr != null) { - return ldr; - } - for (final ObjectDatabase alt : getAlternates()) { - ldr = alt.openObjectImpl1(curs, objectId); - if (ldr != null) { - return ldr; - } - } - if (tryAgain1()) { - ldr = openObject1(curs, objectId); - if (ldr != null) { - return ldr; - } - } - return null; - } - - private ObjectLoader openObjectImpl2(final WindowCursor curs, - final String objectName, final AnyObjectId objectId) - throws IOException { - ObjectLoader ldr; - - ldr = openObject2(curs, objectName, objectId); - if (ldr != null) { - return ldr; - } - for (final ObjectDatabase alt : getAlternates()) { - ldr = alt.openObjectImpl2(curs, objectName, objectId); - if (ldr != null) { - return ldr; - } - } - return null; - } - - /** - * Fast half of {@link #openObject(WindowCursor, AnyObjectId)}. - * - * @param curs - * temporary working space associated with the calling thread. - * @param objectId - * identity of the object to open. - * @return a {@link ObjectLoader} for accessing the data of the named - * object, or null if the object does not exist. - * @throws IOException - */ - protected abstract ObjectLoader openObject1(WindowCursor curs, + public abstract ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId) throws IOException; - /** - * Slow half of {@link #openObject(WindowCursor, AnyObjectId)}. - * - * @param curs - * temporary working space associated with the calling thread. - * @param objectName - * name of the object to open. - * @param objectId - * identity of the object to open. - * @return a {@link ObjectLoader} for accessing the data of the named - * object, or null if the object does not exist. - * @throws IOException - */ - protected ObjectLoader openObject2(WindowCursor curs, String objectName, - AnyObjectId objectId) throws IOException { - // Assume the search took place during openObject1. - return null; - } - /** * Open the object from all packs containing it. - *

- * If any alternates are present, their packs are also considered. * * @param out * result collection of loaders for this object, filled with @@ -305,92 +129,9 @@ protected ObjectLoader openObject2(WindowCursor curs, String objectName, * id of object to search for * @throws IOException */ - final void openObjectInAllPacks(final Collection out, + abstract void openObjectInAllPacks(final Collection out, final WindowCursor curs, final AnyObjectId objectId) - throws IOException { - openObjectInAllPacks1(out, curs, objectId); - for (final ObjectDatabase alt : getAlternates()) { - alt.openObjectInAllPacks1(out, curs, objectId); - } - } - - /** - * Open the object from all packs containing it. - * - * @param out - * result collection of loaders for this object, filled with - * loaders from all packs containing specified object - * @param curs - * temporary working space associated with the calling thread. - * @param objectId - * id of object to search for - * @throws IOException - */ - void openObjectInAllPacks1(Collection out, - WindowCursor curs, AnyObjectId objectId) throws IOException { - // Assume no pack support - } - - /** - * @return true if the fast-half search should be tried again. - */ - protected boolean tryAgain1() { - return false; - } - - /** - * Get the alternate databases known to this database. - * - * @return the alternate list. Never null, but may be an empty array. - */ - public final ObjectDatabase[] getAlternates() { - ObjectDatabase[] r = alternates.get(); - if (r == null) { - synchronized (alternates) { - r = alternates.get(); - if (r == null) { - try { - r = loadAlternates(); - } catch (IOException e) { - r = NO_ALTERNATES; - } - alternates.set(r); - } - } - } - return r; - } - - /** - * Load the list of alternate databases into memory. - *

- * This method is invoked by {@link #getAlternates()} if the alternate list - * has not yet been populated, or if {@link #closeAlternates()} has been - * called on this instance and the alternate list is needed again. - *

- * If the alternate array is empty, implementors should consider using the - * constant {@link #NO_ALTERNATES}. - * - * @return the alternate list for this database. - * @throws IOException - * the alternate list could not be accessed. The empty alternate - * array {@link #NO_ALTERNATES} will be assumed by the caller. - */ - protected ObjectDatabase[] loadAlternates() throws IOException { - return NO_ALTERNATES; - } - - /** - * Close the list of alternates returned by {@link #loadAlternates()}. - * - * @param alt - * the alternate list, from {@link #loadAlternates()}. - */ - protected void closeAlternates(ObjectDatabase[] alt) { - for (final ObjectDatabase d : alt) { - d.close(); - } - } + throws IOException; /** * Create a new cached database instance over this database. This instance might @@ -398,9 +139,8 @@ protected void closeAlternates(ObjectDatabase[] alt) { * done after instance creation might fail to be noticed. * * @return new cached database instance - * @see CachedObjectDatabase */ public ObjectDatabase newCachedDatabase() { - return new CachedObjectDatabase(this); + return this; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java index ac3c7bf27..c605df9b8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java @@ -72,8 +72,18 @@ * where objects are stored loose by hashing them into directories by their * {@link ObjectId}, or are stored in compressed containers known as * {@link PackFile}s. + *

+ * Optionally an object database can reference one or more alternates; other + * ObjectDatabase instances that are searched in addition to the current + * database. + *

+ * Databases are divided into two halves: a half that is considered to be fast + * to search (the {@code PackFile}s), and a half that is considered to be slow + * to search (loose objects). When alternates are present the fast half is fully + * searched (recursively through all alternates) before the slow half is + * considered. */ -public class ObjectDirectory extends ObjectDatabase { +public class ObjectDirectory extends FileObjectDatabase { private static final PackList NO_PACKS = new PackList(-1, -1, new PackFile[0]); private final Config config; @@ -88,10 +98,10 @@ public class ObjectDirectory extends ObjectDatabase { private final AtomicReference packList; - private final File[] alternateObjectDir; - private final FS fs; + private final AtomicReference alternates; + /** * Initialize a reference to an on-disk object directory. * @@ -99,21 +109,33 @@ public class ObjectDirectory extends ObjectDatabase { * configuration this directory consults for write settings. * @param dir * the location of the objects directory. - * @param alternateObjectDir + * @param alternatePaths * a list of alternate object directories * @param fs - * the file system abstraction which will be necessary to - * perform certain file system operations. + * the file system abstraction which will be necessary to perform + * certain file system operations. + * @throws IOException + * an alternate object cannot be opened. */ - public ObjectDirectory(final Config cfg, final File dir, File[] alternateObjectDir, FS fs) { + public ObjectDirectory(final Config cfg, final File dir, + File[] alternatePaths, FS fs) throws IOException { config = cfg; objects = dir; - this.alternateObjectDir = alternateObjectDir; infoDirectory = new File(objects, "info"); packDirectory = new File(objects, "pack"); alternatesFile = new File(infoDirectory, "alternates"); packList = new AtomicReference(NO_PACKS); this.fs = fs; + + alternates = new AtomicReference(); + if (alternatePaths != null) { + AlternateHandle[] alt; + + alt = new AlternateHandle[alternatePaths.length]; + for (int i = 0; i < alternatePaths.length; i++) + alt[i] = openAlternate(alternatePaths[i]); + alternates.set(alt); + } } /** @@ -141,11 +163,19 @@ public ObjectInserter newInserter() { } @Override - public void closeSelf() { + public void close() { final PackList packs = packList.get(); packList.set(NO_PACKS); for (final PackFile p : packs.packs) p.close(); + + // Fully close all loaded alternates and clear the alternate list. + AlternateHandle[] alt = alternates.get(); + if (alt != null) { + alternates.set(null); + for(final AlternateHandle od : alt) + od.close(); + } } /** @@ -209,8 +239,7 @@ public String toString() { return "ObjectDirectory[" + getDirectory() + "]"; } - @Override - protected boolean hasObject1(final AnyObjectId objectId) { + boolean hasObject1(final AnyObjectId objectId) { for (final PackFile p : packList.get().packs) { try { if (p.hasObject(objectId)) { @@ -228,8 +257,7 @@ protected boolean hasObject1(final AnyObjectId objectId) { return false; } - @Override - protected ObjectLoader openObject1(final WindowCursor curs, + ObjectLoader openObject1(final WindowCursor curs, final AnyObjectId objectId) throws IOException { PackList pList = packList.get(); SEARCH: for (;;) { @@ -256,7 +284,7 @@ protected ObjectLoader openObject1(final WindowCursor curs, } @Override - void openObjectInAllPacks1(final Collection out, + void openObjectInAllPacks(final Collection out, final WindowCursor curs, final AnyObjectId objectId) throws IOException { PackList pList = packList.get(); @@ -282,13 +310,11 @@ void openObjectInAllPacks1(final Collection out, } } - @Override - protected boolean hasObject2(final String objectName) { + boolean hasObject2(final String objectName) { return fileFor(objectName).exists(); } - @Override - protected ObjectLoader openObject2(final WindowCursor curs, + ObjectLoader openObject2(final WindowCursor curs, final String objectName, final AnyObjectId objectId) throws IOException { try { @@ -298,8 +324,7 @@ protected ObjectLoader openObject2(final WindowCursor curs, } } - @Override - protected boolean tryAgain1() { + boolean tryAgain1() { final PackList old = packList.get(); if (old.tryAgain(packDirectory.lastModified())) return old != scanPacks(old); @@ -469,29 +494,36 @@ private Set listPackDirectory() { return nameSet; } - @Override - protected ObjectDatabase[] loadAlternates() throws IOException { - final List l = new ArrayList(4); - if (alternateObjectDir != null) { - for (File d : alternateObjectDir) { - l.add(openAlternate(d)); - } - } else { - final BufferedReader br = open(alternatesFile); - try { - String line; - while ((line = br.readLine()) != null) { - l.add(openAlternate(line)); + AlternateHandle[] myAlternates() { + AlternateHandle[] alt = alternates.get(); + if (alt == null) { + synchronized (alternates) { + alt = alternates.get(); + if (alt == null) { + try { + alt = loadAlternates(); + } catch (IOException e) { + alt = new AlternateHandle[0]; + } + alternates.set(alt); } - } finally { - br.close(); } } + return alt; + } - if (l.isEmpty()) { - return NO_ALTERNATES; + private AlternateHandle[] loadAlternates() throws IOException { + final List l = new ArrayList(4); + final BufferedReader br = open(alternatesFile); + try { + String line; + while ((line = br.readLine()) != null) { + l.add(openAlternate(line)); + } + } finally { + br.close(); } - return l.toArray(new ObjectDatabase[l.size()]); + return l.toArray(new AlternateHandle[l.size()]); } private static BufferedReader open(final File f) @@ -499,19 +531,22 @@ private static BufferedReader open(final File f) return new BufferedReader(new FileReader(f)); } - private ObjectDatabase openAlternate(final String location) + private AlternateHandle openAlternate(final String location) throws IOException { final File objdir = fs.resolve(objects, location); return openAlternate(objdir); } - private ObjectDatabase openAlternate(File objdir) throws IOException { + private AlternateHandle openAlternate(File objdir) throws IOException { final File parent = objdir.getParentFile(); if (FileKey.isGitRepository(parent, fs)) { - final Repository db = RepositoryCache.open(FileKey.exact(parent, fs)); - return new AlternateRepositoryDatabase(db); + FileKey key = FileKey.exact(parent, fs); + FileRepository db = (FileRepository) RepositoryCache.open(key); + return new AlternateRepository(db); } - return new ObjectDirectory(config, objdir, null, fs); + + ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs); + return new AlternateHandle(db); } private static final class PackList { @@ -576,6 +611,10 @@ boolean tryAgain(final long currLastModified) { @Override public ObjectDatabase newCachedDatabase() { + return newCachedFileObjectDatabase(); + } + + FileObjectDatabase newCachedFileObjectDatabase() { return new CachedObjectDirectory(this); } }