/*
* Copyright (C) 2006 http://www.chaidb.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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. See the
* GNU General Public License for more details.
*
*/
package org.chaidb.db;
import org.apache.log4j.Logger;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.log.DefaultLogFile;
import org.chaidb.db.log.Lsn;
import org.chaidb.db.transaction.Transaction;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class DBState {
private static final Logger logger = Logger.getLogger(DBState.class);
public static final String DB_STATE = "chaidb.state";
private static String LOG_FILE_PATH = DefaultLogFile.getInstance().getLogFilePath();
public static String DB_STATE_FILE = null;
static {
if (LOG_FILE_PATH.endsWith(File.separator)) DB_STATE_FILE = LOG_FILE_PATH + DB_STATE;
else DB_STATE_FILE = LOG_FILE_PATH + File.separator + DB_STATE;
}
static private DBState singleton = new DBState();
/**
* the state file reference
*/
private RandomAccessFile file = null;
/**
* The index info for different operation
*/
private static final int LOG_INDEX = 0;
private static final int ARCHIVE_INDEX = 2;
private static final int TXN_INDEX = 4;
private static final int NEED_RECOVERY_INDEX = 8;
private static final int LAST_CHECKPOINT_INDEX = 12;
/**
* if the file not exists then create it.
* if the file exists then read properties.
*/
private DBState() {
}
public static DBState getInstance() {
return singleton;
}
/**
* open the state file based on file name
*/
private void open(String fileName, String mode) throws ChaiDBException {
open(new File(fileName), mode);
}
private void open(File file, String mode) throws ChaiDBException {
try {
this.file = new RandomAccessFile(file, mode);
} catch (FileNotFoundException fnfe) {
try {
String dir = file.getAbsolutePath();
dir = dir.substring(0, dir.lastIndexOf(File.separator));
new File(dir).mkdirs();
this.file = new RandomAccessFile(file, "rws");
} catch (Exception e) {
String details = "Open for file " + file.getName() + " failed. " + e.toString() + ".";
throw new ChaiDBException(0, details);
}
} catch (IOException ioe) {
String details = "Open for file " + file.getName() + " failed. " + ioe.toString() + ".";
throw new ChaiDBException(0, details);
}
}
/**
* close the state file
*/
private void close() throws ChaiDBException {
try {
file.close();
} catch (IOException ioe) {
String details = "Close for the state file failed. " + ioe.toString() + ".";
throw new ChaiDBException(0, details);
}
}
/**
* Reads a state from the database file
*
* @param stateIndex The position to read the state.
* @param state The state data
* state data will be touched.
*/
private synchronized void readState(int stateIndex, byte[] state) throws ChaiDBException {
try {
open(DB_STATE_FILE, "r");
file.seek((long) stateIndex);
file.read(state);
} catch (IOException e) {
String details = "The read state operation failed. " + e.toString() + ".";
throw new ChaiDBException(0, details);
} finally {
close();
}
}
/**
* Write a state back to the state file
*
* @param state The state data
* @param stateIndex The position to put the state data.
*/
private synchronized void writeState(byte[] state, int stateIndex) throws ChaiDBException {
try {
open(DB_STATE_FILE, "rws");
file.seek((long) stateIndex);
file.write(state);
} catch (IOException ioe) {
String details = "The write state operation failed. " + ioe.toString() + ".";
throw new ChaiDBException(0, details);
} finally {
close();
}
}
public void setLatestCheckPoint(Lsn lastCheckPoint) throws ChaiDBException {
byte[] lsn = lastCheckPoint.toBytes();
writeState(lsn, LAST_CHECKPOINT_INDEX);
}
// --Recycle Bin START (10/28/03 2:30 PM):
// public Lsn getLatestCheckPointLsn() throws ChaiDBException {
// byte[] blsn = new byte[Lsn.getLsnLength()];
// readState(LAST_CHECKPOINT_INDEX, blsn);
// Lsn lsn = new Lsn();
// lsn.read(blsn, 0);
// return lsn;
// }
// --Recycle Bin STOP (10/28/03 2:30 PM)
public byte[] getLatestCheckPoint() throws ChaiDBException {
byte[] blsn = new byte[Lsn.getLsnLength()];
readState(LAST_CHECKPOINT_INDEX, blsn);
return blsn;
}
public boolean setNeedRecovery(boolean needRecovery) {
try {
int nNeedRecovery;
nNeedRecovery = needRecovery ? 1 : 0;
byte[] stateValue = ByteTool.intToBytes(nNeedRecovery, true);
writeState(stateValue, NEED_RECOVERY_INDEX);
} catch (Exception e) {
logger.error(e);
return false;
}
return true;
}
public boolean getNeedRecovery() {
try {
byte[] stateValue = new byte[4];
readState(NEED_RECOVERY_INDEX, stateValue);
int nNeedRecovery = ByteTool.bytesToInt(stateValue, 0, true);
return (nNeedRecovery == 1);
} catch (Exception e) {
logger.error(e);
return true;
}
}
public void setLatestLogFileId(short fileId) throws ChaiDBException {
byte[] stateValue = ByteTool.shortToBytes(fileId);
writeState(stateValue, LOG_INDEX);
}
public short getLatestLogFileId() throws ChaiDBException {
byte[] stateValue = new byte[Lsn.LSN_FILEID_SIZE];
readState(LOG_INDEX, stateValue);
return ByteTool.bytesToShort(stateValue, 0, true);
}
public void setArchiveFlag(boolean bArchive) throws ChaiDBException {
short nArchive = bArchive ? (short) 1 : (short) 0;
byte[] stateValue = ByteTool.shortToBytes(nArchive);
writeState(stateValue, ARCHIVE_INDEX);
}
public boolean getArchiveFlag() throws ChaiDBException {
byte[] stateValue = new byte[2];
readState(ARCHIVE_INDEX, stateValue);
short archive = ByteTool.bytesToShort(stateValue, 0, true);
return (archive == 1);
}
public void setLatestTxnId(int TxnId) throws ChaiDBException {
byte[] stateValue = ByteTool.intToBytes(TxnId);
writeState(stateValue, TXN_INDEX);
}
public int getLatestTxnId() throws ChaiDBException {
byte[] stateValue = new byte[Transaction.TXN_ID_SIZE];
readState(TXN_INDEX, stateValue);
return ByteTool.bytesToInt(stateValue, 0, true);
}
}