]> source.dussan.org Git - jgit.git/commitdiff
Refactor alternate object databases below ObjectDirectory 15/915/4
authorShawn O. Pearce <spearce@spearce.org>
Sat, 19 Jun 2010 03:23:13 +0000 (20:23 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Sat, 26 Jun 2010 00:46:41 +0000 (17:46 -0700)
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 <spearce@spearce.org>
org.eclipse.jgit/src/org/eclipse/jgit/lib/AlternateRepositoryDatabase.java [deleted file]
org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDatabase.java [deleted file]
org.eclipse.jgit/src/org/eclipse/jgit/lib/CachedObjectDirectory.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/FileObjectDatabase.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/lib/FileRepository.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.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 (file)
index 64b1254..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * 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}.
- * <p>
- * 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<PackedObjectLoader> 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 (file)
index f593bfc..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
- * 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.
- * <p>
- * 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.
- * <p>
- * 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<PackedObjectLoader> 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();
-       }
-}
index 3724f844635419ed9014d297d9c3b54cdb71d72e..37a96d74f2b8bae376f300914b28a6d3a7b37137 100644 (file)
@@ -46,6 +46,7 @@ package org.eclipse.jgit.lib;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collection;
 
 /**
  * The cached instance of an {@link ObjectDirectory}.
@@ -53,21 +54,26 @@ import java.io.IOException;
  * 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<ObjectId> unpackedObjects = new ObjectIdSubclassMap<ObjectId>();
 
+       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 class CachedObjectDirectory extends CachedObjectDatabase {
        }
 
        @Override
-       protected ObjectLoader openObject2(WindowCursor curs, String objectName,
-                       AnyObjectId objectId) throws IOException {
-               if (unpackedObjects.get(objectId) == null)
-                       return null;
-               return super.openObject2(curs, objectName, objectId);
+       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<PackedObjectLoader> 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
-       protected boolean hasObject1(AnyObjectId objectId) {
-               if (unpackedObjects.get(objectId) != null)
-                       return true; // known to be loose
-               return super.hasObject1(objectId);
+       boolean hasObject1(AnyObjectId objectId) {
+               return unpackedObjects.contains(objectId)
+                               || wrapped.hasObject1(objectId);
        }
 
        @Override
-       protected boolean hasObject2(String name) {
-               return false; // loose objects were tested by hasObject1
+       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 {
+               // 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 (file)
index 0000000..a7bf603
--- /dev/null
@@ -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?
+        * <p>
+        * 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.
+        * <p>
+        * 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();
+               }
+       }
+}
index 0ad558b9493e040bb3a486f789a9edeb102f9cd7..12248fb42572250b88ee62670ef357183776a91c 100644 (file)
@@ -56,6 +56,8 @@ import java.util.Set;
 
 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 @@ public class FileRepository extends Repository {
         */
        public Set<ObjectId> getAdditionalHaves() {
                HashSet<ObjectId> r = new HashSet<ObjectId>();
-               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());
index df52ae02f8853ba1c913c8197fdf5574dea3fecf..c44a7ac6f275424dacc0d2622a3f30a37a6b3228 100644 (file)
@@ -45,30 +45,17 @@ package org.eclipse.jgit.lib;
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Abstraction of arbitrary object storage.
  * <p>
  * 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.
- * <p>
- * 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<ObjectDatabase[]> alternates;
-
        /** Initialize a new database instance for access. */
        protected ObjectDatabase() {
-               alternates = new AtomicReference<ObjectDatabase[]>();
+               // Protected to force extension.
        }
 
        /**
@@ -103,163 +90,21 @@ public abstract class ObjectDatabase {
        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.
-        * <p>
-        * 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?
-        * <p>
-        * 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.
-        * <p>
-        * 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 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.
@@ -269,32 +114,11 @@ public abstract class ObjectDatabase {
         *         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.
-        * <p>
-        * 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 @@ public abstract class ObjectDatabase {
         *            id of object to search for
         * @throws IOException
         */
-       final void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
+       abstract void openObjectInAllPacks(final Collection<PackedObjectLoader> 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<PackedObjectLoader> 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.
-        * <p>
-        * 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.
-        * <p>
-        * 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 @@ public abstract class ObjectDatabase {
         * 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;
        }
 }
index ac3c7bf27925899e01498aff085ce04a2e092ced..c605df9b8a72320354b9dd7c4e711bda55bfc246 100644 (file)
@@ -72,8 +72,18 @@ import org.eclipse.jgit.util.FS;
  * 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.
+ * <p>
+ * Optionally an object database can reference one or more alternates; other
+ * ObjectDatabase instances that are searched in addition to the current
+ * database.
+ * <p>
+ * 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> packList;
 
-       private final File[] alternateObjectDir;
-
        private final FS fs;
 
+       private final AtomicReference<AlternateHandle[]> 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 <code>objects</code> 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<PackList>(NO_PACKS);
                this.fs = fs;
+
+               alternates = new AtomicReference<AlternateHandle[]>();
+               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 class ObjectDirectory extends ObjectDatabase {
        }
 
        @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 class ObjectDirectory extends ObjectDatabase {
                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 @@ public class ObjectDirectory extends ObjectDatabase {
                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 @@ public class ObjectDirectory extends ObjectDatabase {
        }
 
        @Override
-       void openObjectInAllPacks1(final Collection<PackedObjectLoader> out,
+       void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
                        final WindowCursor curs, final AnyObjectId objectId)
                        throws IOException {
                PackList pList = packList.get();
@@ -282,13 +310,11 @@ public class ObjectDirectory extends ObjectDatabase {
                }
        }
 
-       @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 @@ public class ObjectDirectory extends ObjectDatabase {
                }
        }
 
-       @Override
-       protected boolean tryAgain1() {
+       boolean tryAgain1() {
                final PackList old = packList.get();
                if (old.tryAgain(packDirectory.lastModified()))
                        return old != scanPacks(old);
@@ -469,29 +494,36 @@ public class ObjectDirectory extends ObjectDatabase {
                return nameSet;
        }
 
-       @Override
-       protected ObjectDatabase[] loadAlternates() throws IOException {
-               final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(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<AlternateHandle> l = new ArrayList<AlternateHandle>(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 @@ public class ObjectDirectory extends ObjectDatabase {
                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 @@ public class ObjectDirectory extends ObjectDatabase {
 
        @Override
        public ObjectDatabase newCachedDatabase() {
+               return newCachedFileObjectDatabase();
+       }
+
+       FileObjectDatabase newCachedFileObjectDatabase() {
                return new CachedObjectDirectory(this);
        }
 }