/*
* 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.helper.cache.storage;
import com.coyotegulch.jisp.*;
import org.chaidb.db.helper.cache.Cache;
import org.chaidb.db.helper.cache.CacheException;
import java.io.File;
import java.io.IOException;
public class BTreePathStorage {
public BTreePathStorage(String storagePath) {
this(storagePath, DEFAULT_CACHE_SIZE);
}
public BTreePathStorage(String storagePath, int cacheSize) {
this.keyFile = new File(storagePath + KEY_FILE_SUFFIX);
this.dataFile = new File(storagePath + DATA_FILE_SUFFIX);
this.cache = new Cache(cacheSize);
this.cache.clearCache();
}
public void init() throws SecondaryStorageException {
Jisp.setClassLoader(this.getClass().getClassLoader());
boolean toCreate = !keyFile.exists() && !dataFile.exists();
IndexedObjectDatabase dataStorage = null;
BTreeIndex keyStorage = null;
// create new storage
if (toCreate) {
try {
dataStorage = new IndexedObjectDatabase(dataFile.getAbsolutePath(), true);
keyStorage = new BTreeIndex(keyFile.getAbsolutePath(), DEFAULT_INDEX_ORDER, false);
} catch (Exception e) {
throw new SecondaryStorageException("create storage failed!", e);
} finally {
try {
keyStorage.close();
dataStorage.close();
} catch (IOException e) {
}
}
}
// open storage
boolean isConsistent = keyFile.exists() && dataFile.exists();
if (isConsistent) {
// populate cache
try {
dataStorage = new IndexedObjectDatabase(dataFile.getAbsolutePath(), false);
keyStorage = new BTreeIndex(keyFile.getAbsolutePath());
dataStorage.attachIndex(keyStorage);
int keyCount = keyStorage.count();
if (keyCount == 0) {
return;
}
int cacheSize = cache.maxSize();
int loopTime = (keyCount > cacheSize) ? cacheSize : keyCount;
for (int i = 1; i <= loopTime; i++) {
IntKey key = new IntKey(i);
cache.putIntoCache(key, dataStorage.read(key, keyStorage));
}
} catch (Exception e) {
throw new SecondaryStorageException("initializing storage failed.", e);
} finally {
try {
keyStorage.close();
dataStorage.close();
} catch (IOException e) {
}
}
} else {
StringBuffer errorMessage = new StringBuffer("!");
if (!keyFile.exists()) {
errorMessage.append(" missing key file!");
}
if (!dataFile.exists()) {
errorMessage.append(" missing data file!");
}
throw new SecondaryStorageException("storage is corrupted" + errorMessage);
}
}
public synchronized int store(String value) throws SecondaryStorageException {
if (value == null) {
throw new SecondaryStorageException("value MUST NOT be null!");
}
IndexedObjectDatabase dataStorage = null;
BTreeIndex keyStorage = null;
try {
dataStorage = new IndexedObjectDatabase(dataFile.getAbsolutePath(), false);
keyStorage = new BTreeIndex(keyFile.getAbsolutePath());
dataStorage.attachIndex(keyStorage);
IntKey nextKey = new IntKey(keyStorage.count() + KEY_START_FROM); // start from 1
dataStorage.write(new OrderedObject[]{nextKey}, value.getBytes(DATA_CHARSET));
cache.putIntoCache(nextKey, value);
return nextKey.intValue();
} catch (Exception e) {
throw new SecondaryStorageException("store into storage failed, value=" + value, e);
} finally {
try {
keyStorage.close();
dataStorage.close();
} catch (IOException e) {
}
}
}
public synchronized String retrieve(int key) throws SecondaryStorageException {
if (key < 0) {
throw new SecondaryStorageException("key MUST NOT less than 0!");
}
final IntKey intKey = new IntKey(key);
IndexedObjectDatabase dataStorage = null;
BTreeIndex keyStorage = null;
String value = null;
try {
value = (String) cache.getFromCache(intKey);
if (value != null) {
return value;
}
} catch (CacheException e) {
throw new SecondaryStorageException("get value from cache failed, value=" + value, e);
}
try {
dataStorage = new IndexedObjectDatabase(dataFile.getAbsolutePath(), false);
keyStorage = new BTreeIndex(keyFile.getAbsolutePath());
dataStorage.attachIndex(keyStorage);
byte[] valueInBytes = (byte[]) dataStorage.read(intKey, keyStorage);
if (valueInBytes != null) {
value = new String(valueInBytes, DATA_CHARSET);
cache.putIntoCache(intKey, value);
}
return value;
} catch (Exception e) {
throw new SecondaryStorageException("retrieve from storage failed, key=" + key, e);
} finally {
try {
keyStorage.close();
dataStorage.close();
} catch (IOException e) {
}
}
}
public synchronized int size() throws SecondaryStorageException {
BTreeIndex keyStorage = null;
try {
keyStorage = new BTreeIndex(keyFile.getAbsolutePath());
return keyStorage.count();
} catch (Exception e) {
throw new SecondaryStorageException("count storage size failed.", e);
} finally {
try {
keyStorage.close();
} catch (IOException e) {
}
}
}
private File keyFile;
private File dataFile;
private Cache cache;
private static final String KEY_FILE_SUFFIX = ".key";
private static final String DATA_FILE_SUFFIX = ".dat";
private static final String DATA_CHARSET = "UTF-8";
private static final int DEFAULT_CACHE_SIZE = 1024;
private static final int DEFAULT_INDEX_ORDER = 5;
private static final int KEY_START_FROM = 1;
}