directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akaras...@apache.org
Subject svn commit: r486848 - in /directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm: JdbmStore.java JdbmStoreConfiguration.java
Date Wed, 13 Dec 2006 21:40:22 GMT
Author: akarasulu
Date: Wed Dec 13 13:40:21 2006
New Revision: 486848

URL: http://svn.apache.org/viewvc?view=rev&rev=486848
Log:
forgot to add these classes in last commit

Added:
    directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
    directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStoreConfiguration.java

Added: directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java
URL: http://svn.apache.org/viewvc/directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java?view=auto&rev=486848
==============================================================================
--- directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java (added)
+++ directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStore.java Wed Dec 13 13:40:21 2006
@@ -0,0 +1,1910 @@
+/*
+ *  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.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import jdbm.RecordManager;
+import jdbm.helper.MRU;
+import jdbm.recman.BaseRecordManager;
+import jdbm.recman.CacheRecordManager;
+
+import org.apache.directory.server.core.ServerUtils;
+import org.apache.directory.server.core.partition.Oid;
+import org.apache.directory.server.core.partition.impl.btree.Index;
+import org.apache.directory.server.core.partition.impl.btree.IndexAssertion;
+import org.apache.directory.server.core.partition.impl.btree.IndexAssertionEnumeration;
+import org.apache.directory.server.core.partition.impl.btree.IndexConfiguration;
+import org.apache.directory.server.core.partition.impl.btree.IndexNotFoundException;
+import org.apache.directory.server.core.partition.impl.btree.IndexRecord;
+import org.apache.directory.server.core.schema.AttributeTypeRegistry;
+import org.apache.directory.server.core.schema.OidRegistry;
+import org.apache.directory.shared.ldap.exception.LdapAuthenticationNotSupportedException;
+import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
+import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.shared.ldap.message.ResultCodeEnum;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class JdbmStore
+{
+    private static final Logger log = LoggerFactory.getLogger( JdbmPartition.class );
+
+    private static final int DEFAULT_CACHE_SIZE = 10000;
+
+    /** The objectClass name for aliases: 'alias' */
+    private static final String ALIAS_OBJECT = "alias";
+
+    /**
+     * The aliased Dn attribute name: aliasedObjectName for LDAP and
+     * aliasedEntryName or X.500.
+     */
+    private static final String ALIAS_ATTRIBUTE = "aliasedObjectName";
+
+
+    /** the JDBM record manager used by this database */
+    private RecordManager recMan;
+    /** the normalized suffix DN of this backend database */
+    private LdapDN normSuffix;
+    /** the user provided suffix DN of this backend database */
+    private LdapDN upSuffix;
+    /** the working directory to use for files */
+    private File workingDirectory;
+    /** the master table storing entries by primary key */
+    private JdbmMasterTable master;
+    /** a map of attribute names to user indices */
+    private Map<String, Index> indices;
+    /** a map of index names to system indices */
+    private Map<String, Index> sysIndices;
+
+    /** true if open */
+    private boolean initialized;
+    /** true if we sync disks on every write operation */
+    private boolean isSyncOnWrite = true;
+    
+
+    /** the normalized distinguished name index */
+    private Index ndnIdx;
+    /** the user provided distinguished name index */
+    private Index updnIdx;
+    /** the attribute existance index */
+    private Index existanceIdx;
+    /** the parent child relationship index */
+    private Index hierarchyIdx;
+    /** the one level scope alias index */
+    private Index oneAliasIdx;
+    /** the subtree scope alias index */
+    private Index subAliasIdx;
+    /** a system index on aliasedObjectName attribute */
+    private Index aliasIdx;
+    
+    private static AttributeType OBJECT_CLASS_AT;
+    private static AttributeType ALIAS_AT;
+    
+    private AttributeTypeRegistry attributeTypeRegistry;
+    private OidRegistry oidRegistry;
+
+    // ------------------------------------------------------------------------
+    // C O N S T R U C T O R S
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a store based on JDBM B+Trees.
+     */
+    public JdbmStore()
+    {
+    }
+
+    
+    /**
+     * Use this method to initialize the indices.  Only call this after
+     * the registries and the optimizer have been enabled.  The '2' at the end
+     * shows this is the 3rd init method called in the init sequence.
+     * 
+     * @param indices
+     * @throws NamingException
+     */
+    protected void initIndices2(Set indices ) throws NamingException
+    {
+        Set<String> sysOidSet = new HashSet<String>();
+        sysOidSet.add( Oid.EXISTANCE );
+        sysOidSet.add( Oid.HIERARCHY );
+        sysOidSet.add( Oid.UPDN );
+        sysOidSet.add( Oid.NDN );
+        sysOidSet.add( Oid.ONEALIAS );
+        sysOidSet.add( Oid.SUBALIAS );
+        sysOidSet.add( Oid.ALIAS );
+
+        // Used to calculate the system indices we must automatically add
+        Set<String> customAddedSystemIndices = new HashSet<String>();
+        
+        for ( Iterator ii = indices.iterator(); ii.hasNext(); /**/ )
+        {
+            /*
+             * NOTE
+             * ====
+             * 
+             * The object returned by the indexedAttributes property
+             * of the configuration may include just a simple set of <String> 
+             * names for the attributes being index OR may include a set 
+             * of IndexConfiguration objects.
+             * 
+             * If the objects are strings extra information about the
+             * cacheSize of an index is not available and so the default is
+             * used.  If an IndexConfiguration is available then the custom
+             * cacheSize is used.
+             */
+            
+            Object nextObject = ii.next();
+            String name = null;
+            int cacheSize = IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE;
+            int numDupLimit = IndexConfiguration.DEFAULT_DUPLICATE_LIMIT;
+            
+            // no custom cacheSize info is available so default sticks
+            if ( nextObject instanceof String ) 
+            {
+                name = ( String ) nextObject;
+                log.warn( "Using default cache size of {} for index on attribute {}", 
+                    new Integer( cacheSize ), name );
+            }
+            // custom cache size is used
+            else if ( nextObject instanceof IndexConfiguration )
+            {
+                IndexConfiguration indexConfiguration = ( IndexConfiguration ) nextObject;
+                name = indexConfiguration.getAttributeId();
+                cacheSize = indexConfiguration.getCacheSize();
+                numDupLimit = indexConfiguration.getDuplicateLimit();
+                
+                if ( cacheSize <= 0 ) 
+                {
+                    log.warn( "Cache size {} for index on attribute is null or negative. Using default value.", 
+                        new Integer(cacheSize), name );
+                    cacheSize = IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE;
+                }
+                else
+                {
+                    log.info( "Using cache size of {} for index on attribute {}", 
+                        new Integer( cacheSize ), name );
+                }
+                
+                if ( cacheSize <= 0 ) 
+                {
+                    log.warn( "Duplicate limit {} for index on attribute is null or negative. Using default value.", 
+                        new Integer(numDupLimit), name );
+                    cacheSize = IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE;
+                }
+                else
+                {
+                    log.info( "Using duplicate limit of {} for index on attribute {}", 
+                        new Integer( numDupLimit ), name );
+                }
+            }
+            
+            String oid = oidRegistry.getOid( name );
+            AttributeType type = attributeTypeRegistry.lookup( oid );
+
+            // check if attribute is a system attribute
+            if ( sysOidSet.contains( oid ) )
+            {
+                if ( oid.equals( Oid.EXISTANCE ) )
+                {
+                    setExistanceIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.EXISTANCE );
+                }
+                else if ( oid.equals( Oid.HIERARCHY ) )
+                {
+                    setHierarchyIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.HIERARCHY );
+                }
+                else if ( oid.equals( Oid.UPDN ) )
+                {
+                    setUpdnIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.UPDN );
+                }
+                else if ( oid.equals( Oid.NDN ) )
+                {
+                    setNdnIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.NDN );
+                }
+                else if ( oid.equals( Oid.ONEALIAS ) )
+                {
+                    setOneAliasIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.ONEALIAS );
+                }
+                else if ( oid.equals( Oid.SUBALIAS ) )
+                {
+                    setSubAliasIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.SUBALIAS);
+                }
+                else if ( oid.equals( Oid.ALIAS ) )
+                {
+                    setAliasIndexOn( type, cacheSize, numDupLimit );
+                    customAddedSystemIndices.add( Oid.ALIAS );
+                }
+                else
+                {
+                    throw new NamingException( "Unidentified system index " + oid );
+                }
+            }
+            else
+            {
+                addIndexOn( type, cacheSize, numDupLimit );
+            }
+        }
+        
+        // -------------------------------------------------------------------
+        // Add all system indices that were not custom configured by iterating
+        // through all system index oids and checking of that index is 
+        // contained within the customAddedSystemIndices set.  If it is not
+        // contained in this set then the system index was not custom 
+        // configured above and must be configured with defaults below.
+        // -------------------------------------------------------------------
+        
+        for ( Iterator ii = sysOidSet.iterator(); ii.hasNext(); /**/ )
+        {
+            String systemIndexName = ( String ) ii.next();
+            if ( ! customAddedSystemIndices.contains( systemIndexName ) )
+            {
+                AttributeType type = attributeTypeRegistry.lookup( systemIndexName );
+                log.warn( "Using default cache size of {} for index on attribute {}", 
+                    new Integer( IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE ), systemIndexName );
+                if ( systemIndexName.equals( Oid.EXISTANCE ) )
+                {
+                    setExistanceIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE, 
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.HIERARCHY ) )
+                {
+                    setHierarchyIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.UPDN ) )
+                {
+                    setUpdnIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.NDN ) )
+                {
+                    setNdnIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.ONEALIAS ) )
+                {
+                    setOneAliasIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.SUBALIAS ) )
+                {
+                    setSubAliasIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else if ( systemIndexName.equals( Oid.ALIAS ) )
+                {
+                    setAliasIndexOn( type, IndexConfiguration.DEFAULT_INDEX_CACHE_SIZE,
+                        IndexConfiguration.DEFAULT_DUPLICATE_LIMIT );
+                }
+                else
+                {
+                    throw new NamingException( "Unidentified system index " + systemIndexName );
+                }
+            }
+        }
+    }
+
+    
+    /**
+     * Called last (4th) to check if the suffix entry has been created on disk,
+     * and if not it is created.
+     *  
+     * @param suffix
+     * @param entry
+     * @throws NamingException
+     */
+    protected void initSuffixEntry3( String suffix, Attributes entry ) throws NamingException
+    {
+        // add entry for context, if it does not exist
+        Attributes suffixOnDisk = getSuffixEntry();
+        if ( suffixOnDisk == null )
+        {
+            LdapDN dn = new LdapDN( suffix );
+            LdapDN normalizedSuffix = LdapDN.normalize( dn, attributeTypeRegistry.getNormalizerMapping() );
+            add( normalizedSuffix, entry );
+        }
+    }
+
+    
+    public synchronized void init( JdbmStoreConfiguration config ) throws NamingException
+    {
+        isSyncOnWrite = config.isSyncOnWrite();
+
+        oidRegistry = config.getOidRegistry();
+        attributeTypeRegistry = config.getAttributeTypeRegistry();
+
+        OBJECT_CLASS_AT = attributeTypeRegistry.lookup( "objectClass" );
+        ALIAS_AT = attributeTypeRegistry.lookup( ALIAS_ATTRIBUTE );
+        
+        this.upSuffix = new LdapDN( config.getSuffixDn() );
+        this.normSuffix = LdapDN.normalize( upSuffix, attributeTypeRegistry.getNormalizerMapping() );
+
+        workingDirectory = config.getWorkingDirectory();
+        workingDirectory.mkdirs();
+
+        try
+        {
+            String path = workingDirectory.getPath() + File.separator + "master";
+            BaseRecordManager base = new BaseRecordManager( path );
+            base.disableTransactions();
+            
+            int cacheSize = config.getCacheSize();
+            if ( cacheSize < 0 )
+            {
+                cacheSize = DEFAULT_CACHE_SIZE;
+                log.warn( "Using the default entry cache size of {} for {} partition", 
+                    new Integer( cacheSize ), config.getName() );
+            }
+            else
+            {
+                log.info( "Using the custom configured cache size of {} for {} partition", 
+                    new Integer( cacheSize ), config.getName() );
+            }
+            recMan = new CacheRecordManager( base, new MRU( cacheSize ) );
+        }
+        catch ( IOException e )
+        {
+            NamingException ne = new NamingException( "Could not initialize RecordManager" );
+            ne.setRootCause( e );
+            throw ne;
+        }
+
+        master = new JdbmMasterTable( recMan );
+        indices = new HashMap<String,Index>();
+        sysIndices = new HashMap<String,Index>();
+
+        initIndices2( config.getIndexedAttributes() );
+        initSuffixEntry3( config.getSuffixDn(), config.getContextEntry() );
+        
+        initialized = true;
+    }
+
+
+    public synchronized void destroy()
+    {
+        if ( !initialized )
+        {
+            return;
+        }
+
+        List<Index> array = new ArrayList<Index>();
+        array.addAll( indices.values() );
+
+        if ( null != ndnIdx )
+        {
+            array.add( ndnIdx );
+        }
+
+        if ( null != updnIdx )
+        {
+            array.add( updnIdx );
+        }
+
+        if ( null != aliasIdx )
+        {
+            array.add( aliasIdx );
+        }
+
+        if ( null != oneAliasIdx )
+        {
+            array.add( oneAliasIdx );
+        }
+
+        if ( null != subAliasIdx )
+        {
+            array.add( subAliasIdx );
+        }
+
+        if ( null != hierarchyIdx )
+        {
+            array.add( hierarchyIdx );
+        }
+
+        if ( null != existanceIdx )
+        {
+            array.add( existanceIdx );
+        }
+
+        Iterator list = array.iterator();
+
+        while ( list.hasNext() )
+        {
+            Index index = ( Index ) list.next();
+
+            try
+            {
+                index.close();
+            }
+            catch ( Throwable t )
+            {
+                log.error( "Failed to close an index.", t );
+            }
+        }
+
+        try
+        {
+            master.close();
+        }
+        catch ( Throwable t )
+        {
+            log.error( "Failed to close the master.", t );
+        }
+
+        try
+        {
+            recMan.close();
+        }
+        catch ( Throwable t )
+        {
+            log.error( "Failed to close the record manager", t );
+        }
+
+        initialized = false;
+    }
+
+
+    public boolean isInitialized()
+    {
+        return initialized;
+    }
+
+
+    public synchronized void sync() throws NamingException
+    {
+        if ( !initialized )
+        {
+            return;
+        }
+
+        List<Index> array = new ArrayList<Index>();
+        array.addAll( indices.values() );
+        array.add( ndnIdx );
+        array.add( updnIdx );
+        array.add( aliasIdx );
+        array.add( oneAliasIdx );
+        array.add( subAliasIdx );
+        array.add( hierarchyIdx );
+        array.add( existanceIdx );
+
+        Iterator list = array.iterator();
+
+        // Sync all user defined indices
+        while ( list.hasNext() )
+        {
+            Index idx = ( Index ) list.next();
+
+            idx.sync();
+        }
+
+        master.sync();
+
+        try
+        {
+            recMan.commit();
+        }
+        catch ( Throwable t )
+        {
+            throw ( NamingException ) new NamingException( "Failed to commit changes to the record manager." )
+                .initCause( t );
+        }
+    }
+
+
+    // ------------------------------------------------------------------------
+    // I N D E X   M E T H O D S
+    // ------------------------------------------------------------------------
+
+    public void addIndexOn( AttributeType spec, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        Index idx = new JdbmIndex( spec, workingDirectory, cacheSize, numDupLimit );
+        indices.put( spec.getOid(), idx );
+    }
+
+
+    public Index getExistanceIndex()
+    {
+        return existanceIdx;
+    }
+
+
+    public void setExistanceIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( existanceIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        existanceIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), existanceIdx );
+    }
+
+
+    public Index getHierarchyIndex()
+    {
+        return hierarchyIdx;
+    }
+
+
+    public void setHierarchyIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( hierarchyIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        hierarchyIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), hierarchyIdx );
+    }
+
+
+    public Index getAliasIndex()
+    {
+        return aliasIdx;
+    }
+
+
+    public void setAliasIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( aliasIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        aliasIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), aliasIdx );
+    }
+
+
+    public Index getOneAliasIndex()
+    {
+        return oneAliasIdx;
+    }
+
+
+    public void setOneAliasIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( oneAliasIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        oneAliasIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), oneAliasIdx );
+    }
+
+
+    public Index getSubAliasIndex()
+    {
+        return subAliasIdx;
+    }
+
+
+    public void setSubAliasIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( subAliasIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        subAliasIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), subAliasIdx );
+    }
+
+
+    public Index getUpdnIndex()
+    {
+        return updnIdx;
+    }
+
+
+    public void setUpdnIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( updnIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        updnIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), updnIdx );
+    }
+
+
+    public Index getNdnIndex()
+    {
+        return ndnIdx;
+    }
+
+
+    public void setNdnIndexOn( AttributeType attrType, int cacheSize, int numDupLimit ) throws NamingException
+    {
+        if ( ndnIdx != null )
+        {
+            NamingException e = new NamingException( "Index already set!" );
+            throw e;
+        }
+
+        ndnIdx = new JdbmIndex( attrType, workingDirectory, cacheSize, numDupLimit );
+        sysIndices.put( attrType.getOid(), ndnIdx );
+    }
+
+
+    public Iterator getUserIndices()
+    {
+        return indices.keySet().iterator();
+    }
+
+
+    public Iterator getSystemIndices()
+    {
+        return sysIndices.keySet().iterator();
+    }
+
+
+    public boolean hasUserIndexOn( String id ) throws NamingException
+    {
+        return indices.containsKey( oidRegistry.getOid( id ) );
+    }
+
+
+    public boolean hasSystemIndexOn( String id ) throws NamingException
+    {
+        return sysIndices.containsKey( oidRegistry.getOid( id ) );
+    }
+
+
+    /**
+     * @see org.apache.directory.server.core.partition.impl.btree.BTreePartition#getUserIndex(String)
+     */
+    public Index getUserIndex( String id ) throws IndexNotFoundException
+    {
+        try
+        {
+            id = oidRegistry.getOid( id );
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed to identify OID for: " + id, e );
+            throw new IndexNotFoundException( "Failed to identify OID for: " + id, id, e );
+        }
+
+        if ( indices.containsKey( id ) )
+        {
+            return ( Index ) indices.get( id );
+        }
+        else
+        {
+            String name = "unknown";
+            try
+            {
+                name = oidRegistry.getPrimaryName( id );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "Failed to resolve primary name for " + id + " in user index lookup";
+                log.error( msg, e );
+                throw new IndexNotFoundException( msg, id, e );
+            }
+            
+            throw new IndexNotFoundException( "A user index on attribute " + id + " (" 
+                + name + ") does not exist!" );
+        }
+    }
+
+
+    /**
+     * @see BTreePartition#getEntryId(String)
+     */
+    public Index getSystemIndex( String id ) throws IndexNotFoundException
+    {
+        try
+        {
+            id = oidRegistry.getOid( id );
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Failed to identify OID for: " + id, e );
+            throw new IndexNotFoundException( "Failed to identify OID for: " + id, id, e );
+        }
+
+
+        if ( sysIndices.containsKey( id ) )
+        {
+            return ( Index ) sysIndices.get( id );
+        }
+        else
+        {
+            String name = "unknown";
+            try
+            {
+                name = oidRegistry.getPrimaryName( id );
+            }
+            catch ( NamingException e )
+            {
+                String msg = "Failed to resolve primary name for " + id + " in user index lookup";
+                log.error( msg, e );
+                throw new IndexNotFoundException( msg, id, e );
+            }
+            
+            throw new IndexNotFoundException( "A system index on attribute " + id + " (" 
+                + name + ") does not exist!" );
+        }
+    }
+
+
+    public BigInteger getEntryId( String dn ) throws NamingException
+    {
+        return ndnIdx.forwardLookup( dn );
+    }
+
+
+    public String getEntryDn( BigInteger id ) throws NamingException
+    {
+        return ( String ) ndnIdx.reverseLookup( id );
+    }
+
+
+    public BigInteger getParentId( String dn ) throws NamingException
+    {
+        BigInteger childId = ndnIdx.forwardLookup( dn );
+        return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
+    }
+
+
+    public BigInteger getParentId( BigInteger childId ) throws NamingException
+    {
+        return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
+    }
+
+
+    public String getEntryUpdn( BigInteger id ) throws NamingException
+    {
+        return ( String ) updnIdx.reverseLookup( id );
+    }
+
+
+    public String getEntryUpdn( String dn ) throws NamingException
+    {
+        BigInteger id = ndnIdx.forwardLookup( dn );
+        return ( String ) updnIdx.reverseLookup( id );
+    }
+
+
+    public int count() throws NamingException
+    {
+        return master.count();
+    }
+
+
+    /**
+     * Removes the index entries for an alias before the entry is deleted from
+     * the master table.
+     * 
+     * @todo Optimize this by walking the hierarchy index instead of the name 
+     * @param aliasId the id of the alias entry in the master table
+     * @throws NamingException if we cannot delete the indices
+     */
+    private void dropAliasIndices( BigInteger aliasId ) throws NamingException
+    {
+        String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
+        BigInteger targetId = getEntryId( targetDn );
+        String aliasDn = getEntryDn( aliasId );
+        LdapDN ancestorDn = ( LdapDN ) new LdapDN( aliasDn ).getPrefix( 1 );
+        BigInteger ancestorId = getEntryId( ancestorDn.toString() );
+
+        /*
+         * We cannot just drop all tuples in the one level and subtree indices
+         * linking baseIds to the targetId.  If more than one alias refers to
+         * the target then droping all tuples with a value of targetId would
+         * make all other aliases to the target inconsistent.
+         * 
+         * We need to walk up the path of alias ancestors until we reach the 
+         * upSuffix, deleting each ( ancestorId, targetId ) tuple in the
+         * subtree scope alias.  We only need to do this for the direct parent
+         * of the alias on the one level subtree.
+         */
+        oneAliasIdx.drop( ancestorId, targetId );
+        subAliasIdx.drop( ancestorId, targetId );
+
+        while ( !ancestorDn.equals( normSuffix ) )
+        {
+            ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 );
+            ancestorId = getEntryId( ancestorDn.toString() );
+
+            subAliasIdx.drop( ancestorId, targetId );
+        }
+
+        // Drops all alias tuples pointing to the id of the alias to be deleted
+        aliasIdx.drop( aliasId );
+    }
+
+
+    /**
+     * Adds indices for an aliasEntry to be added to the database while checking
+     * for constrained alias constructs like alias cycles and chaining.
+     * 
+     * @param aliasDn normalized distinguished name for the alias entry
+     * @param aliasTarget the user provided aliased entry dn as a string
+     * @param aliasId the id of alias entry to add
+     * @throws NamingException if index addition fails, of the alias is not 
+     * allowed due to chaining or cycle formation.
+     */
+    private void addAliasIndices( BigInteger aliasId, LdapDN aliasDn, String aliasTarget ) throws NamingException
+    {
+        LdapDN normalizedAliasTargetDn = null; // Name value of aliasedObjectName
+        BigInteger targetId = null; // Id of the aliasedObjectName
+        LdapDN ancestorDn = null; // Name of an alias entry relative
+        BigInteger ancestorId = null; // Id of an alias entry relative
+
+        // Access aliasedObjectName, normalize it and generate the Name 
+        normalizedAliasTargetDn = new LdapDN( aliasTarget );
+        normalizedAliasTargetDn.normalize( attributeTypeRegistry.getNormalizerMapping() );
+
+        /*
+         * Check For Cycles
+         * 
+         * Before wasting time to lookup more values we check using the target
+         * dn to see if we have the possible formation of an alias cycle.  This
+         * happens when the alias refers back to a target that is also a 
+         * relative of the alias entry.  For detection we test if the aliased
+         * entry Dn starts with the target Dn.  If it does then we know the 
+         * aliased target is a relative and we have a perspecitive cycle.
+         */
+        if ( aliasDn.startsWith( normalizedAliasTargetDn ) )
+        {
+            if ( aliasDn.equals( normalizedAliasTargetDn ) )
+            {
+                throw new NamingException( "[36] aliasDereferencingProblem - " + "attempt to create alias to itself." );
+            }
+
+            throw new NamingException( "[36] aliasDereferencingProblem - "
+                + "attempt to create alias with cycle to relative " + aliasTarget
+                + " not allowed from descendent alias " + aliasDn );
+        }
+
+        /*
+         * Check For Aliases External To Naming Context
+         * 
+         * id may be null but the alias may be to a valid entry in 
+         * another namingContext.  Such aliases are not allowed and we
+         * need to point it out to the user instead of saying the target
+         * does not exist when it potentially could outside of this upSuffix.
+         */
+        if ( !normalizedAliasTargetDn.startsWith( normSuffix ) )
+        {
+            // Complain specifically about aliases to outside naming contexts
+            throw new NamingException( "[36] aliasDereferencingProblem -"
+                + " the alias points to an entry outside of the " + upSuffix.getUpName()
+                + " namingContext to an object whose existance cannot be" + " determined." );
+        }
+
+        // L O O K U P   T A R G E T   I D
+        targetId = ndnIdx.forwardLookup( normalizedAliasTargetDn.toNormName() );
+
+        /*
+         * Check For Target Existance
+         * 
+         * We do not allow the creation of inconsistant aliases.  Aliases should
+         * not be broken links.  If the target does not exist we start screaming
+         */
+        if ( null == targetId )
+        {
+            // Complain about target not existing
+            throw new NamingException( "[33] aliasProblem - "
+                + "the alias when dereferenced would not name a known object "
+                + "the aliasedObjectName must be set to a valid existing " + "entry." );
+        }
+
+        /*
+         * Detect Direct Alias Chain Creation
+         * 
+         * Rather than resusitate the target to test if it is an alias and fail
+         * due to chaing creation we use the alias index to determine if the
+         * target is an alias.  Hence if the alias we are about to create points
+         * to another alias as its target in the aliasedObjectName attribute, 
+         * then we have a situation where an alias chain is being created.  
+         * Alias chaining is not allowed so we throw and exception. 
+         */
+        if ( null != aliasIdx.reverseLookup( targetId ) )
+        {
+            // Complain about illegal alias chain
+            throw new NamingException( "[36] aliasDereferencingProblem -"
+                + " the alias points to another alias.  Alias chaining is" + " not supported by this backend." );
+        }
+
+        // Add the alias to the simple alias index
+        aliasIdx.add( normalizedAliasTargetDn.getNormName(), aliasId );
+
+        /*
+         * Handle One Level Scope Alias Index
+         * 
+         * The first relative is special with respect to the one level alias
+         * index.  If the target is not a sibling of the alias then we add the
+         * index entry maping the parent's id to the aliased target id.
+         */
+        ancestorDn = ( LdapDN ) aliasDn.clone();
+        ancestorDn.remove( aliasDn.size() - 1 );
+        ancestorId = getEntryId( ancestorDn.toNormName() );
+
+        if ( !NamespaceTools.isSibling( normalizedAliasTargetDn, aliasDn ) )
+        {
+            oneAliasIdx.add( ancestorId, targetId );
+        }
+
+        /*
+         * Handle Sub Level Scope Alias Index
+         * 
+         * Walk the list of relatives from the parents up to the upSuffix, testing
+         * to see if the alias' target is a descendant of the relative.  If the
+         * alias target is not a descentant of the relative it extends the scope
+         * and is added to the sub tree scope alias index.  The upSuffix node is
+         * ignored since everything is under its scope.  The first loop 
+         * iteration shall handle the parents.
+         */
+        while ( !ancestorDn.equals( normSuffix ) && null != ancestorId )
+        {
+            if ( !NamespaceTools.isDescendant( ancestorDn, normalizedAliasTargetDn ) )
+            {
+                subAliasIdx.add( ancestorId, targetId );
+            }
+
+            ancestorDn.remove( ancestorDn.size() - 1 );
+            ancestorId = getEntryId( ancestorDn.toNormName() );
+        }
+    }
+
+    
+    public void add( LdapDN normName, Attributes entry ) throws NamingException
+    {
+        BigInteger id;
+        BigInteger parentId = null;
+
+        id = master.getNextId();
+
+        //
+        // Suffix entry cannot have a parent since it is the root so it is 
+        // capped off using the zero value which no entry can have since 
+        // entry sequences start at 1.
+        //
+
+        LdapDN parentDn = null;
+        if ( normName.equals( normSuffix ) )
+        {
+            parentId = BigInteger.ZERO;
+        }
+        else
+        {
+            parentDn = ( LdapDN ) normName.clone();
+            parentDn.remove( parentDn.size() - 1 );
+            parentId = getEntryId( parentDn.toString() );
+        }
+
+        // don't keep going if we cannot find the parent Id
+        if ( parentId == null )
+        {
+            throw new LdapNameNotFoundException( "Id for parent '" + parentDn + "' not found!" );
+        }
+
+        Attribute objectClass = ServerUtils.getAttribute( OBJECT_CLASS_AT, entry );
+
+        if ( objectClass == null )
+        {
+            String msg = "Entry " + normName.getUpName() + " contains no objectClass attribute: " + entry;
+            throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+        }
+
+        // Start adding the system indices
+        // Why bother doing a lookup if this is not an alias.
+
+        if ( objectClass.contains( ALIAS_OBJECT ) )
+        {
+            Attribute aliasAttr = ServerUtils.getAttribute( ALIAS_AT, entry );
+            addAliasIndices( id, normName, ( String ) aliasAttr.get() );
+        }
+
+        ndnIdx.add( normName.toNormName(), id );
+        updnIdx.add( normName.getUpName(), id );
+        hierarchyIdx.add( parentId, id );
+
+        // Now work on the user defined indices
+        NamingEnumeration list = entry.getIDs();
+        while ( list.hasMore() )
+        {
+            String attributeId = ( String ) list.next();
+            String attributeOid = oidRegistry.getOid( attributeId );
+
+            if ( hasUserIndexOn( attributeOid ) )
+            {
+                Index idx = getUserIndex( attributeOid );
+                
+                // here lookup by attributeId is ok since we got attributeId from 
+                // the entry via the enumeration - it's in there as is for sure
+                NamingEnumeration values = entry.get( attributeId ).getAll();
+
+                while ( values.hasMore() )
+                {
+                    idx.add( values.next(), id );
+                }
+
+                // Adds only those attributes that are indexed
+                existanceIdx.add( attributeOid, id );
+            }
+        }
+
+        master.put( entry, id );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public Attributes lookup( BigInteger id ) throws NamingException
+    {
+        return master.get( id );
+    }
+
+
+    public void delete( BigInteger id ) throws NamingException
+    {
+        Attributes entry = lookup( id );
+        BigInteger parentId = getParentId( id );
+        NamingEnumeration attrs = entry.getIDs();
+
+        Attribute objectClass = ServerUtils.getAttribute( OBJECT_CLASS_AT, entry );
+        if ( objectClass.contains( ALIAS_OBJECT ) )
+        {
+            dropAliasIndices( id );
+        }
+
+        ndnIdx.drop( id );
+        updnIdx.drop( id );
+        hierarchyIdx.drop( id );
+
+        // Remove parent's reference to entry only if entry is not the upSuffix
+        if ( !parentId.equals( BigInteger.ZERO ) )
+        {
+            hierarchyIdx.drop( parentId, id );
+        }
+
+        while ( attrs.hasMore() )
+        {
+            String attributeId = ( ( String ) attrs.next() );
+            String attributeOid = oidRegistry.getOid( attributeId );
+
+            if ( hasUserIndexOn( attributeOid ) )
+            {
+                Index index = getUserIndex( attributeOid );
+
+                // here lookup by attributeId is ok since we got attributeId from 
+                // the entry via the enumeration - it's in there as is for sure
+                NamingEnumeration values = entry.get( attributeId ).getAll();
+
+                while ( values.hasMore() )
+                {
+                    index.drop( values.next(), id );
+                }
+
+                existanceIdx.drop( attributeOid, id );
+            }
+        }
+
+        master.delete( id );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public NamingEnumeration list( BigInteger id ) throws NamingException
+    {
+        return hierarchyIdx.listIndices( id );
+    }
+
+
+    public int getChildCount( BigInteger id ) throws NamingException
+    {
+        return hierarchyIdx.count( id );
+    }
+
+
+    public LdapDN getSuffix()
+    {
+        return normSuffix;
+    }
+
+    public LdapDN getUpSuffix()
+    {
+        return upSuffix;
+    }
+
+
+    public Attributes getSuffixEntry() throws NamingException
+    {
+        BigInteger id = getEntryId( normSuffix.toNormName() );
+
+        if ( null == id )
+        {
+            return null;
+        }
+
+        return lookup( id );
+    }
+
+
+    public void setProperty( String propertyName, String propertyValue ) throws NamingException
+    {
+        master.setProperty( propertyName, propertyValue );
+    }
+
+
+    public String getProperty( String propertyName ) throws NamingException
+    {
+        return master.getProperty( propertyName );
+    }
+
+
+    public Attributes getIndices( BigInteger id ) throws NamingException
+    {
+        Attributes attributes = new LockableAttributesImpl();
+
+        // Get the distinguishedName to id mapping
+        attributes.put( "_nDn", getEntryDn( id ) );
+        attributes.put( "_upDn", getEntryUpdn( id ) );
+        attributes.put( "_parent", getParentId( id ) );
+
+        // Get all standard index attribute to value mappings
+        Iterator idxList = this.indices.values().iterator();
+        while ( idxList.hasNext() )
+        {
+            Index index = ( Index ) idxList.next();
+            NamingEnumeration list = index.listReverseIndices( id );
+            while ( list.hasMore() )
+            {
+                IndexRecord rec = ( IndexRecord ) list.next();
+                Object val = rec.getIndexKey();
+                String attrId = index.getAttribute().getName();
+                Attribute attr = attributes.get( attrId );
+                if ( attr == null )
+                {
+                    attr = new LockableAttributeImpl( attrId );
+                }
+                attr.add( val );
+                attributes.put( attr );
+            }
+        }
+
+        // Get all existance mappings for this id creating a special key
+        // that looks like so 'existance[attribute]' and the value is set to id
+        NamingEnumeration list = existanceIdx.listReverseIndices( id );
+        StringBuffer val = new StringBuffer();
+        while ( list.hasMore() )
+        {
+            IndexRecord rec = ( IndexRecord ) list.next();
+            val.append( "_existance[" );
+            val.append( rec.getIndexKey() );
+            val.append( "]" );
+
+            String valStr = val.toString();
+            Attribute attr = attributes.get( valStr );
+            if ( attr == null )
+            {
+                attr = new LockableAttributeImpl( valStr );
+            }
+            attr.add( rec.getEntryId() );
+            attributes.put( attr );
+            val.setLength( 0 );
+        }
+
+        // Get all parent child mappings for this entry as the parent using the
+        // key 'child' with many entries following it.
+        list = hierarchyIdx.listIndices( id );
+        Attribute childAttr = new LockableAttributeImpl( "_child" );
+        attributes.put( childAttr );
+        while ( list.hasMore() )
+        {
+            IndexRecord rec = ( IndexRecord ) list.next();
+            childAttr.add( rec.getEntryId() );
+        }
+
+        return attributes;
+    }
+
+
+    /**
+     * Adds a set of attribute values while affecting the appropriate indices. 
+     * The entry is not persisted: it is only changed in anticipation for a put 
+     * into the master table.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the attribute and values to add 
+     * @throws NamingException if index alteration or attribute addition
+     * fails.
+     */
+    private void add( BigInteger id, Attributes entry, Attribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getID() );
+        
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+            idx.add( mods, id );
+
+            // If the attr didn't exist for this id add it to existance index
+            if ( !existanceIdx.hasValue( modsOid, id ) )
+            {
+                existanceIdx.add( modsOid, id );
+            }
+        }
+
+        // add all the values in mods to the same attribute in the entry
+        AttributeType type = attributeTypeRegistry.lookup( modsOid );
+        Attribute entryAttrToAddTo = ServerUtils.getAttribute( type, entry );
+
+        if ( entryAttrToAddTo == null )
+        {
+            entryAttrToAddTo = new LockableAttributeImpl( mods.getID() );
+            entry.put( entryAttrToAddTo );
+        }
+
+        for ( int ii = 0; ii < mods.size(); ii++ )
+        {
+            entryAttrToAddTo.add( mods.get( ii ) );
+        }
+
+        if ( modsOid.equals( oidRegistry.getOid( ALIAS_ATTRIBUTE ) ) )
+        {
+            String ndnStr = ( String ) ndnIdx.reverseLookup( id );
+            addAliasIndices( id, new LdapDN( ndnStr ), ( String ) mods.get() );
+        }
+    }
+
+
+    /**
+     * Completely removes the set of values for an attribute having the values 
+     * supplied while affecting the appropriate indices.  The entry is not 
+     * persisted: it is only changed in anticipation for a put into the master 
+     * table.  Note that an empty attribute w/o values will remove all the 
+     * values within the entry where as an attribute w/ values will remove those
+     * attribute values it contains.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the attribute and its values to delete
+     * @throws NamingException if index alteration or attribute modification 
+     * fails.
+     */
+    private void remove( BigInteger id, Attributes entry, Attribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getID() );
+        
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+            idx.drop( mods, id );
+
+            /* 
+             * If no attribute values exist for this entryId in the index then
+             * we remove the existance index entry for the removed attribute.
+             */
+            if ( null == idx.reverseLookup( id ) )
+            {
+                existanceIdx.drop( modsOid, id );
+            }
+        }
+
+        AttributeType attrType = attributeTypeRegistry.lookup( modsOid );
+        /*
+         * If there are no attribute values in the modifications then this 
+         * implies the compelete removal of the attribute from the entry. Else
+         * we remove individual attribute values from the entry in mods one 
+         * at a time.
+         */
+        if ( mods.size() == 0 )
+        {
+            ServerUtils.removeAttribute( attrType, entry );
+        }
+        else
+        {
+            Attribute entryAttr = ServerUtils.getAttribute( attrType, entry );
+            NamingEnumeration values = mods.getAll();
+            while ( values.hasMore() )
+            {
+                entryAttr.remove( values.next() );
+            }
+
+            // if nothing is left just remove empty attribute
+            if ( entryAttr.size() == 0 )
+            {
+                entry.remove( entryAttr.getID() );
+            }
+        }
+
+        // Aliases->single valued comp/partial attr removal is not relevant here
+        if ( modsOid.equals( oidRegistry.getOid( ALIAS_ATTRIBUTE ) ) )
+        {
+            dropAliasIndices( id );
+        }
+    }
+
+
+    /**
+     * Completely replaces the existing set of values for an attribute with the
+     * modified values supplied affecting the appropriate indices.  The entry
+     * is not persisted: it is only changed in anticipation for a put into the
+     * master table.
+     *
+     * @param id the primary key of the entry
+     * @param entry the entry to alter
+     * @param mods the replacement attribute and values
+     * @throws NamingException if index alteration or attribute modification 
+     * fails.
+     */
+    private void replace( BigInteger id, Attributes entry, Attribute mods ) throws NamingException
+    {
+        String modsOid = oidRegistry.getOid( mods.getID() );
+        
+        if ( hasUserIndexOn( modsOid ) )
+        {
+            Index idx = getUserIndex( modsOid );
+
+            // Drop all existing attribute value index entries and add new ones
+            idx.drop( id );
+            idx.add( mods, id );
+
+            /* 
+             * If no attribute values exist for this entryId in the index then
+             * we remove the existance index entry for the removed attribute.
+             */
+            if ( null == idx.reverseLookup( id ) )
+            {
+                existanceIdx.drop( modsOid, id );
+            }
+        }
+
+        String aliasAttributeOid = oidRegistry.getOid( ALIAS_ATTRIBUTE );
+        if ( modsOid.equals( aliasAttributeOid ) )
+        {
+            dropAliasIndices( id );
+        }
+
+        // replaces old attributes with new modified ones if they exist
+        if ( mods.size() > 0 )
+        {
+            entry.put( mods );
+        }
+        else  // removes old attributes if new replacements do not exist
+        {
+            entry.remove( mods.getID() );
+        }
+
+        if ( modsOid.equals( aliasAttributeOid ) && mods.size() > 0 )
+        {
+            String ndnStr = ( String ) ndnIdx.reverseLookup( id );
+            addAliasIndices( id, new LdapDN( ndnStr ), ( String ) mods.get() );
+        }
+    }
+
+
+    public void modify( LdapDN dn, int modOp, Attributes mods ) throws NamingException
+    {
+        NamingEnumeration attrs = null;
+        BigInteger id = getEntryId( dn.toString() );
+        Attributes entry = master.get( id );
+
+        switch ( modOp )
+        {
+            case ( DirContext.ADD_ATTRIBUTE  ):
+                attrs = mods.getIDs();
+
+                while ( attrs.hasMore() )
+                {
+                    String attrId = ( String ) attrs.next();
+                    Attribute attr = mods.get( attrId );
+                    add( id, entry, attr );
+                }
+
+                break;
+            case ( DirContext.REMOVE_ATTRIBUTE  ):
+                attrs = mods.getIDs();
+
+                while ( attrs.hasMore() )
+                {
+                    String attrId = ( String ) attrs.next();
+                    Attribute attr = mods.get( attrId );
+                    remove( id, entry, attr );
+                }
+
+                break;
+            case ( DirContext.REPLACE_ATTRIBUTE  ):
+                attrs = mods.getIDs();
+
+                while ( attrs.hasMore() )
+                {
+                    String attrId = ( String ) attrs.next();
+                    Attribute attr = mods.get( attrId );
+                    replace( id, entry, attr );
+                }
+
+                break;
+            default:
+                throw new NamingException( "Unidentified modification operation" );
+        }
+
+        master.put( entry, id );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public void modify( LdapDN dn, ModificationItem[] mods ) throws NamingException
+    {
+        BigInteger id = getEntryId( dn.toString() );
+        Attributes entry = master.get( id );
+
+        for ( int ii = 0; ii < mods.length; ii++ )
+        {
+            Attribute attrMods = mods[ii].getAttribute();
+
+            switch ( mods[ii].getModificationOp() )
+            {
+                case ( DirContext.ADD_ATTRIBUTE  ):
+                    add( id, entry, attrMods );
+                    break;
+                case ( DirContext.REMOVE_ATTRIBUTE  ):
+                    remove( id, entry, attrMods );
+                    break;
+                case ( DirContext.REPLACE_ATTRIBUTE  ):
+                    replace( id, entry, attrMods );
+                    break;
+                default:
+                    throw new NamingException( "Unidentified modification operation" );
+            }
+        }
+
+        master.put( entry, id );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /**
+     * Changes the relative distinuished name of an entry specified by a 
+     * distinguished name with the optional removal of the old Rdn attribute
+     * value from the entry.  Name changes propagate down as dn changes to the 
+     * descendants of the entry where the Rdn changed. 
+     * 
+     * An Rdn change operation does not change parent child relationships.  It 
+     * merely propagates a name change at a point in the DIT where the Rdn is 
+     * changed. The change propagates down the subtree rooted at the 
+     * distinguished name specified.
+     *
+     * @param dn the normalized distinguished name of the entry to alter
+     * @param newRdn the new Rdn to set
+     * @param deleteOldRdn whether or not to remove the old Rdn attr/val
+     * @throws NamingException if there are any errors propagating the name
+     *        changes.
+     */
+    public void modifyRn( LdapDN dn, String newRdn, boolean deleteOldRdn ) throws NamingException
+    {
+        String newRdnAttr = NamespaceTools.getRdnAttribute( newRdn );
+        String newRdnValue = NamespaceTools.getRdnValue( newRdn );
+        BigInteger id = getEntryId( dn.toString() );
+        Attributes entry = lookup( id );
+        LdapDN updn = new LdapDN( getEntryUpdn( id ) );
+
+        /* 
+         * H A N D L E   N E W   R D N
+         * ====================================================================
+         * Add the new Rdn attribute to the entry.  If an index exists on the 
+         * new Rdn attribute we add the index for this attribute value pair.
+         * Also we make sure that the existance index shows the existance of the
+         * new Rdn attribute within this entry.
+         */
+
+        String newRdnAttrOid = oidRegistry.getOid( newRdnAttr );
+        AttributeType newRdnAttrType = attributeTypeRegistry.lookup( newRdnAttrOid );
+        Attribute rdnAttr = ServerUtils.getAttribute( newRdnAttrType, entry );
+        if ( rdnAttr == null )
+        {
+            rdnAttr = new LockableAttributeImpl( newRdnAttr );
+        }
+
+        // add the new Rdn value only if it is not already present in the entry
+        if ( !rdnAttr.contains( newRdnValue ) )
+        {
+            rdnAttr.add( newRdnValue );
+        }
+
+        entry.put( rdnAttr );
+
+        if ( hasUserIndexOn( newRdnAttrOid ) )
+        {
+            Index idx = getUserIndex( newRdnAttrOid );
+            idx.add( newRdnValue, id );
+
+            // Make sure the altered entry shows the existance of the new attrib
+            if ( !existanceIdx.hasValue( newRdnAttrOid, id ) )
+            {
+                existanceIdx.add( newRdnAttrOid, id );
+            }
+        }
+
+        /*
+         * H A N D L E   O L D   R D N
+         * ====================================================================
+         * If the old Rdn is to be removed we need to get the attribute and 
+         * value for it.  Keep in mind the old Rdn need not be based on the 
+         * same Rdn as the new one.  We remove the Rdn value from the entry
+         * and remove the value/id tuple from the index on the old Rdn attr
+         * if any.  We also test if the delete of the old Rdn index tuple 
+         * removed all the attribute values of the old Rdn using a reverse
+         * lookup.  If so that means we blew away the last value of the old 
+         * Rdn attribute.  In this case we need to remove the attrName/id 
+         * tuple from the existance index.
+         */
+
+        if ( deleteOldRdn )
+        {
+            String oldRdn = updn.get( updn.size() - 1 );
+            String oldRdnAttr = NamespaceTools.getRdnAttribute( oldRdn );
+            String oldRdnAttrOid = oidRegistry.getOid( oldRdnAttr );
+            String oldRdnValue = NamespaceTools.getRdnValue( oldRdn );
+            AttributeType oldRdnAttrType = attributeTypeRegistry.lookup( oldRdnAttrOid );
+            
+            ServerUtils.getAttribute( oldRdnAttrType, entry ).remove( oldRdnValue );
+
+            if ( hasUserIndexOn( oldRdnAttrOid ) )
+            {
+                Index idx = getUserIndex( oldRdnAttrOid );
+                idx.drop( oldRdnValue, id );
+
+                /*
+                 * If there is no value for id in this index due to our
+                 * drop above we remove the oldRdnAttr from the existance idx
+                 */
+                if ( null == idx.reverseLookup( id ) )
+                {
+                    existanceIdx.drop( oldRdnAttrOid, id );
+                }
+            }
+        }
+
+        /*
+         * H A N D L E   D N   C H A N G E
+         * ====================================================================
+         * 1) Build the new user defined distinguished name
+         *      - clone / copy old updn
+         *      - remove old upRdn from copy
+         *      - add the new upRdn to the copy
+         * 2) Make call to recursive modifyDn method to change the names of the
+         *    entry and its descendants
+         */
+
+        LdapDN newUpdn = ( LdapDN ) updn.clone(); // copy da old updn
+        newUpdn.remove( newUpdn.size() - 1 ); // remove old upRdn
+        newUpdn.add( newUpdn.size(), newRdn ); // add da new upRdn
+        modifyDn( id, newUpdn, false ); // propagate dn changes
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /*
+     * The move operation severs a child from a parent creating a new parent
+     * child relationship.  As a consequence the relationships between the 
+     * old ancestors of the child and its descendants change.  A descendant is
+     *   
+     */
+
+    /**
+     * Recursively modifies the distinguished name of an entry and the names of
+     * its descendants calling itself in the recursion.
+     *
+     * @param id the primary key of the entry
+     * @param updn User provided distinguished name to set as the new DN
+     * @param isMove whether or not the name change is due to a move operation
+     * which affects alias indices.
+     * @throws NamingException if something goes wrong
+     */
+    private void modifyDn( BigInteger id, LdapDN updn, boolean isMove ) throws NamingException
+    {
+        String aliasTarget = null;
+
+        // Now we can handle the appropriate name indices for all cases
+        ndnIdx.drop( id );
+        
+        LdapDN normalizedDn = LdapDN.normalize( updn, attributeTypeRegistry.getNormalizerMapping() );
+        ndnIdx.add( ndnIdx.getNormalized( normalizedDn.toNormName() ), id );
+
+        updnIdx.drop( id );
+        updnIdx.add( updn.getUpName(), id );
+
+        /* 
+         * Read Alias Index Tuples
+         * 
+         * If this is a name change due to a move operation then the one and
+         * subtree indices for aliases were purged before the aliases were 
+         * moved.  Now we must add them for each alias entry we have moved.  
+         * 
+         * aliasTarget is used as a marker to tell us if we're moving an 
+         * alias.  If it is null then the moved entry is not an alias.
+         */
+        if ( isMove )
+        {
+            aliasTarget = ( String ) aliasIdx.reverseLookup( id );
+
+            if ( null != aliasTarget )
+            {
+                addAliasIndices( id, new LdapDN( getEntryDn( id ) ), aliasTarget );
+            }
+        }
+
+        NamingEnumeration children = list( id );
+        while ( children.hasMore() )
+        {
+            // Get the child and its id
+            IndexRecord rec = ( IndexRecord ) children.next();
+            BigInteger childId = rec.getEntryId();
+
+            /* 
+             * Calculate the Dn for the child's new name by copying the parents
+             * new name and adding the child's old upRdn to new name as its Rdn
+             */
+            LdapDN childUpdn = ( LdapDN ) updn.clone();
+            LdapDN oldUpdn = new LdapDN( getEntryUpdn( childId ) );
+            String rdn = oldUpdn.get( oldUpdn.size() - 1 );
+            childUpdn.add( childUpdn.size(), rdn );
+
+            // Recursively change the names of the children below
+            modifyDn( childId, childUpdn, isMove );
+        }
+    }
+
+
+    public void move( LdapDN oldChildDn, LdapDN newParentDn, String newRdn, boolean deleteOldRdn ) throws NamingException
+    {
+        BigInteger childId = getEntryId( oldChildDn.toString() );
+        modifyRn( oldChildDn, newRdn, deleteOldRdn );
+        move( oldChildDn, childId, newParentDn );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    public void move( LdapDN oldChildDn, LdapDN newParentDn ) throws NamingException
+    {
+        BigInteger childId = getEntryId( oldChildDn.toString() );
+        move( oldChildDn, childId, newParentDn );
+        
+        if ( isSyncOnWrite )
+        {
+            sync();
+        }
+    }
+
+
+    /**
+     * Moves an entry under a new parent.  The operation causes a shift in the
+     * parent child relationships between the old parent, new parent and the 
+     * child moved.  All other descendant entries under the child never change
+     * their direct parent child relationships.  Hence after the parent child
+     * relationship changes are broken at the old parent and set at the new
+     * parent a modifyDn operation is conducted to handle name changes 
+     * propagating down through the moved child and its descendants.
+     * 
+     * @param oldChildDn the normalized dn of the child to be moved
+     * @param newParentDn the normalized dn of the new parent for the child
+     * @throws NamingException if something goes wrong
+     */
+    private void move( LdapDN oldChildDn, BigInteger childId, LdapDN newParentDn ) throws NamingException
+    {
+        // Get the child and the new parent to be entries and Ids
+        BigInteger newParentId = getEntryId( newParentDn.toString() );
+        BigInteger oldParentId = getParentId( childId );
+
+        /*
+         * All aliases including and below oldChildDn, will be affected by
+         * the move operation with respect to one and subtree indices since 
+         * their relationship to ancestors above oldChildDn will be 
+         * destroyed.  For each alias below and including oldChildDn we will
+         * drop the index tuples mapping ancestor ids above oldChildDn to the
+         * respective target ids of the aliases.
+         */
+        dropMovedAliasIndices( oldChildDn );
+
+        /*
+         * Drop the old parent child relationship and add the new one
+         * Set the new parent id for the child replacing the old parent id
+         */
+        hierarchyIdx.drop( oldParentId, childId );
+        hierarchyIdx.add( newParentId, childId );
+
+        /*
+         * Build the new user provided DN (updn) for the child using the child's
+         * user provided RDN & the new parent's UPDN.  Basically add the child's
+         * UpRdn String to the tail of the new parent's Updn Name.
+         */
+        LdapDN childUpdn = new LdapDN( getEntryUpdn( childId ) );
+        String childRdn = childUpdn.get( childUpdn.size() - 1 );
+        LdapDN newUpdn = new LdapDN( getEntryUpdn( newParentId ) );
+        newUpdn.add( newUpdn.size(), childRdn );
+
+        // Call the modifyDn operation with the new updn
+        modifyDn( childId, newUpdn, true );
+    }
+
+
+    /**
+     * For all aliases including and under the moved base, this method removes
+     * one and subtree alias index tuples for old ancestors above the moved base
+     * that will no longer be ancestors after the move.
+     * 
+     * @param movedBase the base at which the move occured - the moved node
+     * @throws NamingException if system indices fail
+     */
+    private void dropMovedAliasIndices( final LdapDN movedBase ) throws NamingException
+    {
+        // Find all the aliases from movedBase down
+        IndexAssertion isBaseDescendant = new IndexAssertion()
+        {
+            public boolean assertCandidate( IndexRecord rec ) throws NamingException
+            {
+                String dn = getEntryDn( rec.getEntryId() );
+                if ( dn.endsWith( movedBase.toString() ) )
+                {
+                    return true;
+                }
+
+                return false;
+            }
+        };
+
+        BigInteger movedBaseId = getEntryId( movedBase.toString() );
+        if ( aliasIdx.reverseLookup( movedBaseId ) != null )
+        {
+            dropAliasIndices( movedBaseId, movedBase );
+        }
+
+        NamingEnumeration aliases = new IndexAssertionEnumeration( aliasIdx.listIndices( movedBase.toString(), true ),
+            isBaseDescendant );
+        while ( aliases.hasMore() )
+        {
+            IndexRecord entry = ( IndexRecord ) aliases.next();
+            dropAliasIndices( entry.getEntryId(), movedBase );
+        }
+    }
+
+
+    /**
+     * For the alias id all ancestor one and subtree alias tuples are moved 
+     * above the moved base.
+     * 
+     * @param aliasId the id of the alias 
+     * @param movedBase the base where the move occured
+     * @throws NamingException if indices fail
+     */
+    private void dropAliasIndices( BigInteger aliasId, LdapDN movedBase ) throws NamingException
+    {
+        String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
+        BigInteger targetId = getEntryId( targetDn );
+        String aliasDn = getEntryDn( aliasId );
+
+        /*
+         * Start droping index tuples with the first ancestor right above the 
+         * moved base.  This is the first ancestor effected by the move.
+         */
+        LdapDN ancestorDn = ( LdapDN ) movedBase.getPrefix( 1 );
+        BigInteger ancestorId = getEntryId( ancestorDn.toString() );
+
+        /*
+         * We cannot just drop all tuples in the one level and subtree indices
+         * linking baseIds to the targetId.  If more than one alias refers to
+         * the target then droping all tuples with a value of targetId would
+         * make all other aliases to the target inconsistent.
+         * 
+         * We need to walk up the path of alias ancestors right above the moved 
+         * base until we reach the upSuffix, deleting each ( ancestorId,
+         * targetId ) tuple in the subtree scope alias.  We only need to do 
+         * this for the direct parent of the alias on the one level subtree if
+         * the moved base is the alias.
+         */
+        if ( aliasDn.equals( movedBase.toString() ) )
+        {
+            oneAliasIdx.drop( ancestorId, targetId );
+        }
+
+        subAliasIdx.drop( ancestorId, targetId );
+
+        while ( !ancestorDn.equals( upSuffix ) )
+        {
+            ancestorDn = ( LdapDN ) ancestorDn.getPrefix( 1 );
+            ancestorId = getEntryId( ancestorDn.toString() );
+
+            subAliasIdx.drop( ancestorId, targetId );
+        }
+    }
+
+
+    public void bind( LdapDN bindDn, byte[] credentials, List mechanisms, String saslAuthId ) throws NamingException
+    {
+        // does nothing
+        throw new LdapAuthenticationNotSupportedException(
+            "Bind requests only tunnel down into partitions if there are no authenticators to handle the mechanism.\n"
+                + "Check to see if you have correctly configured authenticators for the server.",
+            ResultCodeEnum.AUTH_METHOD_NOT_SUPPORTED );
+    }
+
+
+    public void unbind( LdapDN bindDn ) throws NamingException
+    {
+        // does nothing
+    }
+}

Added: directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStoreConfiguration.java
URL: http://svn.apache.org/viewvc/directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStoreConfiguration.java?view=auto&rev=486848
==============================================================================
--- directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStoreConfiguration.java (added)
+++ directory/branches/trunks/schema2/apacheds/core/src/main/java/org/apache/directory/server/core/partition/impl/btree/jdbm/JdbmStoreConfiguration.java Wed Dec 13 13:40:21 2006
@@ -0,0 +1,163 @@
+/*
+ *  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.File;
+import java.util.Set;
+
+import javax.naming.directory.Attributes;
+
+import org.apache.directory.server.core.schema.AttributeTypeRegistry;
+import org.apache.directory.server.core.schema.OidRegistry;
+
+
+public class JdbmStoreConfiguration
+{
+    private AttributeTypeRegistry attributeTypeRegistry;
+    private OidRegistry oidRegistry;
+    private File workingDirectory;
+    private Set indexedAttributes;
+    private Attributes contextEntry;
+    private String suffixDn;
+    private boolean isSyncOnWrite;
+    private boolean enableOptimizer;
+    private int cacheSize = 100;
+    private String name;
+    
+    
+    public void setAttributeTypeRegistry( AttributeTypeRegistry attributeTypeRegistry )
+    {
+        this.attributeTypeRegistry = attributeTypeRegistry;
+    }
+    
+    
+    public AttributeTypeRegistry getAttributeTypeRegistry()
+    {
+        return attributeTypeRegistry;
+    }
+    
+    
+    public void setOidRegistry( OidRegistry oidRegistry )
+    {
+        this.oidRegistry = oidRegistry;
+    }
+    
+    
+    public OidRegistry getOidRegistry()
+    {
+        return oidRegistry;
+    }
+    
+    
+    public void setWorkingDirectory( File workingDirectory )
+    {
+        this.workingDirectory = workingDirectory;
+    }
+    
+    
+    public File getWorkingDirectory()
+    {
+        return workingDirectory;
+    }
+    
+    
+    public void setIndexedAttributes( Set indexedAttributes )
+    {
+        this.indexedAttributes = indexedAttributes;
+    }
+    
+    
+    public Set getIndexedAttributes()
+    {
+        return indexedAttributes;
+    }
+    
+    
+    public void setContextEntry( Attributes contextEntry )
+    {
+        this.contextEntry = contextEntry;
+    }
+    
+    
+    public Attributes getContextEntry()
+    {
+        return contextEntry;
+    }
+    
+    
+    public void setSuffixDn( String suffixDn )
+    {
+        this.suffixDn = suffixDn;
+    }
+    
+    
+    public String getSuffixDn()
+    {
+        return suffixDn;
+    }
+
+
+    public void setSyncOnWrite( boolean isSyncOnWrite )
+    {
+        this.isSyncOnWrite = isSyncOnWrite;
+    }
+
+
+    public boolean isSyncOnWrite()
+    {
+        return isSyncOnWrite;
+    }
+
+
+    public void setEnableOptimizer( boolean enableOptimizer )
+    {
+        this.enableOptimizer = enableOptimizer;
+    }
+
+
+    public boolean isEnableOptimizer()
+    {
+        return enableOptimizer;
+    }
+
+
+    public void setCacheSize( int cacheSize )
+    {
+        this.cacheSize = cacheSize;
+    }
+
+
+    public int getCacheSize()
+    {
+        return cacheSize;
+    }
+
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+}



Mime
View raw message