Package org.apache.directory.server.core.partition.impl.btree.jdbm

Source Code of org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTable

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.apache.directory.server.core.partition.impl.btree.jdbm;


import java.io.IOException;
import java.util.Comparator;
import java.util.Map;

import jdbm.RecordManager;
import jdbm.btree.BTree;
import jdbm.helper.Serializer;
import jdbm.helper.TupleBrowser;

import org.apache.directory.server.core.avltree.ArrayMarshaller;
import org.apache.directory.server.core.avltree.ArrayTree;
import org.apache.directory.server.core.avltree.ArrayTreeCursor;
import org.apache.directory.server.core.avltree.Marshaller;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.xdbm.Table;
import org.apache.directory.shared.ldap.cursor.Cursor;
import org.apache.directory.shared.ldap.cursor.EmptyCursor;
import org.apache.directory.shared.ldap.cursor.SingletonCursor;
import org.apache.directory.shared.ldap.schema.SchemaManager;
import org.apache.directory.shared.ldap.schema.comparators.SerializableComparator;
import org.apache.directory.shared.ldap.util.StringTools;
import org.apache.directory.shared.ldap.util.SynchronizedLRUMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* A jdbm Btree wrapper that enables duplicate sorted keys using collections.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev: 902608 $
*/
public class JdbmTable<K,V> implements Table<K,V>
{
    /** A logger for this class */
    private static final Logger LOG = LoggerFactory.getLogger( JdbmTable.class.getSimpleName() );

    /** the key to store and retreive the count information */
    private static final String SZSUFFIX = "_btree_sz";

    /** the name of this table */
    private final String name;

    /** the JDBM record manager for the file this table is managed in */
    private final RecordManager recMan;
   
    /** whether or not this table allows for duplicates */
    private final boolean allowsDuplicates;
   
    /** a key comparator for the keys in this Table */
    private final Comparator<K> keyComparator;
   
    /** a value comparator for the values in this Table */
    private final Comparator<V> valueComparator;

    /** the current count of entries in this Table */
    private int count;
   
    /** the wrappedCursor JDBM btree used in this Table */
    private BTree bt;

    /** the limit at which we start using btree redirection for duplicates */
    private int numDupLimit = JdbmIndex.DEFAULT_DUPLICATE_LIMIT;

    /** a cache of duplicate BTrees */
    private final Map<Long, BTree> duplicateBtrees;

    private final Serializer keySerializer;

    private final Serializer valueSerializer;

    Marshaller<ArrayTree<V>> marshaller;

    /** The global SchemaManager */
    private SchemaManager schemaManager;

    // ------------------------------------------------------------------------
    // C O N S T R U C T O R
    // ------------------------------------------------------------------------

   
    /**
     * Creates a Jdbm BTree based tuple Table abstraction that enables
     * duplicates.
     *
     * @param name the name of the table
     * @param numDupLimit the size limit of duplicates before switching to
     * BTrees for values instead of AvlTrees
     * @param manager the record manager to be used for this table
     * @param keyComparator a key comparator
     * @param valueComparator a value comparator
     * @param keySerializer a serializer to use for the keys instead of using
     * default Java serialization which could be very expensive
     * @param valueSerializer a serializer to use for the values instead of
     * using default Java serialization which could be very expensive
     * @throws IOException if the table's file cannot be created
     */
    @SuppressWarnings("unchecked")
    public JdbmTable( SchemaManager schemaManager, String name, int numDupLimit, RecordManager manager,
        Comparator<K> keyComparator, Comparator<V> valueComparator,
        Serializer keySerializer, Serializer valueSerializer )
        throws IOException
    {
        this.schemaManager = schemaManager;

        // TODO make the size of the duplicate btree cache configurable via constructor
        duplicateBtrees = new SynchronizedLRUMap( 100 );

        if ( valueSerializer != null )
        {
            marshaller = new ArrayMarshaller<V>( valueComparator,
                    new MarshallerSerializerBridge<V>( valueSerializer ) );
        }
        else
        {
            marshaller = new ArrayMarshaller<V>( valueComparator );
        }

        if ( keyComparator == null )
        {
            throw new NullPointerException( I18n.err( I18n.ERR_591 ) );
        }
        else
        {
            this.keyComparator = keyComparator;
        }

        if ( valueComparator == null )
        {
            throw new NullPointerException( I18n.err( I18n.ERR_592 ) );
        }
        else
        {
            this.valueComparator = valueComparator;
        }

        this.numDupLimit = numDupLimit;
        this.name = name;
        this.recMan = manager;

        this.keySerializer = keySerializer;
        this.valueSerializer = valueSerializer;

        this.allowsDuplicates = true;

        long recId = recMan.getNamedObject( name );

        if ( recId == 0 ) // Create new main BTree
        {
            // we do not use the value serializer in the btree since duplicates will use
            // either BTreeRedirect objects or AvlTree objects whose marshalling is
            // explicitly managed by this code.  Value serialization is delegated to these
            // marshallers.

            bt = BTree.createInstance( recMan, keyComparator, keySerializer, null );
            recId = bt.getRecid();
            recMan.setNamedObject( name, recId );
            recId = recMan.insert( 0 );
            recMan.setNamedObject( name + SZSUFFIX, recId );
        }
        else // Load existing BTree
        {
            bt = BTree.load( recMan, recId );
            ((SerializableComparator<K>)bt.getComparator()).setSchemaManager( schemaManager );
            recId = recMan.getNamedObject( name + SZSUFFIX );
            count = ( Integer ) recMan.fetch( recId );
        }

    }


    /**
     * Creates a Jdbm BTree based tuple Table abstraction without duplicates
     * enabled using a simple key comparator.
     *
     * @param name the name of the table
     * @param manager the record manager to be used for this table
     * @param keyComparator a tuple comparator
     * @param keySerializer a serializer to use for the keys instead of using
     * default Java serialization which could be very expensive
     * @param valueSerializer a serializer to use for the values instead of
     * using default Java serialization which could be very expensive
     * @throws IOException if the table's file cannot be created
     */
    public JdbmTable( SchemaManager schemaManager, String name, RecordManager manager, Comparator<K> keyComparator,
                      Serializer keySerializer, Serializer valueSerializer )
        throws IOException
    {
        this.schemaManager = schemaManager;
        this.duplicateBtrees = null;
        this.numDupLimit = Integer.MAX_VALUE;
        this.name = name;
        this.recMan = manager;

        if ( keyComparator == null )
        {
            throw new NullPointerException( I18n.err( I18n.ERR_591 ) );
        }
        else
        {
            this.keyComparator = keyComparator;
        }

        this.valueComparator = null;

        this.keySerializer = keySerializer;
        this.valueSerializer = valueSerializer;

        this.allowsDuplicates = false;

        long recId = recMan.getNamedObject( name );

        if ( recId != 0 )
        {
            bt = BTree.load( recMan, recId );
            ((SerializableComparator<K>)bt.getComparator()).setSchemaManager( schemaManager );
            bt.setValueSerializer( valueSerializer );
            recId = recMan.getNamedObject( name + SZSUFFIX );
            count = ( Integer ) recMan.fetch( recId );
        }
        else
        {
            bt = BTree.createInstance( recMan, keyComparator, keySerializer, valueSerializer );
            recId = bt.getRecid();
            recMan.setNamedObject( name, recId );
            recId = recMan.insert( 0 );
            recMan.setNamedObject( name + SZSUFFIX, recId );
        }
    }


    // ------------------------------------------------------------------------
    // Simple Table Properties
    // ------------------------------------------------------------------------

   
    /**
     * @see Table#getKeyComparator()
     */
    public Comparator<K> getKeyComparator()
    {
        return keyComparator;
    }


    /**
     * @see Table#getValueComparator()
     */
    public Comparator<V> getValueComparator()
    {
        return valueComparator;
    }


    public Serializer getKeySerializer()
    {
        return keySerializer;
    }


    public Serializer getValueSerializer()
    {
        return valueSerializer;
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#isDupsEnabled()
     */
    public boolean isDupsEnabled()
    {
        return allowsDuplicates;
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#getName()
     */
    public String getName()
    {
        return name;
    }


    public boolean isCountExact()
    {
        return false;
    }
       

    // ------------------------------------------------------------------------
    // Count Overloads
    // ------------------------------------------------------------------------

   
    /**
     * @see Table#greaterThanCount(Object)
     */
    public int greaterThanCount( K key ) throws IOException
    {
        // take a best guess
        return count;
    }
   
   
    /**
     * @see Table#lessThanCount(Object)
     */
    public int lessThanCount( K key ) throws IOException
    {
        // take a best guess
        return count;
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#count(java.lang.Object)
     */
    public int count( K key ) throws IOException
    {
        if ( key == null )
        {
            return 0;
        }

        if ( ! allowsDuplicates )
        {
            if ( null == bt.find( key ) )
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }

        DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
       
        if ( values.isArrayTree() )
        {
            return values.getArrayTree().size();
        }

        return getBTree( values.getBTreeRedirect() ).size();
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#count()
     */
    public int count() throws IOException
    {
        return count;
    }

   
    // ------------------------------------------------------------------------
    // get/has/put/remove Methods and Overloads
    // ------------------------------------------------------------------------


    @SuppressWarnings("unchecked")
    public V get( K key ) throws Exception
    {
        if ( key == null )
        {
            return null;
        }

        if ( ! allowsDuplicates )
        {
            return ( V ) bt.find( key );
        }                        

       
        DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
        if ( values.isArrayTree() )
        {
            ArrayTree<V> set = values.getArrayTree();

            if ( set.getFirst() == null )
            {
                return null;
            }
           
            return set.getFirst();
        }

        // Handle values if they are stored in another BTree
        BTree tree = getBTree( values.getBTreeRedirect() );

        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
        tree.browse().getNext( tuple );
        //noinspection unchecked
       
        return ( V ) tuple.getKey();
    }

   
    /**
     * @see Table#hasGreaterOrEqual(Object,Object)
     */
    public boolean hasGreaterOrEqual( K key, V val ) throws IOException
    {
        if ( key == null )
        {
            return false;
        }

        if ( ! allowsDuplicates )
        {
            throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
        }

        DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
       
        if ( values.isArrayTree() )
        {
            ArrayTree<V> set = values.getArrayTree();
            V result = set.findGreaterOrEqual( val );
            return result != null;
        }

        // last option is to try a btree with BTreeRedirects
        BTree tree = getBTree( values.getBTreeRedirect() );
       
        return tree.size() != 0 && btreeHas( tree, val, true );
    }


    /**
     * @see Table#hasLessOrEqual(Object,Object)
     */
    public boolean hasLessOrEqual( K key, V val ) throws IOException
    {
        if ( key == null )
        {
            return false;
        }

        if ( ! allowsDuplicates )
        {
            throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
        }

        DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
       
        if ( values.isArrayTree() )
        {
            ArrayTree<V> set = values.getArrayTree();
            V result = set.findLessOrEqual( val );
            return result != null;
        }

        // last option is to try a btree with BTreeRedirects
        BTree tree = getBTree( values.getBTreeRedirect() );
       
        return tree.size() != 0 && btreeHas( tree, val, false );
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#hasGreaterOrEqual(Object)
     */
    @SuppressWarnings("unchecked")
    public boolean hasGreaterOrEqual( K key ) throws IOException
    {
        // See if we can find the border between keys greater than and less
        // than in the set of keys.  This will be the spot we search from.
        jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );

        // Test for equality first since it satisfies both greater/less than
        if ( null != tuple && keyComparator.compare( ( K ) tuple.getKey(), key ) == 0 )
        {
            return true;
        }

        // Greater searches are easy and quick thanks to findGreaterOrEqual
        // A null return above means there were no equal or greater keys
        if ( null == tuple )
        {
            return false;
        }

        // Not Null! - we found a tuple with equal or greater key value
        return true;
    }


    /**
     * @see Table#hasLessOrEqual(Object)
     */
    @SuppressWarnings("unchecked")
    public boolean hasLessOrEqual( K key ) throws IOException
    {
        // Can only find greater than or equal to with JDBM so we find that
        // and work backwards to see if we can find one less than the key
        jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );

        // Test for equality first since it satisfies equal to condition
        if ( null != tuple && keyComparator.compare( ( K ) tuple.getKey(), key ) == 0 )
        {
            return true;
        }

        if ( null == tuple )
        {
            /*
             * Jdbm failed to find a key greater than or equal to the argument
             * which means all the keys in the table are less than the
             * supplied key argument.  We can hence return true if the table
             * contains any Tuples.
             */
            return count > 0;
        }
        else
        {
            /*
             * We have the next tuple whose key is the next greater than the
             * key argument supplied.  We use this key to advance a browser to
             * that tuple and scan down to lesser Tuples until we hit one
             * that is less than the key argument supplied.  Usually this will
             * be the previous tuple if it exists.
             */
            TupleBrowser browser = bt.browse( tuple.getKey() );
            if ( browser.getPrevious( tuple ) )
            {
                return true;
            }
        }

        return false;
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#has(java.lang.Object,
     * java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    public boolean has( K key, V value ) throws IOException
    {
        if ( key == null )
        {
            return false;
        }

        if ( ! allowsDuplicates )
        {
            V stored = ( V ) bt.find( key );
            return null != stored && stored.equals( value );
        }
       
        DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
       
        if ( values.isArrayTree() )
        {
            return values.getArrayTree().find( value ) != null;
        }
       
        return getBTree( values.getBTreeRedirect() ).find( value ) != null;
    }
   

    /**
     * @see Table#has(java.lang.Object)
     */
    public boolean has( K key ) throws IOException
    {
        return key != null && bt.find(key) != null;
    }


    /**
     * @see org.apache.directory.server.xdbm.Table#put(java.lang.Object,
     * java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    public synchronized void put( K key, V value ) throws Exception
    {
        try
        {
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "---> Add {} = {}", name, key );
            }

            if ( ( value == null ) || ( key == null ) )
            {
                throw new IllegalArgumentException( I18n.err( I18n.ERR_594 ) );
            }
           
            V replaced;
   
            if ( ! allowsDuplicates )
            {
                replaced = ( V ) bt.insert( key, value, true );
   
                if ( null == replaced )
                {
                    count++;
                }
   
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Add ONE {} = {}", name, key );
                }
               
                return;
            }
           
            DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
           
            if ( values.isArrayTree() )
            {
                ArrayTree<V> set = values.getArrayTree();
                replaced = set.insert( value );
               
                if ( replaced != null )// if the value already present returns the same value
                {
                    return;
                }
                if ( set.size() > numDupLimit )
                {
                    BTree tree = convertToBTree( set );
                    BTreeRedirect redirect = new BTreeRedirect( tree.getRecid() );
                    bt.insert( key, BTreeRedirectMarshaller.INSTANCE.serialize( redirect ), true );
                   
                    if ( LOG.isDebugEnabled() )
                    {
                        LOG.debug( "<--- Add new BTREE {} = {}", name, key );
                    }
                }
                else
                {
                    bt.insert( key, marshaller.serialize( set ), true );
                   
                    if ( LOG.isDebugEnabled() )
                    {
                        LOG.debug( "<--- Add AVL {} = {}", name, key );
                    }
                }
   
                count++;
                return;
            }
           
            BTree tree = getBTree( values.getBTreeRedirect() );
            replaced = ( V ) tree.insert( value, StringTools.EMPTY_BYTES, true );
           
            if ( replaced == null )
            {
                count++;
            }
           
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "<--- Add BTREE {} = {}", name, key );
            }
        }
        catch ( Exception e )
        {
            LOG.error( I18n.err( I18n.ERR_131, key, name ), e );
            throw e;
        }
    }
   

    /**
     * @see org.apache.directory.server.xdbm.Table#remove(java.lang.Object,
     * java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    public synchronized void remove( K key, V value ) throws IOException
    {
        try
        {
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "---> Remove " + name + " = " + key + ", " + value );
            }
           
            if ( key == null )
            {
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Remove NULL key " + name );
                }
                return;
            }
   
            if ( ! allowsDuplicates )
            {
                V oldValue = ( V ) bt.find( key );
           
                // Remove the value only if it is the same as value.
                if ( ( oldValue != null ) && oldValue.equals( value ) )
                {
                    bt.remove( key );
                    count--;
                   
                    if ( LOG.isDebugEnabled() )
                    {
                        LOG.debug( "<--- Remove ONE " + name + " = " + key + ", " + value );
                    }
                   
                    return;
                }
   
                return;
            }
   
            DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
           
            if ( values.isArrayTree() )
            {
                ArrayTree<V> set = values.getArrayTree();
   
                // If removal succeeds then remove if set is empty else replace it
                if ( set.remove( value ) != null )
                {
                    if ( set.isEmpty() )
                    {
                        bt.remove( key );
                    }
                    else
                    {
                        bt.insert( key, marshaller.serialize( set ), true );
                    }
                    count--;

                    if ( LOG.isDebugEnabled() )
                    {
                        LOG.debug( "<--- Remove AVL " + name + " = " + key + ", " + value );
                    }
                   
                    return;
                }
   
                return;
            }
   
            // if the number of duplicates falls below the numDupLimit value
            BTree tree = getBTree( values.getBTreeRedirect() );
           
            if ( tree.find( value ) != null )
            {
                if ( tree.remove( value ) != null )
                {
                    /*
                     * If we drop below the duplicate limit then we revert from using
                     * a Jdbm BTree to using an in memory AvlTree.
                     */
                    if ( tree.size() <= numDupLimit )
                    {
                        ArrayTree<V> avlTree = convertToArrayTree( tree );
                        bt.insert( key, marshaller.serialize( avlTree ), true );
                        recMan.delete( tree.getRecid() );
                    }
                   
                    count--;
                   
                    if ( LOG.isDebugEnabled() )
                    {
                        LOG.debug( "<--- Remove BTREE " + name + " = " + key + ", " + value );
                    }
                   
                    return;
                }
            }
        }
        catch ( Exception e )
        {
            LOG.error( I18n.err( I18n.ERR_132, key, value, name ), e );
        }
    }


    /**
     * @see Table#remove(Object)
     */
    public synchronized void remove( K key ) throws IOException
    {
        try
        {
            if ( LOG.isDebugEnabled() )
            {
                LOG.debug( "---> Remove {} = {}", name, key );
            }
           
            if ( key == null )
            {
                return;
            }
   
            Object returned = bt.remove( key );
   
            if ( null == returned )
            {
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Remove AVL {} = {} (not found)", name, key );
                }
               
                return;
            }
   
            if ( ! allowsDuplicates )
            {
                this.count--;
            
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Remove ONE {} = {}", name, key );
                }
               
                return;
            }
   
            byte[] serialized = ( byte[] ) returned;
   
            if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
            {
                BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
                this.count -= tree.size();
               
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Remove BTree {} = {}", name, key );
                }

                recMan.delete( tree.getRecid() );
                duplicateBtrees.remove( tree.getRecid() );
                return;
            }
            else
            {
                ArrayTree<V> set = marshaller.deserialize( serialized );
                this.count -= set.size();
   
                if ( LOG.isDebugEnabled() )
                {
                    LOG.debug( "<--- Remove AVL {} = {}", name, key );
                }
               
                return;
            }
        }
        catch ( Exception e )
        {
            LOG.error( I18n.err( I18n.ERR_133, key, name ), e );
           
            if ( e instanceof IOException )
            {
                throw (IOException)e;
            }
        }
    }


    public Cursor<org.apache.directory.server.xdbm.Tuple<K,V>> cursor() throws Exception
    {
        if ( allowsDuplicates )
        {
            return new DupsCursor<K,V>( this );
        }

        return new NoDupsCursor<K,V>( this );
    }


    @SuppressWarnings("unchecked")
    public Cursor<org.apache.directory.server.xdbm.Tuple<K,V>> cursor( K key ) throws Exception
    {
        if ( key == null )
        {
            return new EmptyCursor<org.apache.directory.server.xdbm.Tuple<K,V>>();
        }

        Object raw = bt.find( key );

        if ( null == raw )
        {
            return new EmptyCursor<org.apache.directory.server.xdbm.Tuple<K,V>>();
        }

        if ( ! allowsDuplicates )
        {
            return new SingletonCursor<org.apache.directory.server.xdbm.Tuple<K,V>>( new org.apache.directory.server.xdbm.Tuple<K,V>( key, ( V ) raw ) );
        }

        byte[] serialized = ( byte[] ) raw;
       
        if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
        {
            BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
            return new KeyTupleBTreeCursor<K,V>( tree, key, valueComparator );
        }

        ArrayTree<V> set = marshaller.deserialize( serialized );
        return new KeyTupleArrayCursor<K,V>( set, key );
    }


    @SuppressWarnings("unchecked")
    public Cursor<V> valueCursor( K key ) throws Exception
    {
        if ( key == null )
        {
            return new EmptyCursor<V>();
        }

        Object raw = bt.find( key );

        if ( null == raw )
        {
            return new EmptyCursor<V>();
        }

        if ( ! allowsDuplicates )
        {
            return new SingletonCursor<V>( ( V ) raw );
        }

        byte[] serialized = ( byte[] ) raw;
        if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
        {
            BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
            return new KeyBTreeCursor<V>( tree, valueComparator );
        }

        return new ArrayTreeCursor<V>( marshaller.deserialize( serialized ) );
    }


    // ------------------------------------------------------------------------
    // Maintenance Operations
    // ------------------------------------------------------------------------


    /**
     * @see Table#close()
     */
    public synchronized void close() throws IOException
    {
        sync();
    }


    /**
     * Synchronizes the buffers with disk.
     *
     * @throws IOException if errors are encountered on the flush
     */
    public synchronized void sync() throws IOException
    {
        long recId = recMan.getNamedObject( name + SZSUFFIX );
        recMan.update( recId, count );
    }

   
    public Marshaller<ArrayTree<V>> getMarshaller()
    {
        return marshaller;
    }
   

    // ------------------------------------------------------------------------
    // Private/Package Utility Methods
    // ------------------------------------------------------------------------
    DupsContainer<V> getDupsContainer( byte[] serialized ) throws IOException
    {
        if ( serialized == null )
        {
            return new DupsContainer<V>( new ArrayTree<V>( valueComparator ) );
        }

        if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
        {
            return new DupsContainer<V>( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
        }

        return new DupsContainer<V>( marshaller.deserialize( serialized ) );
    }


    /**
     * Returns the main BTree used by this table.
     *
     * @return the main JDBM BTree used by this table
     */
    BTree getBTree()
    {
        return bt;
    }


    BTree getBTree( BTreeRedirect redirect ) throws IOException
    {
        if ( duplicateBtrees.containsKey( redirect.getRecId() ) )
        {
            return duplicateBtrees.get( redirect.getRecId() );
        }
       
        BTree tree = BTree.load( recMan, redirect.getRecId() );
        ((SerializableComparator<K>)tree.getComparator()).setSchemaManager( schemaManager );
        duplicateBtrees.put( redirect.getRecId(), tree );
        return tree;
    }


    @SuppressWarnings("unchecked")
    private boolean btreeHas( BTree tree, V key, boolean isGreaterThan ) throws IOException
    {
        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
       
        TupleBrowser browser = tree.browse( key );
        if ( isGreaterThan )
        {
            return browser.getNext( tuple );
        }
        else
        {
            if ( browser.getPrevious( tuple ) )
            {
                return true;
            }
            else
            {
                /*
                 * getPrevious() above fails which means the browser has is
                 * before the first Tuple of the btree.  A call to getNext()
                 * should work every time.
                 */
                browser.getNext( tuple );

                /*
                 * Since the browser is positioned now on the Tuple with the
                 * smallest key we just need to check if it equals this key
                 * which is the only chance for returning true.
                 */
                V firstKey = ( V ) tuple.getKey();
                return valueComparator.compare( key, firstKey ) == 0;
            }
        }
    }


    @SuppressWarnings("unchecked")
    private ArrayTree<V> convertToArrayTree( BTree bTree ) throws IOException
    {
        ArrayTree<V> avlTree = new ArrayTree<V>( valueComparator );
        TupleBrowser browser = bTree.browse();
        jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
       
        while ( browser.getNext( tuple ) )
        {
            avlTree.insert( ( V ) tuple.getKey() );
        }

        return avlTree;
    }
   

    private BTree convertToBTree( ArrayTree<V> arrayTree ) throws Exception
    {
        BTree bTree;

        if ( valueSerializer != null )
        {
            bTree = BTree.createInstance( recMan, valueComparator, valueSerializer, null );
        }
        else
        {
            bTree = BTree.createInstance( recMan, valueComparator );
        }

        Cursor<V> keys = new ArrayTreeCursor<V>( arrayTree );
        keys.beforeFirst();
       
        while ( keys.next() )
        {
            bTree.insert( keys.get(), StringTools.EMPTY_BYTES, true );
        }
       
        return bTree;
    }
}
TOP

Related Classes of org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmTable

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.