package org.araqne.logstorage.file;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.araqne.logstorage.file.DataBlockV3;
import org.araqne.logstorage.file.DataBlockV3Params;
import org.araqne.logstorage.file.IndexBlockV3Header;
import org.araqne.logstorage.file.InvalidLogFileHeaderException;
import org.araqne.logstorage.file.LogBlock;
import org.araqne.logstorage.file.LogBlockCursor;
import org.araqne.logstorage.file.LogFileHeader;
import org.araqne.storage.api.FilePath;
import org.araqne.storage.api.StorageInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBlockCursorV3o implements LogBlockCursor {
private final Logger logger = LoggerFactory.getLogger(LogBlockCursorV3o.class);
private static final int FILE_VERSION = 3;
public static final int INDEX_ITEM_SIZE = 28;
private FilePath indexPath;
private FilePath dataPath;
private String compressionMethod;
private List<IndexBlockV3Header> indexBlockHeaders = new ArrayList<IndexBlockV3Header>();
private long totalCount;
private StorageInputStream dataStream;
private int currentBlockIndex;
public LogBlockCursorV3o(FilePath indexPath, FilePath dataPath) throws IOException {
this.indexPath = indexPath;
this.dataPath = dataPath;
loadIndexFile();
loadDataFile();
}
private void loadIndexFile() throws IOException {
BufferedInputStream indexReader = null;
StorageInputStream indexStream = null;
try {
indexStream = indexPath.newInputStream();
LogFileHeader indexFileHeader = LogFileHeader.extractHeader(indexStream);
if (indexFileHeader.version() != FILE_VERSION)
throw new InvalidLogFileHeaderException("version not match, index file " + indexPath.getAbsolutePath());
long length = indexStream.length();
long pos = indexFileHeader.size();
indexReader = new BufferedInputStream(indexPath.newInputStream());
indexReader.skip(pos);
ByteBuffer bb = ByteBuffer.allocate(INDEX_ITEM_SIZE);
int id = 0;
while (pos < length) {
indexReader.read(bb.array());
IndexBlockV3Header header = new IndexBlockV3Header(id++, bb.getLong(), bb.getLong(), bb.getLong(), bb.getInt(),
totalCount + 1);
header.fp = pos;
header.ascLogCount = totalCount;
// skip reserved blocks
if (!header.isReserved()) {
totalCount += header.logCount;
indexBlockHeaders.add(header);
}
pos += INDEX_ITEM_SIZE;
bb.clear();
}
long t = 0;
for (int i = indexBlockHeaders.size() - 1; i >= 0; i--) {
IndexBlockV3Header h = indexBlockHeaders.get(i);
h.dscLogCount = t;
t += h.logCount;
}
} finally {
if (indexReader != null) {
try {
indexReader.close();
} catch (IOException e) {
}
}
if (indexStream != null) {
try {
indexStream.close();
} catch (IOException e) {
}
}
}
}
private void loadDataFile() throws IOException {
this.dataStream = dataPath.newInputStream();
LogFileHeader dataFileHeader = LogFileHeader.extractHeader(dataStream);
if (dataFileHeader.version() != FILE_VERSION)
throw new InvalidLogFileHeaderException("version not match, data file");
byte[] ext = dataFileHeader.getExtraData();
compressionMethod = new String(ext, 4, ext.length - 4).trim();
if (compressionMethod.length() == 0)
compressionMethod = null;
}
private DataBlockV3 loadDataBlock(IndexBlockV3Header h) throws IOException {
DataBlockV3Params p = new DataBlockV3Params();
p.indexHeader = h;
p.dataStream = dataStream;
p.dataPath = dataPath;
p.compressionMethod = compressionMethod;
DataBlockV3 b = new DataBlockV3(p);
return b;
}
@Override
public void close() throws IOException {
dataStream.close();
}
@Override
public boolean hasNext() {
return currentBlockIndex < indexBlockHeaders.size();
}
@Override
public LogBlock next() {
if (!hasNext())
throw new NoSuchElementException();
LogBlock lb = new LogBlock();
IndexBlockV3Header indexHeader = indexBlockHeaders.get(currentBlockIndex);
Map<String, Object> m = lb.getData();
m.put("block_id", currentBlockIndex);
m.put("min_time", new Date(indexHeader.minTime));
m.put("max_time", new Date(indexHeader.maxTime));
m.put("log_count", indexHeader.logCount);
m.put("reserved", indexHeader.isReserved());
currentBlockIndex++;
try {
DataBlockV3 db = loadDataBlock(indexHeader);
m.put("ver", (int) db.getVersion());
m.put("fp", db.getDataFp());
m.put("flag", Integer.toHexString(db.getFlag()));
m.put("iv", db.getIv());
m.put("signature", db.getSignature());
m.put("original_size", db.getOriginalSize());
m.put("compressed_size", db.getCompressedSize());
m.put("data", db.getCompressedBuffer()); // FIXME : non-compression case (check LogFileReaderV3)
} catch (Throwable t) {
logger.debug("logpresso logstorage: broken data block, file=" + dataPath.getAbsolutePath() + ", fp=" + indexHeader.dataFp, t);
}
return lb;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}