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); } }