Package org.chaidb.db.index.btree

Source Code of org.chaidb.db.index.btree.BTreeChanges

/*
* 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;

import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.BitMap;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
import org.chaidb.db.index.btree.bufmgr.StorageProxy;

import java.io.*;
import java.util.*;

public class BTreeChanges {
    private final static String EXT = ".trk";
    private final static int OBSOLETE_FLAG = 0x9D5ACD59;
    private final static int INIT_FLAG = ~OBSOLETE_FLAG;
    private final static int MAX_BITMAP_SIZE = 0x10000; // 64k:2G/(4096*8)
    private final static int INIT_CAP = MAX_BITMAP_SIZE;
    private final static int COUNT_PER = 10;

    // position
    private final static int FLAG_OFF = 0;
    private final static int TREEID_OFF = FLAG_OFF + 4;
    private final static int DATA_OFF = TREEID_OFF + 4;

    private static PageBufferManager pbm = PageBufferManager.getInstance();
    private int treeid;
    private LinkedHashMap bitMaps;
    private String btreepath;
    private String trackName;

    private boolean reading = false; // when backup, reading the track file
    private static final int BLOCK_SIZE = BTreeSpec.PAGE_SIZE;

    public BTreeChanges(int treeid) throws ChaiDBException {
        this.treeid = treeid;
        bitMaps = new LinkedHashMap(1);
        BitMap bitMap = new BitMap(INIT_CAP);
        bitMaps.put(new Integer(0), bitMap);
        btreepath = pbm.getBTreeName(treeid);
        trackName = btreepath + EXT;

        // init bitMap from file
        try {
            init();
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID);
        }
    }

    // for backup use
    public BTreeChanges(String btreename) throws ChaiDBException {
        bitMaps = new LinkedHashMap(1);
        BitMap bitMap = new BitMap(INIT_CAP);
        bitMaps.put(new Integer(0), bitMap);
        this.btreepath = btreename;
        trackName = btreename + EXT;
        reading = true;

        // init bitMap from file
        try {
            init();
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID);
        }
    }

    public void setChange(Object change) {
        // parameters validate
        if (change.getClass() != PageNumber.class) {
            // bad parameters
            return;
        }
        PageNumber pno = (PageNumber) change;
        if (pno.getTreeId() != treeid) {
            return;
        }
        BitMap bm;
        Integer pageInFile = new Integer(pno.getFileNumber());
        bm = (BitMap) bitMaps.get(pageInFile);
        if (bm == null) {
            bm = new BitMap(INIT_CAP);
            bitMaps.put(pageInFile, bm);
        }
        bm.set(pno.getPageInFile());
    }

    public long getChangeSize() {
        Iterator it = bitMaps.values().iterator();
        int len = 0;
        while (it.hasNext()) {
            len += ((BitMap) it.next()).getTrueCount();
        }
        return len * BTreeSpec.PAGE_SIZE;
    }

    public String getBTreeName() {
        return btreepath;
    }

    private boolean isRightFile(RandomAccessFile raf) {
        try {
            raf.seek(0);
            int flag = raf.readInt();
            if (flag == OBSOLETE_FLAG) return false;
            else return true;
        } catch (IOException e) {
            return false;
        }
    }

    private void init() throws IOException, ChaiDBException {
        if (trackName == null || trackName.length() == 0) {
            return;
        }

        /**
         * Several cases:
         * 1: file not exists
         * 2: bad file
         * 3: correct file
         */
        File f = new File(trackName);
        RandomAccessFile raf = null;
        int flag;
        try {
            if (!f.exists()) { // this would not be run
                if (reading) {
                    throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
                }
                raf = new RandomAccessFile(trackName, "rw");
                raf.setLength(4);
                flag = OBSOLETE_FLAG;
                raf.writeInt(flag);
                return;
            } else {
                raf = new RandomAccessFile(trackName, "rw");
                if (!isRightFile(raf) && reading) { // when reading track file, it's bad
                    throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
                } else {
                    if (f.length() < 8) {
                        if (reading) {
                            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
                        } else {
                            raf.setLength(8);
                            flag = OBSOLETE_FLAG;
                            raf.seek(FLAG_OFF);
                            raf.writeInt(flag);
                            if (!reading) {
                                raf.writeInt(treeid);
                            } else { // should read a empty track file when backup, it's bad, throw exception
                                throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
                            }
                        }
                        return;
                    } else {

                        // Set tree to obsolete
                        flag = raf.readInt();
                        flag = OBSOLETE_FLAG;
                        raf.seek(FLAG_OFF);
                        raf.writeInt(flag);

                        // read treeid
                        raf.seek(TREEID_OFF);
                        int treeid = raf.readInt();
                        if (reading) { // reading track file, init treeid
                            this.treeid = treeid;
                        } else { // init a track file, write treeid
                            raf.seek(TREEID_OFF);
                            raf.writeInt(this.treeid);
                        }

                        BitMap bm = (BitMap) bitMaps.get(new Integer(0));
                        long len = f.length() - DATA_OFF;
                        if (len > 0x10000) {
                            len -= 0x10000;
                        }
                        bm.read(raf, (int) len);
                        return;
                    }
                }
            }
        } finally {
            if (raf != null) {
                raf.close();
            }
        }
    }

    private void readAFile(int pageInFile, RandomAccessFile raf) throws IOException {
        raf.seek(DATA_OFF + MAX_BITMAP_SIZE * pageInFile);
        Integer fnum = new Integer(pageInFile);
        BitMap bm = (BitMap) bitMaps.get(fnum);
        if (bm == null) {
            bm = new BitMap(INIT_CAP);
        }
        bm.read(raf);
    }

    private void beginFlush(RandomAccessFile raf) throws IOException {
        int flag = OBSOLETE_FLAG;
        raf.seek(0);
        raf.writeInt(flag);
        raf.getFD().sync();
    }

    private void endFlush(RandomAccessFile raf) throws IOException {
        int flag = INIT_FLAG;
        raf.seek(0);
        raf.writeInt(flag);
        raf.getFD().sync();
    }

    public void flush() throws ChaiDBException {
        if (trackName == null || trackName.length() == 0) {
            return;
        }
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(trackName, "rw");
            beginFlush(raf);
            raf.seek(TREEID_OFF); // not necessary
            raf.writeInt(treeid);
            Iterator it = bitMaps.values().iterator();
            while (it.hasNext()) {
                ((BitMap) it.next()).flush(raf);
            }
            endFlush(raf);
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
                }
            }
        }
    }

    public boolean changesOf(String btreename) {
        return this.btreepath.equals(btreename);
    }

    public void backupChanges(DataOutputStream dataOut) throws ChaiDBException {
        try {
            File f = new File(trackName);
            int fileCount = (int) ((f.length() - DATA_OFF) / MAX_BITMAP_SIZE);

            // read changes
            RandomAccessFile raf = new RandomAccessFile(trackName, "r");
            try {
                for (int i = 0; i < fileCount; i++) {
                    readAFile(i, raf);
                }
            } finally {
                raf.close();
            }

            // generate pagenumbers
            Iterator it = bitMaps.entrySet().iterator();
            ArrayList pnos = new ArrayList();
            int pos[];
            while (it.hasNext()) {
                Map.Entry ent = (Map.Entry) it.next();
                pos = ((BitMap) ent.getValue()).getTruePositions();
                for (int i = 0; i < pos.length; i++) {
                    pnos.add(new PageNumber(treeid, ((Integer) ent.getKey()).intValue(), pos[i]));
                }
            }

            // backup changes
            int size = 0; // counter for snap file to 4k blocks
            int pnocount = pnos.size();
            int readed = 0;
            int endpos = 0;
            dataOut.writeInt(treeid);
            dataOut.writeInt(pnocount);
            size += 8;

            // write data offset
            size += 4 * pnocount;

            size += 4; //allocate space to write header size
            final int odd = size % BLOCK_SIZE;
            int pageRemain = 0;
            if (odd != 0) {
                pageRemain = BLOCK_SIZE - odd;
            }
            dataOut.writeInt(size + pageRemain); //write header size

            // write pagenumbers
            for (int i = 0; i < pnocount; i++) {
                dataOut.writeInt(((PageNumber) pnos.get(i)).getPageNumber());
            }

            for (int i = 0; i < pageRemain; i++) {
                dataOut.write(0);
            }

            // write datum
            while (readed < pnocount) {
                endpos = readed + COUNT_PER;
                if (endpos > pnocount) endpos = pnocount;
                List datum = StorageProxy.readPages(btreepath, pnos, readed, endpos - readed);
                while (datum.size() > 0) {
                    dataOut.write((byte[]) datum.remove(0));
                }
                readed += endpos;
            }
            //stream will be closed by outer method
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        } catch (ChaiDBException e) {
            throw e;
        }
    }

    public void backupChanges(String dstpath) throws ChaiDBException {
        DataOutputStream out = null;
        try {
            out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dstpath)));
            backupChanges(out);
        } catch (FileNotFoundException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    ;
                }
            }
        }
    }

    public static void backupChanges(String btreepath, String dstpath) throws ChaiDBException {
        BTreeChanges bchange = new BTreeChanges(btreepath);
        bchange.backupChanges(dstpath);
    }

    public static void restoreChanges(String btreepath, DataInputStream in) throws ChaiDBException {
        try {
            int size = 0;
            int treeid = in.readInt();
            int count = in.readInt();
            size += 8;
            int data_offset = in.readInt();
            size += 4;
            ArrayList pnos = new ArrayList(count);

            // read page numbers
            PageNumber pn;
            for (int i = 0; i < count; i++) {
                pn = new PageNumber(in.readInt());
                pn.setTreeId(treeid);
                pnos.add(pn);
            }
            size += 4 * count;

            int remain = data_offset - size;
            for (int i = 0; i < remain; i++) {
                in.read();
            }

            // write to btree
            int writed = 0, endpos = 0;
            ArrayList pages;
            byte[] data;
            while (writed < count) {
                endpos += COUNT_PER;
                if (endpos > count) endpos = count;
                pages = new ArrayList(COUNT_PER);
                for (int i = writed; i < endpos; i++) {
                    data = new byte[BTreeSpec.PAGE_SIZE];
                    in.read(data);
                    pages.add(data);
                }
                StorageProxy.writePages(btreepath, pnos, writed, pages, 0, endpos - writed);
                writed = endpos;
            }
            in.close();
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        } catch (ChaiDBException e) {
            throw e;
        }
    }

    public static void restoreChanges(String btreepath, String srcpath) throws ChaiDBException {

        try {
            DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(srcpath)));
            restoreChanges(btreepath, in);
        } catch (FileNotFoundException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        }
    }

    public static String getTrackFileName(String btreename) {
        return btreename + EXT;
    }

    public static boolean isTrackFile(String fname) {
        return fname.endsWith(EXT);
    }

    public static boolean trackFileExist(int treeid) {
        try {
            return new File(PageBufferManager.getInstance().getBTreeName(treeid) + EXT).exists();
        } catch (ChaiDBException e) {
            // when the btree is not opened, following code will be excute. that's impossible
            // this time return false is ok.
            return false;
        }
    }

    public static void createEmptyTrackFile(String btreename) throws ChaiDBException {
        try {
            RandomAccessFile raf = new RandomAccessFile(getTrackFileName(btreename), "rw");
            try {
                raf.writeInt(INIT_FLAG);
            } finally {
                raf.close();
            }
        } catch (IOException e) {
            throw new ChaiDBException(ErrorCode.BTREE_TRACKFILE_IO_ERROR);
        }
    }
}
TOP

Related Classes of org.chaidb.db.index.btree.BTreeChanges

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.