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; 021 022 023 import java.io.IOException; 024 import java.util.ArrayList; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.HashSet; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.Properties; 032 import java.util.Set; 033 import java.util.UUID; 034 035 import javax.naming.ConfigurationException; 036 import javax.naming.NameNotFoundException; 037 import javax.naming.directory.SearchControls; 038 039 import org.apache.directory.server.constants.ServerDNConstants; 040 import org.apache.directory.server.core.CoreSession; 041 import org.apache.directory.server.core.DefaultCoreSession; 042 import org.apache.directory.server.core.DirectoryService; 043 import org.apache.directory.server.core.LdapPrincipal; 044 import org.apache.directory.server.core.entry.ClonedServerEntry; 045 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor; 046 import org.apache.directory.server.core.filtering.CursorList; 047 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 048 import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext; 049 import org.apache.directory.server.core.interceptor.context.AddOperationContext; 050 import org.apache.directory.server.core.interceptor.context.BindOperationContext; 051 import org.apache.directory.server.core.interceptor.context.CompareOperationContext; 052 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext; 053 import org.apache.directory.server.core.interceptor.context.EntryOperationContext; 054 import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext; 055 import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext; 056 import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext; 057 import org.apache.directory.server.core.interceptor.context.ListOperationContext; 058 import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext; 059 import org.apache.directory.server.core.interceptor.context.LookupOperationContext; 060 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 061 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext; 062 import org.apache.directory.server.core.interceptor.context.MoveOperationContext; 063 import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext; 064 import org.apache.directory.server.core.interceptor.context.RenameOperationContext; 065 import org.apache.directory.server.core.interceptor.context.SearchOperationContext; 066 import org.apache.directory.server.core.interceptor.context.UnbindOperationContext; 067 import org.apache.directory.server.i18n.I18n; 068 import org.apache.directory.shared.ldap.MultiException; 069 import org.apache.directory.shared.ldap.NotImplementedException; 070 import org.apache.directory.shared.ldap.codec.controls.CascadeControl; 071 import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl; 072 import org.apache.directory.shared.ldap.codec.controls.replication.syncDoneValue.SyncDoneValueControl; 073 import org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue.SyncInfoValueControl; 074 import org.apache.directory.shared.ldap.codec.controls.replication.syncRequestValue.SyncRequestValueControl; 075 import org.apache.directory.shared.ldap.codec.controls.replication.syncStateValue.SyncStateValueControl; 076 import org.apache.directory.shared.ldap.codec.search.controls.entryChange.EntryChangeControl; 077 import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl; 078 import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl; 079 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl; 080 import org.apache.directory.shared.ldap.constants.AuthenticationLevel; 081 import org.apache.directory.shared.ldap.constants.SchemaConstants; 082 import org.apache.directory.shared.ldap.cursor.EmptyCursor; 083 import org.apache.directory.shared.ldap.cursor.SingletonCursor; 084 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute; 085 import org.apache.directory.shared.ldap.entry.DefaultServerEntry; 086 import org.apache.directory.shared.ldap.entry.EntryAttribute; 087 import org.apache.directory.shared.ldap.entry.ServerEntry; 088 import org.apache.directory.shared.ldap.entry.Value; 089 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeTypeException; 090 import org.apache.directory.shared.ldap.exception.LdapNoSuchObjectException; 091 import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException; 092 import org.apache.directory.shared.ldap.filter.ExprNode; 093 import org.apache.directory.shared.ldap.filter.PresenceNode; 094 import org.apache.directory.shared.ldap.filter.SearchScope; 095 import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect; 096 import org.apache.directory.shared.ldap.name.DN; 097 import org.apache.directory.shared.ldap.schema.AttributeType; 098 import org.apache.directory.shared.ldap.schema.Normalizer; 099 import org.apache.directory.shared.ldap.schema.SchemaManager; 100 import org.apache.directory.shared.ldap.schema.UsageEnum; 101 import org.apache.directory.shared.ldap.util.DateUtils; 102 import org.apache.directory.shared.ldap.util.NamespaceTools; 103 import org.apache.directory.shared.ldap.util.StringTools; 104 import org.apache.directory.shared.ldap.util.tree.DnBranchNode; 105 import org.slf4j.Logger; 106 import org.slf4j.LoggerFactory; 107 108 109 /** 110 * A root {@link Partition} that contains all other partitions, and 111 * routes all operations to the child partition that matches to its base suffixes. 112 * It also provides some extended operations such as accessing rootDSE and 113 * listing base suffixes. 114 * 115 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 116 * @version $Rev: 927404 $, $Date: 2010-03-25 14:55:18 +0100 (Thu, 25 Mar 2010) $ 117 */ 118 public class DefaultPartitionNexus extends AbstractPartition implements PartitionNexus 119 { 120 /** A logger for this class */ 121 private static final Logger LOG = LoggerFactory.getLogger( DefaultPartitionNexus.class ); 122 123 /** Speedup for logs */ 124 private static final boolean IS_DEBUG = LOG.isDebugEnabled(); 125 126 /** the vendorName string proudly set to: Apache Software Foundation*/ 127 private static final String ASF = "Apache Software Foundation"; 128 129 /** the read only rootDSE attributes */ 130 private final ServerEntry rootDSE; 131 132 /** The DirectoryService instance */ 133 private DirectoryService directoryService; 134 135 /** The global schemaManager */ 136 private SchemaManager schemaManager; 137 138 /** the partitions keyed by normalized suffix strings */ 139 private Map<String, Partition> partitions = new HashMap<String, Partition>(); 140 141 /** A structure to hold all the partitions */ 142 private DnBranchNode<Partition> partitionLookupTree = new DnBranchNode<Partition>(); 143 144 /** the system partition */ 145 private Partition system; 146 147 /** the closed state of this partition */ 148 private boolean initialized; 149 150 151 /** 152 * Creates the root nexus singleton of the entire system. The root DSE has 153 * several attributes that are injected into it besides those that may 154 * already exist. As partitions are added to the system more namingContexts 155 * attributes are added to the rootDSE. 156 * 157 * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a> 158 * @param rootDSE the root entry for the DSA 159 * @throws javax.naming.Exception on failure to initialize 160 */ 161 public DefaultPartitionNexus( ServerEntry rootDSE ) throws Exception 162 { 163 // setup that root DSE 164 this.rootDSE = rootDSE; 165 166 // Add the basic informations 167 rootDSE.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, ServerDNConstants.CN_SCHEMA_DN ); 168 rootDSE.put( SchemaConstants.SUPPORTED_LDAP_VERSION_AT, "3" ); 169 rootDSE.put( SchemaConstants.SUPPORTED_FEATURES_AT, SchemaConstants.FEATURE_ALL_OPERATIONAL_ATTRIBUTES ); 170 rootDSE.put( SchemaConstants.SUPPORTED_EXTENSION_AT, NoticeOfDisconnect.EXTENSION_OID ); 171 172 // Add the supported controls 173 rootDSE.put( 174 SchemaConstants.SUPPORTED_CONTROL_AT, 175 PersistentSearchControl.CONTROL_OID, 176 EntryChangeControl.CONTROL_OID, 177 SubentriesControl.CONTROL_OID, 178 ManageDsaITControl.CONTROL_OID, 179 CascadeControl.CONTROL_OID, 180 PagedResultsControl.CONTROL_OID, 181 // Replication controls 182 SyncDoneValueControl.CONTROL_OID, 183 SyncInfoValueControl.CONTROL_OID, 184 SyncRequestValueControl.CONTROL_OID, 185 SyncStateValueControl.CONTROL_OID 186 ); 187 188 // Add the objectClasses 189 rootDSE.put( SchemaConstants.OBJECT_CLASS_AT, 190 SchemaConstants.TOP_OC, 191 SchemaConstants.EXTENSIBLE_OBJECT_OC ); 192 193 // Add the 'vendor' name and version infos 194 rootDSE.put( SchemaConstants.VENDOR_NAME_AT, ASF ); 195 196 Properties props = new Properties(); 197 198 try 199 { 200 props.load( getClass().getResourceAsStream( "version.properties" ) ); 201 } 202 catch ( IOException e ) 203 { 204 LOG.error( I18n.err( I18n.ERR_33 ) ); 205 } 206 207 rootDSE.put( SchemaConstants.VENDOR_VERSION_AT, props.getProperty( "apacheds.version", "UNKNOWN" ) ); 208 } 209 210 211 /* (non-Javadoc) 212 * @see org.apache.directory.server.core.partition.PartitionNexus#initialize() 213 */ 214 protected void doInit( ) throws Exception 215 { 216 // NOTE: We ignore ContextPartitionConfiguration parameter here. 217 if ( initialized ) 218 { 219 return; 220 } 221 222 //this.directoryService = directoryService; 223 schemaManager = directoryService.getSchemaManager(); 224 225 // Initialize and normalize the localy used DNs 226 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN ); 227 adminDn.normalize( schemaManager.getNormalizerMapping() ); 228 229 initializeSystemPartition( directoryService ); 230 231 List<Partition> initializedPartitions = new ArrayList<Partition>(); 232 initializedPartitions.add( 0, this.system ); 233 234 235 try 236 { 237 for ( Partition partition : directoryService.getPartitions() ) 238 { 239 partition.setSchemaManager( schemaManager ); 240 CoreSession adminSession = new DefaultCoreSession( 241 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); 242 243 AddContextPartitionOperationContext opCtx = 244 new AddContextPartitionOperationContext( adminSession, partition ); 245 addContextPartition( opCtx ); 246 initializedPartitions.add( opCtx.getPartition() ); 247 } 248 249 initialized = true; 250 } 251 finally 252 { 253 if ( !initialized ) 254 { 255 Iterator<Partition> i = initializedPartitions.iterator(); 256 while ( i.hasNext() ) 257 { 258 Partition partition = i.next(); 259 i.remove(); 260 try 261 { 262 partition.destroy(); 263 } 264 catch ( Exception e ) 265 { 266 LOG.warn( "Failed to destroy a partition: " + partition.getSuffixDn(), e ); 267 } 268 finally 269 { 270 unregister( partition ); 271 } 272 } 273 } 274 } 275 } 276 277 278 private Partition initializeSystemPartition( DirectoryService directoryService ) throws Exception 279 { 280 // initialize system partition first 281 Partition override = directoryService.getSystemPartition(); 282 283 if ( override != null ) 284 { 285 286 // --------------------------------------------------------------- 287 // check a few things to make sure users configured it properly 288 // --------------------------------------------------------------- 289 290 if ( ! override.getId().equals( "system" ) ) 291 { 292 throw new ConfigurationException( I18n.err( I18n.ERR_262, override.getId() ) ); 293 } 294 295 296 system = override; 297 } 298 else 299 { 300 } 301 302 system.initialize( ); 303 304 305 // Add root context entry for system partition 306 DN systemSuffixDn = new DN( ServerDNConstants.SYSTEM_DN ); 307 systemSuffixDn.normalize( schemaManager.getNormalizerMapping() ); 308 ServerEntry systemEntry = new DefaultServerEntry( schemaManager, systemSuffixDn ); 309 310 // Add the ObjectClasses 311 systemEntry.put( SchemaConstants.OBJECT_CLASS_AT, 312 SchemaConstants.TOP_OC, 313 SchemaConstants.ORGANIZATIONAL_UNIT_OC, 314 SchemaConstants.EXTENSIBLE_OBJECT_OC 315 ); 316 317 // Add some operational attributes 318 systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN ); 319 systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() ); 320 systemEntry.add( SchemaConstants.ENTRY_CSN_AT, directoryService.getCSN().toString() ); 321 systemEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() ); 322 systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ), 323 NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) ); 324 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 325 adminDn.normalize( schemaManager.getNormalizerMapping() ); 326 CoreSession adminSession = new DefaultCoreSession( 327 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); 328 AddOperationContext addOperationContext = new AddOperationContext( adminSession, systemEntry ); 329 330 if ( !system.hasEntry( new EntryOperationContext( adminSession, systemEntry.getDn() ) ) ) 331 { 332 system.add( addOperationContext ); 333 } 334 335 String key = system.getSuffixDn().getName(); 336 337 if ( partitions.containsKey( key ) ) 338 { 339 throw new ConfigurationException( I18n.err( I18n.ERR_263, key ) ); 340 } 341 342 synchronized ( partitionLookupTree ) 343 { 344 partitions.put( key, system ); 345 partitionLookupTree.add( system.getSuffixDn(), system ); 346 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT ); 347 348 if ( namingContexts == null ) 349 { 350 namingContexts = new DefaultServerAttribute( 351 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.NAMING_CONTEXTS_AT ), 352 system.getSuffixDn().getName() ); 353 rootDSE.put( namingContexts ); 354 } 355 else 356 { 357 namingContexts.add( system.getSuffixDn().getName() ); 358 } 359 } 360 361 return system; 362 } 363 364 365 /* (non-Javadoc) 366 * @see org.apache.directory.server.core.partition.PartitionNexus#destroy() 367 */ 368 protected synchronized void doDestroy() 369 { 370 if ( !initialized ) 371 { 372 return; 373 } 374 375 // make sure this loop is not fail fast so all backing stores can 376 // have an attempt at closing down and synching their cached entries 377 for ( String suffix : new HashSet<String>( this.partitions.keySet() ) ) 378 { 379 try 380 { 381 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ); 382 adminDn.normalize( schemaManager.getNormalizerMapping() ); 383 CoreSession adminSession = new DefaultCoreSession( 384 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService ); 385 removeContextPartition( new RemoveContextPartitionOperationContext( 386 adminSession, new DN( suffix ) ) ); 387 } 388 catch ( Exception e ) 389 { 390 LOG.warn( "Failed to destroy a partition: " + suffix, e ); 391 } 392 } 393 394 initialized = false; 395 } 396 397 398 /* (non-Javadoc) 399 * @see org.apache.directory.server.core.partition.PartitionNexus#getId() 400 */ 401 public String getId() 402 { 403 return "NEXUS"; 404 } 405 406 407 /* (non-Javadoc) 408 * @see org.apache.directory.server.core.partition.PartitionNexus#setId(java.lang.String) 409 */ 410 public void setId( String id ) 411 { 412 throw new UnsupportedOperationException( I18n.err( I18n.ERR_264 ) ); 413 } 414 415 416 /** 417 * {@inheritDoc} 418 */ 419 public SchemaManager getSchemaManager() 420 { 421 return schemaManager; 422 } 423 424 425 /** 426 * {@inheritDoc} 427 */ 428 public void setSchemaManager( SchemaManager schemaManager ) 429 { 430 this.schemaManager = schemaManager; 431 } 432 433 434 /* (non-Javadoc) 435 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffixDn() 436 */ 437 public DN getSuffixDn() 438 { 439 return DN.EMPTY_DN; 440 } 441 442 443 /* (non-Javadoc) 444 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffix() 445 */ 446 public String getSuffix() 447 { 448 return StringTools.EMPTY; 449 } 450 451 452 /* (non-Javadoc) 453 * @see org.apache.directory.server.core.partition.PartitionNexus#setSuffix(java.lang.String) 454 */ 455 public void setSuffix( String suffix ) 456 { 457 throw new UnsupportedOperationException(); 458 } 459 460 461 /* (non-Javadoc) 462 * @see org.apache.directory.server.core.partition.PartitionNexus#isInitialized() 463 */ 464 public boolean isInitialized() 465 { 466 return initialized; 467 } 468 469 470 /* (non-Javadoc) 471 * @see org.apache.directory.server.core.partition.PartitionNexus#sync() 472 */ 473 public void sync() throws Exception 474 { 475 MultiException error = null; 476 477 for ( Partition partition : this.partitions.values() ) 478 { 479 try 480 { 481 partition.sync(); 482 } 483 catch ( Exception e ) 484 { 485 LOG.warn( "Failed to flush partition data out.", e ); 486 if ( error == null ) 487 { 488 //noinspection ThrowableInstanceNeverThrown 489 error = new MultiException( I18n.err( I18n.ERR_265 ) ); 490 } 491 492 // @todo really need to send this info to a monitor 493 error.addThrowable( e ); 494 } 495 } 496 497 if ( error != null ) 498 { 499 throw error; 500 } 501 } 502 503 // ------------------------------------------------------------------------ 504 // DirectoryPartition Interface Method Implementations 505 // ------------------------------------------------------------------------ 506 /* (non-Javadoc) 507 * @see org.apache.directory.server.core.partition.PartitionNexus#add(org.apache.directory.server.core.interceptor.context.AddOperationContext) 508 */ 509 public void add( AddOperationContext addContext ) throws Exception 510 { 511 Partition backend = getPartition( addContext.getDn() ); 512 backend.add( addContext ); 513 } 514 515 516 /* (non-Javadoc) 517 * @see org.apache.directory.server.core.partition.PartitionNexus#bind(org.apache.directory.server.core.interceptor.context.BindOperationContext) 518 */ 519 public void bind( BindOperationContext bindContext ) throws Exception 520 { 521 Partition partition = getPartition( bindContext.getDn() ); 522 partition.bind( bindContext ); 523 } 524 525 526 /* (non-Javadoc) 527 * @see org.apache.directory.server.core.partition.PartitionNexus#compare(org.apache.directory.server.core.interceptor.context.CompareOperationContext) 528 */ 529 public boolean compare( CompareOperationContext compareContext ) throws Exception 530 { 531 Partition partition = getPartition( compareContext.getDn() ); 532 //AttributeTypeRegistry registry = schemaManager.getAttributeTypeRegistry(); 533 534 // complain if we do not recognize the attribute being compared 535 if ( !schemaManager.getAttributeTypeRegistry().contains( compareContext.getOid() ) ) 536 { 537 throw new LdapInvalidAttributeTypeException( I18n.err( I18n.ERR_266, compareContext.getOid() ) ); 538 } 539 540 AttributeType attrType = schemaManager.lookupAttributeTypeRegistry( compareContext.getOid() ); 541 542 EntryAttribute attr = partition.lookup( compareContext.newLookupContext( 543 compareContext.getDn() ) ).get( attrType.getName() ); 544 545 // complain if the attribute being compared does not exist in the entry 546 if ( attr == null ) 547 { 548 throw new LdapNoSuchAttributeException(); 549 } 550 551 // see first if simple match without normalization succeeds 552 if ( attr.contains( (Value<?>)compareContext.getValue() ) ) 553 { 554 return true; 555 } 556 557 // now must apply normalization to all values (attr and in request) to compare 558 559 /* 560 * Get ahold of the normalizer for the attribute and normalize the request 561 * assertion value for comparisons with normalized attribute values. Loop 562 * through all values looking for a match. 563 */ 564 Normalizer normalizer = attrType.getEquality().getNormalizer(); 565 Value<?> reqVal = normalizer.normalize( compareContext.getValue() ); 566 567 for ( Value<?> value:attr ) 568 { 569 Value<?> attrValObj = normalizer.normalize( value ); 570 571 if ( attrValObj.equals( reqVal ) ) 572 { 573 return true; 574 } 575 } 576 577 return false; 578 } 579 580 581 /* (non-Javadoc) 582 * @see org.apache.directory.server.core.partition.PartitionNexus#delete(org.apache.directory.server.core.interceptor.context.DeleteOperationContext) 583 */ 584 public void delete( DeleteOperationContext deleteContext ) throws Exception 585 { 586 Partition backend = getPartition( deleteContext.getDn() ); 587 backend.delete( deleteContext ); 588 } 589 590 591 /* (non-Javadoc) 592 * @see org.apache.directory.server.core.partition.PartitionNexus#hasEntry(org.apache.directory.server.core.interceptor.context.EntryOperationContext) 593 */ 594 public boolean hasEntry( EntryOperationContext opContext ) throws Exception 595 { 596 DN dn = opContext.getDn(); 597 598 if ( IS_DEBUG ) 599 { 600 LOG.debug( "Check if DN '" + dn + "' exists." ); 601 } 602 603 if ( dn.size() == 0 ) 604 { 605 return true; 606 } 607 608 Partition backend = getPartition( dn ); 609 return backend.hasEntry( opContext ); 610 } 611 612 613 /* (non-Javadoc) 614 * @see org.apache.directory.server.core.partition.PartitionNexus#list(org.apache.directory.server.core.interceptor.context.ListOperationContext) 615 */ 616 public EntryFilteringCursor list( ListOperationContext opContext ) throws Exception 617 { 618 Partition backend = getPartition( opContext.getDn() ); 619 return backend.list( opContext ); 620 } 621 622 623 /* (non-Javadoc) 624 * @see org.apache.directory.server.core.partition.PartitionNexus#lookup(org.apache.directory.server.core.interceptor.context.LookupOperationContext) 625 */ 626 public ClonedServerEntry lookup( LookupOperationContext opContext ) throws Exception 627 { 628 DN dn = opContext.getDn(); 629 630 if ( dn.size() == 0 ) 631 { 632 ClonedServerEntry retval = new ClonedServerEntry( rootDSE ); 633 Set<AttributeType> attributeTypes = rootDSE.getAttributeTypes(); 634 635 if ( opContext.getAttrsId() != null && ! opContext.getAttrsId().isEmpty() ) 636 { 637 for ( AttributeType attributeType:attributeTypes ) 638 { 639 String oid = attributeType.getOid(); 640 641 if ( ! opContext.getAttrsId().contains( oid ) ) 642 { 643 retval.removeAttributes( attributeType ); 644 } 645 } 646 return retval; 647 } 648 else 649 { 650 return new ClonedServerEntry( rootDSE ); 651 } 652 } 653 654 Partition backend = getPartition( dn ); 655 return backend.lookup( opContext ); 656 } 657 658 659 /* (non-Javadoc) 660 * @see org.apache.directory.server.core.partition.PartitionNexus#lookup(java.lang.Long) 661 */ 662 public ClonedServerEntry lookup( Long id ) throws Exception 663 { 664 // TODO not implemented until we can use id to figure out the partition using 665 // the partition ID component of the 64 bit Long identifier 666 throw new NotImplementedException(); 667 } 668 669 670 /* (non-Javadoc) 671 * @see org.apache.directory.server.core.partition.PartitionNexus#modify(org.apache.directory.server.core.interceptor.context.ModifyOperationContext) 672 */ 673 public void modify( ModifyOperationContext modifyContext ) throws Exception 674 { 675 // Special case : if we don't have any modification to apply, just return 676 if ( modifyContext.getModItems().size() == 0 ) 677 { 678 return; 679 } 680 681 Partition backend = getPartition( modifyContext.getDn() ); 682 backend.modify( modifyContext ); 683 } 684 685 686 /* (non-Javadoc) 687 * @see org.apache.directory.server.core.partition.PartitionNexus#move(org.apache.directory.server.core.interceptor.context.MoveOperationContext) 688 */ 689 public void move( MoveOperationContext opContext ) throws Exception 690 { 691 Partition backend = getPartition( opContext.getDn() ); 692 backend.move( opContext ); 693 } 694 695 696 /* (non-Javadoc) 697 * @see org.apache.directory.server.core.partition.PartitionNexus#moveAndRename(org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext) 698 */ 699 public void moveAndRename( MoveAndRenameOperationContext opContext ) throws Exception 700 { 701 Partition backend = getPartition( opContext.getDn() ); 702 backend.moveAndRename( opContext ); 703 } 704 705 706 /* (non-Javadoc) 707 * @see org.apache.directory.server.core.partition.PartitionNexus#rename(org.apache.directory.server.core.interceptor.context.RenameOperationContext) 708 */ 709 public void rename( RenameOperationContext opContext ) throws Exception 710 { 711 Partition backend = getPartition( opContext.getDn() ); 712 backend.rename( opContext ); 713 } 714 715 716 private EntryFilteringCursor searchRootDSE( SearchOperationContext searchOperationContext ) throws Exception 717 { 718 SearchControls searchControls = searchOperationContext.getSearchControls(); 719 720 String[] ids = searchControls.getReturningAttributes(); 721 722 // ----------------------------------------------------------- 723 // If nothing is asked for then we just return the entry asis. 724 // We let other mechanisms filter out operational attributes. 725 // ----------------------------------------------------------- 726 if ( ( ids == null ) || ( ids.length == 0 ) ) 727 { 728 ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone(); 729 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( rootDSE ), searchOperationContext ); 730 } 731 732 // ----------------------------------------------------------- 733 // Collect all the real attributes besides 1.1, +, and * and 734 // note if we've seen these special attributes as well. 735 // ----------------------------------------------------------- 736 737 Set<String> realIds = new HashSet<String>(); 738 boolean allUserAttributes = searchOperationContext.isAllUserAttributes(); 739 boolean allOperationalAttributes = searchOperationContext.isAllOperationalAttributes(); 740 boolean noAttribute = searchOperationContext.isNoAttributes(); 741 742 for ( String id:ids ) 743 { 744 String idTrimmed = id.trim(); 745 746 try 747 { 748 realIds.add( schemaManager.getAttributeTypeRegistry().getOidByName( idTrimmed ) ); 749 } 750 catch ( Exception e ) 751 { 752 realIds.add( idTrimmed ); 753 } 754 } 755 756 // return nothing 757 if ( noAttribute ) 758 { 759 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, DN.EMPTY_DN ); 760 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( serverEntry ), searchOperationContext ); 761 } 762 763 // return everything 764 if ( allUserAttributes && allOperationalAttributes ) 765 { 766 ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone(); 767 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( rootDSE ), searchOperationContext ); 768 } 769 770 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, DN.EMPTY_DN ); 771 772 ServerEntry rootDSE = getRootDSE( new GetRootDSEOperationContext( searchOperationContext.getSession() ) ); 773 774 for ( EntryAttribute attribute:rootDSE ) 775 { 776 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attribute.getUpId() ); 777 778 if ( realIds.contains( type.getOid() ) ) 779 { 780 serverEntry.put( attribute ); 781 } 782 else if ( allUserAttributes && ( type.getUsage() == UsageEnum.USER_APPLICATIONS ) ) 783 { 784 serverEntry.put( attribute ); 785 } 786 else if ( allOperationalAttributes && ( type.getUsage() != UsageEnum.USER_APPLICATIONS ) ) 787 { 788 serverEntry.put( attribute ); 789 } 790 } 791 792 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( serverEntry ), searchOperationContext ); 793 } 794 795 796 /* (non-Javadoc) 797 * @see org.apache.directory.server.core.partition.PartitionNexus#search(org.apache.directory.server.core.interceptor.context.SearchOperationContext) 798 */ 799 public EntryFilteringCursor search( SearchOperationContext opContext ) throws Exception 800 { 801 DN base = opContext.getDn(); 802 SearchControls searchCtls = opContext.getSearchControls(); 803 ExprNode filter = opContext.getFilter(); 804 805 // TODO since we're handling the *, and + in the EntryFilteringCursor 806 // we may not need this code: we need see if this is actually the 807 // case and remove this code. 808 if ( base.size() == 0 ) 809 { 810 // We are searching from the rootDSE. We have to distinguish three cases : 811 // 1) The scope is OBJECT : we have to return the rootDSE entry, filtered 812 // 2) The scope is ONELEVEL : we have to return all the Namin 813 boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE; 814 815 boolean isOnelevelScope = searchCtls.getSearchScope() == SearchControls.ONELEVEL_SCOPE; 816 817 boolean isSublevelScope = searchCtls.getSearchScope() == SearchControls.SUBTREE_SCOPE; 818 819 // test for (objectClass=*) 820 boolean isSearchAll = false; 821 822 // We have to be careful, as we may have a filter which is not a PresenceFilter 823 if ( filter instanceof PresenceNode ) 824 { 825 isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID ); 826 } 827 828 /* 829 * if basedn is "", filter is "(objectclass=*)" and scope is object 830 * then we have a request for the rootDSE 831 */ 832 if ( ( filter instanceof PresenceNode) && isObjectScope && isSearchAll ) 833 { 834 return searchRootDSE( opContext ); 835 } 836 else if ( isObjectScope && ( ! isSearchAll ) ) 837 { 838 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext ); 839 } 840 else if( isOnelevelScope ) 841 { 842 List<EntryFilteringCursor> cursors = new ArrayList<EntryFilteringCursor>(); 843 for ( Partition p : partitions.values() ) 844 { 845 opContext.setDn( p.getSuffixDn() ); 846 opContext.setScope( SearchScope.OBJECT ); 847 cursors.add( p.search( opContext ) ); 848 } 849 850 return new CursorList( cursors, opContext ); 851 } 852 else if ( isSublevelScope ) 853 { 854 List<EntryFilteringCursor> cursors = new ArrayList<EntryFilteringCursor>(); 855 for ( Partition p : partitions.values() ) 856 { 857 ClonedServerEntry entry = p.lookup( new LookupOperationContext( directoryService.getAdminSession(), p.getSuffixDn() ) ); 858 if( entry != null ) 859 { 860 Partition backend = getPartition( entry.getDn() ); 861 opContext.setDn( entry.getDn() ); 862 cursors.add( backend.search( opContext ) ); 863 } 864 } 865 866 // don't feed the above Cursors' list to a BaseEntryFilteringCursor it is skipping the naming context entry of each partition 867 return new CursorList( cursors, opContext ); 868 } 869 870 // TODO : handle searches based on the RootDSE 871 throw new LdapNoSuchObjectException(); 872 } 873 874 base.normalize( schemaManager.getNormalizerMapping() ); 875 Partition backend = getPartition( base ); 876 return backend.search( opContext ); 877 } 878 879 880 /* (non-Javadoc) 881 * @see org.apache.directory.server.core.partition.PartitionNexus#unbind(org.apache.directory.server.core.interceptor.context.UnbindOperationContext) 882 */ 883 public void unbind( UnbindOperationContext unbindContext ) throws Exception 884 { 885 Partition partition = getPartition( unbindContext.getDn() ); 886 partition.unbind( unbindContext ); 887 } 888 889 890 /* (non-Javadoc) 891 * @see org.apache.directory.server.core.partition.PartitionNexus#getRootDSE(org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext) 892 */ 893 public ClonedServerEntry getRootDSE( GetRootDSEOperationContext getRootDSEContext ) 894 { 895 return new ClonedServerEntry( rootDSE ); 896 } 897 898 899 /* (non-Javadoc) 900 * @see org.apache.directory.server.core.partition.PartitionNexus#addContextPartition(org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext) 901 */ 902 public synchronized void addContextPartition( AddContextPartitionOperationContext opContext ) throws Exception 903 { 904 Partition partition = opContext.getPartition(); 905 906 // Turn on default indices 907 String key = partition.getSuffixDn().getNormName(); 908 909 if ( partitions.containsKey( key ) ) 910 { 911 throw new ConfigurationException( I18n.err( I18n.ERR_263, key ) ); 912 } 913 914 if ( ! partition.isInitialized() ) 915 { 916 partition.initialize( ); 917 } 918 919 synchronized ( partitionLookupTree ) 920 { 921 DN partitionSuffix = partition.getSuffixDn(); 922 923 if ( partitionSuffix == null ) 924 { 925 throw new ConfigurationException( I18n.err( I18n.ERR_267, partition.getId() ) ); 926 } 927 928 partitions.put( partitionSuffix.getNormName(), partition ); 929 partitionLookupTree.add( partition.getSuffixDn(), partition ); 930 931 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT ); 932 933 if ( namingContexts == null ) 934 { 935 namingContexts = new DefaultServerAttribute( 936 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.NAMING_CONTEXTS_AT ), partitionSuffix.getName() ); 937 rootDSE.put( namingContexts ); 938 } 939 else 940 { 941 namingContexts.add( partitionSuffix.getName() ); 942 } 943 } 944 } 945 946 947 /* (non-Javadoc) 948 * @see org.apache.directory.server.core.partition.PartitionNexus#removeContextPartition(org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext) 949 */ 950 public synchronized void removeContextPartition( RemoveContextPartitionOperationContext removeContextPartition ) throws Exception 951 { 952 // Get the Partition name. It's a DN. 953 String key = removeContextPartition.getDn().getNormName(); 954 955 // Retrieve this partition from the aprtition's table 956 Partition partition = partitions.get( key ); 957 958 if ( partition == null ) 959 { 960 String msg = I18n.err( I18n.ERR_34, key ); 961 LOG.error( msg ); 962 throw new NameNotFoundException( msg ); 963 } 964 965 String partitionSuffix = partition.getSuffixDn().getName(); 966 967 // Retrieve the namingContexts from the RootDSE : the partition 968 // suffix must be present in those namingContexts 969 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT ); 970 971 if ( namingContexts != null ) 972 { 973 if ( namingContexts.contains( partitionSuffix ) ) 974 { 975 namingContexts.remove( partitionSuffix ); 976 } 977 else 978 { 979 String msg = I18n.err( I18n.ERR_35, key ); 980 LOG.error( msg ); 981 throw new NameNotFoundException( msg ); 982 } 983 } 984 985 // Update the partition tree 986 partitionLookupTree.remove( partition ); 987 partitions.remove( key ); 988 partition.destroy(); 989 } 990 991 992 /* (non-Javadoc) 993 * @see org.apache.directory.server.core.partition.PartitionNexus#getSystemPartition() 994 */ 995 public Partition getSystemPartition() 996 { 997 return system; 998 } 999 1000 1001 /* (non-Javadoc) 1002 * @see org.apache.directory.server.core.partition.PartitionNexus#getPartition(org.apache.directory.shared.ldap.name.DN) 1003 */ 1004 public Partition getPartition( DN dn ) throws Exception 1005 { 1006 Partition parent = partitionLookupTree.getParentElement( dn ); 1007 1008 if ( parent == null ) 1009 { 1010 throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_268, dn ) ); 1011 } 1012 else 1013 { 1014 return parent; 1015 } 1016 } 1017 1018 1019 /* (non-Javadoc) 1020 * @see org.apache.directory.server.core.partition.PartitionNexus#getMatchedName(org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext) 1021 */ 1022 public DN getMatchedName( GetMatchedNameOperationContext matchedNameContext ) throws Exception 1023 { 1024 DN dn = ( DN ) matchedNameContext.getDn().clone(); 1025 1026 while ( dn.size() > 0 ) 1027 { 1028 if ( hasEntry( new EntryOperationContext( matchedNameContext.getSession(), dn ) ) ) 1029 { 1030 return dn; 1031 } 1032 1033 dn.remove( dn.size() - 1 ); 1034 } 1035 1036 return dn; 1037 } 1038 1039 1040 /* (non-Javadoc) 1041 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffix(org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext) 1042 */ 1043 public DN getSuffix( GetSuffixOperationContext getSuffixContext ) throws Exception 1044 { 1045 Partition backend = getPartition( getSuffixContext.getDn() ); 1046 return backend.getSuffixDn(); 1047 } 1048 1049 1050 /* (non-Javadoc) 1051 * @see org.apache.directory.server.core.partition.PartitionNexus#listSuffixes(org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext) 1052 */ 1053 public Set<String> listSuffixes( ListSuffixOperationContext emptyContext ) throws Exception 1054 { 1055 return Collections.unmodifiableSet( partitions.keySet() ); 1056 } 1057 1058 1059 /* (non-Javadoc) 1060 * @see org.apache.directory.server.core.partition.PartitionNexus#registerSupportedExtensions(java.util.Set) 1061 */ 1062 public void registerSupportedExtensions( Set<String> extensionOids ) throws Exception 1063 { 1064 EntryAttribute supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT ); 1065 1066 if ( supportedExtension == null ) 1067 { 1068 rootDSE.set( SchemaConstants.SUPPORTED_EXTENSION_AT ); 1069 supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT ); 1070 } 1071 1072 for ( String extensionOid : extensionOids ) 1073 { 1074 supportedExtension.add( extensionOid ); 1075 } 1076 } 1077 1078 1079 /* (non-Javadoc) 1080 * @see org.apache.directory.server.core.partition.PartitionNexus#registerSupportedSaslMechanisms(java.util.Set) 1081 */ 1082 public void registerSupportedSaslMechanisms( Set<String> supportedSaslMechanisms ) throws Exception 1083 { 1084 EntryAttribute supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT ); 1085 1086 if ( supportedSaslMechanismsAttribute == null ) 1087 { 1088 rootDSE.set( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT ); 1089 supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT ); 1090 } 1091 1092 for ( String saslMechanism : supportedSaslMechanisms ) 1093 { 1094 supportedSaslMechanismsAttribute.add( saslMechanism ); 1095 } 1096 } 1097 1098 1099 /** 1100 * Unregisters an ContextPartition with this BackendManager. Called for each 1101 * registered Backend right befor it is to be stopped. This prevents 1102 * protocol server requests from reaching the Backend and effectively puts 1103 * the ContextPartition's naming context offline. 1104 * 1105 * Operations against the naming context should result in an LDAP BUSY 1106 * result code in the returnValue if the naming context is not online. 1107 * 1108 * @param partition ContextPartition component to unregister with this 1109 * BackendNexus. 1110 * @throws Exception if there are problems unregistering the partition 1111 */ 1112 private void unregister( Partition partition ) throws Exception 1113 { 1114 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT ); 1115 1116 if ( namingContexts != null ) 1117 { 1118 namingContexts.remove( partition.getSuffixDn().getName() ); 1119 } 1120 1121 partitions.remove( partition.getSuffixDn().getName() ); 1122 } 1123 1124 1125 /** 1126 * @return the directoryService 1127 */ 1128 public DirectoryService getDirectoryService() 1129 { 1130 return directoryService; 1131 } 1132 1133 1134 /** 1135 * @param directoryService the directoryService to set 1136 */ 1137 public void setDirectoryService( DirectoryService directoryService ) 1138 { 1139 this.directoryService = directoryService; 1140 } 1141 }