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