View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.partition.impl.btree.jdbm;
18  
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.math.BigInteger;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import javax.naming.Name;
29  import javax.naming.NamingEnumeration;
30  import javax.naming.NamingException;
31  import javax.naming.directory.Attribute;
32  import javax.naming.directory.Attributes;
33  import javax.naming.directory.DirContext;
34  import javax.naming.directory.ModificationItem;
35  
36  import jdbm.RecordManager;
37  import jdbm.helper.MRU;
38  import jdbm.recman.BaseRecordManager;
39  import jdbm.recman.CacheRecordManager;
40  
41  import org.apache.ldap.common.exception.LdapNameNotFoundException;
42  import org.apache.ldap.common.exception.LdapSchemaViolationException;
43  import org.apache.ldap.common.message.LockableAttributeImpl;
44  import org.apache.ldap.common.message.LockableAttributesImpl;
45  import org.apache.ldap.common.message.ResultCodeEnum;
46  import org.apache.ldap.common.name.LdapName;
47  import org.apache.ldap.common.schema.AttributeType;
48  import org.apache.ldap.common.schema.Normalizer;
49  import org.apache.ldap.common.util.NamespaceTools;
50  import org.apache.ldap.server.DirectoryServiceConfiguration;
51  import org.apache.ldap.server.configuration.DirectoryPartitionConfiguration;
52  import org.apache.ldap.server.partition.DirectoryPartition;
53  import org.apache.ldap.server.partition.impl.btree.BTreeDirectoryPartition;
54  import org.apache.ldap.server.partition.impl.btree.Index;
55  import org.apache.ldap.server.partition.impl.btree.IndexAssertion;
56  import org.apache.ldap.server.partition.impl.btree.IndexAssertionEnumeration;
57  import org.apache.ldap.server.partition.impl.btree.IndexNotFoundException;
58  import org.apache.ldap.server.partition.impl.btree.IndexRecord;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  
62  
63  /***
64   * A {@link DirectoryPartition} that stores entries in
65   * <a href="http://jdbm.sourceforge.net/">JDBM</a> database.
66   *
67   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
68   * @version $Rev: 307234 $
69   */
70  public class JdbmDirectoryPartition extends BTreeDirectoryPartition
71  {
72      private static final Logger log = LoggerFactory.getLogger( JdbmDirectoryPartition.class );
73  
74      /*** the JDBM record manager used by this database */
75      private RecordManager recMan;
76      /*** the user provided suffix of this backend database */
77      private Name upSuffix;
78      /*** the normalized suffix of this backend database */
79      private Name normSuffix;
80      /*** the working directory to use for files */
81      private File workingDirectory;
82      /*** the master table storing entries by primary key */
83      private JdbmMasterTable master;
84      /*** a map of attribute names to user indices */
85      private Map indices;
86      /*** a map of index names to system indices */
87      private Map sysIndices;
88  
89      /*** true if open */
90      private boolean initialized;
91  
92      /*** the normalized distinguished name index */
93      private Index ndnIdx;
94      /*** the user provided distinguished name index */
95      private Index updnIdx;
96      /*** the attribute existance index */
97      private Index existanceIdx;
98      /*** the parent child relationship index */
99      private Index hierarchyIdx;
100     /*** the one level scope alias index */
101     private Index oneAliasIdx;
102     /*** the subtree scope alias index */
103     private Index subAliasIdx;
104     /*** a system index on aliasedObjectName attribute */
105     private Index aliasIdx;
106 
107 
108     // ------------------------------------------------------------------------
109     // C O N S T R U C T O R S
110     // ------------------------------------------------------------------------
111 
112 
113     /***
114      * Creates a store based on JDBM B+Trees.
115      */
116     public JdbmDirectoryPartition()
117     {
118     }
119 
120     public synchronized void init( DirectoryServiceConfiguration factoryCfg, DirectoryPartitionConfiguration cfg ) throws NamingException
121     {
122         this.upSuffix = new LdapName( cfg.getSuffix() );
123         this.normSuffix = cfg.getNormalizedSuffix( factoryCfg.getGlobalRegistries().getMatchingRuleRegistry() );
124 
125         File workingDirectory = new File(
126                 factoryCfg.getStartupConfiguration().getWorkingDirectory().getPath() +
127                 File.separator + cfg.getName() );
128         
129         workingDirectory.mkdirs();
130         
131         this.workingDirectory = workingDirectory;
132             
133         try 
134         {
135             String path = workingDirectory.getPath() + File.separator + "master";
136             BaseRecordManager base = new BaseRecordManager( path );
137             base.disableTransactions();
138             recMan = new CacheRecordManager( base, new MRU( 1000 ) );
139         } 
140         catch ( IOException e )
141         {
142             NamingException ne = new NamingException( 
143                 "Could not initialize RecordManager" );
144             ne.setRootCause( e );
145             throw ne;
146         }
147 
148         master = new JdbmMasterTable( recMan );
149         indices = new HashMap();
150         sysIndices = new HashMap();
151         
152         super.init( factoryCfg, cfg );
153         initialized = true;
154     }
155     
156     
157     public synchronized void destroy()
158     {
159         if ( !initialized )
160         {
161             return;
162         }
163 
164         ArrayList array = new ArrayList();
165         array.addAll( indices.values() );
166         
167         if ( null != ndnIdx )
168         {
169             array.add( ndnIdx );
170         }
171         
172         if ( null != updnIdx )
173         {
174             array.add( updnIdx );
175         }
176 
177         if ( null != aliasIdx )
178         {
179             array.add( aliasIdx );
180         }
181 
182         if ( null != oneAliasIdx )
183         {
184             array.add( oneAliasIdx );
185         }
186 
187         if ( null != subAliasIdx )
188         {
189             array.add( subAliasIdx );
190         }
191 
192         if ( null != hierarchyIdx )
193         {
194             array.add( hierarchyIdx );
195         }
196 
197         if ( null != existanceIdx )
198         {
199             array.add( existanceIdx );
200         }
201         
202         Iterator list = array.iterator();
203         
204         while ( list.hasNext() ) 
205         {
206             Index index = ( Index ) list.next();
207 
208             try 
209             {
210                index.close();
211             } 
212             catch ( Throwable t ) 
213             {
214                 log.error( "Failed to close an index.", t );
215             }
216         }
217 
218         try 
219         {
220             master.close();
221         } 
222         catch ( Throwable t ) 
223         {
224             log.error( "Failed to close the master.", t );
225         }
226 
227         try 
228         {
229             recMan.close();
230         } 
231         catch ( Throwable t ) 
232         {
233             log.error( "Failed to close the record manager", t );
234         }
235 
236         initialized = false;
237     }
238 
239 
240     public boolean isInitialized()
241     {
242         return initialized;
243     }
244 
245 
246     public synchronized void sync() throws NamingException
247     {
248         if( !initialized )
249         {
250             return;
251         }
252 
253         ArrayList array = new ArrayList();
254         array.addAll( indices.values() );
255         array.add( ndnIdx );
256         array.add( updnIdx );
257         array.add( aliasIdx );
258         array.add( oneAliasIdx );
259         array.add( subAliasIdx );
260         array.add( hierarchyIdx );
261         array.add( existanceIdx );
262         
263         Iterator list = array.iterator();
264 
265         // Sync all user defined indices
266         while ( list.hasNext() ) 
267         {
268             Index idx = ( Index ) list.next();
269 
270             idx.sync();
271         }
272         
273         master.sync();
274 
275         try 
276         {
277             recMan.commit();
278         }
279         catch ( Throwable t ) 
280         {
281             throw ( NamingException ) new NamingException(
282                     "Failed to commit changes to the record manager." ).initCause( t );
283         }        
284     }
285 
286 
287     // ------------------------------------------------------------------------
288     // I N D E X   M E T H O D S
289     // ------------------------------------------------------------------------
290 
291 
292     public void addIndexOn( AttributeType spec ) throws NamingException
293     {
294         Index idx = new JdbmIndex( spec, workingDirectory );
295         indices.put( spec.getName().toLowerCase(), idx );
296     }
297 
298     
299     public Index getExistanceIndex() 
300     {
301         return existanceIdx;
302     }
303     
304 
305     public void setExistanceIndexOn( AttributeType attrType ) throws NamingException
306     {
307         if ( existanceIdx != null )
308         {
309             NamingException e = new NamingException( "Index already set!" );
310             throw e;
311         }
312 
313         existanceIdx = new JdbmIndex( attrType, workingDirectory );
314         sysIndices.put( attrType.getName().toLowerCase(), existanceIdx );
315     }
316 
317     
318     public Index getHierarchyIndex() 
319     {
320         return hierarchyIdx;
321     }
322     
323 
324     public void setHierarchyIndexOn( AttributeType attrType ) throws NamingException
325     {
326         if ( hierarchyIdx != null )
327         {
328             NamingException e = new NamingException( "Index already set!" );
329             throw e;
330         }
331 
332         hierarchyIdx = new JdbmIndex( attrType, workingDirectory );
333         sysIndices.put( attrType.getName().toLowerCase(), hierarchyIdx );
334     }
335 
336     
337     public Index getAliasIndex()
338     {
339         return aliasIdx;    
340     }
341 
342 
343     public void setAliasIndexOn( AttributeType attrType ) throws NamingException
344     {
345         if ( aliasIdx != null )
346         {
347             NamingException e = new NamingException( "Index already set!" );
348             throw e;
349         }
350 
351         aliasIdx = new JdbmIndex( attrType, workingDirectory );
352         sysIndices.put( attrType.getName().toLowerCase(), aliasIdx );
353     }    
354     
355 
356     public Index getOneAliasIndex()
357     {
358         return oneAliasIdx;
359     }
360 
361 
362     public void setOneAliasIndexOn( AttributeType attrType ) throws NamingException
363     {
364         if ( oneAliasIdx != null )
365         {
366             NamingException e = new NamingException( "Index already set!" );
367             throw e;
368         }
369 
370         oneAliasIdx = new JdbmIndex( attrType, workingDirectory );
371         sysIndices.put( attrType.getName().toLowerCase(), oneAliasIdx );
372     }
373 
374 
375     public Index getSubAliasIndex()
376     {
377         return subAliasIdx;
378     }
379 
380 
381     public void setSubAliasIndexOn( AttributeType attrType ) throws NamingException
382     {
383         if ( subAliasIdx != null )
384         {
385             NamingException e = new NamingException( "Index already set!" );
386             throw e;
387         }
388 
389         subAliasIdx = new JdbmIndex( attrType, workingDirectory );
390         sysIndices.put( attrType.getName().toLowerCase(), subAliasIdx );
391     }
392 
393 
394     public Index getUpdnIndex()
395     {
396         return updnIdx;
397     }
398 
399 
400     public void setUpdnIndexOn( AttributeType attrType ) throws NamingException
401     {
402         if ( updnIdx != null )
403         {
404             NamingException e = new NamingException( "Index already set!" );
405             throw e;
406         }
407 
408         updnIdx = new JdbmIndex( attrType, workingDirectory );
409         sysIndices.put( attrType.getName().toLowerCase(), updnIdx );
410     }
411 
412     
413     public Index getNdnIndex() 
414     {
415         return ndnIdx;
416     }
417     
418 
419     public void setNdnIndexOn( AttributeType attrType ) throws NamingException
420     {
421         if ( ndnIdx != null )
422         {
423             NamingException e = new NamingException( "Index already set!" );
424             throw e;
425         }
426 
427         ndnIdx = new JdbmIndex( attrType, workingDirectory );
428         sysIndices.put( attrType.getName().toLowerCase(), ndnIdx );
429     }
430 
431     
432     public Iterator getUserIndices()
433     {
434         return indices.keySet().iterator();
435     }
436 
437 
438     public Iterator getSystemIndices()
439     {
440         return sysIndices.keySet().iterator();
441     }
442 
443 
444     public boolean hasUserIndexOn( String attribute )
445     {
446         return indices.containsKey( attribute ) ||
447             indices.containsKey( attribute.toLowerCase() );
448     }
449 
450 
451     public boolean hasSystemIndexOn( String attribute )
452     {
453         return sysIndices.containsKey( attribute ) ||
454             sysIndices.containsKey( attribute.toLowerCase() );
455     }
456 
457 
458     /***
459      * @todo replace lookups to use the OID instead of the name.  Also note
460      * that the OID registry can be used to go between names and oids.
461      * 
462      * @see org.apache.ldap.server.partition.impl.btree.BTreeDirectoryPartition#getUserIndex(String)
463      */
464     public Index getUserIndex( String attribute ) throws IndexNotFoundException
465     {
466         String lowerCased = attribute.toLowerCase();
467 
468         if ( indices.containsKey( attribute ) ) 
469         {
470             return ( Index ) indices.get( attribute );
471         } 
472         else if ( indices.containsKey( lowerCased ) ) 
473         {
474             return ( Index ) indices.get( lowerCased );
475         } 
476         else 
477         {
478             throw new IndexNotFoundException( "An index on attribute " +
479                 attribute + " does not exist!" );
480         }
481     }
482     
483     
484     /***
485      * @todo replace lookups to use the OID instead of the name.  Also note
486      * that the OID registry can be used to go between names and oids.
487      * 
488      * @see BTreeDirectoryPartition#getEntryId(String)
489      */
490     public Index getSystemIndex( String indexName ) throws IndexNotFoundException
491     {
492         String lowerCased = indexName.toLowerCase();
493 
494         if ( sysIndices.containsKey( indexName ) ) 
495         {
496             return ( Index ) sysIndices.get( indexName );
497         } 
498         else if ( sysIndices.containsKey( lowerCased ) ) 
499         {
500             return ( Index ) sysIndices.get( lowerCased );
501         } 
502         else 
503         {
504             throw new IndexNotFoundException( "A system index by the name of " +
505                 indexName + " does not exist!" );
506         }
507     }
508 
509 
510     public BigInteger getEntryId( String dn ) throws NamingException
511     {
512         return ndnIdx.forwardLookup( dn );
513     }
514 
515 
516     public String getEntryDn( BigInteger id ) throws NamingException
517     {
518         return ( String ) ndnIdx.reverseLookup( id );
519     }
520 
521 
522     public BigInteger getParentId( String dn ) throws NamingException
523     {
524         BigInteger childId = ndnIdx.forwardLookup( dn );
525         return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
526     }
527 
528 
529     public BigInteger getParentId( BigInteger childId ) throws NamingException
530     {
531         return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
532     }
533     
534     
535     public String getEntryUpdn( BigInteger id ) throws NamingException
536     {
537         return ( String ) updnIdx.reverseLookup( id );
538     }
539 
540 
541     public String getEntryUpdn( String dn ) throws NamingException
542     {
543         BigInteger id = ndnIdx.forwardLookup( dn );
544         return ( String ) updnIdx.reverseLookup( id );
545     }
546 
547 
548     public int count() throws NamingException
549     {
550         return master.count();
551     }
552     
553     
554     /***
555      * Removes the index entries for an alias before the entry is deleted from
556      * the master table.
557      * 
558      * @todo Optimize this by walking the hierarchy index instead of the name 
559      * @param aliasId the id of the alias entry in the master table
560      * @throws NamingException if we cannot delete the indices
561      */
562     private void dropAliasIndices( BigInteger aliasId ) throws NamingException
563     {
564         String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
565         BigInteger targetId = getEntryId( targetDn );
566         String aliasDn = getEntryDn( aliasId );
567         Name ancestorDn = new LdapName( aliasDn ).getPrefix( 1 );
568         BigInteger ancestorId = getEntryId( ancestorDn.toString() );
569         
570         /*
571          * We cannot just drop all tuples in the one level and subtree indices
572          * linking baseIds to the targetId.  If more than one alias refers to
573          * the target then droping all tuples with a value of targetId would
574          * make all other aliases to the target inconsistent.
575          * 
576          * We need to walk up the path of alias ancestors until we reach the 
577          * upSuffix, deleting each ( ancestorId, targetId ) tuple in the
578          * subtree scope alias.  We only need to do this for the direct parent
579          * of the alias on the one level subtree.
580          */
581         oneAliasIdx.drop( ancestorId, targetId );
582         subAliasIdx.drop( ancestorId, targetId );
583         
584         while ( ! ancestorDn.equals( upSuffix ) )
585         {
586             ancestorDn = ancestorDn.getPrefix( 1 );
587             ancestorId = getEntryId( ancestorDn.toString() );
588             
589             subAliasIdx.drop( ancestorId, targetId );
590         }    
591 
592         // Drops all alias tuples pointing to the id of the alias to be deleted
593         aliasIdx.drop( aliasId );
594     }
595     
596     
597     /***
598      * Adds indices for an aliasEntry to be added to the database while checking
599      * for constrained alias constructs like alias cycles and chaining.
600      * 
601      * @param aliasDn normalized distinguished name for the alias entry
602      * @param aliasTarget the user provided aliased entry dn as a string
603      * @param aliasId the id of alias entry to add
604      * @throws NamingException if index addition fails, of the alias is not 
605      * allowed due to chaining or cycle formation.
606      */
607     private void addAliasIndices( BigInteger aliasId, Name aliasDn, 
608         String aliasTarget ) throws NamingException
609     {
610         Name targetDn = null;            // Name value of aliasedObjectName
611         BigInteger targetId = null;      // Id of the aliasedObjectName
612         Normalizer normalizer = null;    // Temporary handle for Dn's
613         Name ancestorDn = null;          // Name of an alias entry relative
614         BigInteger ancestorId = null;    // Id of an alias entry relative
615 
616         // Access aliasedObjectName, normalize it and generate the Name 
617         normalizer = oneAliasIdx.getAttribute().getEquality().getNormalizer();
618         targetDn = new LdapName( ( String ) normalizer.normalize( aliasTarget ) );
619            
620         /*
621          * Check For Cycles
622          * 
623          * Before wasting time to lookup more values we check using the target
624          * dn to see if we have the possible formation of an alias cycle.  This
625          * happens when the alias refers back to a target that is also a 
626          * relative of the alias entry.  For detection we test if the aliased
627          * entry Dn starts with the target Dn.  If it does then we know the 
628          * aliased target is a relative and we have a perspecitive cycle.
629          */ 
630         if ( aliasDn.startsWith( targetDn ) )
631         {
632             if ( aliasDn.equals( targetDn ) )
633             {
634                 throw new NamingException( "[36] aliasDereferencingProblem - " 
635                     + "attempt to create alias to itself." );
636             }
637             
638             throw new NamingException( "[36] aliasDereferencingProblem - " 
639                 + "attempt to create alias with cycle to relative " 
640                 + aliasTarget + " not allowed from descendent alias " 
641                 + aliasDn );
642         }
643             
644         /*
645          * Check For Aliases External To Naming Context
646          * 
647          * id may be null but the alias may be to a valid entry in 
648          * another namingContext.  Such aliases are not allowed and we
649          * need to point it out to the user instead of saying the target
650          * does not exist when it potentially could outside of this upSuffix.
651          */
652         if ( ! targetDn.startsWith( upSuffix ) )
653         {
654             // Complain specifically about aliases to outside naming contexts
655             throw new NamingException( "[36] aliasDereferencingProblem -"
656                 + " the alias points to an entry outside of the " + upSuffix
657                 + " namingContext to an object whose existance cannot be"
658                 + " determined." );
659         }
660 
661         // L O O K U P   T A R G E T   I D
662         targetId = ndnIdx.forwardLookup( targetDn.toString() );
663 
664         /*
665          * Check For Target Existance
666          * 
667          * We do not allow the creation of inconsistant aliases.  Aliases should
668          * not be broken links.  If the target does not exist we start screaming
669          */
670         if ( null == targetId )
671         {
672             // Complain about target not existing
673             throw new NamingException( "[33] aliasProblem - " 
674                 + "the alias when dereferenced would not name a known object "
675                 + "the aliasedObjectName must be set to a valid existing "
676                 + "entry." );
677         }
678         
679         /*
680          * Detect Direct Alias Chain Creation
681          * 
682          * Rather than resusitate the target to test if it is an alias and fail
683          * due to chaing creation we use the alias index to determine if the
684          * target is an alias.  Hence if the alias we are about to create points
685          * to another alias as its target in the aliasedObjectName attribute, 
686          * then we have a situation where an alias chain is being created.  
687          * Alias chaining is not allowed so we throw and exception. 
688          */
689         if ( null != aliasIdx.reverseLookup( targetId ) )
690         {
691             // Complain about illegal alias chain
692             throw new NamingException( "[36] aliasDereferencingProblem -" 
693                 + " the alias points to another alias.  Alias chaining is" 
694                 + " not supported by this backend." );
695         }
696         
697         // Add the alias to the simple alias index
698         aliasIdx.add( aliasTarget, aliasId );
699         
700         /*
701          * Handle One Level Scope Alias Index
702          * 
703          * The first relative is special with respect to the one level alias
704          * index.  If the target is not a sibling of the alias then we add the
705          * index entry maping the parent's id to the aliased target id.
706          */
707         ancestorDn = aliasDn.getPrefix( 1 );
708         ancestorId = getEntryId( ancestorDn.toString() );
709         
710         if ( ! NamespaceTools.isSibling( targetDn, aliasDn ) )
711         {
712             oneAliasIdx.add( ancestorId, targetId );
713         }
714 
715         /*
716          * Handle Sub Level Scope Alias Index
717          * 
718          * Walk the list of relatives from the parents up to the upSuffix, testing
719          * to see if the alias' target is a descendant of the relative.  If the
720          * alias target is not a descentant of the relative it extends the scope
721          * and is added to the sub tree scope alias index.  The upSuffix node is
722          * ignored since everything is under its scope.  The first loop 
723          * iteration shall handle the parents.
724          */
725         while ( ! ancestorDn.equals( upSuffix ) && null != ancestorId )
726         {
727             if ( ! NamespaceTools.isDescendant( ancestorDn, targetDn ) )
728             {
729                 subAliasIdx.add( ancestorId, targetId );
730             }
731             
732             ancestorDn = ancestorDn.getPrefix( 1 );
733             ancestorId = getEntryId( ancestorDn.toString() );
734         }        
735     }
736 
737 
738     public void add( String updn, Name dn, Attributes entry ) throws NamingException
739     {
740         BigInteger id;
741         BigInteger parentId = null;
742 
743         id = master.getNextId();
744 
745         //
746         // Suffix entry cannot have a parent since it is the root so it is 
747         // capped off using the zero value which no entry can have since 
748         // entry sequences start at 1.
749         //
750         
751         if ( dn.equals( normSuffix ) )
752         {
753             parentId = BigInteger.ZERO;
754         }
755         else 
756         {
757             parentId = getEntryId( dn.getPrefix( 1 ).toString() );
758         }
759 
760         // don't keep going if we cannot find the parent Id
761         if ( parentId == null )
762         {
763             throw new LdapNameNotFoundException( "Id for parent '" + dn.getPrefix( 1 ).toString() + "' not found!" );
764         }
765 
766         Attribute objectClass = entry.get( "objectClass" );
767 
768         if ( objectClass == null )
769         {
770             String msg = "Entry " + updn + " contains no objectClass attribute: " + entry;
771 
772             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSVIOLATION );
773         }
774 
775         // Start adding the system indices
776         // Why bother doing a lookup if this is not an alias.
777 
778         if ( entry.get( "objectClass" ).contains( DirectoryPartition.ALIAS_OBJECT ) )
779         {
780             addAliasIndices( id, dn, ( String ) entry.get( DirectoryPartition.ALIAS_ATTRIBUTE ).get() );
781         }
782         
783         ndnIdx.add( dn.toString(), id );
784         updnIdx.add( updn, id );
785         hierarchyIdx.add( parentId, id );
786         
787         // Now work on the user defined indices
788         NamingEnumeration list = entry.getIDs();
789         while ( list.hasMore() ) 
790         {
791             String attribute = ( String ) list.next();
792             
793             if ( hasUserIndexOn( attribute ) ) 
794             {
795                 Index idx = getUserIndex( attribute );
796                 NamingEnumeration values = entry.get( attribute ).getAll();
797                 
798                 while ( values.hasMore() ) 
799                 {
800                     idx.add( values.next(), id );
801                 }
802 
803                 // Adds only those attributes that are indexed
804                 existanceIdx.add( attribute.toLowerCase(), id );
805             }
806         }
807 
808         master.put( entry, id );
809     }
810 
811 
812     public Attributes lookup( BigInteger id ) throws NamingException
813     {
814         return master.get( id );
815     }
816 
817 
818     public void delete( BigInteger id ) throws  NamingException
819     {
820         Attributes entry = lookup( id );
821         BigInteger parentId = getParentId( id );
822         NamingEnumeration attrs = entry.getIDs();
823         
824         if ( entry.get( "objectClass" ).contains( DirectoryPartition.ALIAS_OBJECT ) )
825         {
826             dropAliasIndices( id );
827         }
828 
829         ndnIdx.drop( id );
830         updnIdx.drop( id );
831         hierarchyIdx.drop( id );
832         
833         // Remove parent's reference to entry only if entry is not the upSuffix
834         if ( ! parentId.equals( BigInteger.ZERO ) )
835         {
836             hierarchyIdx.drop( parentId, id );
837         }
838         
839         while ( attrs.hasMore() ) 
840         {
841             String attr = ( ( String ) attrs.next() );
842 
843             if ( hasUserIndexOn( attr ) )
844             {
845                 Index index = getUserIndex( attr );
846                 NamingEnumeration values = entry.get( attr ).getAll();
847                 
848                 while ( values.hasMore() )
849                 {
850                     index.drop( values.next(), id );
851                 }
852 
853                 existanceIdx.drop( attr.toLowerCase(), id );
854             }
855         }
856 
857         master.delete( id );
858     }
859 
860 
861     public NamingEnumeration list( BigInteger id ) throws  NamingException
862     {
863         return hierarchyIdx.listIndices( id );
864     }
865 
866 
867     public int getChildCount( BigInteger id ) throws NamingException
868     {
869         return hierarchyIdx.count( id );
870     }
871 
872 
873     public Name getSuffix( boolean normalized )
874     {
875         if ( normalized )
876         {
877             return normSuffix;
878         }
879         
880         return upSuffix;
881     }
882 
883 
884     public Attributes getSuffixEntry() throws NamingException
885     {
886         BigInteger id = getEntryId( upSuffix.toString() );
887 
888         if ( null == id )
889         {
890             return null;
891         }
892 
893         return lookup( id );
894     }
895 
896 
897     public void setProperty( String propertyName, String propertyValue )
898         throws NamingException
899     {
900         master.setProperty( propertyName, propertyValue );
901     }
902 
903 
904     public String getProperty( String propertyName ) throws NamingException
905     {
906         return master.getProperty( propertyName );
907     }
908 
909 
910     public Attributes getIndices( BigInteger id ) throws  NamingException
911     {
912         Attributes attributes = new LockableAttributesImpl();
913 
914         // Get the distinguishedName to id mapping
915         attributes.put( "_nDn", getEntryDn( id ) );
916         attributes.put( "_upDn", getEntryUpdn( id ) );
917         attributes.put( "_parent", getParentId( id ) );
918 
919         // Get all standard index attribute to value mappings
920         Iterator idxList = this.indices.values().iterator();
921         while ( idxList.hasNext() )
922         {
923             Index index = ( Index ) idxList.next();
924             NamingEnumeration list = index.listReverseIndices( id );
925             while ( list.hasMore() ) 
926             {
927                 IndexRecord rec = ( IndexRecord ) list.next();
928                 Object val = rec.getIndexKey();
929                 String attrId = index.getAttribute().getName();
930                 Attribute attr = attributes.get( attrId );
931                 if ( attr == null)
932                 {
933                     attr = new LockableAttributeImpl( attrId );
934                 }
935                 attr.add( val );
936                 attributes.put( attr );
937             }
938         }
939 
940         // Get all existance mappings for this id creating a special key
941         // that looks like so 'existance[attribute]' and the value is set to id
942         NamingEnumeration list = existanceIdx.listReverseIndices( id );
943         StringBuffer val = new StringBuffer();
944         while ( list.hasMore() ) 
945         {
946             IndexRecord rec = ( IndexRecord ) list.next();
947             val.append( "_existance[" ); 
948             val.append( rec.getIndexKey() );
949             val.append( "]" );
950 
951             String valStr = val.toString();
952             Attribute attr = attributes.get( valStr );
953             if ( attr == null )
954             {
955                 attr = new LockableAttributeImpl( valStr );
956             }
957             attr.add( rec.getEntryId() );
958             attributes.put( attr );
959             val.setLength( 0 );
960         }
961 
962         // Get all parent child mappings for this entry as the parent using the
963         // key 'child' with many entries following it.
964         list = hierarchyIdx.listIndices( id );
965         Attribute childAttr = new LockableAttributeImpl( "_child" );
966         attributes.put( childAttr );
967         while ( list.hasMore() )
968         {
969             IndexRecord rec = ( IndexRecord ) list.next();
970             childAttr.add( rec.getEntryId() );
971         }
972 
973         return attributes;
974     }
975 
976 
977     /***
978      * Adds a set of attribute values while affecting the appropriate indices. 
979      * The entry is not persisted: it is only changed in anticipation for a put 
980      * into the master table.
981      *
982      * @param id the primary key of the entry
983      * @param entry the entry to alter
984      * @param mods the attribute and values to add 
985      * @throws NamingException if index alteration or attribute addition
986      * fails.
987      */
988     private void add( BigInteger id, Attributes entry, Attribute mods )
989         throws NamingException 
990     {
991         if ( hasUserIndexOn( mods.getID() ) )
992         {
993             Index idx = getUserIndex( mods.getID() );
994             idx.add( mods, id );
995 
996             // If the attr didn't exist for this id add it to existance index
997             if ( ! existanceIdx.hasValue( mods.getID().toLowerCase(), id ) )
998             {
999                 existanceIdx.add( mods.getID().toLowerCase(), id );
1000             }
1001         }
1002 
1003         // add all the values in mods to the same attribute in the entry
1004         Attribute entryAttrToAddTo = entry.get( mods.getID() );
1005 
1006         if ( entryAttrToAddTo == null )
1007         {
1008             entryAttrToAddTo = new LockableAttributeImpl( mods.getID() );
1009             entry.put( entryAttrToAddTo );
1010         }
1011 
1012         for ( int ii = 0; ii < mods.size(); ii++ )
1013         {
1014             entryAttrToAddTo.add( mods.get( ii ) );
1015         }
1016 
1017         if ( mods.getID().equals( DirectoryPartition.ALIAS_ATTRIBUTE ) )
1018         {
1019             String ndnStr = ( String ) ndnIdx.reverseLookup( id );
1020             addAliasIndices( id, new LdapName( ndnStr ), 
1021                 ( String ) mods.get() );
1022         }
1023     }
1024     
1025     
1026     /***
1027      * Completely removes the set of values for an attribute having the values 
1028      * supplied while affecting the appropriate indices.  The entry is not 
1029      * persisted: it is only changed in anticipation for a put into the master 
1030      * table.  Note that an empty attribute w/o values will remove all the 
1031      * values within the entry where as an attribute w/ values will remove those
1032      * attribute values it contains.
1033      *
1034      * @param id the primary key of the entry
1035      * @param entry the entry to alter
1036      * @param mods the attribute and its values to delete
1037      * @throws NamingException if index alteration or attribute modification 
1038      * fails.
1039      */
1040     private void remove( BigInteger id, Attributes entry, Attribute mods )
1041         throws NamingException
1042     {
1043         if ( hasUserIndexOn( mods.getID() ) )
1044         {
1045             Index idx = getUserIndex( mods.getID() );
1046             idx.drop( mods, id );
1047     
1048             /* 
1049              * If no attribute values exist for this entryId in the index then
1050              * we remove the existance index entry for the removed attribute.
1051              */
1052             if ( null == idx.reverseLookup( id ) )
1053             {
1054                 existanceIdx.drop( mods.getID(), id );
1055             }
1056         }
1057 
1058         /*
1059          * If there are no attribute values in the modifications then this 
1060          * implies the compelete removal of the attribute from the entry. Else
1061          * we remove individual attribute values from the entry in mods one 
1062          * at a time.
1063          */
1064         if ( mods.size() == 0 )
1065         {
1066             entry.remove( mods.getID() );
1067         }
1068         else
1069         {
1070             Attribute entryAttr = entry.get( mods.getID() );
1071             NamingEnumeration values = mods.getAll();
1072             while ( values.hasMore() ) 
1073             {
1074                 entryAttr.remove( values.next() );
1075             }
1076         }
1077 
1078         // Aliases->single valued comp/partial attr removal is not relevant here
1079         if ( mods.getID().equals( DirectoryPartition.ALIAS_ATTRIBUTE ) )
1080         {
1081             dropAliasIndices( id );
1082         }
1083     }
1084 
1085 
1086     /***
1087      * Completely replaces the existing set of values for an attribute with the
1088      * modified values supplied affecting the appropriate indices.  The entry
1089      * is not persisted: it is only changed in anticipation for a put into the
1090      * master table.
1091      *
1092      * @param id the primary key of the entry
1093      * @param entry the entry to alter
1094      * @param mods the replacement attribute and values
1095      * @throws NamingException if index alteration or attribute modification 
1096      * fails.
1097      */
1098     private void replace( BigInteger id, Attributes entry, Attribute mods )
1099         throws NamingException
1100     {
1101         if ( hasUserIndexOn( mods.getID() ) )
1102         {
1103             Index idx = getUserIndex( mods.getID() );
1104             
1105             // Drop all existing attribute value index entries and add new ones
1106             idx.drop( id );
1107             idx.add( mods, id );
1108     
1109             /* 
1110              * If no attribute values exist for this entryId in the index then
1111              * we remove the existance index entry for the removed attribute.
1112              */
1113             if ( null == idx.reverseLookup( id ) )
1114             {
1115                 existanceIdx.drop( mods.getID(), id );
1116             }
1117         }
1118 
1119         if ( mods.getID().equals( DirectoryPartition.ALIAS_ATTRIBUTE ) )
1120         {
1121             dropAliasIndices( id );
1122         }
1123         
1124         // Automatically replaces old attributes with new modified ones
1125         entry.put( mods );
1126         
1127         if ( mods.getID().equals( DirectoryPartition.ALIAS_ATTRIBUTE ) )
1128         {
1129             String ndnStr = ( String ) ndnIdx.reverseLookup( id );
1130             addAliasIndices( id, new LdapName( ndnStr ), 
1131                 ( String ) mods.get() );
1132         }
1133     }
1134 
1135 
1136     public void modify( Name dn, int modOp, Attributes mods ) throws NamingException
1137     {
1138         NamingEnumeration attrs = null;
1139         BigInteger id = getEntryId( dn.toString() );
1140         Attributes entry = master.get( id );
1141         
1142         switch ( modOp )
1143         {
1144             case( DirContext.ADD_ATTRIBUTE ):
1145                 attrs = mods.getIDs();
1146                 
1147                 while ( attrs.hasMore() )
1148                 {
1149                     String attrId = ( String ) attrs.next();
1150                     Attribute attr = mods.get( attrId );
1151                     add( id, entry, attr );
1152                 }
1153                 
1154                 break;
1155             case( DirContext.REMOVE_ATTRIBUTE ):
1156                 attrs = mods.getIDs();
1157                 
1158                 while ( attrs.hasMore() )
1159                 {
1160                     String attrId = ( String ) attrs.next();
1161                     Attribute attr = mods.get( attrId );
1162                     remove( id, entry, attr );
1163                 }
1164                 
1165                 break;
1166             case( DirContext.REPLACE_ATTRIBUTE ):
1167                 attrs = mods.getIDs();
1168                 
1169                 while ( attrs.hasMore() )
1170                 {
1171                     String attrId = ( String ) attrs.next();
1172                     Attribute attr = mods.get( attrId );
1173                     replace( id, entry, attr );
1174                 }
1175                 
1176                 break;
1177             default:
1178                 throw new NamingException( 
1179                     "Unidentified modification operation" );
1180         }
1181         
1182         master.put( entry, id );
1183     }
1184     
1185 
1186     public void modify( Name dn, ModificationItem [] mods ) throws NamingException
1187     {
1188         BigInteger id = getEntryId( dn.toString() );
1189         Attributes entry = master.get( id );
1190         
1191         for ( int ii = 0; ii < mods.length; ii++ )
1192         {
1193             Attribute attrMods = mods[ii].getAttribute();
1194 
1195             switch ( mods[ ii ].getModificationOp() )
1196             {
1197                 case( DirContext.ADD_ATTRIBUTE ):
1198                     add( id, entry, attrMods );
1199                     break;
1200                 case( DirContext.REMOVE_ATTRIBUTE ):
1201                     remove( id, entry, attrMods );
1202                     break;
1203                 case( DirContext.REPLACE_ATTRIBUTE ):
1204                     replace( id, entry, attrMods );
1205                     break;
1206                 default:
1207                     throw new NamingException( 
1208                         "Unidentified modification operation" );
1209             }
1210         }
1211         
1212         master.put( entry, id );
1213     }
1214 
1215 
1216     /***
1217      * Changes the relative distinuished name of an entry specified by a 
1218      * distinguished name with the optional removal of the old Rdn attribute
1219      * value from the entry.  Name changes propagate down as dn changes to the 
1220      * descendants of the entry where the Rdn changed. 
1221      * 
1222      * An Rdn change operation does not change parent child relationships.  It 
1223      * merely propagates a name change at a point in the DIT where the Rdn is 
1224      * changed. The change propagates down the subtree rooted at the 
1225      * distinguished name specified.
1226      *
1227      * @param dn the normalized distinguished name of the entry to alter
1228      * @param newRdn the new Rdn to set
1229      * @param deleteOldRdn whether or not to remove the old Rdn attr/val
1230      * @throws NamingException if there are any errors propagating the name 
1231      *        changes.
1232      */
1233     public void modifyRn( Name dn, String newRdn, boolean deleteOldRdn )
1234         throws NamingException
1235     {
1236         String newRdnAttr = NamespaceTools.getRdnAttribute( newRdn );
1237         String newRdnValue = NamespaceTools.getRdnValue( newRdn );
1238         BigInteger id = getEntryId( dn.toString() );
1239         Attributes entry = lookup( id );
1240         Name updn = new LdapName( getEntryUpdn( id ) );
1241 
1242         /* 
1243          * H A N D L E   N E W   R D N
1244          * ====================================================================
1245          * Add the new Rdn attribute to the entry.  If an index exists on the 
1246          * new Rdn attribute we add the index for this attribute value pair.
1247          * Also we make sure that the existance index shows the existance of the
1248          * new Rdn attribute within this entry.
1249          */
1250 
1251         Attribute rdnAttr = entry.get( newRdnAttr );
1252         if ( rdnAttr == null )
1253         {
1254             rdnAttr = new LockableAttributeImpl( newRdnAttr );
1255         }
1256 
1257         // add the new Rdn value only if it is not already present in the entry
1258         if ( ! rdnAttr.contains( newRdnValue ) )
1259         {
1260             rdnAttr.add( newRdnValue );
1261         }
1262 
1263         entry.put( rdnAttr );
1264         
1265         if ( hasUserIndexOn( newRdnAttr ) )
1266         {
1267             Index idx = getUserIndex( newRdnAttr );
1268             idx.add( newRdnValue, id );
1269             
1270             // Make sure the altered entry shows the existance of the new attrib
1271             if ( ! existanceIdx.hasValue( newRdnAttr.toLowerCase(), id ) )
1272             {
1273                 existanceIdx.add( newRdnAttr.toLowerCase(), id );
1274             }
1275         }
1276 
1277         /*
1278          * H A N D L E   O L D   R D N
1279          * ====================================================================
1280          * If the old Rdn is to be removed we need to get the attribute and 
1281          * value for it.  Keep in mind the old Rdn need not be based on the 
1282          * same Rdn as the new one.  We remove the Rdn value from the entry
1283          * and remove the value/id tuple from the index on the old Rdn attr
1284          * if any.  We also test if the delete of the old Rdn index tuple 
1285          * removed all the attribute values of the old Rdn using a reverse
1286          * lookup.  If so that means we blew away the last value of the old 
1287          * Rdn attribute.  In this case we need to remove the attrName/id 
1288          * tuple from the existance index.
1289          */
1290 
1291         if ( deleteOldRdn )
1292         {
1293             String oldRdn = updn.get( updn.size() - 1 );
1294             String oldRdnAttr = NamespaceTools.getRdnAttribute( oldRdn );
1295             String oldRdnValue = NamespaceTools.getRdnValue( oldRdn );
1296             
1297             entry.get( oldRdnAttr ).remove( oldRdnValue );
1298 
1299             if ( hasUserIndexOn( oldRdnAttr ) )
1300             {
1301                 Index idx = getUserIndex( oldRdnAttr );
1302                 idx.drop( oldRdnValue, id );
1303                 
1304                 /*
1305                  * If there is no value for id in this index due to our
1306                  * drop above we remove the oldRdnAttr from the existance idx
1307                  */
1308                 if ( null == idx.reverseLookup( id ) )
1309                 {
1310                     existanceIdx.drop( oldRdnAttr, id );
1311                 }
1312             }
1313         }
1314         
1315         /*
1316          * H A N D L E   D N   C H A N G E
1317          * ====================================================================
1318          * 1) Build the new user defined distinguished name
1319          *      - clone / copy old updn
1320          *      - remove old upRdn from copy
1321          *      - add the new upRdn to the copy
1322          * 2) Make call to recursive modifyDn method to change the names of the
1323          *    entry and its descendants
1324          */
1325 
1326         Name newUpdn = ( Name ) updn.clone();      // copy da old updn
1327         newUpdn.remove( newUpdn.size() - 1 );      // remove old upRdn
1328         newUpdn.add( newUpdn.size(), newRdn );   // add da new upRdn
1329         modifyDn( id, newUpdn, false );            // propagate dn changes
1330     }
1331     
1332     
1333     /*
1334      * The move operation severs a child from a parent creating a new parent
1335      * child relationship.  As a consequence the relationships between the 
1336      * old ancestors of the child and its descendants change.  A descendant is
1337      *   
1338      */
1339 
1340     /***
1341      * Recursively modifies the distinguished name of an entry and the names of
1342      * its descendants calling itself in the recursion.
1343      *
1344      * @param id the primary key of the entry
1345      * @param updn User provided distinguished name to set as the new DN
1346      * @param isMove whether or not the name change is due to a move operation
1347      * which affects alias indices.
1348      * @throws NamingException if something goes wrong
1349      */
1350     private void modifyDn( BigInteger id, Name updn, boolean isMove )
1351         throws  NamingException
1352     {
1353         String aliasTarget = null;
1354 
1355         // Now we can handle the appropriate name indices for all cases
1356         ndnIdx.drop( id );
1357         ndnIdx.add( ndnIdx.getNormalized( updn.toString() ), id );
1358         
1359         updnIdx.drop( id );
1360         updnIdx.add( updn.toString(), id );
1361         
1362         /* 
1363          * Read Alias Index Tuples
1364          * 
1365          * If this is a name change due to a move operation then the one and
1366          * subtree indices for aliases were purged before the aliases were 
1367          * moved.  Now we must add them for each alias entry we have moved.  
1368          * 
1369          * aliasTarget is used as a marker to tell us if we're moving an 
1370          * alias.  If it is null then the moved entry is not an alias.
1371          */
1372         if ( isMove ) 
1373         {
1374             aliasTarget = ( String ) aliasIdx.reverseLookup( id );
1375     
1376             if ( null != aliasTarget )
1377             {
1378                 addAliasIndices( id, new LdapName( getEntryDn( id ) ), 
1379                     aliasTarget );
1380             }
1381         }
1382         
1383         NamingEnumeration children = list( id );
1384         while ( children.hasMore() ) 
1385         {
1386             // Get the child and its id
1387             IndexRecord rec = ( IndexRecord ) children.next();
1388             BigInteger childId = rec.getEntryId();
1389             
1390             /* 
1391              * Calculate the Dn for the child's new name by copying the parents
1392              * new name and adding the child's old upRdn to new name as its Rdn
1393              */
1394             Name childUpdn = ( Name ) updn.clone();
1395             Name oldUpdn = new LdapName( getEntryUpdn( childId ) );
1396             String rdn = LdapName.getRdn( oldUpdn ); 
1397             childUpdn.add( childUpdn.size(),  rdn );
1398 
1399             // Recursively change the names of the children below
1400             modifyDn( childId, childUpdn, isMove );
1401         }
1402     }
1403 
1404 
1405     public void move( Name oldChildDn, Name newParentDn, String newRdn,
1406         boolean deleteOldRdn ) throws NamingException
1407     {
1408         BigInteger childId = getEntryId( oldChildDn.toString() );
1409         modifyRn( oldChildDn, newRdn, deleteOldRdn );
1410         move( oldChildDn, childId, newParentDn );
1411     }
1412 
1413 
1414     public void move( Name oldChildDn, Name newParentDn ) throws NamingException
1415     {
1416         BigInteger childId = getEntryId( oldChildDn.toString() );
1417         move( oldChildDn, childId, newParentDn );
1418     }
1419 
1420 
1421     /***
1422      * Moves an entry under a new parent.  The operation causes a shift in the
1423      * parent child relationships between the old parent, new parent and the 
1424      * child moved.  All other descendant entries under the child never change
1425      * their direct parent child relationships.  Hence after the parent child
1426      * relationship changes are broken at the old parent and set at the new
1427      * parent a modifyDn operation is conducted to handle name changes 
1428      * propagating down through the moved child and its descendants.
1429      * 
1430      * @param oldChildDn the normalized dn of the child to be moved
1431      * @param newParentDn the normalized dn of the new parent for the child
1432      * @throws NamingException if something goes wrong
1433      */
1434     private void move( Name oldChildDn, BigInteger childId, Name newParentDn ) throws NamingException
1435     {
1436         // Get the child and the new parent to be entries and Ids
1437         BigInteger newParentId = getEntryId( newParentDn.toString() );
1438         BigInteger oldParentId = getParentId( childId );
1439         
1440         /*
1441          * All aliases including and below oldChildDn, will be affected by
1442          * the move operation with respect to one and subtree indices since 
1443          * their relationship to ancestors above oldChildDn will be 
1444          * destroyed.  For each alias below and including oldChildDn we will
1445          * drop the index tuples mapping ancestor ids above oldChildDn to the
1446          * respective target ids of the aliases.
1447          */
1448         dropMovedAliasIndices( oldChildDn );
1449         
1450         /*
1451          * Drop the old parent child relationship and add the new one
1452          * Set the new parent id for the child replacing the old parent id
1453          */
1454         hierarchyIdx.drop( oldParentId, childId );
1455         hierarchyIdx.add( newParentId, childId );
1456 
1457         /*
1458          * Build the new user provided DN (updn) for the child using the child's
1459          * user provided RDN & the new parent's UPDN.  Basically add the child's
1460          * UpRdn String to the tail of the new parent's Updn Name.
1461          */
1462         Name childUpdn = new LdapName( getEntryUpdn( childId ) );
1463         String childRdn = childUpdn.get( childUpdn.size() - 1 ); 
1464         Name newUpdn = new LdapName( getEntryUpdn( newParentId ) );
1465         newUpdn.add( newUpdn.size(), childRdn );
1466 
1467         // Call the modifyDn operation with the new updn
1468         modifyDn( childId, newUpdn, true );
1469     }
1470 
1471 
1472     /***
1473      * For all aliases including and under the moved base, this method removes
1474      * one and subtree alias index tuples for old ancestors above the moved base
1475      * that will no longer be ancestors after the move.
1476      * 
1477      * @param movedBase the base at which the move occured - the moved node
1478      * @throws NamingException if system indices fail
1479      */
1480     private void dropMovedAliasIndices( final Name movedBase ) throws NamingException
1481     {
1482         // Find all the aliases from movedBase down
1483         IndexAssertion isBaseDescendant = new IndexAssertion()
1484         {
1485             public boolean assertCandidate( IndexRecord rec )
1486                 throws NamingException
1487             {
1488                 String dn = getEntryDn( rec.getEntryId() );
1489                 if ( dn.endsWith( movedBase.toString() ) )
1490                 {
1491                     return true;
1492                 }
1493                 
1494                 return false;
1495             }
1496         };
1497         
1498         BigInteger movedBaseId = getEntryId( movedBase.toString() );
1499         if ( aliasIdx.reverseLookup( movedBaseId ) != null ) 
1500         {
1501             dropAliasIndices( movedBaseId, movedBase );
1502         }
1503         
1504         NamingEnumeration aliases = new IndexAssertionEnumeration(
1505             aliasIdx.listIndices( movedBase.toString(), true ), 
1506             isBaseDescendant );
1507         while ( aliases.hasMore() )
1508         {
1509             IndexRecord entry = ( IndexRecord ) aliases.next();
1510             dropAliasIndices( entry.getEntryId(), movedBase );
1511         }
1512     }
1513     
1514     
1515     /***
1516      * For the alias id all ancestor one and subtree alias tuples are moved 
1517      * above the moved base.
1518      * 
1519      * @param aliasId the id of the alias 
1520      * @param movedBase the base where the move occured
1521      * @throws NamingException if indices fail
1522      */
1523     private void dropAliasIndices( BigInteger aliasId, Name movedBase )
1524         throws NamingException
1525     {
1526         String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
1527         BigInteger targetId = getEntryId( targetDn );
1528         String aliasDn = getEntryDn( aliasId );
1529         
1530         /*
1531          * Start droping index tuples with the first ancestor right above the 
1532          * moved base.  This is the first ancestor effected by the move.
1533          */
1534         Name ancestorDn = movedBase.getPrefix( 1 );
1535         BigInteger ancestorId = getEntryId( ancestorDn.toString() );
1536         
1537         /*
1538          * We cannot just drop all tuples in the one level and subtree indices
1539          * linking baseIds to the targetId.  If more than one alias refers to
1540          * the target then droping all tuples with a value of targetId would
1541          * make all other aliases to the target inconsistent.
1542          * 
1543          * We need to walk up the path of alias ancestors right above the moved 
1544          * base until we reach the upSuffix, deleting each ( ancestorId,
1545          * targetId ) tuple in the subtree scope alias.  We only need to do 
1546          * this for the direct parent of the alias on the one level subtree if
1547          * the moved base is the alias.
1548          */
1549         if ( aliasDn.equals( movedBase.toString() ) )
1550         {
1551             oneAliasIdx.drop( ancestorId, targetId );
1552         }
1553         
1554         subAliasIdx.drop( ancestorId, targetId );
1555         
1556         while ( ! ancestorDn.equals( upSuffix ) )
1557         {
1558             ancestorDn = ancestorDn.getPrefix( 1 );
1559             ancestorId = getEntryId( ancestorDn.toString() );
1560             
1561             subAliasIdx.drop( ancestorId, targetId );
1562         }    
1563     }
1564 }
1565