/*
* JCarder -- cards Java programs to keep threads disentangled
*
* Copyright (C) 2006-2007 Enea AB
* Copyright (C) 2007 Ulrik Svensson
* Copyright (C) 2007 Joel Rosdahl
*
* This program is made available under the GNU GPL version 2, with a special
* exception for linking with JUnit. See the accompanying file LICENSE.txt for
* details.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.enea.jcarder.common.contexts;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import net.jcip.annotations.NotThreadSafe;
import com.enea.jcarder.common.Lock;
import com.enea.jcarder.common.LockingContext;
import com.enea.jcarder.util.logging.Logger;
@NotThreadSafe
public final class ContextFileReader
implements ContextReaderIfc {
// TODO Make the directory of the database files configurable?
public static final String EVENT_DB_FILENAME = "jcarder_events.db";
public static final String CONTEXTS_DB_FILENAME = "jcarder_contexts.db";
static final long MAGIC_COOKIE = 3927194112434171438L;
static final int MAJOR_VERSION = 1;
static final int MINOR_VERSION = 0;
static final Charset CHARSET = Charset.forName("UTF-8");
private final Logger mLogger;
private final ByteBuffer mBuffer;
public ContextFileReader(Logger logger, File file) throws IOException {
mLogger = logger;
RandomAccessFile raFile = new RandomAccessFile(file, "r");
final String path = file.getCanonicalPath();
mLogger.info("Opening for reading: " + path);
FileChannel roChannel = raFile.getChannel();
if (roChannel.size() > Integer.MAX_VALUE) {
throw new IOException("File too large: " + path);
}
mBuffer = roChannel.map(FileChannel.MapMode.READ_ONLY,
0,
(int) roChannel.size());
roChannel.close();
raFile.close();
validateHeader(path);
}
private void validateHeader(String filename) throws IOException {
mBuffer.rewind();
if (MAGIC_COOKIE != mBuffer.getLong()) {
throw new IOException("Invalid file contents in: " + filename);
}
final int majorVersion = mBuffer.getInt();
final int minorVersion = mBuffer.getInt();
if (majorVersion != MAJOR_VERSION) {
throw new IOException("Incompatible version: "
+ majorVersion + "." + minorVersion
+ " in: " + filename);
}
}
private String readString() {
final int stringBytes = mBuffer.getInt();
mBuffer.limit(stringBytes + mBuffer.position());
final String result = CHARSET.decode(mBuffer).toString();
mBuffer.limit(mBuffer.capacity());
return result;
}
public LockingContext readContext(int id) {
mBuffer.position(id);
return new LockingContext(readString(),
readString(),
readString());
}
public Lock readLock(int id) {
mBuffer.position(id);
String className = readString();
int objectId = mBuffer.getInt();
return new Lock(className, objectId);
}
}