/*
* 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.logrecord;
import org.apache.log4j.Logger;
import org.chaidb.db.Db;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.helper.Config;
import org.chaidb.db.helper.FileUtil;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.LogRecord;
import org.chaidb.db.log.Lsn;
import java.io.File;
/**
* file operations log.
* Fields:
* fileName: String file name
* operationFlag: byte operation flag:delete or new
* fileOrDirFlag: byte file or directory
*/
public class FileOperationLogRecord extends LogRecord {
private static final Logger logger = Logger.getLogger(FileOperationLogRecord.class);
private static PageBufferManager bpm = PageBufferManager.getInstance();
static final byte FILENAME_LENGTH_SIZE = 2; //4 bytes
static final byte FLAG_SIZE = 1; //1 bytes
public static final byte FILE_UPDATE_OPE_BYTE = 2;
public static final byte FILE_NEW_OPERATION = 1;
public static final byte FILE_DELETE_OPERATION = 0;
public static final byte FILE_FLAG = 1;
public static final byte DIRECTORY_FLAG = 0;
/**
* file name
*/
private String fileName = "";
/**
* file name String length
*/
private short rNameLen;
private String rName = "";
/**
* operation flag
* flag :1 new
* :0 delete
*/
private byte operationFlag;
/**
* file or directory flag
* fileOrDirFlag: 0 dir
* : 1 file
*/
private byte fileOrDirFlag;
/**
* Default Constructor
*/
public FileOperationLogRecord() {
super();
}
/**
* Constructor
*
* @param fileName
* @param operationFlag
* @param fileOrDirFlag
* @param txnId
*/
public FileOperationLogRecord(String fileName, byte operationFlag, byte fileOrDirFlag, int txnId) {
super();
this.fileName = fileName;
this.rName = bpm.getRelatviePath(fileName);
this.rNameLen = (short) rName.length();
this.operationFlag = operationFlag;
this.fileOrDirFlag = fileOrDirFlag;
super.setTxnId(txnId);
super.setType(LOG_FILE_OPERATION);
}
/**
* get operation flag
*/
public byte getOperationFlag() {
return operationFlag;
}
public String getFileName() {
return fileName;
}
public byte getFileOrDirFlag() {
return fileOrDirFlag;
}
/**
* just for compatible
*/
public String getBTreeFileName() {
return null;
}
/**
* user interface to add a log record and put it to buffer pool
*/
public Lsn log() throws ChaiDBException {
if (!Config.TXN_SUPPORT) {
return null;
}
super.log();
LogManager logMgr = Db.getLogManager();
Lsn newLsn = null;
// if (operationFlag == FILE_DELETE_OPERATION) {
// newLsn = logMgr.put(this, LogManager.LOG_FLUSH);//flush log to log file while do delete collection/index operation
// } else {
// newLsn = logMgr.put(this, LogManager.LOG_DATA);//not flush log to log file,just put it to log buffer
// }
/* modified by marriane 2003-11-6,flush log either deleting or creating operation for fixing inconsistent issue */
newLsn = logMgr.put(this, LogManager.LOG_FLUSH);
return newLsn;
}
/**
* converts a byte array into a log record instance
*
* @param bArr
* @param start pointer
* @return boolean true|false
*/
public boolean read(byte[] bArr, int start) throws ChaiDBException {
/* construct a new LogRecord instance */
super.read(bArr, start);
/* get the values of FileOperationLogRecord Object */
int step = start + super.getRecordLength();
rNameLen = ByteTool.bytesToShort(bArr, step, msbFirst);
step += FILENAME_LENGTH_SIZE;
byte[] fileNameArr = new byte[rNameLen];
System.arraycopy(bArr, step, fileNameArr, 0, rNameLen);
rName = ByteTool.toString(fileNameArr);
fileName = bpm.getFullPath(rName);
step += rNameLen;
operationFlag = bArr[step];
step += FLAG_SIZE;
fileOrDirFlag = bArr[step];
return true;
}
/**
* redo or undo the operation for recovery
* REDO:
* NEW_REDO:
* if file is directory,mkdirs()
* else, createNewFile()
* DELETE_REDO:
* if file is directory,delete all files in the path and the path self.
* else, delete the file self.
* UNDO:
* NEW_UNDO:
* if file is directory,delete all files in the path and the path self.
* else, delete the file self.
* DELETE_UNDO:
* do REDO operation,because deleting operation couldn't be UNDO.
*
* @param recoverFlag REDO or UNDO.
* @return boolean true|false
*/
public boolean recover(short recoverFlag) throws ChaiDBException {
try {
File file = new File(fileName);
boolean existFlag = file.exists();
/* do REDO */
if ((recoverFlag == LogRecord.REDO) && (operationFlag == FILE_NEW_OPERATION) && (!existFlag)) {
/* renew the file or recreate the directory */
if (fileOrDirFlag == DIRECTORY_FLAG) {
file.mkdirs();
} else {
file.createNewFile();
}
} else
if (((recoverFlag == LogRecord.REDO) && (operationFlag == FILE_DELETE_OPERATION) && existFlag) || ((recoverFlag == LogRecord.UNDO) && (((operationFlag == FILE_NEW_OPERATION) && existFlag) || (operationFlag == FILE_DELETE_OPERATION) || (operationFlag == FILE_UPDATE_OPE_BYTE)))) {
/* modified by marriane for close btree while delete
directory or file */
try {
if (file.isFile()) {
bpm.dCloseBTree(file.getAbsolutePath());
} else {
bpm.dCloseAllTreesofCollection(file);
}
} finally {
FileUtil.removeFileOrDirectory(file); //modified by marriane 2003-11-6 for fixing bug #4644.
}
}
return true;
} catch (Exception e) {
{
logger.debug(e);
}
return false;
}
}
/**
* print data and help in debugging log files
*/
public void print() throws ChaiDBException {
super.print();
if (fileOrDirFlag == FILE_FLAG) {
{
logger.debug("file opration");
}
} else if (fileOrDirFlag == DIRECTORY_FLAG) {
{
logger.debug("directory operatin");
}
}
{
logger.debug("name:" + fileName);
}
if (operationFlag == FILE_NEW_OPERATION) {
{
logger.debug("[operation]: NEW FILE");
}
} else if (operationFlag == FILE_DELETE_OPERATION) {
{
logger.debug("[operation]: DELETE FILE");
}
}
}
/**
* converts a log record instance into a byte array.
* The byte array has the following format:
* ----------------------------------------------------------------------
* | hdr | type | txn | prevLsn | rNameLen | fileName | operationFlag |
* ----------------------------------------------------------------------
* | fileOrDirFlag
* ----------------------------------------------------------------------
* rNameLen : 2 bytes.
* fileName: rNameLen bytes.
* operationFlag: 1 bytes.
* fileOrDirFlag: 1 bytes.
*
* @param byteArray
* @param start pointer
*/
public void toBytes(byte[] byteArray, int start) throws ChaiDBException {
super.toBytes(byteArray, start);
int step = start + super.getRecordLength();
ByteTool.shortToBytes(byteArray, step, rNameLen);
step += FILENAME_LENGTH_SIZE;
System.arraycopy(rName.getBytes(), 0, byteArray, step, rNameLen);
step += rNameLen;
byteArray[step] = operationFlag;
step += FLAG_SIZE;
byteArray[step] = fileOrDirFlag;
}
/**
* get current log record type total length
*
* @return int total lenth
*/
public int getRecordLength() {
return super.getRecordLength() + FILENAME_LENGTH_SIZE + rNameLen + (FLAG_SIZE * 2);
}
}