/*
* 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.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class EventFileHeader {
public static final String MAGIC_STRING_INDEX = "KRAKEN_EVENT_IDX";
public static final String MAGIC_STRING_POINTER = "KRAKEN_EVENT_PTR";
public static final String MAGIC_STRING_DATA = "KRAKEN_EVENT_DAT";
private static final String STRING_ENCODING = "Latin1";
private static final short ALIGNED_HEADER_SIZE_BASE = 22;
private static final short ALIGNED_HEADER_SIZE_POS = 20;
private String magicString;
private short bom = (short) 0xFEFF;
private short version;
private short headerSize;
private byte[] extraData;
private EventFileHeader() {
}
public EventFileHeader(short version, String magicString) {
this.version = version;
this.magicString = magicString;
this.headerSize = getAlignedHeaderSize();
magicStringValidate(this);
}
public static EventFileHeader extractHeader(File f) throws IOException {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(f, "r");
if (f.length() < ALIGNED_HEADER_SIZE_BASE)
throw new IllegalArgumentException("File size is too small: " + f.getAbsolutePath());
raf.seek(ALIGNED_HEADER_SIZE_POS);
short hdrSize = raf.readShort();
if (hdrSize > Short.MAX_VALUE)
throw new IllegalArgumentException("Invalid header size: " + f.getAbsolutePath());
raf.seek(0);
byte[] hdr = new byte[hdrSize];
raf.readFully(hdr);
return unserialize(hdr);
} finally {
if (raf != null)
raf.close();
}
}
public static EventFileHeader unserialize(byte[] array) {
try {
EventFileHeader hdr = new EventFileHeader();
ByteBuffer buf = ByteBuffer.wrap(array);
byte[] magic = new byte[16];
buf.get(magic);
hdr.magicString = new String(magic, STRING_ENCODING);
hdr.bom = buf.getShort();
hdr.version = buf.getShort();
hdr.headerSize = buf.getShort();
if (hdr.headerSize != buf.position())
hdr.extraData = Arrays.copyOfRange(buf.array(), buf.position(), hdr.headerSize);
magicStringValidate(hdr);
return hdr;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
private static void magicStringValidate(EventFileHeader hdr) {
if (!MAGIC_STRING_INDEX.equals(hdr.magicString) && !MAGIC_STRING_POINTER.equals(hdr.magicString)
&& !MAGIC_STRING_DATA.equals(hdr.magicString)) {
throw new IllegalArgumentException();
}
}
public String magicString() {
return magicString;
}
public short version() {
return version;
}
public int size() {
return headerSize;
}
public byte[] getExtraData() {
return extraData;
}
public void setExtraData(byte[] e) {
this.extraData = Arrays.copyOf(e, e.length);
this.headerSize = getAlignedHeaderSize();
}
public byte[] serialize() {
ByteBuffer buf = ByteBuffer.allocate(getAlignedHeaderSize());
try {
buf.put(magicString.getBytes(STRING_ENCODING));
buf.putShort(bom);
buf.putShort(version);
if (buf.position() != 20)
throw new IllegalStateException();
buf.putShort(getAlignedHeaderSize());
if (extraData != null)
buf.put(extraData);
} catch (UnsupportedEncodingException e) {
}
return buf.array();
}
private short getAlignedHeaderSize() {
int length = ALIGNED_HEADER_SIZE_BASE + ((extraData != null) ? extraData.length : 0);
return (short) ((length + 3) / 4 * 4);
}
}