All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jxta.impl.xindice.core.filer.BTreeFiler Maven / Gradle / Ivy

package net.jxta.impl.xindice.core.filer;


/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xindice" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999-2001, The dbXML
 * Group, L.L.C., http://www.dbxmlgroup.com.  For more
 * information on the Apache Software Foundation, please see
 * .
 *
 */
import net.jxta.impl.xindice.core.DBException;
import net.jxta.impl.xindice.core.FaultCodes;
import net.jxta.impl.xindice.core.data.Key;
import net.jxta.impl.xindice.core.data.Record;
import net.jxta.impl.xindice.core.data.RecordSet;
import net.jxta.impl.xindice.core.data.Value;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
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 short PAGESIZE = 512;
    // TODO: MAXKEYSIZE might need tuning
    private static final short MAXKEYSIZE = 256;

    private BTreeFilerHeader fileHeader;

    private static final int DBE_CANNOT_READ = (int) (572l);
    
    public BTreeFiler() {
        super();
        fileHeader = (BTreeFilerHeader) getFileHeader();
    }

    public void setLocation(String dir, String file) {
        setFile(new File(dir, file + ".tbl"));
    }

    public String getName() {
        return getFile().getName();
    }

    @Override
    public boolean open() throws DBException {
        if (super.open()) {
            // These are the only properties that can be changed after creation
            fileHeader.setMaxKeySize(MAXKEYSIZE);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean create() throws DBException {
        fileHeader.setPageSize(PAGESIZE);
        fileHeader.setMaxKeySize(MAXKEYSIZE);
        return super.create();
    }

    public Record readRecord(Key key) throws DBException {
        if (key == null || key.getLength() == 0) {
            return null;
        }

        checkOpened();
        try {
            long pos = findValue(key);
            Record record = readRecord(pos);

            record.setKey(key);
            return record;
        } catch (BTreeNotFoundException b) {// do nothing
        } catch (BTreeException b) {
            throw b;
        } catch (IOException e) {
            throw new FilerException(DBE_CANNOT_READ, "Can't read record '" + key + "': " + e.getMessage(), e);
        }
        return null;
    }

    public Record readRecord(long pos) throws DBException {
        checkOpened();
        try {
            Page startPage = getPage(pos);
            Value v = readValue(startPage);
            BTreeFilerPageHeader sph = (BTreeFilerPageHeader) startPage.getPageHeader();

            HashMap meta = new HashMap(4);

            meta.put(Record.CREATED, sph.getCreated());
            meta.put(Record.MODIFIED, sph.getModified());
            meta.put(Record.LIFETIME, sph.getLifetime());
            meta.put(Record.EXPIRATION, sph.getExpiration());

            return new Record(null, v, meta);
        } catch (IOException e) {
            throw new FilerException(DBE_CANNOT_READ, "Can't read record : " + e.getMessage(), e);
        }
    }

    public long writeRecord(Key key, Value value) throws DBException {

        return writeRecord(key, value, 0, 0);
    }

    public long writeRecord(Key key, Value value, long lifetime, long expiration) throws DBException {

        if (key == null || key.getLength() == 0) {
            throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Invalid key: '" + key + "'");
        }
        if (value == null) {
            throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Invalid null value");
        }
        checkOpened();
        try {
            Page p;
            long pos;
            try {
                pos = findValue(key);
                p = getPage(pos);
            } catch (BTreeNotFoundException b) {
                p = getFreePage();
                pos = p.getPageNum();
                addValue(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.setLifetime(lifetime);
            ph.setExpiration(expiration);
            ph.setStatus(RECORD);

            writeValue(p, value);
            flush();
            return pos;
        } catch (IOException e) {
            throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Can't write record '" + key + "': " + e.getMessage(), e);
        }
    }

    public long writeRecord(long pos, Value value) throws DBException {

        if (value == null) {
            throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Invalid null value");
        }
        checkOpened();
        try {
            writeValue(pos, value);
            flush();
            return pos;
        } catch (IOException e) {
            throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Can't write record '" + value + "': " + e.getMessage(), e);
        }
    }

    public boolean deleteRecord(Key key) throws DBException {
        if (key == null || key.getLength() == 0) {
            return false;
        }
        checkOpened();
        try {
            long pos = findValue(key);
            Page p = getPage(pos);

            removeValue(key);
            unlinkPages(p.getPageNum());

            fileHeader.decRecordCount();

            flush();

            return true;
        } catch (BTreeNotFoundException b) {// not found move on
        } catch (IOException e) {
            throw new FilerException(FaultCodes.DBE_CANNOT_DROP, "Can't delete record '" + key + "': " + e.getMessage(), e);
        }
        return false;
    }

    public long getRecordCount() throws DBException {
        checkOpened();
        return fileHeader.getRecordCount();
    }

    public RecordSet getRecordSet() throws DBException {
        checkOpened();
        return new BTreeFilerRecordSet();
    }

    /**
     * BTreeFilerRecordSet
     */

    private class BTreeFilerRecordSet implements RecordSet, BTreeCallback {
        private List keys = new ArrayList();
        private Iterator it;

        public BTreeFilerRecordSet() throws DBException {
            try {
                query(null, this);
                it = keys.iterator();
            } catch (IOException e) {
                throw new FilerException(FaultCodes.GEN_CRITICAL_ERROR, "Error generating RecordSet", e);
            }
        }

        public synchronized boolean indexInfo(Value value, long pointer) {
            keys.add(new Key(value));
            return true;
        }

        public synchronized Key getNextKey() {
            return it.next();
        }

        public synchronized Record getNextRecord() throws DBException {
            return readRecord(it.next());
        }

        public synchronized Value getNextValue() throws DBException {
            return getNextRecord().getValue();
        }

        public synchronized boolean hasMoreRecords() {
            return it.hasNext();
        }
    }

    // //////////////////////////////////////////////////////////////////

    @Override
    public FileHeader createFileHeader() {
        return new BTreeFilerHeader();
    }

    @Override
    public FileHeader createFileHeader(boolean read) throws IOException {
        return new BTreeFilerHeader(read);
    }

    @Override
    public FileHeader createFileHeader(long pageCount) {
        return new BTreeFilerHeader(pageCount);
    }

    @Override
    public FileHeader createFileHeader(long pageCount, int pageSize) {
        return new BTreeFilerHeader(pageCount, pageSize);
    }

    @Override
    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);
        }

        @Override
        public synchronized void read(RandomAccessFile raf) throws IOException {
            super.read(raf);
            totalBytes = raf.readLong();
        }

        @Override
        public synchronized void write(RandomAccessFile raf) throws IOException {
            super.write(raf);
            raf.writeLong(totalBytes);
        }

        /**
         * The total number of bytes in use by the file
         * @param totalBytes the new total number of bytes
         */
        public synchronized void setTotalBytes(long totalBytes) {
            this.totalBytes = totalBytes;
            setDirty();
        }

        /**
         * The total number of bytes in use by the file
         * @return the total number of bytes
         */
        public synchronized long getTotalBytes() {
            return totalBytes;
        }
    }


    /**
     * BTreeFilerPageHeader
     */

    private final class BTreeFilerPageHeader extends BTreePageHeader {
        private long created = 0;
        private long modified = 0;
        private long lifetime = 0;
        private long expiration = 0;

        public BTreeFilerPageHeader() {}

        public BTreeFilerPageHeader(DataInputStream dis) throws IOException {
            super(dis);
        }

        @Override
        public synchronized void read(DataInputStream dis) throws IOException {
            super.read(dis);

            if (getStatus() == UNUSED) {
                return;
            }

            created = dis.readLong();
            modified = dis.readLong();
            lifetime = dis.readLong();
            expiration = dis.readLong();
        }

        @Override
        public synchronized void write(DataOutputStream dos) throws IOException {
            super.write(dos);
            dos.writeLong(created);
            dos.writeLong(modified);
            dos.writeLong(lifetime);
            dos.writeLong(expiration);
        }

        @Override
        public synchronized void setRecordLen(int recordLen) {
            fileHeader.setTotalBytes((fileHeader.totalBytes - getRecordLen()) + recordLen);
            super.setRecordLen(recordLen);
        }

        /**
         * UNIX-time when this record was created
         * @param created creation time
         */
        public synchronized void setCreated(long created) {
            this.created = created;
            setDirty();
        }

        /**
         * UNIX-time when this record was created
         * @return creation time
         */
        public synchronized long getCreated() {
            return created;
        }

        /**
         * UNIX-time when this record was last modified
         * @param modified modified time
         */
        public synchronized void setModified(long modified) {
            this.modified = modified;
            setDirty();
        }

        /**
         *  UNIX-time when this record was last modified
         * @return modified time
         */
        public synchronized long getModified() {
            return modified;
        }

        /**
         *  JXTA-lifetime this record's lifetime
         * @param lifetime the new record lifetime
         */
        public synchronized void setLifetime(long lifetime) {
            this.lifetime = lifetime;
            setDirty();
        }

        /**
         * JXTA-lifetime this record's lifetime
         * @return the record lifetime
         */
        public synchronized long getLifetime() {
            return lifetime;
        }

        /**
         * JXTA-expiration this record's expiration
         * @param expiration the record expiration time
         */
        public synchronized void setExpiration(long expiration) {
            this.expiration = expiration;
            setDirty();
        }

        /**
         * JXTA-expiration this record's expiration
         * @return the record expiration time
         */
        public synchronized long getExpiration() {
            return expiration;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy