Mercurial > jhg
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 617:65c01508f002 | 618:7c0d2ce340b8 |
|---|---|
| 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; | 19 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; |
| 20 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; | 20 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; |
| 21 | 21 |
| 22 import java.io.File; | 22 import java.io.File; |
| 23 import java.io.FileInputStream; | 23 import java.io.FileInputStream; |
| 24 import java.io.FileNotFoundException; | |
| 25 import java.io.FileOutputStream; | 24 import java.io.FileOutputStream; |
| 26 import java.io.IOException; | 25 import java.io.IOException; |
| 27 import java.io.OutputStream; | |
| 28 import java.nio.ByteBuffer; | 26 import java.nio.ByteBuffer; |
| 29 import java.nio.MappedByteBuffer; | 27 import java.nio.MappedByteBuffer; |
| 30 import java.nio.channels.FileChannel; | 28 import java.nio.channels.FileChannel; |
| 31 | 29 |
| 32 import org.tmatesoft.hg.core.HgIOException; | 30 import org.tmatesoft.hg.core.HgIOException; |
| 99 context.getLog().dump(getClass(), Error, ex, null); | 97 context.getLog().dump(getClass(), Error, ex, null); |
| 100 } | 98 } |
| 101 return new DataAccess(); // non-null, empty. | 99 return new DataAccess(); // non-null, empty. |
| 102 } | 100 } |
| 103 | 101 |
| 104 public DataSerializer createWriter(final Transaction tr, File f, boolean createNewIfDoesntExist) throws HgIOException { | 102 public DataSerializer createWriter(final Transaction tr, File f, boolean createNewIfDoesntExist) { |
| 105 if (!f.exists() && !createNewIfDoesntExist) { | 103 if (!f.exists() && !createNewIfDoesntExist) { |
| 106 return new DataSerializer(); | 104 return new DataSerializer(); |
| 107 } | 105 } |
| 108 try { | 106 // TODO invert RevlogStreamWriter to send DataSource here instead of grabbing DataSerializer |
| 109 final File transactionFile = tr.prepare(f); | 107 // to control the moment transaction gets into play and whether it fails or not |
| 110 return new StreamDataSerializer(context.getLog(), new FileOutputStream(transactionFile, true)) { | 108 return new TransactionAwareFileSerializer(tr, f); |
| 111 @Override | |
| 112 public void done() { | |
| 113 super.done(); | |
| 114 // FIXME invert RevlogStreamWriter to send DataSource here instead of grabbing DataSerializer | |
| 115 // besides, DataSerializer#done is invoked regardless of whether write was successful or not, | |
| 116 // while Transaction#done() assumes there's no error | |
| 117 try { | |
| 118 tr.done(transactionFile); | |
| 119 } catch (HgIOException ex) { | |
| 120 context.getLog().dump(DataAccessProvider.class, Error, ex, null); | |
| 121 } | |
| 122 } | |
| 123 }; | |
| 124 } catch (final FileNotFoundException ex) { | |
| 125 context.getLog().dump(getClass(), Error, ex, null); | |
| 126 return new DataSerializer() { | |
| 127 public void write(byte[] data, int offset, int length) throws IOException { | |
| 128 throw ex; | |
| 129 } | |
| 130 }; | |
| 131 } | |
| 132 } | 109 } |
| 133 | 110 |
| 134 private static class MemoryMapFileAccess extends DataAccess { | 111 private static class MemoryMapFileAccess extends DataAccess { |
| 135 private FileChannel fileChannel; | 112 private FileChannel fileChannel; |
| 136 private long position = 0; // always points to buffer's absolute position in the file | 113 private long position = 0; // always points to buffer's absolute position in the file |
| 406 } | 383 } |
| 407 fileChannel = null; | 384 fileChannel = null; |
| 408 } | 385 } |
| 409 } | 386 } |
| 410 } | 387 } |
| 411 | 388 |
| 412 public/*XXX, private, once HgCloneCommand stops using it */ static class StreamDataSerializer extends DataSerializer { | 389 /** |
| 413 private final OutputStream out; | 390 * Appends serialized changes to the end of the file |
| 414 private final LogFacility log; | 391 */ |
| 415 private byte[] buffer; | 392 private static class TransactionAwareFileSerializer extends DataSerializer { |
| 416 | 393 |
| 417 public StreamDataSerializer(LogFacility logFacility, OutputStream os) { | 394 private final Transaction transaction; |
| 418 assert os != null; | 395 private final File file; |
| 419 out = os; | 396 private FileOutputStream fos; |
| 420 log = logFacility; | 397 private File transactionFile; |
| 421 } | 398 private boolean writeFailed = false; |
| 422 | 399 |
| 423 @Override | 400 public TransactionAwareFileSerializer(Transaction tr, File f) { |
| 424 public void write(byte[] data, int offset, int length) throws IOException { | 401 transaction = tr; |
| 425 out.write(data, offset, length); | 402 file = f; |
| 426 } | 403 } |
| 427 | 404 |
| 428 @Override | 405 @Override |
| 429 public void writeInt(int... values) throws IOException { | 406 public void write(byte[] data, int offset, int length) throws HgIOException { |
| 430 ensureBufferSize(4*values.length); // sizeof(int) | |
| 431 int idx = 0; | |
| 432 for (int v : values) { | |
| 433 DataSerializer.bigEndian(v, buffer, idx); | |
| 434 idx += 4; | |
| 435 } | |
| 436 out.write(buffer, 0, idx); | |
| 437 } | |
| 438 | |
| 439 @Override | |
| 440 public void writeByte(byte... values) throws IOException { | |
| 441 if (values.length == 1) { | |
| 442 out.write(values[0]); | |
| 443 } else { | |
| 444 out.write(values, 0, values.length); | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 private void ensureBufferSize(int bytesNeeded) { | |
| 449 if (buffer == null || buffer.length < bytesNeeded) { | |
| 450 buffer = new byte[bytesNeeded]; | |
| 451 } | |
| 452 } | |
| 453 | |
| 454 @Override | |
| 455 public void done() { | |
| 456 try { | 407 try { |
| 457 out.flush(); | 408 if (fos == null) { |
| 458 out.close(); | 409 transactionFile = transaction.prepare(file); |
| 410 fos = new FileOutputStream(transactionFile, true); | |
| 411 } | |
| 412 fos.write(data, offset, length); | |
| 413 fos.flush(); | |
| 459 } catch (IOException ex) { | 414 } catch (IOException ex) { |
| 460 log.dump(getClass(), Error, ex, "Failure to close stream"); | 415 writeFailed = true; |
| 416 transaction.failure(transactionFile, ex); | |
| 417 throw new HgIOException("Write failure", ex, transactionFile); | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 @Override | |
| 422 public void done() throws HgIOException { | |
| 423 if (fos != null) { | |
| 424 assert transactionFile != null; | |
| 425 try { | |
| 426 fos.close(); | |
| 427 if (!writeFailed) { | |
| 428 // XXX, Transaction#done() assumes there's no error , but perhaps it's easier to | |
| 429 // rely on #failure(), and call #done() always (or change #done() to #success() | |
| 430 transaction.done(transactionFile); | |
| 431 } | |
| 432 fos = null; | |
| 433 } catch (IOException ex) { | |
| 434 if (!writeFailed) { | |
| 435 // do not eclipse original exception | |
| 436 transaction.failure(transactionFile, ex); | |
| 437 } | |
| 438 throw new HgIOException("Write failure", ex, transactionFile); | |
| 439 } | |
| 461 } | 440 } |
| 462 } | 441 } |
| 463 } | 442 } |
| 464 } | 443 } |
