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 }