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 java.io.*;
import java.text.*;
import java.util.*;

import org.netbeans.mdr.persistence.btreeimpl.btreeindex.*;
import org.netbeans.mdr.persistence.*;
import org.netbeans.mdr.util.Logger;

/**
 * This class implements Storage using Btree files.  Almost all implementation
 * is in BtreeDatabase, which see.
 */
public class BtreeStorage implements Storage {
    
    /* Key used to store value of @link #storageNumbersCounter. */
    private static final String COUNTER_KEY = "counter";
    
    /* Suffix of the name of SinglevaluedIndex used to store storage ids -> codes mapping. */
    private static final String MAP1_INDEX_PREFIX = "storageIds:";
    
    /* Suffix of the name of SinglevaluedIndex used to store storage codes -> ids mapping. */
    private static final String MAP2_INDEX_PREFIX = "storageCodes:";
    
    /* The btree database we have opened */
    private BtreeDatabase btreeDB;
    
    /*The cached mofid entry type info */
    private transient EntryTypeInfo mofIdEntryTypeInfo;
    
    /*The cached mofid class code */
    private transient int mofIdClassCode;
    
    /*Buffer used for reading mofids */
    private transient byte[] buffer = new byte[8];
    
    /* Our MOFID generator */
    MofidGenerator gen;
    
    /* The name of our database */
    private final String btreeDBName;
    
    String storageUUID;
    
    /* Object resolver */
    private ObjectResolver resolver;
    
    /* Index storing storageId -> code of storageId mapping */
    private SinglevaluedIndex storageIdsMap;
    /* Index storing code of storageId -> storageId mapping */
    private SinglevaluedIndex storageCodesMap;
    /* Value of the last used code. */
    private int storageNumbersCounter;
    /* Cache for storageId -> integer mapping. */
    private HashMap tempMap = null;
    /* Cache for integer -> storageId mapping. */
    private HashMap tempMap2 = null;
    
    private final Map properties;
    
    /** Create a BtreeStorage object.  Note that this does not create
     * or open the Btree repository
     * @param name the name of the btree.  This will be the base name
     * for the btree files
     */
    public BtreeStorage(String name, Map properties) {
        this.properties = properties;
        Logger.getDefault().log("DATABASE: " + name);
        btreeDBName = name;
        storageUUID = (String) properties.get(BtreeFactory.STORAGE_UUID);
        if (storageUUID != null)
            Logger.getDefault().log("Storage UUID: " + storageUUID);
        
    }
    
    Object getProperty(String name) {
        return properties.get(name);
    }
    
    /** Return our name
     */
    public String getName() {
        return btreeDBName;
    }

    /** Returns storage id
     */
    public synchronized String getStorageId() {
        return gen.getMofidPrefix();
    }
    
    public synchronized long getSerialNumber () {
        return gen.getNextMofid();
    }
    
    
    /** determine if the btree currently exists
     * @return true if it exists
     */
    public boolean exists() {
        return BtreeDatabase.exists(btreeDBName);
    }
    
    /** delete the btree repository.
     * @return true if, at method end, there is no repository.  false if
     * the repository exists but could not be deleted for any reason
     */
    public synchronized boolean delete() throws StorageException {
        checkRepositoryClosed();
        return BtreeDatabase.delete(btreeDBName);
    }
    
    /** Create btree repository
     * @param replace whether to replace an existing repository
     * @exception StorageException on any error creating the repository
     */
    public synchronized void create(boolean replace, ObjectResolver resolver) throws StorageException {
        checkRepositoryClosed();
        if (exists()) {
            if (replace) {
                if (!delete()) {
                    throw new StorageBadRequestException(
                    MessageFormat.format(
                    "Unable to delete btree repository {0}",
                    new Object[] {btreeDBName} ) );
                }
            }
            else {
                throw new StorageBadRequestException(
                MessageFormat.format(
                "Btree repository {0} already exists",
                new Object[] {btreeDBName} ) );
            }
        }
        
        this.resolver = resolver;
        btreeDB = new BtreeDatabase(btreeDBName, this, true);
        gen = btreeDB.getMofidGenerator();
        storageIdsMap = createSinglevaluedIndex (MAP1_INDEX_PREFIX,
                            Storage.EntryType.STRING, Storage.EntryType.INT);
        storageCodesMap = createSinglevaluedIndex (MAP2_INDEX_PREFIX,
                            Storage.EntryType.INT, Storage.EntryType.STRING);
    }
    
    /** Open a btree MDR
     * @param createIfNoExist whether to create the repository if it
     * doesn't exist
     * @exception StorageException on any error opening or creating
     * the repository
     */
    public synchronized void open(boolean createIfNoExist, ObjectResolver resolver)
    throws StorageException {
        checkRepositoryClosed();
        if (exists()) {
            this.resolver = resolver;
            btreeDB = new BtreeDatabase(btreeDBName, this, false);
            gen = btreeDB.getMofidGenerator();
            storageIdsMap = getSinglevaluedIndex (MAP1_INDEX_PREFIX);
            storageCodesMap = getSinglevaluedIndex (MAP2_INDEX_PREFIX);
            return;
        }
        
        if (createIfNoExist) {
            create(false, resolver);
            return;
        }
        
        throw new StorageBadRequestException(
        MessageFormat.format(
        "Btree repository {0} does not exist",
        new Object[] {btreeDBName} ) );
    }
    
    /** close the btree repository.   This does not commit any outstanding
     * changes.
     */
    public synchronized  void close() throws StorageException {
        checkRepositoryOpen();
        btreeDB.close();
        btreeDB = null;
        gen = null;
        tempMap = null;
    }
    
    /** Return the primary index (the BtreeDatabase)
     */
    public synchronized SinglevaluedIndex getPrimaryIndex() throws StorageException {
        checkRepositoryOpen();
        return btreeDB;
    }
    
    /** Create index that holds exactly one value for each key.
     * @return created index
     * @param name name of the index
     * @param keyType type of keys in the index
     * @param valueType type of values in the index
     */
    
    public synchronized  SinglevaluedIndex createSinglevaluedIndex(String name, Storage.EntryType keyType, Storage.EntryType valueType) throws StorageException {
        checkRepositoryOpen();
        SinglevaluedBtree index =
        new SinglevaluedBtree(name, keyType, valueType,
        new BtreeMDRSource(this, BtreeDatabase.PAGE_SIZE));
        btreeDB.addIndex(name, index, new MOFID(this));
        return index;
    }
    
    /** Create index that holds sorted set of values for each key.
     * @return created index
     * @param name name of the index
     * @param keyType type of keys in the index
     * @param valueType type of values in the index
     * @param unique true if values associated with one key do not contain duplicates
     */
    public synchronized  MultivaluedOrderedIndex createMultivaluedOrderedIndex(String name, Storage.EntryType keyType, Storage.EntryType valueType, boolean unique) throws StorageException {
        checkRepositoryOpen();
        MultivaluedOrderedBtree index =
        new MultivaluedOrderedBtree(name, keyType, valueType, unique,
        new BtreeMDRSource(this, BtreeDatabase.PAGE_SIZE));
        btreeDB.addIndex(name, index, new MOFID(this));
        return index;
    }
    
    /** Create index that hold a set of values for each key. Elements in one Multivalued are
     * not sorted. Set does not contain duplicate values.
     * @return created index
     * @param name name of the index
     * @param keyType type of keys in the index
     * @param valueType type of values in the index
     * @param unique true if values associated with one key do not contain duplicates
     */
    public synchronized  MultivaluedIndex createMultivaluedIndex(String name, Storage.EntryType keyType, Storage.EntryType valueType, boolean unique) throws StorageException {
        checkRepositoryOpen();
        MultivaluedBtree index =
        new MultivaluedBtree(name, keyType, valueType, unique,
        new BtreeMDRSource(this, BtreeDatabase.PAGE_SIZE));
        btreeDB.addIndex(name, index, new MOFID(this));
        return index;
    }
    
    /** Retrieve index by name.
     * @param name name of the index
     * @return index of the specified name
     */
    public synchronized Index getIndex(String name) throws StorageException {
        checkRepositoryOpen();
        return (Index) btreeDB.fetchIndex(name);
    }
    
    /** Retrieve index by name.
     * @param name name of the index
     * @return index of the specified name and type
     */
    public synchronized SinglevaluedIndex getSinglevaluedIndex(String name) throws StorageException {
        Object idx = getIndex(name);
        checkIndexType(idx, name, SinglevaluedIndex.class);
        return (SinglevaluedIndex)idx;
    }
    
    /** Retrieve index by name.
     * @param name name of the index
     * @return index of the specified name and type
     */
    public synchronized  MultivaluedIndex getMultivaluedIndex(String name) throws StorageException {
        Object idx = getIndex(name);
        checkIndexType(idx, name, MultivaluedIndex.class);
        return (MultivaluedIndex)idx;
    }
    
    /** Retrieve index by name.
     * @param name name of the index
     * @return index of the specified name and type
     */
    public synchronized  MultivaluedOrderedIndex getMultivaluedOrderedIndex(String name) throws StorageException {
        Object idx = getIndex(name);
        checkIndexType(idx, name, MultivaluedOrderedIndex.class);
        return (MultivaluedOrderedIndex)idx;
    }
    
    /** Delete index.
     * @param name name of the index
     */
    public synchronized  void dropIndex(String name) throws StorageException {
        checkRepositoryOpen();
        btreeDB.dropIndex(name);
    }
    
    /** Notify the Storage that state of the object will be changed.
     * @param key key of object that will be changed
     */
    public void objectStateWillChange(Object key) throws StorageException {
        // do nothing
    }
    
    /** Notify the Storage that state of the object was changed.
     * @param key key of object that was changed
     */
    public synchronized  void objectStateChanged(Object key) throws StorageException {
        checkRepositoryOpen();
        btreeDB.objectStateChanged(key);
    }
    
    /** Save all objects changed since this method was last call.
     * This operation implements transactions on the storage.
     * It must either whole complete or whole fail.
     */
    public synchronized  void commitChanges() throws StorageException {
        checkRepositoryOpen();
        btreeDB.commitChanges();
    }
    
    /** Discard all changes since commitChanges() method was last called.
     * This operation implements transactions on the storage.
     * It must either whole complete or whole fail.
     * Note that, after this method completes, the persistent MDR and
     * any modified objects in memory are inconsistent, so it should
     * be followed shortly by program exit.
     */
    public synchronized  void rollBackChanges() throws StorageException {
        checkRepositoryOpen();
        btreeDB.rollbackChanges();
        close();
        open(false, resolver);
    }
    
    /**
     * Shutdowns btree databes (i.e. flushes cached commited data in transaction cache).
     */
    public synchronized  void shutDown() throws StorageException {
        checkRepositoryOpen();
        btreeDB.shutDown();
    }
    
    /** Returns true if the storage supports more than one index with type
     * {@link Entrytype.STREAMABLE}
     * @return true if the storage supports more than one index with type
     * {@link Entrytype.STREAMABLE}
     */
    public boolean supportsMultipleStorableIndexes() {
        /* Btree supports only one primary index */
        return false;
    }
    
    public synchronized void writeMOFID (java.io.OutputStream outputStream, MOFID mofId) throws StorageException {
       // this looks like it is not needed - MOFID's class is always org.netbeans.mdr.persistence.MOFID
//       if (this.mofIdClassCode == -1) {
//           this.mofIdClassCode = this.btreeDB.getClassCode (mofId.getClass());
//       }
       
       DataOutputStream out = null;
       if (outputStream instanceof DataOutputStream)
           out = (DataOutputStream) outputStream;
       else
           out = new DataOutputStream (outputStream);
       
       /*
       try {
            out.writeInt (this.mofIdClassCode);
       } catch (IOException ioException) {
           throw new StorageIOException (ioException);
       }
       this.writeMOFIDData (out, mofId);
        */
       
        // optimized write:
        long mofidData = mofId.getSerialNumber();
        String storageId = mofId.getStorageID();
        short s;
        if (this.getStorageId().equals(storageId)) {
            s = (short) BtreeFactory.SAME_PREFIX_CODE;
        } else if (BtreeFactory.INTERNAL_PREFIX.equals(storageId)) {
            s = (short) BtreeFactory.INTERNAL_PREFIX_CODE;
        } else {
            s = (short) this.storageIdToNumber(storageId);
        }
        mofidData |= ((long) s) << 48;
        try {
            while (mofidData != 0) {
                int b = ((int) mofidData) & 0x7f;
                mofidData >>>= 7;
                if (mofidData != 0) b |= 0x80;
                out.write(b);
            }
        } catch (IOException e) {
            throw (StorageException) Logger.getDefault().annotate(new StorageIOException(e), e);
        }
    }
    
    public final void writeMOFIDData (java.io.OutputStream out, MOFID mofid) throws StorageException {
        try {
            out.write (this.getMOFIDData(mofid)); 
        } catch (IOException ioException) {
            throw new StorageIOException (ioException);
        }
    }
    
    public synchronized final byte[] getMOFIDData (MOFID mofid) throws StorageException {
        if (this.mofIdEntryTypeInfo == null) {
           this.mofIdEntryTypeInfo = EntryTypeInfo.getEntryTypeInfo(Storage.EntryType.MOFID,this);
       }
       return this.mofIdEntryTypeInfo.toBuffer(mofid);
    }
     
    // this method is always called from a thread safe code (readStreamable)
    // thus it does not need to be synchronized
    // if it shows up that it needs to be synchronized, a separate lock should be used
    // for that to avoid deadlocks
    public synchronized MOFID readMOFID (java.io.InputStream inputStream) throws StorageException {
        DataInputStream in = null;
        if (inputStream instanceof DataInputStream)
            in = (DataInputStream) inputStream;
        else
            in = new DataInputStream (inputStream);

        // optimized read:
        long mofidData = 0;
        try {
            int b;
            int i = 0;
            do {
                b = in.read();
                mofidData |= ((long) (b & 0x7f)) << (i * 7);
                i++;
            } while ((b & 0x80) != 0);
            int storageNumber = (int) (mofidData >> 48);
            String storageId;
            switch (storageNumber) {
                case BtreeFactory.INTERNAL_PREFIX_CODE:
                    storageId = BtreeFactory.INTERNAL_PREFIX;
                break;
                case BtreeFactory.SAME_PREFIX_CODE:
                    storageId = this.getStorageId();
                break;
                default:
                    storageId = this.numberToStorageId(storageNumber);
            }            
            return new MOFID (mofidData & 0xffffffffffffL, storageId);            
        } catch (IOException e) {
            throw (StorageException) Logger.getDefault().annotate(new StorageIOException(e), e);
        }

        /* let's rather do it in optimized way above
        try {
            int mofIdClassCode = in.readInt ();
            if (mofIdClassCode != this.mofIdClassCode)
                throw new IllegalStateException ();        
            return this.readMOFIDData (in);
        }catch (IOException ioException) {
            throw new StorageIOException (ioException);
        }
         */
    }
    
    public synchronized final MOFID readMOFIDData (java.io.InputStream in) throws StorageException {
        if (this.mofIdEntryTypeInfo == null) {
            this.mofIdEntryTypeInfo = EntryTypeInfo.getEntryTypeInfo(Storage.EntryType.MOFID, this);
        }
        try {
            in.read (this.buffer);
            return (MOFID) mofIdEntryTypeInfo.fromBuffer (this.buffer);
        }catch (IOException ioException) {
            throw new StorageIOException (ioException);
        }
    }
    
    /** Return the MOFID generator for this repository */
    public synchronized MofidGenerator getMofidGenerator() {
        return btreeDB.getMofidGenerator();
    }
    
    /** Return the map of MOFID UUIDs we know about
     */
    public synchronized Map getMofidMap() {
        return btreeDB.getMofidMap();
    }
    
    /**
     * Initializes storage id <-> storage code mapping.
     */
    private void initStorageIdsMap () throws StorageException {
        Object value = storageIdsMap.getIfExists (COUNTER_KEY);
        if (value == null) {
            storageNumbersCounter = BtreeFactory.FIRST_EXTERNAL_CODE;
            storageIdsMap.put (COUNTER_KEY, value = new Integer (storageNumbersCounter));
        }
        storageNumbersCounter = ((Integer) value).intValue ();        
        tempMap = new HashMap ();
        tempMap2 = new HashMap ();        
    }
    
    /**
     * Maps an external storage prefix to integer.
     */
    public synchronized int storageIdToNumber (String storageId) throws StorageException {        
        Object value;
        if (tempMap == null)
            initStorageIdsMap ();
        value = tempMap.get (storageId);
        if (value == null) {
            value = storageIdsMap.getIfExists (storageId);
            if (value == null) {
                storageNumbersCounter++;
                value = new Integer (storageNumbersCounter);
                storageIdsMap.replace (COUNTER_KEY, value);
                storageIdsMap.put (storageId, value);
                storageCodesMap.put (value, storageId);
            }
            tempMap.put (storageId, value);            
        }
        return ((Integer) value).intValue ();
    }
    
    /**
     * Resolves external storage number coded by an integer.
     */
    public synchronized String numberToStorageId (int number) throws StorageException {        
        Object value;
        if (tempMap == null)
            initStorageIdsMap ();
        Object code = new Integer (number);
        value = tempMap2.get (code);
        if (value == null) {
            value = storageCodesMap.get (code);
            if (value == null) {
                throw new StorageBadRequestException ("Unknown storage code requested: " + number);
            }
            tempMap2.put (code, value);
        }
        return (String) value;
    }
    
    /**
     * Delegates resolving of external mof ids on the object resolver.
     */
    public synchronized Object resolveObject (MOFID key) throws StorageException {
        return resolver.resolve (key.getStorageID(), key);
    }
    
    /* check that the returned index is of the correct type */
    private void checkIndexType(Object idx, String idxName, Class idxType)
    throws StorageException     {
        if (!idxType.isInstance(idx)) {
            throw new StorageBadRequestException(
            MessageFormat.format("{0} is not a {1}",
            new Object[] { idxName, idxType.getName() } ) );
        }
    }
    
    /* We only operate on one repository at a time */
    private void checkRepositoryClosed() throws StorageException {
        if (btreeDB != null) {
            throw new StorageBadRequestException(
            MessageFormat.format(
            "The btree repository {0} is already open",
            new Object[] { btreeDBName} ) );
        }
    }
    
    /* Many operations aren't possible if the repsitory isn't open */
    private void checkRepositoryOpen() throws StorageException {
        if (btreeDB == null) {
            throw new StorageBadRequestException(
            MessageFormat.format(
            "The btree repository {0} is not open",
            new Object[] { btreeDBName} ) );
        }
    }
        
    /* Fix this when secondary indices are supported */
    private void notSupported() throws StorageException {
        throw new StorageBadRequestException(
        "Secondary indices are not supported!");
    }
    
    
}

... 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.