001    /*
002     *   Licensed to the Apache Software Foundation (ASF) under one
003     *   or more contributor license agreements.  See the NOTICE file
004     *   distributed with this work for additional information
005     *   regarding copyright ownership.  The ASF licenses this file
006     *   to you under the Apache License, Version 2.0 (the
007     *   "License"); you may not use this file except in compliance
008     *   with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *   Unless required by applicable law or agreed to in writing,
013     *   software distributed under the License is distributed on an
014     *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *   KIND, either express or implied.  See the License for the
016     *   specific language governing permissions and limitations
017     *   under the License.
018     *
019     */
020    package org.apache.directory.server.core.partition.avl;
021    
022    
023    import java.io.File;
024    import java.util.ArrayList;
025    import java.util.HashMap;
026    import java.util.HashSet;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    
032    import javax.naming.InvalidNameException;
033    import javax.naming.NamingException;
034    
035    import org.apache.directory.server.constants.ApacheSchemaConstants;
036    import org.apache.directory.server.core.entry.ClonedServerEntry;
037    import org.apache.directory.server.core.entry.ServerAttribute;
038    import org.apache.directory.server.core.entry.ServerEntry;
039    import org.apache.directory.server.core.entry.ServerStringValue;
040    import org.apache.directory.server.core.partition.impl.btree.LongComparator;
041    import org.apache.directory.server.i18n.I18n;
042    import org.apache.directory.server.xdbm.Index;
043    import org.apache.directory.server.xdbm.IndexCursor;
044    import org.apache.directory.server.xdbm.IndexEntry;
045    import org.apache.directory.server.xdbm.IndexNotFoundException;
046    import org.apache.directory.server.xdbm.Store;
047    import org.apache.directory.shared.ldap.constants.SchemaConstants;
048    import org.apache.directory.shared.ldap.cursor.Cursor;
049    import org.apache.directory.shared.ldap.entry.EntryAttribute;
050    import org.apache.directory.shared.ldap.entry.Modification;
051    import org.apache.directory.shared.ldap.entry.ModificationOperation;
052    import org.apache.directory.shared.ldap.entry.Value;
053    import org.apache.directory.shared.ldap.exception.LdapNameNotFoundException;
054    import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
055    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
056    import org.apache.directory.shared.ldap.name.AVA;
057    import org.apache.directory.shared.ldap.name.DN;
058    import org.apache.directory.shared.ldap.name.RDN;
059    import org.apache.directory.shared.ldap.schema.AttributeType;
060    import org.apache.directory.shared.ldap.schema.MatchingRule;
061    import org.apache.directory.shared.ldap.schema.SchemaManager;
062    import org.apache.directory.shared.ldap.util.NamespaceTools;
063    import org.slf4j.Logger;
064    import org.slf4j.LoggerFactory;
065    
066    
067    /**
068     * A Store implementation backed by in memory AVL trees.
069     * 
070     * TODO - this class is extremely like the JdbmStore implementation of the
071     * Store interface which tells us that it's best for us to have some kind 
072     * of abstract class.
073     *
074     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
075     * @version $Rev$, $Date$
076     */
077    public class AvlStore<E> implements Store<E, Long>
078    {
079        /** static logger */
080        private static final Logger LOG = LoggerFactory.getLogger( AvlStore.class );
081    
082        /** Two static declaration to avoid lookup all over the code */
083        private static AttributeType OBJECT_CLASS_AT;
084        private static AttributeType ALIASED_OBJECT_NAME_AT;
085    
086        /** the master table storing entries by primary key */
087        private AvlMasterTable<ServerEntry> master;
088    
089        /** the normalized distinguished name index */
090        private AvlIndex<String, E> ndnIdx;
091    
092        /** the user provided distinguished name index */
093        private AvlIndex<String, E> updnIdx;
094    
095        /** the attribute existence index */
096        private AvlIndex<String, E> existenceIdx;
097    
098        /** a system index on aliasedObjectName attribute */
099        private AvlIndex<String, E> aliasIdx;
100    
101        /** a system index on the entries of descendants of root DN*/
102        private AvlIndex<Long, E> subLevelIdx;
103    
104        /** the parent child relationship index */
105        private AvlIndex<Long, E> oneLevelIdx;
106    
107        /** the one level scope alias index */
108        private AvlIndex<Long, E> oneAliasIdx;
109    
110        /** the subtree scope alias index */
111        private AvlIndex<Long, E> subAliasIdx;
112    
113        /** a system index on objectClass attribute*/
114        private AvlIndex<String, E> objectClassIdx;
115    
116        /** a system index on entryCSN attribute */
117        private AvlIndex<String, E> entryCsnIdx;
118    
119        /** a system index on entryUUID attribute */
120        private AvlIndex<String, E> entryUuidIdx;
121    
122        /** a map of attributeType numeric ID to user userIndices */
123        private Map<String, AvlIndex<? extends Object, E>> userIndices = new HashMap<String, AvlIndex<? extends Object, E>>();
124    
125        /** a map of attributeType numeric ID to system userIndices */
126        private Map<String, AvlIndex<? extends Object, E>> systemIndices = new HashMap<String, AvlIndex<? extends Object, E>>();
127    
128        /** true if initialized */
129        private boolean initialized;
130    
131        /** A pointer on the schemaManager */
132        private SchemaManager schemaManager;
133    
134        /** 
135         * TODO we need to check out why we have so many suffix 
136         * dn and string accessor/mutators on both Store and Partition
137         * interfaces.  I think a lot of this comes from the fact 
138         * that we implemented DN to have both the up and norm
139         * names.
140         */
141        private DN suffixDn;
142    
143        private String name;
144    
145    
146        /**
147         * {@inheritDoc}
148         */
149        public void add( ServerEntry entry ) throws Exception
150        {
151            if ( entry instanceof ClonedServerEntry )
152            {
153                throw new Exception( I18n.err( I18n.ERR_215 ) );
154            }
155    
156            DN normName = entry.getDn();
157    
158            Long id;
159            Long parentId;
160    
161            id = master.getNextId();
162    
163            //
164            // Suffix entry cannot have a parent since it is the root so it is 
165            // capped off using the zero value which no entry can have since 
166            // entry sequences start at 1.
167            //
168    
169            DN parentDn = null;
170    
171            if ( normName.getNormName().equals( suffixDn.getNormName() ) )
172            {
173                parentId = 0L;
174            }
175            else
176            {
177                parentDn = ( DN ) normName.clone();
178                parentDn.remove( parentDn.size() - 1 );
179                parentId = getEntryId( parentDn.getNormName() );
180            }
181    
182            // don't keep going if we cannot find the parent Id
183            if ( parentId == null )
184            {
185                throw new LdapNameNotFoundException( I18n.err( I18n.ERR_216, parentDn ) );
186            }
187    
188            EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT );
189    
190            if ( objectClass == null )
191            {
192                String msg = I18n.err( I18n.ERR_217, normName.getName(), entry );
193                throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
194            }
195    
196            // Start adding the system userIndices
197            // Why bother doing a lookup if this is not an alias.
198            // First, the ObjectClass index
199            for ( Value<?> value : objectClass )
200            {
201                objectClassIdx.add( value.getString(), id );
202            }
203    
204            if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
205            {
206                EntryAttribute aliasAttr = entry.get( ALIASED_OBJECT_NAME_AT );
207                addAliasIndices( id, normName, aliasAttr.getString() );
208            }
209    
210            if ( !Character.isDigit( normName.toNormName().charAt( 0 ) ) )
211            {
212                throw new IllegalStateException( I18n.err( I18n.ERR_218, normName.toNormName() ) );
213            }
214    
215            ndnIdx.add( normName.toNormName(), id );
216            updnIdx.add( normName.getName(), id );
217            oneLevelIdx.add( parentId, id );
218    
219            // Update the EntryCsn index
220            EntryAttribute entryCsn = entry.get( SchemaConstants.ENTRY_CSN_AT );
221    
222            if ( entryCsn == null )
223            {
224                String msg = I18n.err( I18n.ERR_219, normName.getName(), entry );
225                throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
226            }
227    
228            entryCsnIdx.add( entryCsn.getString(), id );
229    
230            // Update the EntryUuid index
231            EntryAttribute entryUuid = entry.get( SchemaConstants.ENTRY_UUID_AT );
232    
233            if ( entryUuid == null )
234            {
235                String msg = I18n.err( I18n.ERR_220, normName.getName(), entry );
236                throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECT_CLASS_VIOLATION );
237            }
238    
239            entryUuidIdx.add( entryUuid.getString(), id );
240    
241            Long tempId = parentId;
242            while ( tempId != null && tempId != 0 && tempId != 1 )
243            {
244                subLevelIdx.add( tempId, id );
245                tempId = getParentId( tempId );
246            }
247    
248            // making entry an ancestor/descendent of itself in sublevel index
249            subLevelIdx.add( id, id );
250    
251            // Now work on the user defined userIndices
252            for ( EntryAttribute attribute : entry )
253            {
254                String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
255    
256                if ( hasUserIndexOn( attributeOid ) )
257                {
258                    Index<Object, E, Long> idx = ( Index<Object, E, Long> ) getUserIndex( attributeOid );
259    
260                    // here lookup by attributeId is OK since we got attributeId from 
261                    // the entry via the enumeration - it's in there as is for sure
262    
263                    for ( Value<?> value : attribute )
264                    {
265                        idx.add( value.get(), id );
266                    }
267    
268                    // Adds only those attributes that are indexed
269                    existenceIdx.add( attributeOid, id );
270                }
271            }
272    
273            master.put( id, entry );
274        }
275    
276    
277        /**
278         * {@inheritDoc}
279         */
280        public void addIndex( Index<? extends Object, E, Long> index ) throws Exception
281        {
282            if ( index instanceof AvlIndex<?, ?> )
283            {
284                userIndices.put( index.getAttributeId(), ( AvlIndex<? extends Object, E> ) index );
285            }
286            else
287            {
288                userIndices.put( index.getAttributeId(), ( AvlIndex<? extends Object, E> ) convert( index ) );
289            }
290        }
291    
292    
293        /**
294         * {@inheritDoc}
295         */
296        public int count() throws Exception
297        {
298            return master.count();
299        }
300    
301    
302        /**
303         * {@inheritDoc}
304         */
305        @SuppressWarnings("unchecked")
306        public void delete( Long id ) throws Exception
307        {
308            ServerEntry entry = lookup( id );
309            Long parentId = getParentId( id );
310    
311            EntryAttribute objectClass = entry.get( OBJECT_CLASS_AT );
312    
313            if ( objectClass.contains( SchemaConstants.ALIAS_OC ) )
314            {
315                dropAliasIndices( id );
316            }
317    
318            for ( Value<?> value : objectClass )
319            {
320                objectClassIdx.drop( value.getString(), id );
321            }
322    
323            ndnIdx.drop( id );
324            updnIdx.drop( id );
325            oneLevelIdx.drop( id );
326            entryCsnIdx.drop( id );
327            entryUuidIdx.drop( id );
328    
329            if ( id != 1 )
330            {
331                subLevelIdx.drop( id );
332            }
333    
334            // Remove parent's reference to entry only if entry is not the upSuffix
335            if ( !parentId.equals( 0L ) )
336            {
337                oneLevelIdx.drop( parentId, id );
338            }
339    
340            for ( EntryAttribute attribute : entry )
341            {
342                String attributeOid = ( ( ServerAttribute ) attribute ).getAttributeType().getOid();
343    
344                if ( hasUserIndexOn( attributeOid ) )
345                {
346                    Index<?, E, Long> index = getUserIndex( attributeOid );
347    
348                    // here lookup by attributeId is ok since we got attributeId from 
349                    // the entry via the enumeration - it's in there as is for sure
350                    for ( Value<?> value : attribute )
351                    {
352                        ( ( AvlIndex ) index ).drop( value.get(), id );
353                    }
354    
355                    existenceIdx.drop( attributeOid, id );
356                }
357            }
358    
359            master.delete( id );
360        }
361    
362    
363        /**
364         * {@inheritDoc}
365         */
366        public void destroy() throws Exception
367        {
368            initialized = false;
369        }
370    
371    
372        /**
373         * {@inheritDoc}
374         */
375        public Index<String, E, Long> getAliasIndex()
376        {
377            return aliasIdx;
378        }
379    
380    
381        /**
382         * {@inheritDoc}
383         */
384        public int getChildCount( Long id ) throws Exception
385        {
386            return oneLevelIdx.count( id );
387        }
388    
389    
390        /**
391         * {@inheritDoc}
392         */
393        public String getEntryDn( Long id ) throws Exception
394        {
395            return ndnIdx.reverseLookup( id );
396        }
397    
398    
399        /**
400         * {@inheritDoc}
401         */
402        public Long getEntryId( String dn ) throws Exception
403        {
404            return ndnIdx.forwardLookup( dn );
405        }
406    
407    
408        /**
409         * {@inheritDoc}
410         */
411        public String getEntryUpdn( Long id ) throws Exception
412        {
413            return updnIdx.reverseLookup( id );
414        }
415    
416    
417        /**
418         * {@inheritDoc}
419         */
420        public String getEntryUpdn( String dn ) throws Exception
421        {
422            Long id = ndnIdx.forwardLookup( dn );
423            return updnIdx.reverseLookup( id );
424        }
425    
426    
427        /**
428         * {@inheritDoc}
429         */
430        public String getName()
431        {
432            return name;
433        }
434    
435    
436        /**
437         * {@inheritDoc}
438         */
439        public Index<String, E, Long> getNdnIndex()
440        {
441            return ndnIdx;
442        }
443    
444    
445        /**
446         * {@inheritDoc}
447         */
448        public Index<Long, E, Long> getOneAliasIndex()
449        {
450            return oneAliasIdx;
451        }
452    
453    
454        /**
455         * {@inheritDoc}
456         */
457        public Index<Long, E, Long> getOneLevelIndex()
458        {
459            return oneLevelIdx;
460        }
461    
462    
463        /**
464         * {@inheritDoc}
465         */
466        public Long getParentId( String dn ) throws Exception
467        {
468            Long childId = ndnIdx.forwardLookup( dn );
469            return oneLevelIdx.reverseLookup( childId );
470        }
471    
472    
473        /**
474         * {@inheritDoc}
475         */
476        public Long getParentId( Long childId ) throws Exception
477        {
478            return oneLevelIdx.reverseLookup( childId );
479        }
480    
481    
482        /**
483         * {@inheritDoc}
484         */
485        public Index<String, E, Long> getPresenceIndex()
486        {
487            return existenceIdx;
488        }
489    
490    
491        /**
492         * {@inheritDoc}
493         */
494        public String getProperty( String propertyName ) throws Exception
495        {
496            return master.getProperty( propertyName );
497        }
498    
499    
500        /**
501         * {@inheritDoc}
502         */
503        public Index<Long, E, Long> getSubAliasIndex()
504        {
505            return subAliasIdx;
506        }
507    
508    
509        /**
510         * {@inheritDoc}
511         */
512        public Index<Long, E, Long> getSubLevelIndex()
513        {
514            return subLevelIdx;
515        }
516    
517    
518        /**
519         * {@inheritDoc}
520         */
521        public DN getSuffix()
522        {
523            if ( suffixDn == null )
524            {
525                return null;
526            }
527    
528            try
529            {
530                return new DN( suffixDn.getNormName() );
531            }
532            catch ( InvalidNameException e )
533            {
534                // shouldn't happen
535                LOG.error( "", e );
536            }
537    
538            return null;
539        }
540    
541    
542        /**
543         * {@inheritDoc}
544         */
545        public DN getUpSuffix()
546        {
547            if ( suffixDn == null )
548            {
549                return null;
550            }
551    
552            try
553            {
554                return new DN( suffixDn.getName() );
555            }
556            catch ( InvalidNameException e )
557            {
558                // shouldn't happen
559                LOG.error( "", e );
560            }
561    
562            return null;
563        }
564    
565    
566        public String getSuffixDn()
567        {
568            if ( suffixDn == null )
569            {
570                return null;
571            }
572    
573            return suffixDn.getName();
574        }
575    
576    
577        /**
578         * {@inheritDoc}
579         */
580        public Index<?, E, Long> getSystemIndex( String id ) throws IndexNotFoundException
581        {
582            try
583            {
584                id = schemaManager.getAttributeTypeRegistry().getOidByName( id );
585            }
586            catch ( NamingException e )
587            {
588                LOG.error( I18n.err( I18n.ERR_1, id ), e.getLocalizedMessage() );
589                throw new IndexNotFoundException( I18n.err( I18n.ERR_1, id ), id, e );
590            }
591    
592            if ( systemIndices.containsKey( id ) )
593            {
594                return systemIndices.get( id );
595            }
596    
597            throw new IndexNotFoundException( I18n.err( I18n.ERR_2, id, name ) );
598        }
599    
600    
601        /**
602         * {@inheritDoc}
603         */
604        public Index<?, E, Long> getIndex( String id ) throws IndexNotFoundException
605        {
606            try
607            {
608                id = schemaManager.getAttributeTypeRegistry().getOidByName( id );
609            }
610            catch ( NamingException e )
611            {
612                LOG.error( I18n.err( I18n.ERR_1, id ), e.getLocalizedMessage() );
613                throw new IndexNotFoundException( I18n.err( I18n.ERR_1, id ), id, e );
614            }
615    
616            if ( userIndices.containsKey( id ) )
617            {
618                return userIndices.get( id );
619            }
620            if ( systemIndices.containsKey( id ) )
621            {
622                return systemIndices.get( id );
623            }
624    
625            throw new IndexNotFoundException( I18n.err( I18n.ERR_2, id, name ) );
626        }
627    
628    
629        /**
630         * {@inheritDoc}
631         */
632        public Index<String, E, Long> getUpdnIndex()
633        {
634            return updnIdx;
635        }
636    
637    
638        /**
639         * {@inheritDoc}
640         */
641        public Index<? extends Object, E, Long> getUserIndex( String id ) throws IndexNotFoundException
642        {
643            try
644            {
645                id = schemaManager.getAttributeTypeRegistry().getOidByName( id );
646            }
647            catch ( NamingException e )
648            {
649                LOG.error( I18n.err( I18n.ERR_1, id ), e.getLocalizedMessage() );
650                throw new IndexNotFoundException( I18n.err( I18n.ERR_1, id ), id, e );
651            }
652    
653            if ( userIndices.containsKey( id ) )
654            {
655                return userIndices.get( id );
656            }
657    
658            throw new IndexNotFoundException( I18n.err( I18n.ERR_3, id, name ) );
659        }
660    
661    
662        /**
663         * {@inheritDoc}
664         */
665        public Set<Index<? extends Object, E, Long>> getUserIndices()
666        {
667            return new HashSet<Index<? extends Object, E, Long>>( userIndices.values() );
668        }
669    
670    
671        /**
672         * {@inheritDoc}
673         */
674        public boolean hasIndexOn( String id ) throws Exception
675        {
676            return hasUserIndexOn( id ) || hasSystemIndexOn( id );
677        }
678    
679    
680        /**
681         * {@inheritDoc}
682         */
683        public boolean hasSystemIndexOn( String id ) throws Exception
684        {
685            return systemIndices.containsKey( id );
686        }
687    
688    
689        /**
690         * {@inheritDoc}
691         */
692        public boolean hasUserIndexOn( String id ) throws Exception
693        {
694            return userIndices.containsKey( id );
695        }
696    
697    
698        /**
699         * {@inheritDoc}
700         * TODO why this and initRegistries on Store interface ???
701         */
702        public void init( SchemaManager schemaManager ) throws Exception
703        {
704            this.schemaManager = schemaManager;
705    
706            OBJECT_CLASS_AT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASS_AT );
707            ALIASED_OBJECT_NAME_AT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ALIASED_OBJECT_NAME_AT );
708    
709            // Create the master table (the table containing all the entries)
710            master = new AvlMasterTable<ServerEntry>( name, new LongComparator(), null, false );
711    
712            suffixDn.normalize( schemaManager.getNormalizerMapping() );
713            // -------------------------------------------------------------------
714            // Initializes the user and system indices
715            // -------------------------------------------------------------------
716    
717            setupSystemIndices();
718            setupUserIndices();
719    
720            // We are done !
721            initialized = true;
722        }
723    
724    
725        private void setupSystemIndices() throws Exception
726        {
727            // let's check and make sure the supplied indices are OK
728    
729            if ( ndnIdx == null )
730            {
731                AttributeType attributeType = schemaManager
732                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_N_DN_AT_OID );
733                ndnIdx = new AvlIndex<String, E>();
734                ndnIdx.setAttributeId( ApacheSchemaConstants.APACHE_N_DN_AT_OID );
735                ndnIdx.initialize( attributeType );
736                systemIndices.put( ApacheSchemaConstants.APACHE_N_DN_AT_OID, ndnIdx );
737            }
738    
739            if ( updnIdx == null )
740            {
741                AttributeType attributeType = schemaManager
742                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_UP_DN_AT_OID );
743                updnIdx = new AvlIndex<String, E>();
744                updnIdx.setAttributeId( ApacheSchemaConstants.APACHE_UP_DN_AT_OID );
745                updnIdx.initialize( attributeType );
746                systemIndices.put( ApacheSchemaConstants.APACHE_UP_DN_AT_OID, updnIdx );
747            }
748    
749            if ( existenceIdx == null )
750            {
751                AttributeType attributeType = schemaManager
752                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_EXISTENCE_AT_OID );
753                existenceIdx = new AvlIndex<String, E>();
754                existenceIdx.setAttributeId( ApacheSchemaConstants.APACHE_EXISTENCE_AT_OID );
755                existenceIdx.initialize( attributeType );
756                systemIndices.put( ApacheSchemaConstants.APACHE_EXISTENCE_AT_OID, existenceIdx );
757            }
758    
759            if ( oneLevelIdx == null )
760            {
761                AttributeType attributeType = schemaManager
762                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID );
763                oneLevelIdx = new AvlIndex<Long, E>();
764                oneLevelIdx.setAttributeId( ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID );
765                oneLevelIdx.initialize( attributeType );
766                systemIndices.put( ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID, oneLevelIdx );
767            }
768    
769            if ( oneAliasIdx == null )
770            {
771                AttributeType attributeType = schemaManager
772                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID );
773                oneAliasIdx = new AvlIndex<Long, E>();
774                oneAliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID );
775                oneAliasIdx.initialize( attributeType );
776                systemIndices.put( ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID, oneAliasIdx );
777            }
778    
779            if ( subAliasIdx == null )
780            {
781                AttributeType attributeType = schemaManager
782                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID );
783                subAliasIdx = new AvlIndex<Long, E>();
784                subAliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID );
785                subAliasIdx.initialize( attributeType );
786                systemIndices.put( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID, subAliasIdx );
787            }
788    
789            if ( aliasIdx == null )
790            {
791                AttributeType attributeType = schemaManager
792                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
793                aliasIdx = new AvlIndex<String, E>();
794                aliasIdx.setAttributeId( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
795                aliasIdx.initialize( attributeType );
796                systemIndices.put( ApacheSchemaConstants.APACHE_ALIAS_AT_OID, aliasIdx );
797            }
798    
799            if ( subLevelIdx == null )
800            {
801                AttributeType attributeType = schemaManager
802                    .lookupAttributeTypeRegistry( ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID );
803                subLevelIdx = new AvlIndex<Long, E>();
804                subLevelIdx.setAttributeId( ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID );
805                subLevelIdx.initialize( attributeType );
806                systemIndices.put( ApacheSchemaConstants.APACHE_SUB_LEVEL_AT_OID, subLevelIdx );
807            }
808    
809            if ( entryCsnIdx == null )
810            {
811                AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ENTRY_CSN_AT_OID );
812                entryCsnIdx = new AvlIndex<String, E>();
813                entryCsnIdx.setAttributeId( SchemaConstants.ENTRY_CSN_AT_OID );
814                entryCsnIdx.initialize( attributeType );
815                systemIndices.put( SchemaConstants.ENTRY_CSN_AT_OID, entryCsnIdx );
816            }
817    
818            if ( entryUuidIdx == null )
819            {
820                AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ENTRY_UUID_AT_OID );
821                entryUuidIdx = new AvlIndex<String, E>();
822                entryUuidIdx.setAttributeId( SchemaConstants.ENTRY_UUID_AT_OID );
823                entryUuidIdx.initialize( attributeType );
824                systemIndices.put( SchemaConstants.ENTRY_UUID_AT_OID, entryUuidIdx );
825            }
826    
827            if ( objectClassIdx == null )
828            {
829                AttributeType attributeType = schemaManager
830                    .lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASS_AT_OID );
831                objectClassIdx = new AvlIndex<String, E>();
832                objectClassIdx.setAttributeId( SchemaConstants.OBJECT_CLASS_AT_OID );
833                objectClassIdx.initialize( attributeType );
834                systemIndices.put( SchemaConstants.OBJECT_CLASS_AT_OID, objectClassIdx );
835            }
836    
837        }
838    
839    
840        private void setupUserIndices() throws Exception
841        {
842            if ( userIndices != null && userIndices.size() > 0 )
843            {
844                Map<String, AvlIndex<? extends Object, E>> tmp = new HashMap<String, AvlIndex<? extends Object, E>>();
845    
846                for ( AvlIndex<? extends Object, E> index : userIndices.values() )
847                {
848                    String oid = schemaManager.getAttributeTypeRegistry().getOidByName( index.getAttributeId() );
849                    AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( oid );
850    
851                    // Check that the attributeType has an EQUALITY matchingRule
852                    MatchingRule mr = attributeType.getEquality();
853    
854                    if ( mr != null )
855                    {
856                        index.initialize( schemaManager.lookupAttributeTypeRegistry( oid ) );
857                        tmp.put( oid, index );
858                    }
859                    else
860                    {
861                        LOG.error( I18n.err( I18n.ERR_4, attributeType.getName() ) );
862                    }
863                }
864    
865                userIndices = tmp;
866            }
867            else
868            {
869                userIndices = new HashMap<String, AvlIndex<? extends Object, E>>();
870            }
871        }
872    
873    
874        /**
875         * {@inheritDoc}
876         */
877        public boolean isInitialized()
878        {
879            return initialized;
880        }
881    
882    
883        /**
884         * {@inheritDoc}
885         */
886        public IndexCursor<Long, E, Long> list( Long id ) throws Exception
887        {
888            IndexCursor<Long, E, Long> cursor = oneLevelIdx.forwardCursor( id );
889            cursor.beforeValue( id, null );
890            return cursor;
891        }
892    
893    
894        /**
895         * {@inheritDoc}
896         */
897        public ServerEntry lookup( Long id ) throws Exception
898        {
899            return master.get( id );
900        }
901    
902    
903        /**
904         * Recursively modifies the distinguished name of an entry and the names of
905         * its descendants calling itself in the recursion.
906         *
907         * @param id the primary key of the entry
908         * @param updn User provided distinguished name to set as the new DN
909         * @param isMove whether or not the name change is due to a move operation
910         * which affects alias userIndices.
911         * @throws NamingException if something goes wrong
912         */
913        private void modifyDn( Long id, DN updn, boolean isMove ) throws Exception
914        {
915            String aliasTarget;
916    
917            // update normalized DN index
918            ndnIdx.drop( id );
919            if ( !updn.isNormalized() )
920            {
921                updn.normalize( schemaManager.getNormalizerMapping() );
922            }
923            ndnIdx.add( updn.toNormName(), id );
924    
925            // update user provided DN index
926            updnIdx.drop( id );
927            updnIdx.add( updn.getName(), id );
928    
929            /* 
930             * Read Alias Index Tuples
931             * 
932             * If this is a name change due to a move operation then the one and
933             * subtree userIndices for aliases were purged before the aliases were
934             * moved.  Now we must add them for each alias entry we have moved.  
935             * 
936             * aliasTarget is used as a marker to tell us if we're moving an 
937             * alias.  If it is null then the moved entry is not an alias.
938             */
939            if ( isMove )
940            {
941                aliasTarget = aliasIdx.reverseLookup( id );
942    
943                if ( null != aliasTarget )
944                {
945                    addAliasIndices( id, new DN( getEntryDn( id ) ), aliasTarget );
946                }
947            }
948    
949            Cursor<IndexEntry<Long, E, Long>> children = list( id );
950            while ( children.next() )
951            {
952                // Get the child and its id
953                IndexEntry<Long, E, Long> rec = children.get();
954                Long childId = rec.getId();
955    
956                /* 
957                 * Calculate the DN for the child's new name by copying the parents
958                 * new name and adding the child's old upRdn to new name as its RDN
959                 */
960                DN childUpdn = ( DN ) updn.clone();
961                DN oldUpdn = new DN( getEntryUpdn( childId ) );
962    
963                String rdn = oldUpdn.get( oldUpdn.size() - 1 );
964                DN rdnDN = new DN( rdn );
965                rdnDN.normalize( schemaManager.getNormalizerMapping() );
966                childUpdn.add( rdnDN.getRdn() );
967    
968                // Modify the child
969                ServerEntry entry = lookup( childId );
970                entry.setDn( childUpdn );
971                master.put( childId, entry );
972    
973                // Recursively change the names of the children below
974                modifyDn( childId, childUpdn, isMove );
975            }
976    
977            children.close();
978        }
979    
980    
981        /**
982         * Adds a set of attribute values while affecting the appropriate userIndices.
983         * The entry is not persisted: it is only changed in anticipation for a put 
984         * into the master table.
985         *
986         * @param id the primary key of the entry
987         * @param entry the entry to alter
988         * @param mods the attribute and values to add 
989         * @throws Exception if index alteration or attribute addition fails
990         */
991        @SuppressWarnings("unchecked")
992        private void add( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception
993        {
994            if ( entry instanceof ClonedServerEntry )
995            {
996                throw new Exception( I18n.err( I18n.ERR_215 ) );
997            }
998    
999            String modsOid = schemaManager.getAttributeTypeRegistry().getOidByName( mods.getId() );
1000    
1001            // Special case for the ObjectClass index
1002            if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
1003            {
1004                for ( Value<?> value : mods )
1005                {
1006                    objectClassIdx.drop( value.getString(), id );
1007                }
1008            }
1009            else if ( hasUserIndexOn( modsOid ) )
1010            {
1011                Index<?, E, Long> index = getUserIndex( modsOid );
1012    
1013                for ( Value<?> value : mods )
1014                {
1015                    ( ( AvlIndex ) index ).add( value.get(), id );
1016                }
1017    
1018                // If the attr didn't exist for this id add it to existence index
1019                if ( !existenceIdx.forward( modsOid, id ) )
1020                {
1021                    existenceIdx.add( modsOid, id );
1022                }
1023            }
1024    
1025            // add all the values in mods to the same attribute in the entry
1026            AttributeType type = schemaManager.lookupAttributeTypeRegistry( modsOid );
1027    
1028            for ( Value<?> value : mods )
1029            {
1030                entry.add( type, value );
1031            }
1032    
1033            if ( modsOid.equals( SchemaConstants.ALIASED_OBJECT_NAME_AT_OID ) )
1034            {
1035                String ndnStr = ndnIdx.reverseLookup( id );
1036                addAliasIndices( id, new DN( ndnStr ), mods.getString() );
1037            }
1038        }
1039    
1040    
1041        /**
1042         * Completely removes the set of values for an attribute having the values 
1043         * supplied while affecting the appropriate userIndices.  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 Exception if index alteration or attribute modification fails.
1053         */
1054        @SuppressWarnings("unchecked")
1055        private void remove( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception
1056        {
1057            if ( entry instanceof ClonedServerEntry )
1058            {
1059                throw new Exception( I18n.err( I18n.ERR_215 ) );
1060            }
1061    
1062            String modsOid = schemaManager.getAttributeTypeRegistry().getOidByName( mods.getId() );
1063    
1064            // Special case for the ObjectClass index
1065            if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
1066            {
1067                for ( Value<?> value : mods )
1068                {
1069                    objectClassIdx.drop( value.getString(), id );
1070                }
1071            }
1072            else if ( hasUserIndexOn( modsOid ) )
1073            {
1074                Index<?, E, Long> index = getUserIndex( modsOid );
1075    
1076                for ( Value<?> value : mods )
1077                {
1078                    ( ( AvlIndex ) index ).drop( value.get(), id );
1079                }
1080    
1081                /* 
1082                 * If no attribute values exist for this entryId in the index then
1083                 * we remove the existance index entry for the removed attribute.
1084                 */
1085                if ( null == index.reverseLookup( id ) )
1086                {
1087                    existenceIdx.drop( modsOid, id );
1088                }
1089            }
1090    
1091            AttributeType attrType = schemaManager.lookupAttributeTypeRegistry( modsOid );
1092            /*
1093             * If there are no attribute values in the modifications then this 
1094             * implies the compelete removal of the attribute from the entry. Else
1095             * we remove individual attribute values from the entry in mods one 
1096             * at a time.
1097             */
1098            if ( mods.size() == 0 )
1099            {
1100                entry.removeAttributes( attrType );
1101            }
1102            else
1103            {
1104                EntryAttribute entryAttr = entry.get( attrType );
1105    
1106                for ( Value<?> value : mods )
1107                {
1108                    if ( value instanceof ServerStringValue )
1109                    {
1110                        entryAttr.remove( ( String ) value.get() );
1111                    }
1112                    else
1113                    {
1114                        entryAttr.remove( ( byte[] ) value.get() );
1115                    }
1116                }
1117    
1118                // if nothing is left just remove empty attribute
1119                if ( entryAttr.size() == 0 )
1120                {
1121                    entry.removeAttributes( entryAttr.getId() );
1122                }
1123            }
1124    
1125            // Aliases->single valued comp/partial attr removal is not relevant here
1126            if ( modsOid.equals( SchemaConstants.ALIASED_OBJECT_NAME_AT_OID ) )
1127            {
1128                dropAliasIndices( id );
1129            }
1130        }
1131    
1132    
1133        /**
1134         * Completely replaces the existing set of values for an attribute with the
1135         * modified values supplied affecting the appropriate userIndices.  The entry
1136         * is not persisted: it is only changed in anticipation for a put into the
1137         * master table.
1138         *
1139         * @param id the primary key of the entry
1140         * @param entry the entry to alter
1141         * @param mods the replacement attribute and values
1142         * @throws Exception if index alteration or attribute modification 
1143         * fails.
1144         */
1145        @SuppressWarnings("unchecked")
1146        private void replace( Long id, ServerEntry entry, EntryAttribute mods ) throws Exception
1147        {
1148            if ( entry instanceof ClonedServerEntry )
1149            {
1150                throw new Exception( I18n.err( I18n.ERR_215 ) );
1151            }
1152    
1153            String modsOid = schemaManager.getAttributeTypeRegistry().getOidByName( mods.getId() );
1154    
1155            // Special case for the ObjectClass index
1156            if ( modsOid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
1157            {
1158                // if the id exists in the index drop all existing attribute 
1159                // value index entries and add new ones
1160                if ( objectClassIdx.reverse( id ) )
1161                {
1162                    objectClassIdx.drop( id );
1163                }
1164    
1165                for ( Value<?> value : mods )
1166                {
1167                    objectClassIdx.add( value.getString(), id );
1168                }
1169            }
1170            else if ( hasUserIndexOn( modsOid ) )
1171            {
1172                Index<?, E, Long> index = getUserIndex( modsOid );
1173    
1174                // if the id exists in the index drop all existing attribute value index entries and add new ones
1175                if ( index.reverse( id ) )
1176                {
1177                    ( ( AvlIndex<?, E> ) index ).drop( id );
1178                }
1179    
1180                for ( Value<?> value : mods )
1181                {
1182                    ( ( AvlIndex<Object, E> ) index ).add( value.get(), id );
1183                }
1184    
1185                /* 
1186                 * If no attribute values exist for this entryId in the index then
1187                 * we remove the existance index entry for the removed attribute.
1188                 */
1189                if ( null == index.reverseLookup( id ) )
1190                {
1191                    existenceIdx.drop( modsOid, id );
1192                }
1193            }
1194    
1195            String aliasAttributeOid = SchemaConstants.ALIASED_OBJECT_NAME_AT_OID;
1196    
1197            if ( modsOid.equals( aliasAttributeOid ) )
1198            {
1199                dropAliasIndices( id );
1200            }
1201    
1202            // replaces old attributes with new modified ones if they exist
1203            if ( mods.size() > 0 )
1204            {
1205                entry.put( mods );
1206            }
1207            else
1208            // removes old attributes if new replacements do not exist
1209            {
1210                entry.remove( mods );
1211            }
1212    
1213            if ( modsOid.equals( aliasAttributeOid ) && mods.size() > 0 )
1214            {
1215                String ndnStr = ndnIdx.reverseLookup( id );
1216                addAliasIndices( id, new DN( ndnStr ), mods.getString() );
1217            }
1218        }
1219    
1220    
1221        public void modify( DN dn, ModificationOperation modOp, ServerEntry mods ) throws Exception
1222        {
1223            if ( mods instanceof ClonedServerEntry )
1224            {
1225                throw new Exception( I18n.err( I18n.ERR_215 ) );
1226            }
1227    
1228            Long id = getEntryId( dn.getNormName() );
1229            ServerEntry entry = ( ServerEntry ) master.get( id );
1230    
1231            for ( AttributeType attributeType : mods.getAttributeTypes() )
1232            {
1233                EntryAttribute attr = mods.get( attributeType );
1234    
1235                switch ( modOp )
1236                {
1237                    case ADD_ATTRIBUTE:
1238                        add( id, entry, attr );
1239                        break;
1240    
1241                    case REMOVE_ATTRIBUTE:
1242                        remove( id, entry, attr );
1243                        break;
1244    
1245                    case REPLACE_ATTRIBUTE:
1246                        replace( id, entry, attr );
1247    
1248                        break;
1249    
1250                    default:
1251                        throw new Exception( I18n.err( I18n.ERR_221 ) );
1252                }
1253            }
1254    
1255            master.put( id, entry );
1256        }
1257    
1258    
1259        public void modify( DN dn, List<Modification> mods ) throws Exception
1260        {
1261            Long id = getEntryId( dn.getNormName() );
1262            modify( id, mods );
1263        }
1264    
1265    
1266        public void modify( long entryId, List<Modification> mods ) throws Exception
1267        {
1268            ServerEntry entry = ( ServerEntry ) master.get( entryId );
1269    
1270            for ( Modification mod : mods )
1271            {
1272                ServerAttribute attrMods = ( ServerAttribute ) mod.getAttribute();
1273    
1274                switch ( mod.getOperation() )
1275                {
1276                    case ADD_ATTRIBUTE:
1277                        add( entryId, entry, attrMods );
1278                        break;
1279    
1280                    case REMOVE_ATTRIBUTE:
1281                        remove( entryId, entry, attrMods );
1282                        break;
1283    
1284                    case REPLACE_ATTRIBUTE:
1285                        replace( entryId, entry, attrMods );
1286                        break;
1287    
1288                    default:
1289                        throw new Exception( I18n.err( I18n.ERR_221 ) );
1290                }
1291            }
1292    
1293            master.put( entryId, entry );
1294        }
1295    
1296    
1297        public void move( DN oldChildDn, DN newParentDn, RDN newRdn, boolean deleteOldRdn ) throws Exception
1298        {
1299            Long childId = getEntryId( oldChildDn.getNormName() );
1300            rename( oldChildDn, newRdn, deleteOldRdn );
1301            DN newUpdn = move( oldChildDn, childId, newParentDn );
1302    
1303            // Update the current entry
1304            ServerEntry entry = lookup( childId );
1305            entry.setDn( newUpdn );
1306            master.put( childId, entry );
1307        }
1308    
1309    
1310        public void move( DN oldChildDn, DN newParentDn ) throws Exception
1311        {
1312            Long childId = getEntryId( oldChildDn.getNormName() );
1313            DN newUpdn = move( oldChildDn, childId, newParentDn );
1314    
1315            // Update the current entry
1316            ServerEntry entry = lookup( childId );
1317            entry.setDn( newUpdn );
1318            master.put( childId, entry );
1319        }
1320    
1321    
1322        /**
1323         * Moves an entry under a new parent.  The operation causes a shift in the
1324         * parent child relationships between the old parent, new parent and the 
1325         * child moved.  All other descendant entries under the child never change
1326         * their direct parent child relationships.  Hence after the parent child
1327         * relationship changes are broken at the old parent and set at the new
1328         * parent a modifyDn operation is conducted to handle name changes 
1329         * propagating down through the moved child and its descendants.
1330         * 
1331         * @param oldChildDn the normalized dn of the child to be moved
1332         * @param childId the id of the child being moved
1333         * @param newParentDn the normalized dn of the new parent for the child
1334         * @throws NamingException if something goes wrong
1335         */
1336        private DN move( DN oldChildDn, Long childId, DN newParentDn ) throws Exception
1337        {
1338            // Get the child and the new parent to be entries and Ids
1339            Long newParentId = getEntryId( newParentDn.getNormName() );
1340            Long oldParentId = getParentId( childId );
1341    
1342            /*
1343             * All aliases including and below oldChildDn, will be affected by
1344             * the move operation with respect to one and subtree userIndices since
1345             * their relationship to ancestors above oldChildDn will be 
1346             * destroyed.  For each alias below and including oldChildDn we will
1347             * drop the index tuples mapping ancestor ids above oldChildDn to the
1348             * respective target ids of the aliases.
1349             */
1350            dropMovedAliasIndices( oldChildDn );
1351    
1352            /*
1353             * Drop the old parent child relationship and add the new one
1354             * Set the new parent id for the child replacing the old parent id
1355             */
1356            oneLevelIdx.drop( oldParentId, childId );
1357            oneLevelIdx.add( newParentId, childId );
1358    
1359            updateSubLevelIndex( childId, oldParentId, newParentId );
1360    
1361            /*
1362             * Build the new user provided DN (updn) for the child using the child's
1363             * user provided RDN & the new parent's UPDN.  Basically add the child's
1364             * UpRdn String to the tail of the new parent's Updn Name.
1365             */
1366            DN childUpdn = new DN( getEntryUpdn( childId ) );
1367            String childRdn = childUpdn.get( childUpdn.size() - 1 );
1368            DN newUpdn = new DN( getEntryUpdn( newParentId ) );
1369            newUpdn.add( newUpdn.size(), childRdn );
1370    
1371            // Call the modifyDn operation with the new updn
1372            modifyDn( childId, newUpdn, true );
1373    
1374            return newUpdn;
1375        }
1376    
1377    
1378        /**
1379         * Changes the relative distinguished name of an entry specified by a 
1380         * distinguished name with the optional removal of the old RDN attribute
1381         * value from the entry.  Name changes propagate down as dn changes to the 
1382         * descendants of the entry where the RDN changed. 
1383         * 
1384         * An RDN change operation does not change parent child relationships.  It 
1385         * merely propagates a name change at a point in the DIT where the RDN is 
1386         * changed. The change propagates down the subtree rooted at the 
1387         * distinguished name specified.
1388         *
1389         * @param dn the normalized distinguished name of the entry to alter
1390         * @param newRdn the new RDN to set
1391         * @param deleteOldRdn whether or not to remove the old RDN attr/val
1392         * @throws Exception if there are any errors propagating the name changes
1393         */
1394        @SuppressWarnings("unchecked")
1395        public void rename( DN dn, RDN newRdn, boolean deleteOldRdn ) throws Exception
1396        {
1397            Long id = getEntryId( dn.getNormName() );
1398            ServerEntry entry = lookup( id );
1399            DN updn = entry.getDn();
1400    
1401            /* 
1402             * H A N D L E   N E W   R D N
1403             * ====================================================================
1404             * Add the new RDN attribute to the entry.  If an index exists on the 
1405             * new RDN attribute we add the index for this attribute value pair.
1406             * Also we make sure that the existance index shows the existance of the
1407             * new RDN attribute within this entry.
1408             */
1409    
1410            for ( AVA newAtav : newRdn )
1411            {
1412                String newNormType = newAtav.getNormType();
1413                Object newNormValue = newAtav.getNormValue().get();
1414                AttributeType newRdnAttrType = schemaManager.lookupAttributeTypeRegistry( newNormType );
1415    
1416                entry.add( newRdnAttrType, newAtav.getUpValue() );
1417    
1418                if ( hasUserIndexOn( newNormType ) )
1419                {
1420                    Index<?, E, Long> index = getUserIndex( newNormType );
1421                    ( ( Index ) index ).add( newNormValue, id );
1422    
1423                    // Make sure the altered entry shows the existence of the new attrib
1424                    if ( !existenceIdx.forward( newNormType, id ) )
1425                    {
1426                        existenceIdx.add( newNormType, id );
1427                    }
1428                }
1429            }
1430    
1431            /*
1432             * H A N D L E   O L D   R D N
1433             * ====================================================================
1434             * If the old RDN is to be removed we need to get the attribute and 
1435             * value for it.  Keep in mind the old RDN need not be based on the 
1436             * same attr as the new one.  We remove the RDN value from the entry
1437             * and remove the value/id tuple from the index on the old RDN attr
1438             * if any.  We also test if the delete of the old RDN index tuple 
1439             * removed all the attribute values of the old RDN using a reverse
1440             * lookup.  If so that means we blew away the last value of the old 
1441             * RDN attribute.  In this case we need to remove the attrName/id 
1442             * tuple from the existance index.
1443             * 
1444             * We only remove an ATAV of the old RDN if it is not included in the
1445             * new RDN.
1446             */
1447    
1448            if ( deleteOldRdn )
1449            {
1450                RDN oldRdn = updn.getRdn();
1451                for ( AVA oldAtav : oldRdn )
1452                {
1453                    // check if the new ATAV is part of the old RDN
1454                    // if that is the case we do not remove the ATAV
1455                    boolean mustRemove = true;
1456                    for ( AVA newAtav : newRdn )
1457                    {
1458                        if ( oldAtav.equals( newAtav ) )
1459                        {
1460                            mustRemove = false;
1461                            break;
1462                        }
1463                    }
1464    
1465                    if ( mustRemove )
1466                    {
1467                        String oldNormType = oldAtav.getNormType();
1468                        String oldNormValue = oldAtav.getNormValue().getString();
1469                        AttributeType oldRdnAttrType = schemaManager.lookupAttributeTypeRegistry( oldNormType );
1470                        entry.remove( oldRdnAttrType, oldNormValue );
1471    
1472                        if ( hasUserIndexOn( oldNormType ) )
1473                        {
1474                            Index<?, E, Long> index = getUserIndex( oldNormType );
1475                            ( ( AvlIndex ) index ).drop( oldNormValue, id );
1476    
1477                            /*
1478                             * If there is no value for id in this index due to our
1479                             * drop above we remove the oldRdnAttr from the existance idx
1480                             */
1481                            if ( null == index.reverseLookup( id ) )
1482                            {
1483                                existenceIdx.drop( oldNormType, id );
1484                            }
1485                        }
1486                    }
1487                }
1488            }
1489    
1490            /*
1491             * H A N D L E   D N   C H A N G E
1492             * ====================================================================
1493             * 1) Build the new user defined distinguished name
1494             *      - clone / copy old updn
1495             *      - remove old upRdn from copy
1496             *      - add the new upRdn to the copy
1497             * 2) Make call to recursive modifyDn method to change the names of the
1498             *    entry and its descendants
1499             */
1500    
1501            DN newUpdn = ( DN ) updn.clone(); // copy da old updn
1502            newUpdn.remove( newUpdn.size() - 1 ); // remove old upRdn
1503            newUpdn.add( newRdn.getUpName() ); // add da new upRdn
1504    
1505            // gotta normalize cuz this thang is cloned and not normalized by default
1506            newUpdn.normalize( schemaManager.getNormalizerMapping() );
1507    
1508            modifyDn( id, newUpdn, false ); // propagate dn changes
1509    
1510            // Update the current entry
1511            entry.setDn( newUpdn );
1512            master.put( id, entry );
1513        }
1514    
1515    
1516        /**
1517         * {@inheritDoc}
1518         */
1519        public void setAliasIndex( Index<String, E, Long> index ) throws Exception
1520        {
1521            protect( "aliasIndex" );
1522            if ( index instanceof AvlIndex<?, ?> )
1523            {
1524                this.aliasIdx = ( AvlIndex<String, E> ) index;
1525            }
1526            else
1527            {
1528                this.aliasIdx = ( AvlIndex<String, E> ) convert( index );
1529            }
1530    
1531            // FIXME is this attribute ID or its OID
1532            systemIndices.put( index.getAttributeId(), aliasIdx );
1533        }
1534    
1535    
1536        /**
1537         * {@inheritDoc}
1538         */
1539        public void setName( String name )
1540        {
1541            protect( "name" );
1542            this.name = name;
1543        }
1544    
1545    
1546        /**
1547         * {@inheritDoc}
1548         */
1549        public void setNdnIndex( Index<String, E, Long> index ) throws Exception
1550        {
1551            protect( "ndnIndex" );
1552            if ( index instanceof AvlIndex<?, ?> )
1553            {
1554                this.ndnIdx = ( AvlIndex<String, E> ) index;
1555            }
1556            else
1557            {
1558                this.ndnIdx = ( AvlIndex<String, E> ) convert( index );
1559            }
1560    
1561            systemIndices.put( index.getAttributeId(), ndnIdx );
1562        }
1563    
1564    
1565        /**
1566         * {@inheritDoc}
1567         */
1568        public void setOneAliasIndex( Index<Long, E, Long> index ) throws Exception
1569        {
1570            protect( "oneAliasIndex" );
1571            if ( index instanceof AvlIndex<?, ?> )
1572            {
1573                this.oneAliasIdx = ( AvlIndex<Long, E> ) index;
1574            }
1575            else
1576            {
1577                this.oneAliasIdx = ( AvlIndex<Long, E> ) convert( index );
1578            }
1579    
1580            systemIndices.put( index.getAttributeId(), oneAliasIdx );
1581        }
1582    
1583    
1584        /**
1585         * {@inheritDoc}
1586         */
1587        public void setOneLevelIndex( Index<Long, E, Long> index ) throws Exception
1588        {
1589            protect( "oneLevelIndex" );
1590            if ( index instanceof AvlIndex<?, ?> )
1591            {
1592                this.oneLevelIdx = ( AvlIndex<Long, E> ) index;
1593            }
1594            else
1595            {
1596                this.oneLevelIdx = ( AvlIndex<Long, E> ) convert( index );
1597            }
1598    
1599            systemIndices.put( index.getAttributeId(), oneLevelIdx );
1600        }
1601    
1602    
1603        /**
1604         * {@inheritDoc}
1605         */
1606        public void setPresenceIndex( Index<String, E, Long> index ) throws Exception
1607        {
1608            protect( "presenceIndex" );
1609            if ( index instanceof AvlIndex<?, ?> )
1610            {
1611                this.existenceIdx = ( AvlIndex<String, E> ) index;
1612            }
1613            else
1614            {
1615                this.existenceIdx = ( AvlIndex<String, E> ) convert( index );
1616            }
1617    
1618            systemIndices.put( index.getAttributeId(), existenceIdx );
1619        }
1620    
1621    
1622        /**
1623         * {@inheritDoc}
1624         */
1625        public void setProperty( String propertyName, String propertyValue ) throws Exception
1626        {
1627            master.setProperty( propertyName, propertyValue );
1628        }
1629    
1630    
1631        /**
1632         * {@inheritDoc}
1633         */
1634        public void setSubAliasIndex( Index<Long, E, Long> index ) throws Exception
1635        {
1636            protect( "subAliasIndex" );
1637            if ( index instanceof AvlIndex<?, ?> )
1638            {
1639                this.subAliasIdx = ( AvlIndex<Long, E> ) index;
1640            }
1641            else
1642            {
1643                this.subAliasIdx = ( AvlIndex<Long, E> ) convert( index );
1644            }
1645    
1646            systemIndices.put( index.getAttributeId(), subAliasIdx );
1647        }
1648    
1649    
1650        /**
1651         * {@inheritDoc}
1652         */
1653        public void setSubLevelIndex( Index<Long, E, Long> index ) throws Exception
1654        {
1655            protect( "subLevelIndex" );
1656            if ( index instanceof AvlIndex<?, ?> )
1657            {
1658                this.subLevelIdx = ( AvlIndex<Long, E> ) index;
1659            }
1660            else
1661            {
1662                this.subLevelIdx = ( AvlIndex<Long, E> ) convert( index );
1663            }
1664    
1665            systemIndices.put( index.getAttributeId(), subLevelIdx );
1666        }
1667    
1668    
1669        /**
1670         * {@inheritDoc}
1671         */
1672        public void setSuffixDn( String suffixDn )
1673        {
1674            protect( "suffixDn" );
1675            try
1676            {
1677                this.suffixDn = new DN( suffixDn );
1678            }
1679            catch ( InvalidNameException e )
1680            {
1681                throw new IllegalArgumentException( e );
1682            }
1683        }
1684    
1685    
1686        /**
1687         * {@inheritDoc}
1688         */
1689        public void setUpdnIndex( Index<String, E, Long> index ) throws Exception
1690        {
1691            protect( "updnIndex" );
1692            if ( index instanceof AvlIndex<?, ?> )
1693            {
1694                this.updnIdx = ( AvlIndex<String, E> ) index;
1695            }
1696            else
1697            {
1698                this.updnIdx = ( AvlIndex<String, E> ) convert( index );
1699            }
1700    
1701            systemIndices.put( index.getAttributeId(), updnIdx );
1702        }
1703    
1704    
1705        /**
1706         * {@inheritDoc}
1707         */
1708        public void setUserIndices( Set<Index<? extends Object, E, Long>> userIndices )
1709        {
1710            protect( "setUserIndices" );
1711    
1712            for ( Index<? extends Object, E, Long> index : userIndices )
1713            {
1714                if ( index instanceof AvlIndex<?, ?> )
1715                {
1716                    this.userIndices.put( index.getAttributeId(), ( AvlIndex<? extends Object, E> ) index );
1717                    continue;
1718                }
1719    
1720                LOG.warn( "Supplied index {} is not a AvlIndex.  "
1721                    + "Will create new AvlIndex using copied configuration parameters.", index );
1722    
1723                AvlIndex<Object, E> avlIndex = ( AvlIndex<Object, E> ) convert( index );
1724    
1725                this.userIndices.put( index.getAttributeId(), avlIndex );
1726            }
1727        }
1728    
1729    
1730        private <K> AvlIndex<K, E> convert( Index<K, E, Long> index )
1731        {
1732            AvlIndex<K, E> avlIndex = new AvlIndex<K, E>();
1733            avlIndex.setAttributeId( index.getAttributeId() );
1734            return avlIndex;
1735        }
1736    
1737    
1738        private void protect( String method )
1739        {
1740            if ( initialized )
1741            {
1742                throw new IllegalStateException( I18n.err( I18n.ERR_222, method ) );
1743            }
1744        }
1745    
1746    
1747        /**
1748         * {@inheritDoc}
1749         */
1750        public Iterator<String> systemIndices()
1751        {
1752            return systemIndices.keySet().iterator();
1753        }
1754    
1755    
1756        /**
1757         * {@inheritDoc}
1758         */
1759        public Iterator<String> userIndices()
1760        {
1761            return userIndices.keySet().iterator();
1762        }
1763    
1764    
1765        /**
1766         * Adds userIndices for an aliasEntry to be added to the database while checking
1767         * for constrained alias constructs like alias cycles and chaining.
1768         * 
1769         * @param aliasDn normalized distinguished name for the alias entry
1770         * @param aliasTarget the user provided aliased entry dn as a string
1771         * @param aliasId the id of alias entry to add
1772         * @throws NamingException if index addition fails, and if the alias is
1773         * not allowed due to chaining or cycle formation.
1774         * @throws Exception if the wrappedCursor btrees cannot be altered
1775         */
1776        private void addAliasIndices( Long aliasId, DN aliasDn, String aliasTarget ) throws Exception
1777        {
1778            DN normalizedAliasTargetDn; // Name value of aliasedObjectName
1779            Long targetId; // Id of the aliasedObjectName
1780            DN ancestorDn; // Name of an alias entry relative
1781            Long ancestorId; // Id of an alias entry relative
1782    
1783            // Access aliasedObjectName, normalize it and generate the Name 
1784            normalizedAliasTargetDn = new DN( aliasTarget );
1785            normalizedAliasTargetDn.normalize( schemaManager.getNormalizerMapping() );
1786    
1787            /*
1788             * Check For Cycles
1789             * 
1790             * Before wasting time to lookup more values we check using the target
1791             * dn to see if we have the possible formation of an alias cycle.  This
1792             * happens when the alias refers back to a target that is also a 
1793             * relative of the alias entry.  For detection we test if the aliased
1794             * entry Dn starts with the target Dn.  If it does then we know the 
1795             * aliased target is a relative and we have a perspecitive cycle.
1796             */
1797            if ( aliasDn.startsWith( normalizedAliasTargetDn ) )
1798            {
1799                if ( aliasDn.equals( normalizedAliasTargetDn ) )
1800                {
1801                    throw new Exception( I18n.err( I18n.ERR_223 ) );
1802                }
1803    
1804                throw new Exception( I18n.err( I18n.ERR_224, aliasTarget, aliasDn ) );
1805            }
1806    
1807            /*
1808             * Check For Aliases External To Naming Context
1809             * 
1810             * id may be null but the alias may be to a valid entry in 
1811             * another namingContext.  Such aliases are not allowed and we
1812             * need to point it out to the user instead of saying the target
1813             * does not exist when it potentially could outside of this upSuffix.
1814             */
1815            if ( !normalizedAliasTargetDn.startsWith( suffixDn ) )
1816            {
1817                // Complain specifically about aliases to outside naming contexts
1818                throw new Exception( I18n.err( I18n.ERR_225, suffixDn.getName() ) );
1819            }
1820    
1821            // L O O K U P   T A R G E T   I D
1822            targetId = ndnIdx.forwardLookup( normalizedAliasTargetDn.toNormName() );
1823    
1824            /*
1825             * Check For Target Existance
1826             * 
1827             * We do not allow the creation of inconsistant aliases.  Aliases should
1828             * not be broken links.  If the target does not exist we start screaming
1829             */
1830            if ( null == targetId )
1831            {
1832                // Complain about target not existing
1833                throw new Exception( I18n.err( I18n.ERR_226 ) );
1834            }
1835    
1836            /*
1837             * Detect Direct Alias Chain Creation
1838             * 
1839             * Rather than resusitate the target to test if it is an alias and fail
1840             * due to chaing creation we use the alias index to determine if the
1841             * target is an alias.  Hence if the alias we are about to create points
1842             * to another alias as its target in the aliasedObjectName attribute, 
1843             * then we have a situation where an alias chain is being created.  
1844             * Alias chaining is not allowed so we throw and exception. 
1845             */
1846            if ( null != aliasIdx.reverseLookup( targetId ) )
1847            {
1848                // Complain about illegal alias chain
1849                throw new Exception( I18n.err( I18n.ERR_227 ) );
1850            }
1851    
1852            // Add the alias to the simple alias index
1853            aliasIdx.add( normalizedAliasTargetDn.getNormName(), aliasId );
1854    
1855            /*
1856             * Handle One Level Scope Alias Index
1857             * 
1858             * The first relative is special with respect to the one level alias
1859             * index.  If the target is not a sibling of the alias then we add the
1860             * index entry maping the parent's id to the aliased target id.
1861             */
1862            ancestorDn = ( DN ) aliasDn.clone();
1863            ancestorDn.remove( aliasDn.size() - 1 );
1864            ancestorId = getEntryId( ancestorDn.toNormName() );
1865    
1866            // check if alias parent and aliased entry are the same
1867            DN normalizedAliasTargetParentDn = ( DN ) normalizedAliasTargetDn.clone();
1868            normalizedAliasTargetParentDn.remove( normalizedAliasTargetDn.size() - 1 );
1869            if ( !aliasDn.startsWith( normalizedAliasTargetParentDn ) )
1870            {
1871                oneAliasIdx.add( ancestorId, targetId );
1872            }
1873    
1874            /*
1875             * Handle Sub Level Scope Alias Index
1876             * 
1877             * Walk the list of relatives from the parents up to the upSuffix, testing
1878             * to see if the alias' target is a descendant of the relative.  If the
1879             * alias target is not a descentant of the relative it extends the scope
1880             * and is added to the sub tree scope alias index.  The upSuffix node is
1881             * ignored since everything is under its scope.  The first loop 
1882             * iteration shall handle the parents.
1883             */
1884            while ( !ancestorDn.equals( suffixDn ) && null != ancestorId )
1885            {
1886                if ( !NamespaceTools.isDescendant( ancestorDn, normalizedAliasTargetDn ) )
1887                {
1888                    subAliasIdx.add( ancestorId, targetId );
1889                }
1890    
1891                ancestorDn.remove( ancestorDn.size() - 1 );
1892                ancestorId = getEntryId( ancestorDn.toNormName() );
1893            }
1894        }
1895    
1896    
1897        /**
1898         * Removes the index entries for an alias before the entry is deleted from
1899         * the master table.
1900         * 
1901         * @todo Optimize this by walking the hierarchy index instead of the name 
1902         * @param aliasId the id of the alias entry in the master table
1903         * @throws NamingException if we cannot parse ldap names
1904         * @throws Exception if we cannot delete index values in the database
1905         */
1906        private void dropAliasIndices( Long aliasId ) throws Exception
1907        {
1908            String targetDn = aliasIdx.reverseLookup( aliasId );
1909            Long targetId = getEntryId( targetDn );
1910            String aliasDn = getEntryDn( aliasId );
1911            DN ancestorDn = ( DN ) new DN( aliasDn ).getPrefix( 1 );
1912            Long ancestorId = getEntryId( ancestorDn.getNormName() );
1913    
1914            /*
1915             * We cannot just drop all tuples in the one level and subtree userIndices
1916             * linking baseIds to the targetId.  If more than one alias refers to
1917             * the target then droping all tuples with a value of targetId would
1918             * make all other aliases to the target inconsistent.
1919             * 
1920             * We need to walk up the path of alias ancestors until we reach the 
1921             * upSuffix, deleting each ( ancestorId, targetId ) tuple in the
1922             * subtree scope alias.  We only need to do this for the direct parent
1923             * of the alias on the one level subtree.
1924             */
1925            oneAliasIdx.drop( ancestorId, targetId );
1926            subAliasIdx.drop( ancestorId, targetId );
1927    
1928            while ( !ancestorDn.equals( suffixDn ) )
1929            {
1930                ancestorDn = ( DN ) ancestorDn.getPrefix( 1 );
1931                ancestorId = getEntryId( ancestorDn.getNormName() );
1932    
1933                subAliasIdx.drop( ancestorId, targetId );
1934            }
1935    
1936            // Drops all alias tuples pointing to the id of the alias to be deleted
1937            aliasIdx.drop( aliasId );
1938        }
1939    
1940    
1941        /**
1942         * 
1943         * updates the SubLevel Index as part of a move operation.
1944         *
1945         * @param childId child id to be moved
1946         * @param oldParentId old parent's id
1947         * @param newParentId new parent's id
1948         * @throws Exception
1949         */
1950        private void updateSubLevelIndex( Long childId, Long oldParentId, Long newParentId ) throws Exception
1951        {
1952            Long tempId = oldParentId;
1953            List<Long> parentIds = new ArrayList<Long>();
1954    
1955            // find all the parents of the oldParentId
1956            while ( tempId != 0 && tempId != 1 && tempId != null )
1957            {
1958                parentIds.add( tempId );
1959                tempId = getParentId( tempId );
1960            }
1961    
1962            // find all the children of the childId
1963            Cursor<IndexEntry<Long, E, Long>> cursor = subLevelIdx.forwardCursor( childId );
1964    
1965            List<Long> childIds = new ArrayList<Long>();
1966            childIds.add( childId );
1967    
1968            while ( cursor.next() )
1969            {
1970                childIds.add( cursor.get().getId() );
1971            }
1972    
1973            // detach the childId and all its children from oldParentId and all it parents excluding the root
1974            for ( Long pid : parentIds )
1975            {
1976                for ( Long cid : childIds )
1977                {
1978                    subLevelIdx.drop( pid, cid );
1979                }
1980            }
1981    
1982            parentIds.clear();
1983            tempId = newParentId;
1984    
1985            // find all the parents of the newParentId
1986            while ( tempId != 0 && tempId != 1 && tempId != null )
1987            {
1988                parentIds.add( tempId );
1989                tempId = getParentId( tempId );
1990            }
1991    
1992            // attach the childId and all its children to newParentId and all it parents excluding the root
1993            for ( Long id : parentIds )
1994            {
1995                for ( Long cid : childIds )
1996                {
1997                    subLevelIdx.add( id, cid );
1998                }
1999            }
2000        }
2001    
2002    
2003        /**
2004         * For all aliases including and under the moved base, this method removes
2005         * one and subtree alias index tuples for old ancestors above the moved base
2006         * that will no longer be ancestors after the move.
2007         * 
2008         * @param movedBase the base at which the move occured - the moved node
2009         * @throws NamingException if system userIndices fail
2010         */
2011        private void dropMovedAliasIndices( final DN movedBase ) throws Exception
2012        {
2013            //        // Find all the aliases from movedBase down
2014            //        IndexAssertion<Object,E> isBaseDescendant = new IndexAssertion<Object,E>()
2015            //        {
2016            //            public boolean assertCandidate( IndexEntry<Object,E> rec ) throws Exception
2017            //            {
2018            //                String dn = getEntryDn( rec.getId() );
2019            //                return dn.endsWith( movedBase.toString() );
2020            //            }
2021            //        };
2022    
2023            Long movedBaseId = getEntryId( movedBase.getNormName() );
2024    
2025            if ( aliasIdx.reverseLookup( movedBaseId ) != null )
2026            {
2027                dropAliasIndices( movedBaseId, movedBase );
2028            }
2029    
2030            //        throw new NotImplementedException( "Fix the code below this line" );
2031    
2032            //        NamingEnumeration<ForwardIndexEntry> aliases =
2033            //                new IndexAssertionEnumeration( aliasIdx.listIndices( movedBase.toString(), true ), isBaseDescendant );
2034            //
2035            //        while ( aliases.hasMore() )
2036            //        {
2037            //            ForwardIndexEntry entry = aliases.next();
2038            //            dropAliasIndices( (Long)entry.getId(), movedBase );
2039            //        }
2040        }
2041    
2042    
2043        /**
2044         * For the alias id all ancestor one and subtree alias tuples are moved 
2045         * above the moved base.
2046         * 
2047         * @param aliasId the id of the alias 
2048         * @param movedBase the base where the move occured
2049         * @throws Exception if userIndices fail
2050         */
2051        private void dropAliasIndices( Long aliasId, DN movedBase ) throws Exception
2052        {
2053            String targetDn = aliasIdx.reverseLookup( aliasId );
2054            Long targetId = getEntryId( targetDn );
2055            String aliasDn = getEntryDn( aliasId );
2056    
2057            /*
2058             * Start droping index tuples with the first ancestor right above the 
2059             * moved base.  This is the first ancestor effected by the move.
2060             */
2061            DN ancestorDn = ( DN ) movedBase.getPrefix( 1 );
2062            Long ancestorId = getEntryId( ancestorDn.getNormName() );
2063    
2064            /*
2065             * We cannot just drop all tuples in the one level and subtree userIndices
2066             * linking baseIds to the targetId.  If more than one alias refers to
2067             * the target then droping all tuples with a value of targetId would
2068             * make all other aliases to the target inconsistent.
2069             * 
2070             * We need to walk up the path of alias ancestors right above the moved 
2071             * base until we reach the upSuffix, deleting each ( ancestorId,
2072             * targetId ) tuple in the subtree scope alias.  We only need to do 
2073             * this for the direct parent of the alias on the one level subtree if
2074             * the moved base is the alias.
2075             */
2076            if ( aliasDn.equals( movedBase.toString() ) )
2077            {
2078                oneAliasIdx.drop( ancestorId, targetId );
2079            }
2080    
2081            subAliasIdx.drop( ancestorId, targetId );
2082    
2083            while ( !ancestorDn.equals( suffixDn ) )
2084            {
2085                ancestorDn = ( DN ) ancestorDn.getPrefix( 1 );
2086                ancestorId = getEntryId( ancestorDn.getNormName() );
2087    
2088                subAliasIdx.drop( ancestorId, targetId );
2089            }
2090        }
2091    
2092    
2093        /**
2094         * always returns 0 (zero), cause this is a inmemory store
2095         */
2096        public int getCacheSize()
2097        {
2098            return 0;
2099        }
2100    
2101    
2102        public Index<String, E, Long> getEntryCsnIndex()
2103        {
2104            return entryCsnIdx;
2105        }
2106    
2107    
2108        public Index<String, E, Long> getEntryUuidIndex()
2109        {
2110            return entryUuidIdx;
2111        }
2112    
2113    
2114        public Index<String, E, Long> getObjectClassIndex()
2115        {
2116            return objectClassIdx;
2117        }
2118    
2119    
2120        public void setEntryCsnIndex( Index<String, E, Long> index ) throws Exception
2121        {
2122            protect( "entryCsnIndex" );
2123    
2124            if ( index instanceof AvlIndex<?, ?> )
2125            {
2126                this.entryCsnIdx = ( AvlIndex<String, E> ) index;
2127            }
2128            else
2129            {
2130                this.entryCsnIdx = ( AvlIndex<String, E> ) convert( index );
2131            }
2132    
2133            systemIndices.put( index.getAttributeId(), entryCsnIdx );
2134        }
2135    
2136    
2137        public void setSyncOnWrite( boolean sync )
2138        {
2139            // do nothing
2140        }
2141    
2142    
2143        public void setWorkingDirectory( File wkDir )
2144        {
2145            //do nothing
2146        }
2147    
2148    
2149        public File getWorkingDirectory()
2150        {
2151            // returns null always
2152            return null;
2153        }
2154    
2155    
2156        public boolean isSyncOnWrite()
2157        {
2158            return false;
2159        }
2160    
2161    
2162        public void setCacheSize( int size )
2163        {
2164            // do nothing
2165        }
2166    
2167    
2168        public void setObjectClassIndex( Index<String, E, Long> index ) throws NamingException
2169        {
2170            protect( "objectClassIndex" );
2171            if ( index instanceof AvlIndex<?, ?> )
2172            {
2173                this.objectClassIdx = ( AvlIndex<String, E> ) index;
2174            }
2175            else
2176            {
2177                objectClassIdx = convert( index );
2178            }
2179    
2180            systemIndices.put( index.getAttributeId(), objectClassIdx );
2181        }
2182    
2183    
2184        public void setEntryUuidIndex( Index<String, E, Long> index ) throws NamingException
2185        {
2186            protect( "entryUuidIndex" );
2187            if ( index instanceof AvlIndex<?, ?> )
2188            {
2189                this.entryUuidIdx = ( AvlIndex<String, E> ) index;
2190            }
2191            else
2192            {
2193                entryUuidIdx = convert( index );
2194            }
2195    
2196            systemIndices.put( index.getAttributeId(), entryUuidIdx );
2197        }
2198    
2199    
2200        /**
2201         * @{inhertDoc}
2202         */
2203        public void sync() throws Exception
2204        {
2205        }
2206    
2207    
2208        /**
2209         * @{inhertDoc}
2210         */
2211        public Long getDefaultId()
2212        {
2213            return 1L;
2214        }
2215    
2216    }