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 } |