/***************************************************************************
* Copyright (C) 2011 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
package edu.brown.hstore.cmdlog;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.log4j.Logger;
import org.jfree.util.Log;
import org.voltdb.CatalogContext;
import org.voltdb.VoltTable;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.NoConnectionsException;
import org.voltdb.client.ProcCallException;
import org.voltdb.messaging.FastDeserializer;
import org.voltdb.utils.CompressionService;
import org.voltdb.utils.NotImplementedException;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* Transaction Command Log Reader
* @author mkirsch
* @author pavlo
*/
public class CommandLogReader implements Iterable<LogEntry> {
private static final Logger LOG = Logger.getLogger(CommandLogReader.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
final FastDeserializer fd;
final Map<Integer, String> procedures;
boolean groupCommit;
public CommandLogReader(String path) {
FileChannel roChannel = null;
ByteBuffer readonlybuffer = null;
File f = new File(path);
try {
roChannel = new RandomAccessFile(f, "r").getChannel();
LOG.trace("File Size :"+roChannel.size());
readonlybuffer = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());
LOG.trace("Opened file :"+f.getAbsolutePath());
LOG.trace("Size :"+readonlybuffer.remaining());
} catch (IOException ex) {
LOG.trace("Failed to open file :"+f.getAbsolutePath());
throw new RuntimeException(ex);
}
assert(readonlybuffer != null);
this.fd = new FastDeserializer(readonlybuffer);
this.procedures = this.readHeader();
}
@Override
public Iterator<LogEntry> iterator() {
Iterator<LogEntry> it = new Iterator<LogEntry>() {
FastDeserializer decompressedFd;
private LogEntry _next;
{
decompressedFd = new FastDeserializer(ByteBuffer.allocate(0));
this.next();
}
@Override
public boolean hasNext() {
return _next != null;
//return fd.buffer().hasRemaining();
}
@Override
public LogEntry next() {
LogEntry ret = _next;
_next = null;
//Fill the decompressed buffer if it is empty
if (groupCommit && !decompressedFd.buffer().hasRemaining()) {
int sizeCompressed = 0;
try {
sizeCompressed = fd.readInt();
byte[] b = new byte[sizeCompressed];
fd.readFully(b);
byte[] decompressed = CompressionService.decompressBytes(b);
this.decompressedFd.setBuffer(ByteBuffer.wrap(decompressed));
} catch (IOException ex) {
//ex.printStackTrace();
throw new RuntimeException("Failed to decompress data from the WAL file!", ex);
} catch (BufferUnderflowException ex) {
//ex.printStackTrace();
this.decompressedFd.setBuffer(ByteBuffer.allocate(0));
}
}
try {
if (groupCommit)
_next = decompressedFd.readObject(LogEntry.class);
else
_next = fd.readObject(LogEntry.class);
} catch (IOException ex) {
throw new RuntimeException("Failed to deserialize LogEntry!", ex);
} catch (BufferUnderflowException ex) {
_next = null;
}
return (ret);
}
@Override
public void remove() {
throw new NotImplementedException("Can't call remove! You crazy!");
}
};
return (it);
// TODO: We need to figure out how we want to read these entries
// back in. I suppose we could just make a new LocalTransaction
// entry each time. What we really should do is recreate
// the StoredProcedureInvocation and then pass that into
// the HStoreSite so that we can replay the transaction
//
// So maybe we want to make this a StoredProcedure Invocation iterator?
}
/**
*
* @return
*/
protected Map<Integer, String> readHeader() {
Map<Integer, String> procedures = new HashMap<Integer, String>();
try {
this.groupCommit = fd.readBoolean();
int num_procs = fd.readInt();
for (int i = 0; i < num_procs; i++){
Integer proc_id = fd.readInt();
String proc_name = fd.readString();
//LOG.trace("Procedure " + proc_id + " Name : "+proc_name );
procedures.put(new Integer(proc_id), proc_name);
}
LOG.trace("Header read :: num_procs : "+num_procs);
} catch (IOException ex) {
throw new RuntimeException("Failed to read WAL log header!", ex);
}
return (procedures);
}
}