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 }