Mercurial > jhg
diff src/org/tmatesoft/hg/internal/DataAccessProvider.java @ 618:7c0d2ce340b8
Refactor approach how content finds it way down to a commit revision
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 16 May 2013 19:46:13 +0200 |
parents | 65c01508f002 |
children | 868b2ffdcd5c |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/DataAccessProvider.java Wed May 15 20:10:09 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/DataAccessProvider.java Thu May 16 19:46:13 2013 +0200 @@ -21,10 +21,8 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -101,34 +99,13 @@ return new DataAccess(); // non-null, empty. } - public DataSerializer createWriter(final Transaction tr, File f, boolean createNewIfDoesntExist) throws HgIOException { + public DataSerializer createWriter(final Transaction tr, File f, boolean createNewIfDoesntExist) { if (!f.exists() && !createNewIfDoesntExist) { return new DataSerializer(); } - try { - final File transactionFile = tr.prepare(f); - return new StreamDataSerializer(context.getLog(), new FileOutputStream(transactionFile, true)) { - @Override - public void done() { - super.done(); - // FIXME invert RevlogStreamWriter to send DataSource here instead of grabbing DataSerializer - // besides, DataSerializer#done is invoked regardless of whether write was successful or not, - // while Transaction#done() assumes there's no error - try { - tr.done(transactionFile); - } catch (HgIOException ex) { - context.getLog().dump(DataAccessProvider.class, Error, ex, null); - } - } - }; - } catch (final FileNotFoundException ex) { - context.getLog().dump(getClass(), Error, ex, null); - return new DataSerializer() { - public void write(byte[] data, int offset, int length) throws IOException { - throw ex; - } - }; - } + // TODO invert RevlogStreamWriter to send DataSource here instead of grabbing DataSerializer + // to control the moment transaction gets into play and whether it fails or not + return new TransactionAwareFileSerializer(tr, f); } private static class MemoryMapFileAccess extends DataAccess { @@ -408,56 +385,58 @@ } } } + + /** + * Appends serialized changes to the end of the file + */ + private static class TransactionAwareFileSerializer extends DataSerializer { + + private final Transaction transaction; + private final File file; + private FileOutputStream fos; + private File transactionFile; + private boolean writeFailed = false; - public/*XXX, private, once HgCloneCommand stops using it */ static class StreamDataSerializer extends DataSerializer { - private final OutputStream out; - private final LogFacility log; - private byte[] buffer; - - public StreamDataSerializer(LogFacility logFacility, OutputStream os) { - assert os != null; - out = os; - log = logFacility; + public TransactionAwareFileSerializer(Transaction tr, File f) { + transaction = tr; + file = f; } @Override - public void write(byte[] data, int offset, int length) throws IOException { - out.write(data, offset, length); - } - - @Override - public void writeInt(int... values) throws IOException { - ensureBufferSize(4*values.length); // sizeof(int) - int idx = 0; - for (int v : values) { - DataSerializer.bigEndian(v, buffer, idx); - idx += 4; + public void write(byte[] data, int offset, int length) throws HgIOException { + try { + if (fos == null) { + transactionFile = transaction.prepare(file); + fos = new FileOutputStream(transactionFile, true); + } + fos.write(data, offset, length); + fos.flush(); + } catch (IOException ex) { + writeFailed = true; + transaction.failure(transactionFile, ex); + throw new HgIOException("Write failure", ex, transactionFile); } - out.write(buffer, 0, idx); } @Override - public void writeByte(byte... values) throws IOException { - if (values.length == 1) { - out.write(values[0]); - } else { - out.write(values, 0, values.length); - } - } - - private void ensureBufferSize(int bytesNeeded) { - if (buffer == null || buffer.length < bytesNeeded) { - buffer = new byte[bytesNeeded]; - } - } - - @Override - public void done() { - try { - out.flush(); - out.close(); - } catch (IOException ex) { - log.dump(getClass(), Error, ex, "Failure to close stream"); + public void done() throws HgIOException { + if (fos != null) { + assert transactionFile != null; + try { + fos.close(); + if (!writeFailed) { + // XXX, Transaction#done() assumes there's no error , but perhaps it's easier to + // rely on #failure(), and call #done() always (or change #done() to #success() + transaction.done(transactionFile); + } + fos = null; + } catch (IOException ex) { + if (!writeFailed) { + // do not eclipse original exception + transaction.failure(transactionFile, ex); + } + throw new HgIOException("Write failure", ex, transactionFile); + } } } }