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