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 }