Browse Source

Merge branch 'stable-5.12'

* stable-5.12:
  Retry loose object read upon "Stale file handle" exception
  Ignore missing javadoc in test bundles

Change-Id: I67c613c066a3252f9b0d0a3dcc026b57e10bfe1d
changes/90/182490/1
Matthias Sohn 2 years ago
parent
commit
d46af8c69d

+ 1
- 1
org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs View File

org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private

+ 41
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java View File



import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;


import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;


public class ObjectDirectoryTest extends RepositoryTestCase { public class ObjectDirectoryTest extends RepositoryTestCase {


assertTrue(shallowCommits.isEmpty()); assertTrue(shallowCommits.isEmpty());
} }


@Test
public void testOpenLooseObjectSuppressStaleFileHandleException()
throws Exception {
ObjectId id = ObjectId
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());

LooseObjects mock = mock(LooseObjects.class);
UnpackedObjectCache unpackedObjectCacheMock = mock(
UnpackedObjectCache.class);

Mockito.when(mock.getObjectLoader(any(), any(), any()))
.thenThrow(new IOException("Stale File Handle"));
Mockito.when(mock.open(curs, id)).thenCallRealMethod();
Mockito.when(mock.unpackedObjectCache())
.thenReturn(unpackedObjectCacheMock);

assertNull(mock.open(curs, id));
verify(unpackedObjectCacheMock).remove(id);
}

@Test
public void testOpenLooseObjectPropagatesIOExceptions() throws Exception {
ObjectId id = ObjectId
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());

LooseObjects mock = mock(LooseObjects.class);

Mockito.when(mock.getObjectLoader(any(), any(), any()))
.thenThrow(new IOException("some IO failure"));
Mockito.when(mock.open(curs, id)).thenCallRealMethod();

assertThrows(IOException.class, () -> mock.open(curs, id));
}

@Test @Test
public void testShallowFileCorrupt() throws Exception { public void testShallowFileCorrupt() throws Exception {
FileRepository repository = createBareRepository(); FileRepository repository = createBareRepository();

+ 1
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties View File

logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement. logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement.
logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}. logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}.
logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {} logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {}
looseObjectHandleIsStale=loose-object {0} file handle is stale. retry {1} of {2}
maxCountMustBeNonNegative=max count must be >= 0 maxCountMustBeNonNegative=max count must be >= 0
mergeConflictOnNonNoteEntries=Merge conflict on non-note entries: base = {0}, ours = {1}, theirs = {2} mergeConflictOnNonNoteEntries=Merge conflict on non-note entries: base = {0}, ours = {1}, theirs = {2}
mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs = {2} mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs = {2}

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java View File

/***/ public String logLargerFiletimeDiff; /***/ public String logLargerFiletimeDiff;
/***/ public String logSmallerFiletime; /***/ public String logSmallerFiletime;
/***/ public String logXDGConfigHomeInvalid; /***/ public String logXDGConfigHomeInvalid;
/***/ public String looseObjectHandleIsStale;
/***/ public String maxCountMustBeNonNegative; /***/ public String maxCountMustBeNonNegative;
/***/ public String mergeConflictOnNonNoteEntries; /***/ public String mergeConflictOnNonNoteEntries;
/***/ public String mergeConflictOnNotes; /***/ public String mergeConflictOnNotes;

+ 69
- 13
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java View File

import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.Set; import java.util.Set;


import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult; import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
private static final Logger LOG = LoggerFactory private static final Logger LOG = LoggerFactory
.getLogger(LooseObjects.class); .getLogger(LooseObjects.class);


/**
* Maximum number of attempts to read a loose object for which a stale file
* handle exception is thrown
*/
private final static int MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS = 5;

private final File directory; private final File directory;


private final UnpackedObjectCache unpackedObjectCache; private final UnpackedObjectCache unpackedObjectCache;
} }


void close() { void close() {
unpackedObjectCache.clear();
unpackedObjectCache().clear();
} }


/** {@inheritDoc} */ /** {@inheritDoc} */
} }


boolean hasCached(AnyObjectId id) { boolean hasCached(AnyObjectId id) {
return unpackedObjectCache.isUnpacked(id);
return unpackedObjectCache().isUnpacked(id);
} }


/** /**
} }


ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException { ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
File path = fileFor(id);
int readAttempts = 0;
while (readAttempts < MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS) {
readAttempts++;
File path = fileFor(id);
try {
return getObjectLoader(curs, path, id);
} catch (FileNotFoundException noFile) {
if (path.exists()) {
throw noFile;
}
break;
} catch (IOException e) {
if (!FileUtils.isStaleFileHandleInCausalChain(e)) {
throw e;
}
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(
JGitText.get().looseObjectHandleIsStale, id.name(),
Integer.valueOf(readAttempts), Integer.valueOf(
MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS)));
}
}
}
unpackedObjectCache().remove(id);
return null;
}

/**
* Provides a loader for an objectId
*
* @param curs
* cursor on the database
* @param path
* the path of the loose object
* @param id
* the object id
* @return a loader for the loose file object
* @throws IOException
* when file does not exist or it could not be opened
*/
ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
throws IOException {
try (FileInputStream in = new FileInputStream(path)) { try (FileInputStream in = new FileInputStream(path)) {
unpackedObjectCache.add(id);
unpackedObjectCache().add(id);
return UnpackedObject.open(in, path, id, curs); return UnpackedObject.open(in, path, id, curs);
} catch (FileNotFoundException noFile) {
if (path.exists()) {
throw noFile;
}
unpackedObjectCache.remove(id);
return null;
} }
} }


/**
* <p>
* Getter for the field <code>unpackedObjectCache</code>.
* </p>
* This accessor is particularly useful to allow mocking of this class for
* testing purposes.
*
* @return the cache of the objects currently unpacked.
*/
UnpackedObjectCache unpackedObjectCache() {
return unpackedObjectCache;
}

long getSize(WindowCursor curs, AnyObjectId id) throws IOException { long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
File f = fileFor(id); File f = fileFor(id);
try (FileInputStream in = new FileInputStream(f)) { try (FileInputStream in = new FileInputStream(f)) {
unpackedObjectCache.add(id);
unpackedObjectCache().add(id);
return UnpackedObject.getSize(in, id, curs); return UnpackedObject.getSize(in, id, curs);
} catch (FileNotFoundException noFile) { } catch (FileNotFoundException noFile) {
if (f.exists()) { if (f.exists()) {
throw noFile; throw noFile;
} }
unpackedObjectCache.remove(id);
unpackedObjectCache().remove(id);
return -1; return -1;
} }
} }
Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst), Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
StandardCopyOption.ATOMIC_MOVE); StandardCopyOption.ATOMIC_MOVE);
dst.setReadOnly(); dst.setReadOnly();
unpackedObjectCache.add(id);
unpackedObjectCache().add(id);
return InsertLooseObjectResult.INSERTED; return InsertLooseObjectResult.INSERTED;
} }



Loading…
Cancel
Save