package org.thrudb.thrift; import org.apache.log4j.Logger; import org.apache.thrift.TApplicationException; import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; import tokyocabinet.HDB; /** * Allows a way to peek at the content of the message and rewind back to start. * * This is used to log certain actions to disk for redo-logging * * @author jake * */ public class TPeekingTransport extends TFramedTransport { private Logger logger = Logger.getLogger(getClass()); private byte[] peekBuffer = new byte[] {}; private byte[] writeBuffer = new byte[] {}; private int writeMax = 1024; //only store the initial output message private int writePos = 0; private int replayPos = 0; private boolean replayMode = false; private int replayEnd = 0; //stop reply at this point in the buf private boolean recording = false; private boolean logging = false; private HDB log; private String nextLogId = "-1"; public TPeekingTransport(TTransport baseTransport, HDB log) { super(baseTransport); this.log = log; } @Override public void close() { super.close(); } @Override public boolean isOpen() { return super.isOpen(); } public boolean isRecording() { return recording; } public void setRecording(boolean recording) { this.recording = recording; } @Override public void open() throws TTransportException { super.open(); } @Override public int read(byte[] buf, int off, int len) throws TTransportException { int sz = 0; if (replayMode && replayPos + len <= replayEnd ) { System.arraycopy(peekBuffer, replayPos, buf, 0, len); replayPos += len; sz = len; }else{ sz = super.read(buf, off, len); // Add to peek buffer if (recording && sz > 0) { byte[] newPeekBuffer = new byte[peekBuffer.length + sz]; System.arraycopy(peekBuffer, 0, newPeekBuffer, 0, peekBuffer.length); System.arraycopy(buf, 0, newPeekBuffer, peekBuffer.length, sz); peekBuffer = newPeekBuffer; } } if(logging){ if(!log.putcat(nextLogId.getBytes(), buf)){ throw new TTransportException("Log message"+nextLogId+" is corrupt"); } } return sz; } @Override public void write(byte[] buf, int off, int len) throws TTransportException { if(writeBuffer.length + len < writeMax){ byte[] newWriteBuffer = new byte[writeBuffer.length + len]; System.arraycopy(writeBuffer, 0, newWriteBuffer, 0, writeBuffer.length); System.arraycopy(buf, 0, newWriteBuffer, writeBuffer.length, len); writeBuffer = newWriteBuffer; } super.write(buf, off, len); } public boolean isReplayMode() { return replayMode; } public void setReplayMode(boolean replayMode) { this.replayMode = replayMode; if(replayMode == true) replayEnd = peekBuffer.length; } /** * This is a hack for reading the output struct type * */ public void swapInWriteBuffer(){ peekBuffer = writeBuffer; replayPos = 0; replayMode = true; recording = false; replayEnd = writeMax; } public void reset(){ peekBuffer = new byte[] {}; replayPos = 0; replayMode = false; recording = false; logging = false; writeBuffer = new byte[] {}; writePos = 0; } public byte[] getBuffer(){ return peekBuffer; } public boolean isLogging() { return logging; } public void setLogging(boolean logging) throws TTransportException { this.logging = logging; //get new log in sequence if(logging){ int lsn = log.addint("LSN", 1); if(lsn == Integer.MIN_VALUE){ throw new TTransportException("Logging error:"+log.errmsg()); }else{ logger.info("LSN is now "+lsn); } nextLogId = String.valueOf(lsn)+";"; } } /** * marks this message as having failed and should not be replicated */ public void rollback() throws TTransportException{ if(logging){ //log.out(String.valueOf(nextLogId)); if(!log.put(nextLogId+"r", "e")){ throw new TTransportException("Logging rollback err:"+log.errmsg()); } log.sync(); } } /** * marks this message as a success * @throws TTransportException */ public void commit() throws TTransportException{ if(logging){ if(!log.put(nextLogId+"r", "c")){ throw new TTransportException("Logging commit err:"+log.errmsg()); } log.sync(); } } }