/*
* 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.api;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.chaidb.db.Db;
import org.chaidb.db.DbEnvironment;
import org.chaidb.db.KernelContext;
import org.chaidb.db.DBState;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.Config;
import org.chaidb.db.index.BTreeFactory;
import org.chaidb.db.index.IDBIndex;
import org.chaidb.db.transaction.recover.TransactionRecover;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.ArrayList;
/**
* Database is the instance of Database. It consist of one or more BTrees.
*/
public class Database {
private static Db db = null;
private KernelContext kc;
private HashMap openedBTree = new HashMap(10);
private HashMap btree2Path = new HashMap(10);
private Logger log = Logger.getLogger(Database.class);
public boolean isOpened() {
return opened;
}
private boolean opened =false;
/**
* Create a database instance and open the database.
*
* @throws ChaiDBException
*/
public Database(String dbHome) throws ChaiDBException {
System.setProperty("chaidb.home", dbHome);
PropertyConfigurator.configure(Database.class.getResource("/org/chaidb/log4j.properties"));
if (db == null) {
boolean needRecovery = DBState.getInstance().getNeedRecovery();
recoverWhenServerStart(needRecovery);
db = new Db();
}
kc = new KernelContext();
opened = true;
}
/**
* Close the database. All opened BTree will be closed ahead.
*
* @throws ChaiDBException
*/
public void close() throws ChaiDBException {
ArrayList list = new ArrayList(btree2Path.keySet());
for (int i = 0; i < list.size(); i++) {
BTree tree = (BTree) list.get(i);
tree.close();
}
DBState.getInstance().setNeedRecovery(false);
opened = false;
}
/**
* Open a BTree instance, if the BTree doesn't exist, it will be created.
*
* @param filename file name of the BTree
* @param btreeType Regular BTree or Hyper BTree
* @return the instance of the BTree
* @throws ChaiDBException if there is any error while opening the BTree
*/
public BTree openBTree(String filename, BTreeType btreeType) throws ChaiDBException {
if (openedBTree.containsKey(filename)) {
return (BTree)openedBTree.get(filename);
}
String path = DbEnvironment.getDataHome().concat(filename);
IDBIndex index = BTreeFactory.createBTree(btreeType.typeId);
index.open(path, kc);
BTree bTree = new BTree(this, index, btreeType, kc);
openedBTree.put(filename, bTree);
btree2Path.put(bTree, filename);
return bTree;
}
/**
* Close the BTree instance, this method should only be called by BTree.close()
*
* @param btree
* @throws ChaiDBException
*/
void removeBTreeFromMap(BTree btree) throws ChaiDBException {
if (!btree2Path.containsKey(btree)) {
throw new ChaiDBException(ErrorCode.BTREE_NOT_FOUND);
}
String filename = (String) btree2Path.remove(btree);
openedBTree.remove(filename);
}
/**
* Drop a BTree on disk. The file will be deleted.
*
* @param filename file name of the BTree
* @return true if the BTree was dropped successfully.
* @throws ChaiDBException
*/
public boolean dropBTree(String filename) throws ChaiDBException {
if (openedBTree.containsKey(filename)) {
throw new ChaiDBException(ErrorCode.BTREE_ALREADY_OPENED);
}
String path = DbEnvironment.getDataHome().concat(File.separator).concat(filename);
return new File(path).delete();
}
private void recoverWhenServerStart(boolean needRecovery) throws ChaiDBException {
TransactionRecover txnRecover;
if (needRecovery) {
System.out.println("[" + new Date().toString() + "] Database is recovering from an inconsistent status...");
// It is NOT a real sys error, just to make message about
// "begin recovery" visible even at the default log level.
// This is a request from Sequoia.
log.info("Begin normal recovery...");
long begin = System.currentTimeMillis();
txnRecover = Db.getTxnManager().setRecover(TransactionRecover.NORMAL_RECOVER);
try {
txnRecover.doRecover();
long end = System.currentTimeMillis();
System.out.println("[" + new Date().toString() + "] Recovery finished in " + ((end - begin) / 1000) + " seconds.");
log.info("Success of normal recovery!!!");
} catch (ChaiDBException e) {
System.out.println("[" + new Date().toString() + "] Failure to do recovery!!! Please refer to log file for more information.");
log.info("Failure to do recovery!!!");
log.debug(e);
}
if (!Config.TXN_SUPPORT) {
needRecovery = false;
/*
* Here all items are written to disk. This seems not to be efficent,but
* this behavior is called in a relative low possibility, influencing the
* whole system performance little.
*/
DBState.getInstance().setNeedRecovery(needRecovery);
}
} else { //In order to present system crush. when this.needRecovery == 0 ;
if (!DbEnvironment.READ_ONLY) {
if (Config.TXN_SUPPORT) {
try {
Db.getTxnManager().doCheckpoint();
} catch (ChaiDBException e) {
throw e;
}
needRecovery = true;
/*
* Here all items are written to disk. This seems not to be efficent,but
* this behavior is called in a relative low possibility, influencing the
* whole system performance little.
*/
DBState.getInstance().setNeedRecovery(needRecovery);
}
}
}
}
/**
* Begin a transaction, use Transaction.commit or Transaction.rollback then.
*
* @return instance of new transaction
* @throws ChaiDBException
*/
public Transaction beginTransaction() throws ChaiDBException {
Transaction transaction = new Transaction();
kc.setTxn(transaction.getTxn());
return transaction;
}
}