+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
/**
* The cached instance of an {@link ObjectDirectory}.
* 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)
}
@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();
}
}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
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;
*/
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());
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.
}
/**
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.
* 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
* 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
* 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;
}
}
* 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;
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.
*
* 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);
+ }
}
/**
}
@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();
+ }
}
/**
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)) {
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 (;;) {
}
@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();
}
}
- @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 {
}
}
- @Override
- protected boolean tryAgain1() {
+ boolean tryAgain1() {
final PackList old = packList.get();
if (old.tryAgain(packDirectory.lastModified()))
return old != scanPacks(old);
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)
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 {
@Override
public ObjectDatabase newCachedDatabase() {
+ return newCachedFileObjectDatabase();
+ }
+
+ FileObjectDatabase newCachedFileObjectDatabase() {
return new CachedObjectDirectory(this);
}
}