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