/*
* 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.index.btree.bufmgr;
import org.apache.log4j.Logger;
import org.apache.xerces.parsers.DOMParser;
import org.chaidb.db.DbEnvironment;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.io.*;
import java.util.*;
/**
* @author Stanley
*/
public class StorageMeta {
private String storageMetaFile;
private HashMap colStorageMap = new HashMap();
private String defaultPrimaryDir = DbEnvironment.getDataHome();
private static Logger logger = Logger.getLogger(StorageMeta.class);
private static final String FILE_NAME = "collection_storage.xml";
private static final String XML_HEADER = "<?xml version=\"1.0\" ?>";
private static final String NEW_LINE = "\n";
private static final String COLLECTIONS = "Collections";
private static final String COLLECTION = "Collection";
private static final String COLLECTION_ID = "id";
private static final String PRIMARY_DIRECTORY = "primaryDir";
private static final String EXTENDED_DIRECTORY = "ExtendedDirectory";
private static final String EXTENDED_PATH = "path";
private static final String CURRENT_DIRECTORY = "currentDir";
public StorageMeta() {
storageMetaFile = DbEnvironment.DB_HOME + File.separator + "conf" + File.separator + FILE_NAME;
readFromDisk();
}
/**
* For unit test
*
* @param confFile the configure file path
*/
public StorageMeta(String confFile, String defaultDir) {
storageMetaFile = confFile;
defaultPrimaryDir = defaultDir;
readFromDisk();
}
/**
* To load a collection Storage metadata from disk into collectionStorageMap
*/
private synchronized void readFromDisk() {
String collectionID;
String primaryDir;
LinkedList extendedDirs;
String extendedPath;
String currentDir;
CollectionStorage colStorage;
NodeList childNodes;
Document doc = getDocument();
if (doc == null) {
return;
}
NodeList nodeList = doc.getElementsByTagName(COLLECTION);
if (nodeList.getLength() == 0) {
return;
}
for (int i = 0; i < nodeList.getLength(); i++) {
collectionID = null;
primaryDir = null;
extendedDirs = new LinkedList();
currentDir = null;
Node node = nodeList.item(i);
collectionID = node.getAttributes().getNamedItem(COLLECTION_ID).getNodeValue();
primaryDir = node.getAttributes().getNamedItem(PRIMARY_DIRECTORY).getNodeValue();
currentDir = node.getAttributes().getNamedItem(CURRENT_DIRECTORY).getNodeValue();
childNodes = node.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
Node childNode = childNodes.item(j);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
extendedPath = childNode.getAttributes().getNamedItem(EXTENDED_PATH).getNodeValue();
extendedDirs.addLast(extendedPath);
}
}
colStorage = new CollectionStorage(primaryDir, extendedDirs, currentDir);
colStorageMap.put(new Integer(collectionID), colStorage);
}
}
/**
* This method can write data to a tmp file, when your all operations
* successly you should call commit to commit the changes, or call rollback
* to discard changes if any operation failed.
*/
private void writeToDisk() throws ChaiDBException {
StringBuffer buff = new StringBuffer(XML_HEADER + NEW_LINE);
Iterator entries = colStorageMap.entrySet().iterator();
Map.Entry entry;
CollectionStorage colStorage;
String collectionID;
buff.append("<").append(COLLECTIONS).append(">").append(NEW_LINE);
{
while (entries.hasNext()) {
entry = (Map.Entry) entries.next();
collectionID = ((Integer) entry.getKey()).toString();
colStorage = (CollectionStorage) entry.getValue();
buff.append("<").append(COLLECTION).append(" ");
buff.append(COLLECTION_ID).append("=").append("\"").append(collectionID).append("\"").append(" ");
buff.append(PRIMARY_DIRECTORY).append("=").append("\"").append(colStorage.getPrimaryDirectory()).append("\"").append(" ");
buff.append(CURRENT_DIRECTORY).append("=").append("\"").append(colStorage.getCurrentDirectory()).append("\"").append(" ");
buff.append(">").append(NEW_LINE);
Iterator dires = colStorage.extendedDirs.iterator();
while (dires.hasNext()) {
buff.append("<").append(EXTENDED_DIRECTORY).append(" ");
buff.append(EXTENDED_PATH).append("=").append("\"").append((String) dires.next()).append("\"").append(" ");
buff.append("/>").append(NEW_LINE);
}
buff.append("</").append(COLLECTION).append(">").append(NEW_LINE);
}
}
buff.append("</").append(COLLECTIONS).append(">");
FileOutputStream fo = null;
try {
fo = new FileOutputStream(storageMetaFile);
fo.write(buff.toString().getBytes("UTF-8"));
fo.flush();
} catch (UnsupportedEncodingException e) {
logger.error(e);
} catch (IOException ioe) {
String details = "The operation saving collection storage metadata into " + storageMetaFile + " failed" + ". " + ioe.toString() + ".";
throw new ChaiDBException(ErrorCode.RUNTIME_IO_ERROR, details);
} finally {
if (fo != null) {
try {
fo.close();
} catch (Exception fe) {
}
}
}
}
private boolean fileExists() {
File f = new File(storageMetaFile);
if (!f.exists() || f.length() == 0) {
return false;
}
return true;
}
/**
* Get DOM Document from storageMetaFile
*
* @return Document if the file exists and is well-format. null if not.
*/
private Document getDocument() {
if (!fileExists()) {
return null;
}
Document doc = null;
try {
InputSource input = new InputSource(new FileInputStream(storageMetaFile));
DOMParser parser = new DOMParser();
parser.parse(input);
doc = parser.getDocument();
} catch (Exception e) {
String details = "The operation reading collection storage metadata from " + storageMetaFile + " failed" + ". " + e.toString() + ".";
logger.info(details);
//throw new ChaiDBException(ErrorCode.FILE_NOT_FOUND_ERROR, details);
}
return doc;
}
/**
* @param fileName the input file name
* @return String the primary directory
*/
public synchronized String getPrimaryDirectory(String fileName) {
CollectionStorage collectionStorage = null;
String relativePath = getRelativePath(fileName);
collectionStorage = (CollectionStorage) colStorageMap.get(getCollectionID(fileName));
if (collectionStorage == null) {
return defaultPrimaryDir + relativePath;
}
return collectionStorage.getPrimaryDirectory() + relativePath;
}
/**
* @param fileName
* @return
*/
public synchronized String getCurrentFileName(String fileName) {
CollectionStorage collectionStorage = null;
//String relativePath = getRelativePath(fileName);
String relativePath = new File(fileName).getName();
collectionStorage = (CollectionStorage) colStorageMap.get(getCollectionID(fileName));
if (collectionStorage == null) {
return defaultPrimaryDir + relativePath;
}
return collectionStorage.getCurrentDirectory() + relativePath;
}
/**
* @param fileName
* @return
*/
public synchronized Iterator getAllFileNames(String fileName) {
LinkedList fileList = new LinkedList();
CollectionStorage collectionStorage = null;
String relativePath = getRelativePath(fileName);
collectionStorage = (CollectionStorage) colStorageMap.get(getCollectionID(fileName));
if (collectionStorage == null) {
fileList.add(defaultPrimaryDir + relativePath);
} else {
fileList.add(collectionStorage.getPrimaryDirectory() + relativePath);
Iterator extendedFiles = collectionStorage.getExtendedDirectory().iterator();
while (extendedFiles.hasNext()) {
fileList.add(((String) extendedFiles.next()) + relativePath);
}
}
return fileList.iterator();
}
/**
* @param CollectionID
* @param extendedDir
* @throws ChaiDBException
*/
public synchronized void addExtendedDirectory(int CollectionID, String extendedDir) throws ChaiDBException {
CollectionStorage collectionStorage = null;
collectionStorage = (CollectionStorage) colStorageMap.get(new Integer(CollectionID));
if (collectionStorage == null) {
collectionStorage = new CollectionStorage(defaultPrimaryDir);
collectionStorage.addExtendedDir(extendedDir);
colStorageMap.put(new Integer(CollectionID), collectionStorage);
} else {
collectionStorage.addExtendedDir(extendedDir);
}
writeToDisk();
}
/**
* @param CollectionID
* @param primaryDir
* @param extendedDir
* @throws ChaiDBException
*/
public synchronized void addExtendedDirectory(int CollectionID, String primaryDir, String extendedDir) throws ChaiDBException {
CollectionStorage collectionStorage = null;
collectionStorage = (CollectionStorage) colStorageMap.get(new Integer(CollectionID));
if (collectionStorage == null) {
collectionStorage = new CollectionStorage(primaryDir);
collectionStorage.addExtendedDir(extendedDir);
colStorageMap.put(new Integer(CollectionID), collectionStorage);
} else {
collectionStorage.addExtendedDir(extendedDir);
}
writeToDisk();
}
/**
* @param collectionID
* @return
*/
public synchronized String listCollectionDir(int collectionID) {
CollectionStorage colStorage = null;
StringBuffer buff = new StringBuffer();
colStorage = (CollectionStorage) colStorageMap.get(new Integer(collectionID));
if (colStorage == null) {
colStorage = new CollectionStorage(defaultPrimaryDir);
}
buff.append(PRIMARY_DIRECTORY).append("=").append("\"").append(colStorage.getPrimaryDirectory()).append("\"").append(" ");
buff.append(CURRENT_DIRECTORY).append("=").append("\"").append(colStorage.getCurrentDirectory()).append("\"").append(" ");
buff.append(NEW_LINE);
Iterator dires = colStorage.getExtendedDirectory().iterator();
while (dires.hasNext()) {
buff.append(EXTENDED_DIRECTORY).append(" ");
buff.append(EXTENDED_PATH).append("=").append("\"").append((String) dires.next()).append("\"").append(" ");
buff.append(NEW_LINE);
}
return buff.toString();
}
public synchronized Iterator listColDirs(int colId) throws ChaiDBException {
CollectionStorage colStorage = null;
String colPath = Integer.toString(colId);
LinkedList colDirs = new LinkedList();
colStorage = (CollectionStorage) colStorageMap.get(new Integer(colId));
if (colStorage == null) {
colStorage = new CollectionStorage(defaultPrimaryDir);
}
colDirs.add(defaultPrimaryDir);
Iterator dirs = colStorage.getExtendedDirectory().iterator();
while (dirs.hasNext()) {
colDirs.add((String) dirs.next() + File.separator + colPath);
}
return colDirs.iterator();
}
public synchronized Iterator removeExtendedColDirs(int colId) throws ChaiDBException {
CollectionStorage colStorage = null;
String colPath = Integer.toString(colId);
LinkedList extendedColDirs = new LinkedList();
colStorage = (CollectionStorage) colStorageMap.remove(new Integer(colId));
if (colStorage == null) {
colStorage = new CollectionStorage(defaultPrimaryDir);
}
Iterator dirs = colStorage.getExtendedDirectory().iterator();
while (dirs.hasNext()) {
extendedColDirs.add((String) dirs.next() + File.separator + colPath);
}
writeToDisk();
return extendedColDirs.iterator();
}
/**
* @param fileName
* @return
*/
public synchronized Integer getCollectionID(String fileName) {
String colPath = null;
String colName = null;
int lastPosition = -1;
if (fileName.indexOf(defaultPrimaryDir) == -1) return null;
colPath = fileName.substring(defaultPrimaryDir.length());
lastPosition = colPath.indexOf(File.separator);
if (lastPosition == -1) {
return null;
} else {
colName = colPath.substring(0, lastPosition);
try {
return new Integer(colName);
} catch (Exception e) {
return null;
}
}
}
/**
* get btree file name on relative path
*
* @param fileName a virtual btree file name
* @return relative path file name
*/
private String getRelativePath(String fileName) {
String colPath = null;
int lastPosition = -1;
int lastSecPosition = -1;
lastPosition = fileName.lastIndexOf(File.separator);
colPath = fileName.substring(0, lastPosition);
lastSecPosition = colPath.lastIndexOf(File.separator);
return fileName.substring(lastSecPosition + 1);
}
/**
* for Dropping DB
*/
public Set removeAllExtendedDirs() throws ChaiDBException {
Set allDirs = new HashSet();
Iterator allColStorage = colStorageMap.values().iterator();
CollectionStorage colStorage = null;
while (allColStorage.hasNext()) {
colStorage = (CollectionStorage) allColStorage.next();
allDirs.addAll(colStorage.getExtendedDirectory());
}
colStorageMap.clear();
writeToDisk();
return allDirs;
}
public CollectionStorage getColStorage(int collectionId) {
CollectionStorage colStorage = (CollectionStorage) colStorageMap.get(new Integer(collectionId));
if (colStorage == null) {
colStorage = new CollectionStorage(defaultPrimaryDir);
}
return colStorage;
}
}