/*
* Copyright 2012 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.eventstorage.engine.file;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.krakenapps.api.DateFormat;
import org.krakenapps.eventstorage.Event;
import org.krakenapps.eventstorage.EventRecord;
import org.krakenapps.eventstorage.engine.BufferedRandomAccessFile;
import org.krakenapps.eventstorage.engine.DatapathUtil;
import org.krakenapps.eventstorage.engine.DatapathUtil.FileType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EventDataFile extends FileWriter {
private static final int DAT_SIZE = 21;
private static final int DEFAULT_CACHE_SIZE = 16 * 1024 * 1024;
private Logger logger = LoggerFactory.getLogger(EventDataFile.class);
private Date day;
private boolean write;
private EventFileHeader dathdr;
private BufferedRandomAccessFile datbuf;
private Object datlock = new Object();
private ByteBuffer cache;
private Object cachelock = new Object();
public EventDataFile(int tableId, Date day, boolean write) throws IOException {
this(tableId, day, true, DEFAULT_CACHE_SIZE);
}
public EventDataFile(int tableId, Date day, boolean write, int cacheSize) throws IOException {
super(tableId, DatapathUtil.getFilePath(tableId, day, FileType.Data));
boolean success = false;
try {
this.day = day;
this.write = write;
this.dathdr = getHeader(EventFileHeader.MAGIC_STRING_DATA);
this.datbuf = new BufferedRandomAccessFile(getFile(), write ? "rw" : "r", 524288, dathdr.size());
this.cache = ByteBuffer.allocate(cacheSize);
success = true;
} finally {
if (!success)
close();
}
}
public Date getDay() {
return day;
}
public Event read(long fp, EventRecord record) throws IOException {
touch();
if (fp < datbuf.length()) {
synchronized (datlock) {
datbuf.seek(fp);
long id = datbuf.readLong();
if (id != record.getId())
logger.warn("kraken eventstorage: invalid data. fp [{}], record id [{}], data id [{}]", new Object[] { fp,
record.getId(), id });
Date created = new Date(datbuf.readLong());
int datlen = datbuf.readInt();
byte reserved = datbuf.readByte();
byte[] data = new byte[datlen];
datbuf.read(data);
return new Event(getTableId(), record.getId(), created, record.getDate(), record.getCount(), reserved, data);
}
} else {
synchronized (cachelock) {
int pos = cache.position();
cache.position((int) (fp - datbuf.length()));
long id = cache.getLong();
if (id != record.getId())
logger.warn("kraken eventstorage: invalid data. fp [{}], record id [{}], data id [{}]", new Object[] { fp,
record.getId(), id });
Date created = new Date(cache.getLong());
int datlen = cache.getInt();
byte reserved = cache.get();
byte[] data = new byte[datlen];
cache.get(data);
cache.position(pos);
return new Event(getTableId(), record.getId(), created, record.getDate(), record.getCount(), reserved, data);
}
}
}
public Set<Long> getIds() throws IOException {
Set<Long> ids = new HashSet<Long>();
long fp = 0L;
synchronized (datlock) {
while (fp < datbuf.length()) {
datbuf.seek(fp);
long id = datbuf.readLong();
ids.add(id);
datbuf.readLong();
fp += DAT_SIZE + datbuf.readInt();
}
}
return ids;
}
public long writeAndGetFp(EventRecord record) throws IOException {
synchronized (datlock) {
long fp = datbuf.length() + cache.position();
write(record);
return fp;
}
}
@Override
protected void doWrite(EventRecord record) throws IOException {
if (!write) {
write = true;
datbuf.close();
datbuf = new BufferedRandomAccessFile(getFile(), "rw", 524288, dathdr.size());
}
synchronized (cachelock) {
if (cache.remaining() < record.getDataLength() + DAT_SIZE)
flush(true);
cache.putLong(record.getId());
cache.putLong(record.getDate().getTime());
cache.putInt(record.getDataLength());
cache.put((byte) 0x00); // reserved
cache.put((record.getData() != null) ? record.getData() : new byte[0]);
}
}
@Override
protected void doFlush(boolean sync) throws IOException {
synchronized (datlock) {
datbuf.seek(datbuf.length());
datbuf.write(cache.array(), 0, cache.position());
cache.clear();
datbuf.flush(sync);
}
}
@Override
protected void doClose() {
if (datbuf != null)
datbuf.close();
}
@Override
public String toString() {
return "EventDataFile [tableId=" + getTableId() + ", day=" + DateFormat.format("yyyy-MM-dd", day) + "]";
}
}