/*
* 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.log;
import org.apache.log4j.Logger;
import org.chaidb.db.DBState;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.Config;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class DefaultLogFile extends LogFile {
private static final Logger logger = Logger.getLogger(DefaultLogFile.class);
/* log file instance */
private static DefaultLogFile defaultLogFile = null;
public static final String CHAIDB_DATALOG_PATH = "chaidb.datalog.path";
public static final String DEFAULT_DATALOG_DIR_NAME = "datalog";
/**
* Default Constructor
*/
private DefaultLogFile() {
try {
init(getDefaultPath());
} catch (ChaiDBException e) {
; //impossible
}
}
/**
* Get instance
*
* @return an instance of LogFile based on default log path
*/
public static DefaultLogFile getInstance() {
if (defaultLogFile == null) {
defaultLogFile = new DefaultLogFile();
}
return defaultLogFile;
}
private String getDefaultPath() {
String logPath = null;
if (((logPath = System.getProperty(CHAIDB_DATALOG_PATH)) != null) && (logPath.length() > 0)) {
return logPath;
}
try {
logPath = Config.getCurrentDBDirectory() + File.separator + DEFAULT_DATALOG_DIR_NAME;
} catch (Exception e) {
logPath = Config.getDbHome() + File.separator + DEFAULT_DATALOG_DIR_NAME;
}
return logPath;
}
/**
* get log file name according to Lsn's fileId value
*
* @param lsn
* @return String fileName
*/
public String getLogFileNameIfExists(Lsn lsn) throws ChaiDBException {
try {
int logFileId = lsn.getFileId();
String fileName = getFileName(logFileId);
String fileFullName = getFullFileName(logFileId);
File logFile = new File(fileFullName);
if (logFile.exists()) {
return fileName;
} else {
return null;
}
} catch (Exception e) {
throw new ChaiDBException(ErrorCode.LOG_ERROR_BASE, e.toString());
}
}
/**
* write newest fileId to a fileid.idb file
* 1)if it's the first time to write newest fileId to fileid.idb,
* create the file and add fileid to it.
* 2)if file has exist,overwrite the old fileid.
*
* @return boolean true|false
*/
public static boolean writeNewestFileIdToFile(int fileId) throws ChaiDBException {
if (fileId < LogManager.FIRSTREC_FILEID) {
return false;
}
try {
DBState.getInstance().setLatestLogFileId((short) fileId);
} catch (Exception e) {
logger.debug(e);
throw new ChaiDBException(ErrorCode.LSN_FILE_IO_ERROR, e.toString());
}
return true;
}
/**
* get newest fileId from fileid.idb file
*
* @return int fileId
*/
public static int getNewestFileIdFromStateFile() {
int fileId = LogManager.FIRSTREC_FILEID;
try {
fileId = DBState.getInstance().getLatestLogFileId();
if (fileId == 0) {
fileId = LogManager.FIRSTREC_FILEID;
}
} catch (Exception e) {
fileId = LogManager.FIRSTREC_FILEID;
}
return fileId;
}
/**
* get the lastest lsn in fileid.idb file
*
* @return Lsn lastLsn
*/
public Lsn getLastLsnInDir(int[] endOffset) throws ChaiDBException {
int fileId = getNewestFileIdFromStateFile();
return getLastLsn(fileId, endOffset);
}
/**
* get first log record bytes of all files, no use
*
* @return byte[] logRecord byte array
*/
public LogRecord getFirstLogRecord() throws ChaiDBException {
Lsn lsn = getFirstLsn();
LogRecord logObj = getLogRecordByLsn(lsn);
return logObj;
}
/**
* get latest log record bytes of all files, no use
*
* @return byte[] logRecord byte array
*/
public LogRecord getLastestLogRecord() throws ChaiDBException {
int[] endOffset = new int[1];
Lsn lsn = getLastLsnInDir(endOffset);
LogRecord logObj = getLogRecordByLsn(lsn);
return logObj;
}
/**
* get a log record from log file by input lsn,//it's not used,just for test
*
* @param lsn
* @return LogRecord object
*/
public LogRecord getLogRecordByLsn(Lsn lsn) throws ChaiDBException {
int fileId = lsn.getFileId();
int offset = lsn.getOffset();
int hdrLen = Hdr.getHdrLength();
int fileLen = getDefaultLogFileLength(fileId);
if ((fileLen == 0) || (offset > fileLen)) {
throw new ChaiDBException(ErrorCode.LOG_RECORD_NOT_EXIST);
}
RandomAccessFile rfile = null;
try {
rfile = open(fileId, "r");
/* get the record header(Hdr) */
byte[] hdrArr = new byte[hdrLen];
rfile.seek(offset);
rfile.read(hdrArr, 0, hdrLen);
Hdr hdr = new Hdr();
if (hdr.read(hdrArr, 0)) {
int logRecLen = hdr.getLength();
byte[] logObjArr = new byte[logRecLen];
rfile.seek(offset);
rfile.read(logObjArr, 0, logRecLen);
return RecordFactory.createRecord(logObjArr, 0);
} else {
throw new ChaiDBException(ErrorCode.LOG_RECORD_HEADER_CANNOT_READ);
}
} catch (IOException ie) {
throw new ChaiDBException(ErrorCode.LOG_FILE_IO_ERROR, ie);
} finally {
if (rfile != null) {
try {
rfile.close();
} catch (IOException e) {
throw new ChaiDBException(ErrorCode.LOG_FILE_IO_ERROR, e);
}
}
}
}
/**
* get log file's length, only called by getLogRecordByLsn, for test purpose
*
* @param fileId
* @return int fileLen
*/
public int getDefaultLogFileLength(int fileId) {
if (fileId <= 0) {
return 0;
}
int fileLen = 0;
String fileName = getFullFileName(fileId);
File logFile = new File(fileName);
if (logFile.exists()) {
fileLen = new Long(logFile.length()).intValue();
if (fileLen > Lsn.getLsnLength()) {
/*subtract lastest lsn length in log file,modified by marriane 2001-11-27*/
if (fileId == getNewestFileIdFromStateFile()) {
fileLen = fileLen - Lsn.getLsnLength();
}
} else {
fileLen = 0;
}
} else {
fileLen = 0;
}
return fileLen;
}
protected void changeWriteFile(int fileId, int offset) throws ChaiDBException {
super.changeWriteFile(fileId, offset);
writeNewestFileIdToFile(fileId);
}
}