alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.mdr.persistence.btreeimpl.btreestorage;

import org.netbeans.mdr.persistence.btreeimpl.btreeindex.*;
import org.netbeans.mdr.persistence.*;
import java.io.*;
import java.text.*;
import java.util.*;

/**
 * BtreePageSource implementation for pages which are stored directly in a file 
 * (the primary index pages) rather than as repository objects. The file
 * access is all handled by the FileCache class.
 *
 * @author	Dana Bergen
 * @version	1.0
 */

public class BtreeFileSource extends Object 
			implements BtreePageSource, FileCache.NotifyOnCommit {

/*
 * A page exists in two forms: as an instantiated BtreePage object, and as
 * a byte array.  On disk or in the FileCache, it is in the array form.
 * Each BtreePage has a reference (pageBuffer) to its array in the
 * FileCache.
 *
 * BtreeFileSource also maintains a cache of instantiated BtreePage's.
 * These cached BtreePage's may be reused, that is reinitialized
 * from a different page buffer for a different page. 
 *
 * All pages in the BtreePage cache are pinned with respect to the
 * FileCache regardless of whether they are pinned with respect to the
 * BtreePage cache. A page is unpinned in the BtreePage cache if the BtreePage
 * is not in use.  When a BtreePage is removed from the BtreePage cache, it is
 * then unpinned with respect to the FileCache.
 */
    static final int MAGIC = 123456789;
    static final int VERSION = 1;
    static final int NO_PAGEID = -1; 
    static final int NEXTFREE_OFFSET = FileHeader.HEADER_SIZE + 8;
    static final int CACHE_SIZE = 5;


    private FileCache	fileCache;	// source of pages from file
    private int		fileId;		// file where our pages are stored,
    					// for making FileCache requests
    private int		pageSize;
    private EntryTypeInfo pageIdInfo;
    private byte[]	noPageId;
    private boolean	metaChanged;
    private int		nextFree;	// page number of next free page 
    private BtreeStorage     storage;

    private Hashtable	btreeCache;	// BtreePages hashed on pageId
    private IntrusiveList lruList;	// unpinned BtreePages lru first
    
    private MofidGenerator gen;

    private static class CacheEntry extends IntrusiveList.Member {
        BtreePage page;
	CachedPage fcp;		// FileCache's cache entry
	int pinned;		// number of current users of this page
	boolean needsStore;	// has the page been modified
    }

    /**
     * Constructor for a BtreeFileSource from a new or existing index file.
     *
     * @param	fileId		file ID to use in FileCache requests
     * @param	fileCache	source of index pages
     * @param	pageSize	size of index pages
     * @param	isNew		true if this index is being newly created
     * @param	mGen		Mofid generator
     */
    public BtreeFileSource(int fileId, FileCache fileCache, int pageSize, 
    				boolean isNew, MofidGenerator mGen, BtreeStorage storage) 
				throws StorageException {

	CachedPage	metaFCP, rootFCP;
	CacheEntry	rootEntry;
	byte[]		metadata;
	int		offset;

	this.fileId = fileId;
	this.fileCache = fileCache;
	this.pageSize = pageSize;
        this.storage = storage;
	gen = mGen;

	pageIdInfo = EntryTypeInfo.getEntryTypeInfo(Storage.EntryType.INT, null); 
	noPageId = pageIdInfo.toBuffer(new Integer(NO_PAGEID));
	btreeCache = new Hashtable(10);	
	lruList = new IntrusiveList();

	metaChanged = false;
	
	metaFCP = fileCache.getPage(fileId, 0);
	metadata = metaFCP.contents;
	offset = FileHeader.HEADER_SIZE;

	if (isNew) {
	    // Initialize the metadata page
	    offset = Converter.writeInt(metadata, offset, MAGIC);
	    offset = Converter.writeInt(metadata, offset, VERSION);
	    nextFree = 1;
	    offset = Converter.writeInt(metadata, offset, nextFree);
	    fileCache.setWritable(metaFCP);
	} else {
	    // Read the metadata page and verify
	    int magic, version;
	    magic = Converter.readInt(metadata, offset);
	    offset += 4;
	    version = Converter.readInt(metadata, offset);
	    offset += 4;
	    nextFree = Converter.readInt(metadata, offset);
	     
	    if (magic != MAGIC) {
		throw new StorageBadRequestException(
		    MessageFormat.format(
			"Index file has bad magic number ",
			new Object[] {new Integer(magic) } ));
	    }
	    if (version != VERSION) {
		throw new StorageBadRequestException(
		    MessageFormat.format(
	    "Index file has incorrect version number. {0} expected, {1} found",
		    new Object[] {
			new Integer(VERSION), 
			new Integer(version) } ));
	    }
	}

	fileCache.unpin(metaFCP);

	fileCache.addNotifier(this);
    }	

    public EntryTypeInfo getPageIdInfo() {
	return new IntInfo();
    }

    /**
     * Set the passed-in pageId to contain the special value noPageId
     */
    public void setNoPage(byte[] pageId) {

        System.arraycopy(noPageId, 0, pageId, 0, pageId.length);
    }

    /**
     * Test whether the passed-in pageId contains the special value noPageId
     */
    public boolean isNoPage(byte[] pageId) {
	
	for (int i = 0; i < pageId.length; i++) {
	    if (pageId[i] != noPageId[i]) {
	        return false;
	    }
	}
	return true;
    }

    /**
     * Prepares all cached modified pages to be written out.
     */
    public synchronized void prepareToCommit() throws StorageException {

        Enumeration 	entries;
	CacheEntry	entry;
	CachedPage	metaFCP;

	entries = btreeCache.elements();

	while (entries.hasMoreElements()) {
	    entry = (CacheEntry) entries.nextElement();
	    if (entry.needsStore) { 
		entry.page.store();
	    }
	}
    
	/* Update the metadata page if necessary */
        if (metaChanged) {
	    metaFCP = fileCache.getPage(fileId, 0);
	    fileCache.setWritable(metaFCP);
	    Converter.writeInt(metaFCP.contents, NEXTFREE_OFFSET, nextFree);
	    fileCache.unpin(metaFCP);
	}
	metaChanged = false;
    }

    /**
     * Return the root page if it already exists, otherwise create it.
     *
     * @return BtreePage which is the root page
     */
    public BtreePage getRootPage(Btree btree) throws StorageException {

        if (nextFree > 1) {
	    return getPage(pageIdInfo.toBuffer(new Integer(1)), btree);
	} else {
	    return newPage(btree);
	}
    }

    /**
     * Get a BtreePage by its pageId.
     *
     * @param	pageId	Byte array containing pageId
     *
     * @return	The page identified by pageId.
     *
     * @exception StorageException	If the page is not valid
     */
    public synchronized BtreePage getPage(byte[] pageId, Btree btree) 
    						throws StorageException {
        Integer		pageNum;
	CacheEntry	entry;

	pageNum = (Integer) pageIdInfo.fromBuffer(pageId);
	entry = (CacheEntry) btreeCache.get(pageNum);
	if (entry == null) {
	    entry = addToBtreeCache(pageId, pageNum, false, btree);
	} else if (entry.pinned == 0) {
	    lruList.remove((IntrusiveList.Member)entry);
	}
	entry.pinned++;
	return entry.page;
    }

    private CacheEntry addToBtreeCache(byte[] pageId, 
    			Integer pageNum, boolean isNew, Btree btree) 
						throws StorageException {
        
	CacheEntry	entry;

	entry = getCacheEntry();
	entry.fcp = fileCache.getPage(fileId, pageNum.intValue());
	if (entry.page == null) {
	    entry.page = btree.pageFactory();
	} else {
	    entry.page.uninit();
	}
	entry.page.init(btree, pageId, entry.fcp.contents, isNew);
	btreeCache.put(pageNum, entry);
	return entry;
    }

    private CacheEntry getCacheEntry() throws StorageException {

	CacheEntry entry = null;
	Integer pageNum;

        if (btreeCache.size() >= CACHE_SIZE) {
	    /* look for a page to recycle */
	    entry = (CacheEntry) lruList.removeFirst();
	}
	if (entry != null) {
	    pageNum = (Integer) pageIdInfo.fromBuffer(entry.page.pageId);
	    btreeCache.remove(pageNum);
	    if (entry.needsStore) {
	        entry.page.store();
		entry.needsStore = false;
	    }
	    fileCache.unpin(entry.fcp);
        } else {
    	    entry = new CacheEntry();
	}
	return entry;
    }

    /**
     * Returns a newly allocated btree page.
     *
     * @return	A new BtreePage
     *
     * @exception StorageException	If the page is not valid
     */
    public synchronized BtreePage newPage(Btree btree) throws StorageException {

        CacheEntry	entry;
	Integer		pageNum;
	byte[]		pageId;

	pageNum = new Integer(nextFree);
	nextFree++;
	metaChanged = true;
	pageId = pageIdInfo.toBuffer(pageNum);
	entry = addToBtreeCache(pageId, pageNum, true, btree);
	entry.pinned++;
	dirtyPage(entry.page);
	return entry.page;
    }

    /**
     * Returns a newly allocated BigKeyPage.
     * BigKeyPages are not recycled.  They are only cached while in use.
     */
    public synchronized BigKeyPage newBigKeyPage(Btree btree) 
    						throws StorageException {
	CacheEntry	entry;
	Integer		pageNum;
	byte[]		pageId;

	pageNum = new Integer(nextFree);
	nextFree++;
	metaChanged = true;
	pageId = pageIdInfo.toBuffer(pageNum);
	entry = new CacheEntry();
	entry.fcp = fileCache.getPage(fileId, pageNum.intValue());
	entry.page = new BigKeyPage();
	entry.page.init(btree, pageId, entry.fcp.contents, true);
	btreeCache.put(pageNum, entry);
	entry.pinned++;
	dirtyPage(entry.page);
	return (BigKeyPage)entry.page;
    }
    
    /**
     * Notifies the btree that the caller is done using this page.  This 
     * decrements its pinned count in the btree cache.  It does not change 
     * its pinned status in the FileCache; that only happens when a page is 
     * removed from the btree cache.
     *
     * @param	page	BtreePage to be unpinned
     */
    public synchronized void unpinPage(BtreePage page) {
        
	CacheEntry	entry;

	entry = (CacheEntry) btreeCache.get(pageIdInfo.fromBuffer(page.pageId));
	if (--entry.pinned == 0) {
	    lruList.addLast((IntrusiveList.Member)entry);
	}
    }
	    
    public synchronized void unpinPage(BigKeyPage page) throws StorageException {

	CacheEntry	entry;
	Integer		pageNum;

	entry = (CacheEntry) btreeCache.get(pageIdInfo.fromBuffer(page.pageId));
	if (--entry.pinned == 0) {
	    pageNum = (Integer) pageIdInfo.fromBuffer(entry.page.pageId);
	    btreeCache.remove(pageNum);
	    if (entry.needsStore) {
	        entry.page.store();
		entry.needsStore = false;
	    }
	    fileCache.unpin(entry.fcp);
	}
    }

    /**
     * Notifies the btree that the caller is going to modify this page.
     * This must be called prior to modifying the page contents.
     *
     * @param	page	The page being modified
     */
    public synchronized void dirtyPage(BtreePage page) throws StorageException {
        
	CacheEntry	entry;

	entry = (CacheEntry) btreeCache.get(pageIdInfo.fromBuffer(page.pageId));
	entry.needsStore = true;
	fileCache.setWritable(entry.fcp); 
    }

    public int getPageIdLength() {
        return pageIdInfo.getLength();
    }

    public int getPageSize() {
        return pageSize;
    }

    public long getNextMofid() {
    	if (gen != null)
	    return gen.getNextMofid();
    	throw new RuntimeException("Not implemented");
    }

    public String getMofidPrefix() {
    	if (gen != null)
	    return gen.getMofidPrefix();
    	throw new RuntimeException("Not implemented");
    }
    
    public BtreeStorage getStorage () {
        return storage;
    }
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.