⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 recordfile.java

📁 Java Crawler with domain knowledge path
💻 JAVA
字号:
/** * JDBM LICENSE v1.00 * * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright *    statements and notices.  Redistributions must also contain a *    copy of this document. * * 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 name "JDBM" must not be used to endorse or promote *    products derived from this Software without prior written *    permission of Cees de Groot.  For written permission, *    please contact cg@cdegroot.com. * * 4. Products derived from this Software may not be called "JDBM" *    nor may "JDBM" appear in their names without prior written *    permission of Cees de Groot. * * 5. Due credit should be given to the JDBM Project *    (http://jdbm.sourceforge.net/). * * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS * ``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 * CEES DE GROOT OR ANY 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. * * Copyright 2000 (C) Cees de Groot. All Rights Reserved. * Contributions are Copyright (C) 2000 by their associated contributors. * * $Id: RecordFile.java,v 1.6 2005/06/25 23:12:32 doomdark Exp $ */package jdbm.recman;import java.io.*;import java.util.*;/** *  This class represents a random access file as a set of fixed size *  records. Each record has a physical record number, and records are *  cached in order to improve access. *<p> *  The set of dirty records on the in-use list constitutes a transaction. *  Later on, we will send these records to some recovery thingy. */public final class RecordFile {    final TransactionManager txnMgr;    // Todo: reorganize in hashes and fifos as necessary.    // free -> inUse -> dirty -> inTxn -> free    // free is a cache, thus a FIFO. The rest are hashes.    private final LinkedList free = new LinkedList();    private final HashMap inUse = new HashMap();    private final HashMap dirty = new HashMap();    private final HashMap inTxn = new HashMap();    // transactions disabled?    private boolean transactionsDisabled = false;    /** The length of a single block. */    public final static int BLOCK_SIZE = 8192;//4096;    /** The extension of a record file */    final static String extension = ".db";    /** A block of clean data to wipe clean pages. */    final static byte[] cleanData = new byte[BLOCK_SIZE];    private RandomAccessFile file;    private final String fileName;    /**     *  Creates a new object on the indicated filename. The file is     *  opened in read/write mode.     *     *  @param fileName the name of the file to open or create, without     *         an extension.     *  @throws IOException whenever the creation of the underlying     *          RandomAccessFile throws it.     */    RecordFile(String fileName) throws IOException {        this.fileName = fileName;        file = new RandomAccessFile(fileName + extension, "rw");        txnMgr = new TransactionManager(this);    }    /**     *  Returns the file name.     */    String getFileName() {        return fileName;    }    /**     *  Disables transactions: doesn't sync and doesn't use the     *  transaction manager.     */    void disableTransactions() {        transactionsDisabled = true;    }    /**     *  Gets a block from the file. The returned byte array is     *  the in-memory copy of the record, and thus can be written     *  (and subsequently released with a dirty flag in order to     *  write the block back).     *     *  @param blockid The record number to retrieve.     */     BlockIo get(long blockid) throws IOException {         Long key = new Long(blockid);         // try in transaction list, dirty list, free list         BlockIo node = (BlockIo) inTxn.get(key);         if (node != null) {             inTxn.remove(key);             inUse.put(key, node);             return node;         }         node = (BlockIo) dirty.get(key);         if (node != null) {             dirty.remove(key);             inUse.put(key, node);             return node;         }         for (Iterator i = free.iterator(); i.hasNext(); ) {             BlockIo cur = (BlockIo) i.next();             if (cur.getBlockId() == blockid) {                 node = cur;                 i.remove();                 inUse.put(key, node);                 return node;             }         }         // sanity check: can't be on in use list         if (inUse.get(key) != null) {             throw new Error("double get for block " + blockid);         }         // get a new node and read it from the file         node = getNewNode(blockid);         long offset = blockid * BLOCK_SIZE;         if (file.length() > 0 && offset <= file.length()) {             read(file, offset, node.getData(), BLOCK_SIZE);         } else {             System.arraycopy(cleanData, 0, node.getData(), 0, BLOCK_SIZE);         }         inUse.put(key, node);         node.setClean();         return node;     }    /**     *  Releases a block.     *     *  @param blockid The record number to release.     *  @param isDirty If true, the block was modified since the get().     */    void release(long blockid, boolean isDirty)    throws IOException {        BlockIo node = (BlockIo) inUse.get(new Long(blockid));        if (node == null)            throw new IOException("bad blockid " + blockid + " on release");        if (!node.isDirty() && isDirty)            node.setDirty();        release(node);    }    /**     *  Releases a block.     *     *  @param block The block to release.     */    void release(BlockIo block) {        Long key = new Long(block.getBlockId());        inUse.remove(key);        if (block.isDirty()) {            // System.out.println( "Dirty: " + key + block );            dirty.put(key, block);        } else {            if (!transactionsDisabled && block.isInTransaction()) {                inTxn.put(key, block);            } else {                free.add(block);            }        }    }    /**     *  Discards a block (will not write the block even if it's dirty)     *     *  @param block The block to discard.     */    void discard(BlockIo block) {        Long key = new Long(block.getBlockId());        inUse.remove(key);        // note: block not added to free list on purpose, because        //       it's considered invalid    }    /**     *  Commits the current transaction by flushing all dirty buffers     *  to disk.     */    void commit() throws IOException {        // debugging...        if (!inUse.isEmpty() && inUse.size() > 1) {            showList(inUse.values().iterator());            throw new Error("in use list not empty at commit time ("                            + inUse.size() + ")");        }        //  System.out.println("committing...");        if ( dirty.size() == 0 ) {            // if no dirty blocks, skip commit process            return;        }        if (!transactionsDisabled) {            txnMgr.start();        }        for (Iterator i = dirty.values().iterator(); i.hasNext(); ) {            BlockIo node = (BlockIo) i.next();            i.remove();            // System.out.println("node " + node + " map size now " + dirty.size());            if (transactionsDisabled) {                long offset = node.getBlockId() * BLOCK_SIZE;                file.seek(offset);                file.write(node.getData());                node.setClean();                free.add(node);            }            else {                txnMgr.add(node);                inTxn.put(new Long(node.getBlockId()), node);            }        }        if (!transactionsDisabled) {            txnMgr.commit();        }    }    /**     *  Rollback the current transaction by discarding all dirty buffers     */    void rollback() throws IOException {        // debugging...        if (!inUse.isEmpty()) {            showList(inUse.values().iterator());            throw new Error("in use list not empty at rollback time ("                            + inUse.size() + ")");        }        //  System.out.println("rollback...");        dirty.clear();        txnMgr.synchronizeLogFromDisk();        if (!inTxn.isEmpty()) {            showList(inTxn.values().iterator());            throw new Error("in txn list not empty at rollback time ("                            + inTxn.size() + ")");        };    }    /**     *  Commits and closes file.     */    void close() throws IOException {        if (!dirty.isEmpty()) {            commit();        }        txnMgr.shutdown();        if (!inTxn.isEmpty()) {            showList(inTxn.values().iterator());            throw new Error("In transaction not empty");        }        // these actually ain't that bad in a production release        if (!dirty.isEmpty()) {            System.out.println("ERROR: dirty blocks at close time");            showList(dirty.values().iterator());            throw new Error("Dirty blocks at close time");        }        if (!inUse.isEmpty()) {            System.out.println("ERROR: inUse blocks at close time");            showList(inUse.values().iterator());            throw new Error("inUse blocks at close time");        }        // debugging stuff to keep an eye on the free list        // System.out.println("Free list size:" + free.size());        file.close();        file = null;    }    /**     * Force closing the file and underlying transaction manager.     * Used for testing purposed only.     */    void forceClose() throws IOException {      txnMgr.forceClose();      file.close();    }    /**     *  Prints contents of a list     */    private void showList(Iterator i) {        int cnt = 0;        while (i.hasNext()) {            System.out.println("elem " + cnt + ": " + i.next());            cnt++;        }    }    /**     *  Returns a new node. The node is retrieved (and removed)     *  from the released list or created new.     */    private BlockIo getNewNode(long blockid)    throws IOException {        BlockIo retval = null;        if (!free.isEmpty()) {            retval = (BlockIo) free.removeFirst();        }        if (retval == null)            retval = new BlockIo(0, new byte[BLOCK_SIZE]);        retval.setBlockId(blockid);        retval.setView(null);        return retval;    }    /**     *  Synchs a node to disk. This is called by the transaction manager's     *  synchronization code.     */    void synch(BlockIo node) throws IOException {        byte[] data = node.getData();        if (data != null) {            long offset = node.getBlockId() * BLOCK_SIZE;            file.seek(offset);            file.write(data);        }    }    /**     *  Releases a node from the transaction list, if it was sitting     *  there.     *     *  @param recycle true if block data can be reused     */    void releaseFromTransaction(BlockIo node, boolean recycle)    throws IOException {        Long key = new Long(node.getBlockId());        if ((inTxn.remove(key) != null) && recycle) {            free.add(node);        }    }    /**     *  Synchronizes the file.     */    void sync() throws IOException {        file.getFD().sync();    }    /**     * Utility method: Read a block from a RandomAccessFile     */    private static void read(RandomAccessFile file, long offset,                             byte[] buffer, int nBytes) throws IOException {        file.seek(offset);        int remaining = nBytes;        int pos = 0;        while (remaining > 0) {            int read = file.read(buffer, pos, remaining);            if (read == -1) {                System.arraycopy(cleanData, 0, buffer, pos, remaining);                break;            }            remaining -= read;            pos += read;        }    }}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -