package com.dbxml.db.common.btree;
/*
* dbXML - Native XML Database
* Copyright (c) 1999-2006 The dbXML Group, L.L.C.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* $Id: BTreeFiler.java,v 1.8 2008/08/18 17:24:53 bradford Exp $
*/
import com.dbxml.db.core.Collection;
import com.dbxml.db.core.DBException;
import com.dbxml.db.core.FaultCodes;
import com.dbxml.db.core.data.Key;
import com.dbxml.db.core.data.Record;
import com.dbxml.db.core.data.RecordMetaData;
import com.dbxml.db.core.data.RecordSet;
import com.dbxml.db.core.data.Value;
import com.dbxml.db.core.filer.Filer;
import com.dbxml.db.core.filer.FilerException;
import com.dbxml.db.core.transaction.Transaction;
import com.dbxml.util.Configuration;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* BTreeFiler is a Filer implementation based on the BTree class.
*/
public final class BTreeFiler extends BTree implements Filer {
protected static final byte RECORD = 20;
private static final String PAGESIZE = "pagesize";
private Collection collection;
private BTreeFilerHeader fileHeader;
public BTreeFiler(boolean transactionSupported) {
super(false);
setTransactionSupported(transactionSupported);
fileHeader = (BTreeFilerHeader)getFileHeader();
}
public BTreeFiler(File f, boolean transactionSupport) {
this(transactionSupport);
setFile(f);
}
public BTreeFiler(File f) {
this(f, true);
}
public BTreeFiler() {
this(true);
}
public void setLocation(String location) {
setFile(new File(collection.getCollectionRoot(), location + ".tbl"));
}
public String getName() {
return "BTreeFiler";
}
public boolean create() throws DBException {
Configuration cfg = getConfig();
if ( cfg != null )
fileHeader.setPageSize(cfg.getIntAttribute(PAGESIZE, fileHeader.getPageSize()));
return super.create();
}
public void setCollection(Collection collection) {
this.collection = collection;
setLocation(collection.getName());
}
private RecordMetaData buildMetaData(Page p) {
BTreeFilerPageHeader ph = (BTreeFilerPageHeader)p.getPageHeader();
HashMap meta = new HashMap(2);
meta.put(RecordMetaData.CREATED, new Long(ph.getCreated()));
meta.put(RecordMetaData.MODIFIED, new Long(ph.getModified()));
return new RecordMetaData(meta);
}
public RecordMetaData getRecordMetaData(Transaction tx, Key key) throws DBException {
checkOpened();
try {
long pos = findValue(tx, key);
Page page = getPage(tx, pos);
return buildMetaData(page);
}
catch ( BTreeNotFoundException b ) {
}
catch ( BTreeException b ) {
throw b;
}
catch ( Exception e ) {
e.printStackTrace(System.err);
}
return null;
}
public Record readRecord(Transaction tx, Key key) throws DBException {
checkOpened();
try {
long pos = findValue(tx, key);
Page page = getPage(tx, pos);
Value v = readValue(tx, page);
RecordMetaData md = buildMetaData(page);
return new Record(key, v, md);
}
catch ( BTreeNotFoundException b ) {
}
catch ( BTreeException b ) {
throw b;
}
catch ( Exception e ) {
e.printStackTrace(System.err);
}
return null;
}
public boolean writeRecord(Transaction tx, Key key, Value value) throws DBException {
checkOpened();
try {
Page p;
try {
long pos = findValue(tx, key);
p = getPage(tx, pos);
}
catch ( BTreeNotFoundException b ) {
p = getFreePage(tx);
addValue(tx, key, p.getPageNum());
fileHeader.incRecordCount();
}
BTreeFilerPageHeader ph = (BTreeFilerPageHeader)p.getPageHeader();
long t = System.currentTimeMillis();
if ( ph.getStatus() == UNUSED )
ph.setCreated(t);
ph.setModified(t);
ph.setStatus(RECORD);
writeValue(tx, p, value);
}
catch ( DBException d ) {
throw d;
}
catch ( Exception e ) {
e.printStackTrace(System.err);
}
return true;
}
public boolean deleteRecord(Transaction tx, Key key) throws DBException {
checkOpened();
try {
long pos = findValue(tx, key);
Page p = getPage(tx, pos);
removeValue(tx, key);
unlinkPages(tx, p.getPageNum());
fileHeader.decRecordCount();
return true;
}
catch ( BTreeNotFoundException b ) {
}
catch ( BTreeException b ) {
throw b;
}
catch ( Exception e ) {
e.printStackTrace(System.err);
}
return false;
}
public long getRecordCount(Transaction tx) throws DBException {
checkOpened();
return fileHeader.getRecordCount();
}
public RecordSet getRecordSet(Transaction tx) throws DBException {
checkOpened();
return new BTreeFilerRecordSet(tx);
}
/**
* BTreeFilerRecordSet
*/
private class BTreeFilerRecordSet implements RecordSet, BTreeCallback {
private Transaction tx;
private List keys;
private Iterator iter;
public BTreeFilerRecordSet(Transaction tx) throws DBException {
this.tx = tx;
try {
keys = new ArrayList((int)fileHeader.getRecordCount());
query(tx, null, this);
iter = keys.iterator();
}
catch ( IOException e ) {
throw new FilerException(FaultCodes.GEN_CRITICAL_ERROR, "Error generating RecordSet", e);
}
}
public synchronized void indexInfo(Value value, Value extra) {
keys.add(new Key(value));
}
public synchronized Key getNextKey() {
return (Key)iter.next();
}
public synchronized Record getNextRecord() throws DBException {
return readRecord(tx, (Key)iter.next());
}
public synchronized Value getNextValue() throws DBException {
return getNextRecord().getValue();
}
public synchronized boolean hasMoreRecords() {
return iter.hasNext();
}
}
////////////////////////////////////////////////////////////////////
public FileHeader createFileHeader() {
return new BTreeFilerHeader();
}
public FileHeader createFileHeader(boolean read) throws IOException {
return new BTreeFilerHeader(read);
}
public FileHeader createFileHeader(long pageCount) {
return new BTreeFilerHeader(pageCount);
}
public FileHeader createFileHeader(long pageCount, int pageSize) {
return new BTreeFilerHeader(pageCount, pageSize);
}
public PageHeader createPageHeader() {
return new BTreeFilerPageHeader();
}
/**
* BTreeFilerHeader
*/
private final class BTreeFilerHeader extends BTreeFileHeader {
private long totalBytes = 0;
public BTreeFilerHeader() {
}
public BTreeFilerHeader(long pageCount) {
super(pageCount);
}
public BTreeFilerHeader(long pageCount, int pageSize) {
super(pageCount, pageSize);
}
public BTreeFilerHeader(boolean read) throws IOException {
super(read);
}
public synchronized void read(RandomAccessFile raf) throws IOException {
super.read(raf);
totalBytes = raf.readLong();
}
public synchronized void write(RandomAccessFile raf) throws IOException {
super.write(raf);
raf.writeLong(totalBytes);
}
/** The total number of bytes in use by the file */
public synchronized void setTotalBytes(long totalBytes) {
this.totalBytes = totalBytes;
setDirty();
}
/** The total number of bytes in use by the file */
public synchronized long getTotalBytes() {
return totalBytes;
}
}
/**
* BTreeFilerPageHeader
*/
private final class BTreeFilerPageHeader extends BTreePageHeader {
private long created = 0;
private long modified = 0;
public BTreeFilerPageHeader() {
}
public BTreeFilerPageHeader(ByteBuffer buf) throws IOException {
super(buf);
}
public synchronized void read(ByteBuffer buf) throws IOException {
super.read(buf);
if ( getStatus() == UNUSED )
return;
created = buf.getLong();
modified = buf.getLong();
}
public synchronized void write(ByteBuffer buf) throws IOException {
super.write(buf);
buf.putLong(created);
buf.putLong(modified);
}
public synchronized void setRecordLen(int recordLen) {
fileHeader.setTotalBytes((fileHeader.totalBytes - getRecordLen()) + recordLen);
super.setRecordLen(recordLen);
}
/** UNIX-time when this record was created */
public synchronized void setCreated(long created) {
this.created = created;
setDirty();
}
/** UNIX-time when this record was created */
public synchronized long getCreated() {
return created;
}
/** UNIX-time when this record was last modified */
public synchronized void setModified(long modified) {
this.modified = modified;
setDirty();
}
/** UNIX-time when this record was last modified */
public synchronized long getModified() {
return modified;
}
}
}