package com.alimama.mdrill.editlog.read; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.util.HashMap; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; import com.alimama.mdrill.editlog.AddOp; import com.alimama.mdrill.editlog.defined.FSEditLogOpCodes; import com.alimama.mdrill.editlog.defined.HdfsConstants; import com.alimama.mdrill.editlog.util.PureJavaCrc32; import com.alimama.mdrill.editlog.write.DataOutputBuffer; import com.google.common.base.Preconditions; public abstract class FSEditLogOp { public final FSEditLogOpCodes opCode; public long txid; final public static class OpInstanceCache { private HashMap<FSEditLogOpCodes, FSEditLogOp> inst = new HashMap<FSEditLogOpCodes, FSEditLogOp>(); public OpInstanceCache() { inst.put(FSEditLogOpCodes.OP_ADD, new AddOp()); } public FSEditLogOp get(FSEditLogOpCodes opcode) { return inst.get(opcode); } } protected FSEditLogOp(FSEditLogOpCodes opCode) { this.opCode = opCode; this.txid = HdfsConstants.INVALID_TXID; } public long getTransactionId() { Preconditions.checkState(txid != HdfsConstants.INVALID_TXID); return txid; } public String getTransactionIdStr() { return (txid == HdfsConstants.INVALID_TXID) ? "(none)" : "" + txid; } public boolean hasTransactionId() { return (txid != HdfsConstants.INVALID_TXID); } public void setTransactionId(long txid) { this.txid = txid; } public abstract void readFields(DataInputStream in, int logVersion) throws IOException; public abstract void writeFields(DataOutputStream out) throws IOException; public static class Writer { private final DataOutputBuffer buf; private final Checksum checksum; public Writer(DataOutputBuffer out) { this.buf = out; this.checksum = new PureJavaCrc32(); } public void writeOp(FSEditLogOp op) throws IOException { int start = buf.getLength(); buf.writeByte(op.opCode.getOpCode()); buf.writeLong(op.txid); op.writeFields(buf); int end = buf.getLength(); checksum.reset(); checksum.update(buf.getData(), start, end-start); int sum = (int)checksum.getValue(); buf.writeInt(sum); } } public static class Reader { private final DataInputStream in; private final int logVersion; private final Checksum checksum; private final OpInstanceCache cache; private int maxOpSize; public Reader(DataInputStream in,int logVersion,int maxOpSize) { this.logVersion = logVersion; this.checksum = new PureJavaCrc32(); this.in = new DataInputStream(new CheckedInputStream(in, this.checksum)); this.cache = new OpInstanceCache(); this.maxOpSize = maxOpSize; } public void setMaxOpSize(int maxOpSize) { this.maxOpSize = maxOpSize; } public FSEditLogOp readOp(boolean skipBrokenEdits) throws IOException { try { return decodeOp(); } catch (IOException e) { if (!skipBrokenEdits) { throw e; }else{ return null; } } catch (RuntimeException e) { if (!skipBrokenEdits) { throw e; }else{ return null; } } catch (Throwable e) { if (!skipBrokenEdits) { throw new IOException("got unexpected exception " + e.getMessage(), e); }else{ return null; } } } private FSEditLogOp decodeOp() throws IOException { in.mark(maxOpSize); checksum.reset(); byte opCodeByte; try { opCodeByte = in.readByte(); } catch (EOFException eof) { // EOF at an opcode boundary is expected. return null; } FSEditLogOpCodes opCode = FSEditLogOpCodes.fromByte(opCodeByte); if (opCode == FSEditLogOpCodes.OP_INVALID) { throw new IOException("Read invalid opcode " + opCode); } FSEditLogOp op = cache.get(opCode); if (op == null) { throw new IOException("Read invalid opcode " + opCode); } op.setTransactionId(in.readLong()); op.readFields(in, logVersion); validateChecksum(in, checksum, op.txid); return op; } /** * Validate a transaction's checksum */ private void validateChecksum(DataInputStream in, Checksum checksum, long txid) throws IOException { int calculatedChecksum = (int)checksum.getValue(); int readChecksum = in.readInt(); // read in checksum if (readChecksum != calculatedChecksum) { throw new IOException( "Transaction is corrupt. Calculated checksum is " + calculatedChecksum + " but read checksum " + readChecksum+",txid="+txid); } } } }