diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters new file mode 100644 index 000000000..a8583ce82 --- /dev/null +++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java index c5b6a6787..3bb2899b9 100644 --- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java +++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectUploadListener.java @@ -48,6 +48,7 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.nio.file.Path; import java.util.logging.Level; import java.util.logging.Logger; @@ -87,6 +88,29 @@ public class ObjectUploadListener implements ReadListener { private final ByteBuffer buffer = ByteBuffer.allocateDirect(8192); + private final Path path; + + private long uploaded; + + private Callback callback; + + /** + * Callback invoked after object upload completed. + * + * @since 5.1.7 + */ + public interface Callback { + /** + * Notified after object upload completed. + * + * @param path + * path to the object on the backend + * @param size + * uploaded size in bytes + */ + void uploadCompleted(String path, long size); + } + /** * Constructor for ObjectUploadListener. * @@ -113,9 +137,24 @@ public class ObjectUploadListener implements ReadListener { this.inChannel = Channels.newChannel(in); this.out = repository.getOutputStream(id); this.channel = Channels.newChannel(out); + this.path = repository.getPath(id); + this.uploaded = 0L; response.setContentType(Constants.CONTENT_TYPE_GIT_LFS_JSON); } + /** + * Set the callback to invoke after upload completed. + * + * @param callback + * the callback + * @return {@code this}. + * @since 5.1.7 + */ + public ObjectUploadListener setCallback(Callback callback) { + this.callback = callback; + return this; + } + /** * {@inheritDoc} * @@ -126,12 +165,13 @@ public class ObjectUploadListener implements ReadListener { while (in.isReady()) { if (inChannel.read(buffer) > 0) { buffer.flip(); - channel.write(buffer); + uploaded += Integer.valueOf(channel.write(buffer)).longValue(); buffer.compact(); } else { buffer.flip(); while (buffer.hasRemaining()) { - channel.write(buffer); + uploaded += Integer.valueOf(channel.write(buffer)) + .longValue(); } close(); return; @@ -159,6 +199,9 @@ public class ObjectUploadListener implements ReadListener { if (!response.isCommitted()) { response.setStatus(HttpServletResponse.SC_OK); } + if (callback != null) { + callback.uploadCompleted(path.toString(), uploaded); + } } finally { context.complete(); } diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 39cf2dac2..9b0526c0f 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -8,6 +8,20 @@ + + + + + + + + + + + + + + diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java index a83de0626..b238ab471 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java @@ -60,9 +60,24 @@ public class PackInvalidException extends IOException { * * @param path * path of the invalid pack file. + * @deprecated Use {@link #PackInvalidException(File, Throwable)}. */ + @Deprecated public PackInvalidException(File path) { - this(path.getAbsolutePath()); + this(path, null); + } + + /** + * Construct a pack invalid error with cause. + * + * @param path + * path of the invalid pack file. + * @param cause + * cause of the pack file becoming invalid. + * @since 4.5.7 + */ + public PackInvalidException(File path, Throwable cause) { + this(path.getAbsolutePath(), cause); } /** @@ -70,8 +85,23 @@ public class PackInvalidException extends IOException { * * @param path * path of the invalid pack file. + * @deprecated Use {@link #PackInvalidException(String, Throwable)}. */ + @Deprecated public PackInvalidException(String path) { - super(MessageFormat.format(JGitText.get().packFileInvalid, path)); + this(path, null); + } + + /** + * Construct a pack invalid error with cause. + * + * @param path + * path of the invalid pack file. + * @param cause + * cause of the pack file becoming invalid. + * @since 4.5.7 + */ + public PackInvalidException(String path, Throwable cause) { + super(MessageFormat.format(JGitText.get().packFileInvalid, path), cause); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java index b9758bd64..58d12ab23 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BlockBasedFile.java @@ -84,6 +84,9 @@ abstract class BlockBasedFile { /** True once corruption has been detected that cannot be worked around. */ volatile boolean invalid; + /** Exception that caused the packfile to be flagged as invalid */ + protected volatile Exception invalidatingCause; + BlockBasedFile(DfsBlockCache cache, DfsPackDescription desc, PackExt ext) { this.cache = cache; this.key = desc.getStreamKey(ext); @@ -135,8 +138,9 @@ abstract class BlockBasedFile { DfsBlock readOneBlock(long pos, DfsReader ctx, @Nullable ReadableChannel fileChannel) throws IOException { - if (invalid) - throw new PackInvalidException(getFileName()); + if (invalid) { + throw new PackInvalidException(getFileName(), invalidatingCause); + } ctx.stats.readBlock++; long start = System.nanoTime(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java index 05b8f61a4..c131f9457 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java @@ -181,8 +181,9 @@ public final class DfsPackFile extends BlockBasedFile { return idx; } - if (invalid) - throw new PackInvalidException(getFileName()); + if (invalid) { + throw new PackInvalidException(getFileName(), invalidatingCause); + } Repository.getGlobalListenerList() .dispatch(new BeforeDfsPackIndexLoadedEvent(this)); @@ -224,11 +225,13 @@ public final class DfsPackFile extends BlockBasedFile { } } catch (EOFException e) { invalid = true; + invalidatingCause = e; throw new IOException(MessageFormat.format( DfsText.get().shortReadOfIndex, desc.getFileName(INDEX)), e); } catch (IOException e) { invalid = true; + invalidatingCause = e; throw new IOException(MessageFormat.format( DfsText.get().cannotReadIndex, desc.getFileName(INDEX)), e); @@ -720,8 +723,10 @@ public final class DfsPackFile extends BlockBasedFile { private IOException packfileIsTruncated() { invalid = true; - return new IOException(MessageFormat.format( + IOException exc = new IOException(MessageFormat.format( JGitText.get().packfileIsTruncated, getFileName())); + invalidatingCause = exc; + return exc; } private void readFully(long position, byte[] dstbuf, int dstoff, int cnt, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index 869fd02a8..b3af54581 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -132,8 +132,6 @@ public class ObjectDirectory extends FileObjectDatabase { private final File alternatesFile; - private final AtomicReference packList; - private final FS fs; private final AtomicReference alternates; @@ -146,6 +144,8 @@ public class ObjectDirectory extends FileObjectDatabase { private Set shallowCommitsIds; + final AtomicReference packList; + /** * Initialize a reference to an on-disk object directory. * @@ -674,13 +674,8 @@ public class ObjectDirectory extends FileObjectDatabase { transientErrorCount = p.incrementTransientErrorCount(); } if (warnTmpl != null) { - if (LOG.isDebugEnabled()) { - LOG.debug(MessageFormat.format(warnTmpl, - p.getPackFile().getAbsolutePath()), e); - } else { - LOG.warn(MessageFormat.format(warnTmpl, - p.getPackFile().getAbsolutePath())); - } + LOG.warn(MessageFormat.format(warnTmpl, + p.getPackFile().getAbsolutePath()), e); } else { if (doLogExponentialBackoff(transientErrorCount)) { // Don't remove the pack from the list, as the error may be @@ -767,7 +762,7 @@ public class ObjectDirectory extends FileObjectDatabase { return InsertLooseObjectResult.FAILURE; } - private boolean searchPacksAgain(PackList old) { + boolean searchPacksAgain(PackList old) { // Whether to trust the pack folder's modification time. If set // to false we will always scan the .git/objects/pack folder to // check for new pack files. If set to true (default) we use the @@ -917,7 +912,8 @@ public class ObjectDirectory extends FileObjectDatabase { final String packName = base + PACK.getExtension(); final File packFile = new File(packDirectory, packName); final PackFile oldPack = forReuse.remove(packName); - if (oldPack != null && oldPack.getFileSnapshot().isModified(packFile)) { + if (oldPack != null + && !oldPack.getFileSnapshot().isModified(packFile)) { list.add(oldPack); continue; } @@ -1074,7 +1070,7 @@ public class ObjectDirectory extends FileObjectDatabase { return new File(new File(getDirectory(), d), f); } - private static final class PackList { + static final class PackList { /** State just before reading the pack directory. */ final FileSnapshot snapshot; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java index ee3c0a8dd..d834b1a72 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java @@ -135,6 +135,8 @@ public class PackFile implements Iterable { private volatile boolean invalid; + private volatile Exception invalidatingCause; + private boolean invalidBitmap; private AtomicInteger transientErrorCount = new AtomicInteger(); @@ -184,7 +186,7 @@ public class PackFile implements Iterable { idx = loadedIdx; if (idx == null) { if (invalid) { - throw new PackInvalidException(packFile); + throw new PackInvalidException(packFile, invalidatingCause); } try { idx = PackIndex.open(extFile(INDEX)); @@ -208,6 +210,7 @@ public class PackFile implements Iterable { throw e; } catch (IOException e) { invalid = true; + invalidatingCause = e; throw e; } } @@ -661,7 +664,7 @@ public class PackFile implements Iterable { private void doOpen() throws IOException { if (invalid) { - throw new PackInvalidException(packFile); + throw new PackInvalidException(packFile, invalidatingCause); } try { synchronized (readLock) { @@ -671,13 +674,13 @@ public class PackFile implements Iterable { } } catch (InterruptedIOException e) { // don't invalidate the pack, we are interrupted from another thread - openFail(false); + openFail(false, e); throw e; } catch (FileNotFoundException fn) { // don't invalidate the pack if opening an existing file failed // since it may be related to a temporary lack of resources (e.g. // max open files) - openFail(!packFile.exists()); + openFail(!packFile.exists(), fn); throw fn; } catch (EOFException | AccessDeniedException | NoSuchFileException | CorruptObjectException | NoPackSignatureException @@ -685,20 +688,21 @@ public class PackFile implements Iterable { | UnsupportedPackIndexVersionException | UnsupportedPackVersionException pe) { // exceptions signaling permanent problems with a pack - openFail(true); + openFail(true, pe); throw pe; } catch (IOException | RuntimeException ge) { // generic exceptions could be transient so we should not mark the // pack invalid to avoid false MissingObjectExceptions - openFail(false); + openFail(false, ge); throw ge; } } - private void openFail(boolean invalidate) { + private void openFail(boolean invalidate, Exception cause) { activeWindows = 0; activeCopyRawData = 0; invalid = invalidate; + invalidatingCause = cause; doClose(); } @@ -725,7 +729,7 @@ public class PackFile implements Iterable { // Detect the situation and throw a proper exception so that can be properly // managed by the main packfile search loop and the Git client won't receive // any failures. - throw new PackInvalidException(packFile); + throw new PackInvalidException(packFile, invalidatingCause); } if (length < pos + size) size = (int) (length - pos);