Merge branch 'stable-5.8' into stable-5.9
* stable-5.8: LockFile: create OutputStream only when needed Remove ReftableNumbersNotIncreasingException Change-Id: I3274c97cf560398c3c4c27d6759500452f315db0
This commit is contained in:
commit
587c7eab45
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2012, GitHub Inc. and others
|
* Copyright (C) 2012, 2021 GitHub Inc. and others
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -9,10 +9,16 @@
|
||||||
*/
|
*/
|
||||||
package org.eclipse.jgit.internal.storage.file;
|
package org.eclipse.jgit.internal.storage.file;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||||
import org.eclipse.jgit.errors.LockFailedException;
|
import org.eclipse.jgit.errors.LockFailedException;
|
||||||
|
@ -49,4 +55,149 @@ public void lockFailedExceptionRecovery() throws Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockTwice() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
lock.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
lock.commit();
|
||||||
|
assertFalse(lock.isLocked());
|
||||||
|
checkFile(f, "other");
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("second".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
lock.commit();
|
||||||
|
assertFalse(lock.isLocked());
|
||||||
|
checkFile(f, "second");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockTwiceUnlock() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
lock.unlock();
|
||||||
|
assertFalse(lock.isLocked());
|
||||||
|
checkFile(f, "content");
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("second".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
lock.commit();
|
||||||
|
assertFalse(lock.isLocked());
|
||||||
|
checkFile(f, "second");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockWriteTwiceThrows1() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
assertThrows(Exception.class,
|
||||||
|
() -> lock.write("second".getBytes(StandardCharsets.US_ASCII)));
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockWriteTwiceThrows2() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
assertThrows(Exception.class,
|
||||||
|
() -> lock.write("second".getBytes(StandardCharsets.US_ASCII)));
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockWriteTwiceThrows3() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
assertThrows(Exception.class, () -> {
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("second".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockWriteTwiceThrows4() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
assertThrows(Exception.class, () -> {
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("second".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockUnclosedCommitThrows() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
try (OutputStream out = lock.getOutputStream()) {
|
||||||
|
out.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
assertThrows(Exception.class, () -> lock.commit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockNested() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
assertThrows(IllegalStateException.class, () -> lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockHeld() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lock());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
LockFile lock2 = new LockFile(f);
|
||||||
|
assertFalse(lock2.lock());
|
||||||
|
assertFalse(lock2.isLocked());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockForAppend() throws Exception {
|
||||||
|
File f = writeTrashFile("somefile", "content");
|
||||||
|
LockFile lock = new LockFile(f);
|
||||||
|
assertTrue(lock.lockForAppend());
|
||||||
|
assertTrue(lock.isLocked());
|
||||||
|
lock.write("other".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
lock.commit();
|
||||||
|
assertFalse(lock.isLocked());
|
||||||
|
checkFile(f, "contentother");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,11 +404,14 @@ listingPacks=Listing packs
|
||||||
localObjectsIncomplete=Local objects incomplete.
|
localObjectsIncomplete=Local objects incomplete.
|
||||||
localRefIsMissingObjects=Local ref {0} is missing object(s).
|
localRefIsMissingObjects=Local ref {0} is missing object(s).
|
||||||
localRepository=local repository
|
localRepository=local repository
|
||||||
|
lockAlreadyHeld=Lock on {0} already held
|
||||||
lockCountMustBeGreaterOrEqual1=lockCount must be >= 1
|
lockCountMustBeGreaterOrEqual1=lockCount must be >= 1
|
||||||
lockError=lock error: {0}
|
lockError=lock error: {0}
|
||||||
lockFailedRetry=locking {0} failed after {1} retries
|
lockFailedRetry=locking {0} failed after {1} retries
|
||||||
lockOnNotClosed=Lock on {0} not closed.
|
lockOnNotClosed=Lock on {0} not closed.
|
||||||
lockOnNotHeld=Lock on {0} not held.
|
lockOnNotHeld=Lock on {0} not held.
|
||||||
|
lockStreamClosed=Output to lock on {0} already closed
|
||||||
|
lockStreamMultiple=Output to lock on {0} already opened
|
||||||
logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}.
|
logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}.
|
||||||
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 {}.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
|
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
|
||||||
* Copyright (C) 2012, Research In Motion Limited and others
|
* Copyright (C) 2012, 2021 Research In Motion Limited and others
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -433,10 +433,13 @@ public static JGitText get() {
|
||||||
/***/ public String localRefIsMissingObjects;
|
/***/ public String localRefIsMissingObjects;
|
||||||
/***/ public String localRepository;
|
/***/ public String localRepository;
|
||||||
/***/ public String lockCountMustBeGreaterOrEqual1;
|
/***/ public String lockCountMustBeGreaterOrEqual1;
|
||||||
|
/***/ public String lockAlreadyHeld;
|
||||||
/***/ public String lockError;
|
/***/ public String lockError;
|
||||||
/***/ public String lockFailedRetry;
|
/***/ public String lockFailedRetry;
|
||||||
/***/ public String lockOnNotClosed;
|
/***/ public String lockOnNotClosed;
|
||||||
/***/ public String lockOnNotHeld;
|
/***/ public String lockOnNotHeld;
|
||||||
|
/***/ public String lockStreamClosed;
|
||||||
|
/***/ public String lockStreamMultiple;
|
||||||
/***/ public String logInconsistentFiletimeDiff;
|
/***/ public String logInconsistentFiletimeDiff;
|
||||||
/***/ public String logLargerFiletimeDiff;
|
/***/ public String logLargerFiletimeDiff;
|
||||||
/***/ public String logSmallerFiletime;
|
/***/ public String logSmallerFiletime;
|
||||||
|
|
|
@ -128,33 +128,6 @@ CompactionStats getStats() {
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Thrown if the update indices in the stack are not monotonic */
|
|
||||||
public static class ReftableNumbersNotIncreasingException
|
|
||||||
extends RuntimeException {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
String name;
|
|
||||||
|
|
||||||
long lastMax;
|
|
||||||
|
|
||||||
long min;
|
|
||||||
|
|
||||||
ReftableNumbersNotIncreasingException(String name, long lastMax,
|
|
||||||
long min) {
|
|
||||||
this.name = name;
|
|
||||||
this.lastMax = lastMax;
|
|
||||||
this.min = min;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "nls", "boxing" })
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(
|
|
||||||
"ReftableNumbersNotIncreasingException %s: min %d, lastMax %d",
|
|
||||||
name, min, lastMax);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the stack, potentially reusing opened reftableReaders.
|
* Reloads the stack, potentially reusing opened reftableReaders.
|
||||||
*
|
*
|
||||||
|
@ -173,7 +146,6 @@ private void reloadOnce(List<String> names)
|
||||||
List<ReftableReader> newTables = new ArrayList<>();
|
List<ReftableReader> newTables = new ArrayList<>();
|
||||||
List<StackEntry> newStack = new ArrayList<>(stack.size() + 1);
|
List<StackEntry> newStack = new ArrayList<>(stack.size() + 1);
|
||||||
try {
|
try {
|
||||||
ReftableReader last = null;
|
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
StackEntry entry = new StackEntry();
|
StackEntry entry = new StackEntry();
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
|
@ -191,15 +163,6 @@ private void reloadOnce(List<String> names)
|
||||||
newTables.add(t);
|
newTables.add(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last != null) {
|
|
||||||
// TODO: move this to MergedReftable
|
|
||||||
if (last.maxUpdateIndex() >= t.minUpdateIndex()) {
|
|
||||||
throw new ReftableNumbersNotIncreasingException(name,
|
|
||||||
last.maxUpdateIndex(), t.minUpdateIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last = t;
|
|
||||||
|
|
||||||
entry.reftableReader = t;
|
entry.reftableReader = t;
|
||||||
newStack.add(entry);
|
newStack.add(entry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
|
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
|
||||||
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
|
* Copyright (C) 2006-2021, Shawn O. Pearce <spearce@spearce.org> and others
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made available under the
|
* This program and the accompanying materials are made available under the
|
||||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||||
|
@ -96,11 +96,15 @@ static File getLockFile(File file) {
|
||||||
|
|
||||||
private boolean haveLck;
|
private boolean haveLck;
|
||||||
|
|
||||||
FileOutputStream os;
|
private FileOutputStream os;
|
||||||
|
|
||||||
private boolean needSnapshot;
|
private boolean needSnapshot;
|
||||||
|
|
||||||
boolean fsync;
|
private boolean fsync;
|
||||||
|
|
||||||
|
private boolean isAppend;
|
||||||
|
|
||||||
|
private boolean written;
|
||||||
|
|
||||||
private FileSnapshot commitSnapshot;
|
private FileSnapshot commitSnapshot;
|
||||||
|
|
||||||
|
@ -127,6 +131,10 @@ public LockFile(File f) {
|
||||||
* does not hold the lock.
|
* does not hold the lock.
|
||||||
*/
|
*/
|
||||||
public boolean lock() throws IOException {
|
public boolean lock() throws IOException {
|
||||||
|
if (haveLck) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
MessageFormat.format(JGitText.get().lockAlreadyHeld, ref));
|
||||||
|
}
|
||||||
FileUtils.mkdirs(lck.getParentFile(), true);
|
FileUtils.mkdirs(lck.getParentFile(), true);
|
||||||
try {
|
try {
|
||||||
token = FS.DETECTED.createNewFileAtomic(lck);
|
token = FS.DETECTED.createNewFileAtomic(lck);
|
||||||
|
@ -134,18 +142,15 @@ public boolean lock() throws IOException {
|
||||||
LOG.error(JGitText.get().failedCreateLockFile, lck, e);
|
LOG.error(JGitText.get().failedCreateLockFile, lck, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (token.isCreated()) {
|
boolean obtainedLock = token.isCreated();
|
||||||
|
if (obtainedLock) {
|
||||||
haveLck = true;
|
haveLck = true;
|
||||||
try {
|
isAppend = false;
|
||||||
os = new FileOutputStream(lck);
|
written = false;
|
||||||
} catch (IOException ioe) {
|
|
||||||
unlock();
|
|
||||||
throw ioe;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
closeToken();
|
closeToken();
|
||||||
}
|
}
|
||||||
return haveLck;
|
return obtainedLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,12 +163,24 @@ public boolean lock() throws IOException {
|
||||||
* does not hold the lock.
|
* does not hold the lock.
|
||||||
*/
|
*/
|
||||||
public boolean lockForAppend() throws IOException {
|
public boolean lockForAppend() throws IOException {
|
||||||
if (!lock())
|
if (!lock()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
copyCurrentContent();
|
copyCurrentContent();
|
||||||
|
isAppend = true;
|
||||||
|
written = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For tests only
|
||||||
|
boolean isLocked() {
|
||||||
|
return haveLck;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileOutputStream getStream() throws IOException {
|
||||||
|
return new FileOutputStream(lck, isAppend);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy the current file content into the temporary file.
|
* Copy the current file content into the temporary file.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -185,32 +202,31 @@ public boolean lockForAppend() throws IOException {
|
||||||
*/
|
*/
|
||||||
public void copyCurrentContent() throws IOException {
|
public void copyCurrentContent() throws IOException {
|
||||||
requireLock();
|
requireLock();
|
||||||
try {
|
try (FileOutputStream out = getStream()) {
|
||||||
try (FileInputStream fis = new FileInputStream(ref)) {
|
try (FileInputStream fis = new FileInputStream(ref)) {
|
||||||
if (fsync) {
|
if (fsync) {
|
||||||
FileChannel in = fis.getChannel();
|
FileChannel in = fis.getChannel();
|
||||||
long pos = 0;
|
long pos = 0;
|
||||||
long cnt = in.size();
|
long cnt = in.size();
|
||||||
while (0 < cnt) {
|
while (0 < cnt) {
|
||||||
long r = os.getChannel().transferFrom(in, pos, cnt);
|
long r = out.getChannel().transferFrom(in, pos, cnt);
|
||||||
pos += r;
|
pos += r;
|
||||||
cnt -= r;
|
cnt -= r;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final byte[] buf = new byte[2048];
|
final byte[] buf = new byte[2048];
|
||||||
int r;
|
int r;
|
||||||
while ((r = fis.read(buf)) >= 0)
|
while ((r = fis.read(buf)) >= 0) {
|
||||||
os.write(buf, 0, r);
|
out.write(buf, 0, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException fnfe) {
|
} catch (FileNotFoundException fnfe) {
|
||||||
if (ref.exists()) {
|
if (ref.exists()) {
|
||||||
unlock();
|
|
||||||
throw fnfe;
|
throw fnfe;
|
||||||
}
|
}
|
||||||
// Don't worry about a file that doesn't exist yet, it
|
// Don't worry about a file that doesn't exist yet, it
|
||||||
// conceptually has no current content to copy.
|
// conceptually has no current content to copy.
|
||||||
//
|
}
|
||||||
} catch (IOException | RuntimeException | Error ioe) {
|
} catch (IOException | RuntimeException | Error ioe) {
|
||||||
unlock();
|
unlock();
|
||||||
throw ioe;
|
throw ioe;
|
||||||
|
@ -253,18 +269,22 @@ public void write(ObjectId id) throws IOException {
|
||||||
*/
|
*/
|
||||||
public void write(byte[] content) throws IOException {
|
public void write(byte[] content) throws IOException {
|
||||||
requireLock();
|
requireLock();
|
||||||
try {
|
try (FileOutputStream out = getStream()) {
|
||||||
|
if (written) {
|
||||||
|
throw new IOException(MessageFormat
|
||||||
|
.format(JGitText.get().lockStreamClosed, ref));
|
||||||
|
}
|
||||||
if (fsync) {
|
if (fsync) {
|
||||||
FileChannel fc = os.getChannel();
|
FileChannel fc = out.getChannel();
|
||||||
ByteBuffer buf = ByteBuffer.wrap(content);
|
ByteBuffer buf = ByteBuffer.wrap(content);
|
||||||
while (0 < buf.remaining())
|
while (0 < buf.remaining()) {
|
||||||
fc.write(buf);
|
fc.write(buf);
|
||||||
|
}
|
||||||
fc.force(true);
|
fc.force(true);
|
||||||
} else {
|
} else {
|
||||||
os.write(content);
|
out.write(content);
|
||||||
}
|
}
|
||||||
os.close();
|
written = true;
|
||||||
os = null;
|
|
||||||
} catch (IOException | RuntimeException | Error ioe) {
|
} catch (IOException | RuntimeException | Error ioe) {
|
||||||
unlock();
|
unlock();
|
||||||
throw ioe;
|
throw ioe;
|
||||||
|
@ -283,36 +303,68 @@ public void write(byte[] content) throws IOException {
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
requireLock();
|
requireLock();
|
||||||
|
|
||||||
final OutputStream out;
|
if (written || os != null) {
|
||||||
if (fsync)
|
throw new IllegalStateException(MessageFormat
|
||||||
out = Channels.newOutputStream(os.getChannel());
|
.format(JGitText.get().lockStreamMultiple, ref));
|
||||||
else
|
}
|
||||||
out = os;
|
|
||||||
|
|
||||||
return new OutputStream() {
|
return new OutputStream() {
|
||||||
|
|
||||||
|
private OutputStream out;
|
||||||
|
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
private OutputStream get() throws IOException {
|
||||||
|
if (written) {
|
||||||
|
throw new IOException(MessageFormat
|
||||||
|
.format(JGitText.get().lockStreamMultiple, ref));
|
||||||
|
}
|
||||||
|
if (out == null) {
|
||||||
|
os = getStream();
|
||||||
|
if (fsync) {
|
||||||
|
out = Channels.newOutputStream(os.getChannel());
|
||||||
|
} else {
|
||||||
|
out = os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b, int o, int n)
|
public void write(byte[] b, int o, int n)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
out.write(b, o, n);
|
get().write(b, o, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b) throws IOException {
|
public void write(byte[] b) throws IOException {
|
||||||
out.write(b);
|
get().write(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
out.write(b);
|
get().write(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closed = true;
|
||||||
try {
|
try {
|
||||||
if (fsync)
|
if (written) {
|
||||||
|
throw new IOException(MessageFormat
|
||||||
|
.format(JGitText.get().lockStreamClosed, ref));
|
||||||
|
}
|
||||||
|
if (out != null) {
|
||||||
|
if (fsync) {
|
||||||
os.getChannel().force(true);
|
os.getChannel().force(true);
|
||||||
|
}
|
||||||
out.close();
|
out.close();
|
||||||
os = null;
|
os = null;
|
||||||
|
}
|
||||||
|
written = true;
|
||||||
} catch (IOException | RuntimeException | Error ioe) {
|
} catch (IOException | RuntimeException | Error ioe) {
|
||||||
unlock();
|
unlock();
|
||||||
throw ioe;
|
throw ioe;
|
||||||
|
@ -322,7 +374,7 @@ public void close() throws IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
void requireLock() {
|
void requireLock() {
|
||||||
if (os == null) {
|
if (!haveLck) {
|
||||||
unlock();
|
unlock();
|
||||||
throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotHeld, ref));
|
throw new IllegalStateException(MessageFormat.format(JGitText.get().lockOnNotHeld, ref));
|
||||||
}
|
}
|
||||||
|
@ -411,6 +463,8 @@ public boolean commit() {
|
||||||
try {
|
try {
|
||||||
FileUtils.rename(lck, ref, StandardCopyOption.ATOMIC_MOVE);
|
FileUtils.rename(lck, ref, StandardCopyOption.ATOMIC_MOVE);
|
||||||
haveLck = false;
|
haveLck = false;
|
||||||
|
isAppend = false;
|
||||||
|
written = false;
|
||||||
closeToken();
|
closeToken();
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -497,6 +551,8 @@ public void unlock() {
|
||||||
closeToken();
|
closeToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isAppend = false;
|
||||||
|
written = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
|
|
Loading…
Reference in New Issue