001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.backends; 028 029 030 031 import java.util.ArrayList; 032 import java.util.Collection; 033 import java.util.HashMap; 034 import java.util.HashSet; 035 import java.util.LinkedHashSet; 036 import java.util.List; 037 import java.util.Map; 038 import java.util.Set; 039 import java.util.TreeSet; 040 import java.util.concurrent.ConcurrentHashMap; 041 042 import org.opends.messages.Message; 043 import org.opends.server.admin.Configuration; 044 import org.opends.server.admin.server.ConfigurationChangeListener; 045 import org.opends.server.admin.std.server.RootDSEBackendCfg; 046 import org.opends.server.api.Backend; 047 import org.opends.server.config.ConfigEntry; 048 import org.opends.server.config.ConfigException; 049 import org.opends.server.core.AddOperation; 050 import org.opends.server.core.DeleteOperation; 051 import org.opends.server.core.DirectoryServer; 052 import org.opends.server.core.ModifyOperation; 053 import org.opends.server.core.ModifyDNOperation; 054 import org.opends.server.core.SearchOperation; 055 import org.opends.server.loggers.debug.DebugTracer; 056 import org.opends.server.protocols.asn1.ASN1OctetString; 057 import org.opends.server.types.*; 058 import org.opends.server.util.LDIFWriter; 059 import org.opends.server.util.Validator; 060 061 import static org.opends.messages.BackendMessages.*; 062 import static org.opends.messages.ConfigMessages. 063 ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY; 064 import static org.opends.server.config.ConfigConstants.*; 065 import static org.opends.server.loggers.debug.DebugLogger.*; 066 import static org.opends.server.loggers.ErrorLogger.*; 067 import static org.opends.server.util.ServerConstants.*; 068 import static org.opends.server.util.StaticUtils.*; 069 070 071 072 /** 073 * This class defines a backend to hold the Directory Server root DSE. It is a 074 * kind of meta-backend in that it will dynamically generate the root DSE entry 075 * (although there will be some caching) for base-level searches, and will 076 * simply redirect to other backends for operations in other scopes. 077 * <BR><BR> 078 * This should not be treated like a regular backend when it comes to 079 * initializing the server configuration. It should only be initialized after 080 * all other backends are configured. As such, it should have a special entry 081 * in the configuration rather than being placed under the cn=Backends branch 082 * with the other backends. 083 */ 084 public class RootDSEBackend 085 extends Backend 086 implements ConfigurationChangeListener<RootDSEBackendCfg> 087 { 088 /** 089 * The tracer object for the debug logger. 090 */ 091 private static final DebugTracer TRACER = getTracer(); 092 093 094 095 // The set of standard "static" attributes that we will always include in the 096 // root DSE entry and won't change while the server is running. 097 private ArrayList<Attribute> staticDSEAttributes; 098 099 // The set of user-defined attributes that will be included in the root DSE 100 // entry. 101 private ArrayList<Attribute> userDefinedAttributes; 102 103 // Indicates whether the attributes of the root DSE should always be treated 104 // as user attributes even if they are defined as operational in the schema. 105 private boolean showAllAttributes; 106 107 // The set of subordinate base DNs and their associated backends that will be 108 // used for non-base searches. 109 private ConcurrentHashMap<DN,Backend> subordinateBaseDNs; 110 111 // The set of objectclasses that will be used in the root DSE entry. 112 private HashMap<ObjectClass,String> dseObjectClasses; 113 114 // The current configuration state. 115 private RootDSEBackendCfg currentConfig; 116 117 // The DN of the configuration entry for this backend. 118 private DN configEntryDN; 119 120 // The DN for the root DSE. 121 private DN rootDSEDN; 122 123 // The set of base DNs for this backend. 124 private DN[] baseDNs; 125 126 // The set of supported controls for this backend. 127 private HashSet<String> supportedControls; 128 129 // The set of supported features for this backend. 130 private HashSet<String> supportedFeatures; 131 132 133 134 /** 135 * Creates a new backend with the provided information. All backend 136 * implementations must implement a default constructor that use 137 * <CODE>super()</CODE> to invoke this constructor. 138 */ 139 public RootDSEBackend() 140 { 141 super(); 142 143 // Perform all initialization in initializeBackend. 144 } 145 146 147 148 /** 149 * {@inheritDoc} 150 */ 151 @Override() 152 public void configureBackend(Configuration config) 153 throws ConfigException 154 { 155 Validator.ensureNotNull(config); 156 Validator.ensureTrue(config instanceof RootDSEBackendCfg); 157 currentConfig = (RootDSEBackendCfg)config; 158 configEntryDN = config.dn(); 159 } 160 161 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override() 167 public void initializeBackend() 168 throws ConfigException, InitializationException 169 { 170 ConfigEntry configEntry = 171 DirectoryServer.getConfigEntry(configEntryDN); 172 173 // Make sure that a configuration entry was provided. If not, then we will 174 // not be able to complete initialization. 175 if (configEntry == null) 176 { 177 Message message = ERR_ROOTDSE_CONFIG_ENTRY_NULL.get(); 178 throw new ConfigException(message); 179 } 180 181 // Get the set of user-defined attributes for the configuration entry. Any 182 // attributes that we don't recognize will be included directly in the root 183 // DSE. 184 userDefinedAttributes = new ArrayList<Attribute>(); 185 for (List<Attribute> attrs : 186 configEntry.getEntry().getUserAttributes().values()) 187 { 188 for (Attribute a : attrs) 189 { 190 if (! isDSEConfigAttribute(a)) 191 { 192 userDefinedAttributes.add(a); 193 } 194 } 195 } 196 for (List<Attribute> attrs : 197 configEntry.getEntry().getOperationalAttributes().values()) 198 { 199 for (Attribute a : attrs) 200 { 201 if (! isDSEConfigAttribute(a)) 202 { 203 userDefinedAttributes.add(a); 204 } 205 } 206 } 207 208 209 // Create the set of base DNs that we will handle. In this case, it's just 210 // the root DSE. 211 rootDSEDN = DN.nullDN(); 212 this.baseDNs = new DN[] { rootDSEDN }; 213 214 215 // Create the set of subordinate base DNs. If this is specified in the 216 // configuration, then use that set. Otherwise, use the set of non-private 217 // backends defined in the server. 218 try 219 { 220 Set<DN> subDNs = currentConfig.getSubordinateBaseDN(); 221 if (subDNs.isEmpty()) 222 { 223 // This is fine -- we'll just use the set of user-defined suffixes. 224 subordinateBaseDNs = null; 225 } 226 else 227 { 228 subordinateBaseDNs = new ConcurrentHashMap<DN,Backend>(); 229 for (DN baseDN : subDNs) 230 { 231 Backend backend = DirectoryServer.getBackend(baseDN); 232 if (backend == null) 233 { 234 Message message = WARN_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE.get( 235 String.valueOf(baseDN)); 236 logError(message); 237 } 238 else 239 { 240 subordinateBaseDNs.put(baseDN, backend); 241 } 242 } 243 } 244 } 245 catch (Exception e) 246 { 247 if (debugEnabled()) 248 { 249 TRACER.debugCaught(DebugLogLevel.ERROR, e); 250 } 251 252 Message message = WARN_ROOTDSE_SUBORDINATE_BASE_EXCEPTION.get( 253 stackTraceToSingleLineString(e)); 254 throw new InitializationException(message, e); 255 } 256 257 258 // Determine whether all root DSE attributes should be treated as user 259 // attributes. 260 showAllAttributes = currentConfig.isShowAllAttributes(); 261 262 263 // Construct the set of "static" attributes that will always be present in 264 // the root DSE. 265 staticDSEAttributes = new ArrayList<Attribute>(); 266 267 staticDSEAttributes.add(createAttribute(ATTR_VENDOR_NAME, 268 ATTR_VENDOR_NAME_LC, 269 SERVER_VENDOR_NAME)); 270 271 staticDSEAttributes.add(createAttribute(ATTR_VENDOR_VERSION, 272 ATTR_VENDOR_VERSION_LC, 273 DirectoryServer.getVersionString())); 274 275 276 277 // Construct the set of objectclasses to include in the root DSE entry. 278 dseObjectClasses = new HashMap<ObjectClass,String>(2); 279 ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP); 280 if (topOC == null) 281 { 282 topOC = DirectoryServer.getDefaultObjectClass(OC_TOP); 283 } 284 dseObjectClasses.put(topOC, OC_TOP); 285 286 ObjectClass rootDSEOC = 287 DirectoryServer.getObjectClass(OC_ROOT_DSE); 288 if (rootDSEOC == null) 289 { 290 rootDSEOC = DirectoryServer.getDefaultObjectClass(OC_ROOT_DSE); 291 } 292 dseObjectClasses.put(rootDSEOC, OC_ROOT_DSE); 293 294 295 // Define an empty sets for the supported controls and features. 296 supportedControls = new HashSet<String>(0); 297 supportedFeatures = new HashSet<String>(0); 298 299 300 // Set the backend ID for this backend. The identifier needs to be 301 // specific enough to avoid conflict with user backend identifiers. 302 setBackendID("__root.dse__"); 303 304 305 // Register as a change listener. 306 currentConfig.addChangeListener(this); 307 } 308 309 310 311 /** 312 * {@inheritDoc} 313 */ 314 @Override() 315 public void finalizeBackend() 316 { 317 currentConfig.removeChangeListener(this); 318 } 319 320 321 322 /** 323 * Indicates whether the provided attribute is one that is used in the 324 * configuration of this backend. 325 * 326 * @param attribute The attribute for which to make the determination. 327 * 328 * @return <CODE>true</CODE> if the provided attribute is one that is used in 329 * the configuration of this backend, <CODE>false</CODE> if not. 330 */ 331 private boolean isDSEConfigAttribute(Attribute attribute) 332 { 333 AttributeType attrType = attribute.getAttributeType(); 334 if (attrType.hasName(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN.toLowerCase()) || 335 attrType.hasName(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES.toLowerCase()) || 336 attrType.hasName(ATTR_COMMON_NAME)) 337 { 338 return true; 339 } 340 341 return false; 342 } 343 344 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override() 350 public DN[] getBaseDNs() 351 { 352 return baseDNs; 353 } 354 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override() 361 public synchronized long getEntryCount() 362 { 363 // There is always just a single entry in this backend. 364 return 1; 365 } 366 367 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override() 373 public boolean isLocal() 374 { 375 // For the purposes of this method, this is a local backend. 376 return true; 377 } 378 379 380 381 /** 382 * {@inheritDoc} 383 */ 384 @Override() 385 public boolean isIndexed(AttributeType attributeType, IndexType indexType) 386 { 387 // All searches in this backend will always be considered indexed. 388 return true; 389 } 390 391 392 393 /** 394 * {@inheritDoc} 395 */ 396 @Override() 397 public ConditionResult hasSubordinates(DN entryDN) 398 throws DirectoryException 399 { 400 long ret = numSubordinates(entryDN, false); 401 if(ret < 0) 402 { 403 return ConditionResult.UNDEFINED; 404 } 405 else if(ret == 0) 406 { 407 return ConditionResult.FALSE; 408 } 409 else 410 { 411 return ConditionResult.TRUE; 412 } 413 } 414 415 416 417 /** 418 * {@inheritDoc} 419 */ 420 @Override() 421 public long numSubordinates(DN entryDN, boolean subtree) 422 throws DirectoryException 423 { 424 if (entryDN == null || ! entryDN.isNullDN()) 425 { 426 return -1; 427 } 428 429 long count = 0; 430 431 Map<DN,Backend> baseMap; 432 if (subordinateBaseDNs == null) 433 { 434 baseMap = DirectoryServer.getPublicNamingContexts(); 435 } 436 else 437 { 438 baseMap = subordinateBaseDNs; 439 } 440 441 for (DN subBase : baseMap.keySet()) 442 { 443 Backend b = baseMap.get(subBase); 444 Entry subBaseEntry = b.getEntry(subBase); 445 if (subBaseEntry != null) 446 { 447 if(subtree) 448 { 449 long subCount = b.numSubordinates(subBase, true); 450 if(subCount < 0) 451 { 452 return -1; 453 } 454 455 count += subCount; 456 } 457 count ++; 458 } 459 } 460 461 return count; 462 } 463 464 465 466 /** 467 * {@inheritDoc} 468 */ 469 @Override() 470 public Entry getEntry(DN entryDN) 471 throws DirectoryException 472 { 473 // If the requested entry was the root DSE, then create and return it. 474 if ((entryDN == null) || entryDN.isNullDN()) 475 { 476 return getRootDSE(); 477 } 478 479 480 // This method should never be used to get anything other than the root DSE. 481 // If we got here, then that appears to be the case, so log a message. 482 Message message = 483 WARN_ROOTDSE_GET_ENTRY_NONROOT.get(String.valueOf(entryDN)); 484 logError(message); 485 486 487 // Go ahead and check the subordinate backends to see if we can find the 488 // entry there. Note that in order to avoid potential loop conditions, this 489 // will only work if the set of subordinate bases has been explicitly 490 // specified. 491 if (subordinateBaseDNs != null) 492 { 493 for (Backend b : subordinateBaseDNs.values()) 494 { 495 if (b.handlesEntry(entryDN)) 496 { 497 return b.getEntry(entryDN); 498 } 499 } 500 } 501 502 503 // If we've gotten here, then we couldn't find the entry so return null. 504 return null; 505 } 506 507 508 509 /** 510 * Retrieves the root DSE entry for the Directory Server. 511 * 512 * @return The root DSE entry for the Directory Server. 513 */ 514 public Entry getRootDSE() 515 { 516 HashMap<AttributeType,List<Attribute>> dseUserAttrs = 517 new HashMap<AttributeType,List<Attribute>>(); 518 519 HashMap<AttributeType,List<Attribute>> dseOperationalAttrs = 520 new HashMap<AttributeType,List<Attribute>>(); 521 522 523 // Add the "namingContexts" attribute. 524 Attribute publicNamingContextAttr = 525 createDNAttribute(ATTR_NAMING_CONTEXTS, ATTR_NAMING_CONTEXTS_LC, 526 DirectoryServer.getPublicNamingContexts().keySet()); 527 ArrayList<Attribute> publicNamingContextAttrs = new ArrayList<Attribute>(1); 528 publicNamingContextAttrs.add(publicNamingContextAttr); 529 if (showAllAttributes || 530 (! publicNamingContextAttr.getAttributeType().isOperational())) 531 { 532 dseUserAttrs.put(publicNamingContextAttr.getAttributeType(), 533 publicNamingContextAttrs); 534 } 535 else 536 { 537 dseOperationalAttrs.put(publicNamingContextAttr.getAttributeType(), 538 publicNamingContextAttrs); 539 } 540 541 542 // Add the "ds-private-naming-contexts" attribute. 543 Attribute privateNamingContextAttr = 544 createDNAttribute(ATTR_PRIVATE_NAMING_CONTEXTS, 545 ATTR_PRIVATE_NAMING_CONTEXTS, 546 DirectoryServer.getPrivateNamingContexts().keySet()); 547 ArrayList<Attribute> privateNamingContextAttrs = 548 new ArrayList<Attribute>(1); 549 privateNamingContextAttrs.add(privateNamingContextAttr); 550 if (showAllAttributes || 551 (! privateNamingContextAttr.getAttributeType().isOperational())) 552 { 553 dseUserAttrs.put(privateNamingContextAttr.getAttributeType(), 554 privateNamingContextAttrs); 555 } 556 else 557 { 558 dseOperationalAttrs.put(privateNamingContextAttr.getAttributeType(), 559 privateNamingContextAttrs); 560 } 561 562 563 // Add the "supportedControl" attribute. 564 Attribute supportedControlAttr = 565 createAttribute(ATTR_SUPPORTED_CONTROL, ATTR_SUPPORTED_CONTROL_LC, 566 DirectoryServer.getSupportedControls()); 567 ArrayList<Attribute> supportedControlAttrs = new ArrayList<Attribute>(1); 568 supportedControlAttrs.add(supportedControlAttr); 569 if (showAllAttributes || 570 (! supportedControlAttr.getAttributeType().isOperational())) 571 { 572 dseUserAttrs.put(supportedControlAttr.getAttributeType(), 573 supportedControlAttrs); 574 } 575 else 576 { 577 dseOperationalAttrs.put(supportedControlAttr.getAttributeType(), 578 supportedControlAttrs); 579 } 580 581 582 // Add the "supportedExtension" attribute. 583 Attribute supportedExtensionAttr = 584 createAttribute(ATTR_SUPPORTED_EXTENSION, ATTR_SUPPORTED_EXTENSION_LC, 585 DirectoryServer.getSupportedExtensions().keySet()); 586 ArrayList<Attribute> supportedExtensionAttrs = new ArrayList<Attribute>(1); 587 supportedExtensionAttrs.add(supportedExtensionAttr); 588 if (showAllAttributes || 589 (! supportedExtensionAttr.getAttributeType().isOperational())) 590 { 591 dseUserAttrs.put(supportedExtensionAttr.getAttributeType(), 592 supportedExtensionAttrs); 593 } 594 else 595 { 596 dseOperationalAttrs.put(supportedExtensionAttr.getAttributeType(), 597 supportedExtensionAttrs); 598 } 599 600 601 // Add the "supportedFeature" attribute. 602 Attribute supportedFeatureAttr = 603 createAttribute(ATTR_SUPPORTED_FEATURE, ATTR_SUPPORTED_FEATURE_LC, 604 DirectoryServer.getSupportedFeatures()); 605 ArrayList<Attribute> supportedFeatureAttrs = new ArrayList<Attribute>(1); 606 supportedFeatureAttrs.add(supportedFeatureAttr); 607 if (showAllAttributes || 608 (! supportedFeatureAttr.getAttributeType().isOperational())) 609 { 610 dseUserAttrs.put(supportedFeatureAttr.getAttributeType(), 611 supportedFeatureAttrs); 612 } 613 else 614 { 615 dseOperationalAttrs.put(supportedFeatureAttr.getAttributeType(), 616 supportedFeatureAttrs); 617 } 618 619 620 // Add the "supportedSASLMechanisms" attribute. 621 Attribute supportedSASLMechAttr = 622 createAttribute(ATTR_SUPPORTED_SASL_MECHANISMS, 623 ATTR_SUPPORTED_SASL_MECHANISMS_LC, 624 DirectoryServer.getSupportedSASLMechanisms().keySet()); 625 ArrayList<Attribute> supportedSASLMechAttrs = new ArrayList<Attribute>(1); 626 supportedSASLMechAttrs.add(supportedSASLMechAttr); 627 if (showAllAttributes || 628 (! supportedSASLMechAttr.getAttributeType().isOperational())) 629 { 630 dseUserAttrs.put(supportedSASLMechAttr.getAttributeType(), 631 supportedSASLMechAttrs); 632 } 633 else 634 { 635 dseOperationalAttrs.put(supportedSASLMechAttr.getAttributeType(), 636 supportedSASLMechAttrs); 637 } 638 639 640 // Add the "supportedLDAPVersions" attribute. 641 TreeSet<String> versionStrings = new TreeSet<String>(); 642 for (Integer ldapVersion : DirectoryServer.getSupportedLDAPVersions()) 643 { 644 versionStrings.add(ldapVersion.toString()); 645 } 646 Attribute supportedLDAPVersionAttr = 647 createAttribute(ATTR_SUPPORTED_LDAP_VERSION, 648 ATTR_SUPPORTED_LDAP_VERSION_LC, 649 versionStrings); 650 ArrayList<Attribute> supportedLDAPVersionAttrs = 651 new ArrayList<Attribute>(1); 652 supportedLDAPVersionAttrs.add(supportedLDAPVersionAttr); 653 if (showAllAttributes || 654 (! supportedLDAPVersionAttr.getAttributeType().isOperational())) 655 { 656 dseUserAttrs.put(supportedLDAPVersionAttr.getAttributeType(), 657 supportedLDAPVersionAttrs); 658 } 659 else 660 { 661 dseOperationalAttrs.put(supportedLDAPVersionAttr.getAttributeType(), 662 supportedLDAPVersionAttrs); 663 } 664 665 666 // Add the "supportedAuthPasswordSchemes" attribute. 667 Set<String> authPWSchemes = 668 DirectoryServer.getAuthPasswordStorageSchemes().keySet(); 669 if (! authPWSchemes.isEmpty()) 670 { 671 Attribute supportedAuthPWSchemesAttr = 672 createAttribute(ATTR_SUPPORTED_AUTH_PW_SCHEMES, 673 ATTR_SUPPORTED_AUTH_PW_SCHEMES_LC, authPWSchemes); 674 ArrayList<Attribute> supportedAuthPWSchemesAttrs = 675 new ArrayList<Attribute>(1); 676 supportedAuthPWSchemesAttrs.add(supportedAuthPWSchemesAttr); 677 if (showAllAttributes || 678 (! supportedSASLMechAttr.getAttributeType().isOperational())) 679 { 680 dseUserAttrs.put(supportedAuthPWSchemesAttr.getAttributeType(), 681 supportedAuthPWSchemesAttrs); 682 } 683 else 684 { 685 dseOperationalAttrs.put(supportedAuthPWSchemesAttr.getAttributeType(), 686 supportedAuthPWSchemesAttrs); 687 } 688 } 689 690 691 // Add all the standard "static" attributes. 692 for (Attribute a : staticDSEAttributes) 693 { 694 AttributeType type = a.getAttributeType(); 695 696 if (type.isOperational() && (! showAllAttributes)) 697 { 698 List<Attribute> attrs = dseOperationalAttrs.get(type); 699 if (attrs == null) 700 { 701 attrs = new ArrayList<Attribute>(); 702 attrs.add(a); 703 dseOperationalAttrs.put(type, attrs); 704 } 705 else 706 { 707 attrs.add(a); 708 } 709 } 710 else 711 { 712 List<Attribute> attrs = dseUserAttrs.get(type); 713 if (attrs == null) 714 { 715 attrs = new ArrayList<Attribute>(); 716 attrs.add(a); 717 dseUserAttrs.put(type, attrs); 718 } 719 else 720 { 721 attrs.add(a); 722 } 723 } 724 } 725 726 727 // Add all the user-defined attributes. 728 for (Attribute a : userDefinedAttributes) 729 { 730 AttributeType type = a.getAttributeType(); 731 732 if (type.isOperational() && (! showAllAttributes)) 733 { 734 List<Attribute> attrs = dseOperationalAttrs.get(type); 735 if (attrs == null) 736 { 737 attrs = new ArrayList<Attribute>(); 738 attrs.add(a); 739 dseOperationalAttrs.put(type, attrs); 740 } 741 else 742 { 743 attrs.add(a); 744 } 745 } 746 else 747 { 748 List<Attribute> attrs = dseUserAttrs.get(type); 749 if (attrs == null) 750 { 751 attrs = new ArrayList<Attribute>(); 752 attrs.add(a); 753 dseUserAttrs.put(type, attrs); 754 } 755 else 756 { 757 attrs.add(a); 758 } 759 } 760 } 761 762 763 // Construct and return the entry. 764 Entry e = new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs, 765 dseOperationalAttrs); 766 e.processVirtualAttributes(); 767 return e; 768 } 769 770 771 772 /** 773 * Creates an attribute for the root DSE with the following criteria. 774 * 775 * @param name The name for the attribute. 776 * @param lowerName The name for the attribute formatted in all lowercase 777 * characters. 778 * @param value The value to use for the attribute. 779 * 780 * @return The constructed attribute. 781 */ 782 private Attribute createAttribute(String name, String lowerName, 783 String value) 784 { 785 AttributeType type = DirectoryServer.getAttributeType(lowerName); 786 if (type == null) 787 { 788 type = DirectoryServer.getDefaultAttributeType(name); 789 } 790 791 LinkedHashSet<AttributeValue> attrValues = 792 new LinkedHashSet<AttributeValue>(1); 793 attrValues.add(new AttributeValue(type, new ASN1OctetString(value))); 794 795 return new Attribute(type, name, attrValues); 796 } 797 798 799 800 /** 801 * Creates an attribute for the root DSE meant to hold a set of DNs. 802 * 803 * @param name The name for the attribute. 804 * @param lowerName The name for the attribute formatted in all lowercase 805 * characters. 806 * @param values The set of DN values to use for the attribute. 807 * 808 * @return The constructed attribute. 809 */ 810 private Attribute createDNAttribute(String name, String lowerName, 811 Collection<DN> values) 812 { 813 AttributeType type = DirectoryServer.getAttributeType(lowerName); 814 if (type == null) 815 { 816 type = DirectoryServer.getDefaultAttributeType(name); 817 } 818 819 LinkedHashSet<AttributeValue> attrValues = 820 new LinkedHashSet<AttributeValue>(); 821 for (DN dn : values) 822 { 823 attrValues.add(new AttributeValue(type, 824 new ASN1OctetString(dn.toString()))); 825 } 826 827 return new Attribute(type, name, attrValues); 828 } 829 830 831 832 /** 833 * Creates an attribute for the root DSE with the following criteria. 834 * 835 * @param name The name for the attribute. 836 * @param lowerName The name for the attribute formatted in all lowercase 837 * characters. 838 * @param values The set of values to use for the attribute. 839 * 840 * @return The constructed attribute. 841 */ 842 private Attribute createAttribute(String name, String lowerName, 843 Collection<String> values) 844 { 845 AttributeType type = DirectoryServer.getAttributeType(lowerName); 846 if (type == null) 847 { 848 type = DirectoryServer.getDefaultAttributeType(name); 849 } 850 851 LinkedHashSet<AttributeValue> attrValues = 852 new LinkedHashSet<AttributeValue>(); 853 for (String s : values) 854 { 855 attrValues.add(new AttributeValue(type, new ASN1OctetString(s))); 856 } 857 858 return new Attribute(type, name, attrValues); 859 } 860 861 862 863 /** 864 * {@inheritDoc} 865 */ 866 @Override() 867 public boolean entryExists(DN entryDN) 868 throws DirectoryException 869 { 870 // If the specified DN was the null DN, then it exists. 871 if (entryDN.isNullDN()) 872 { 873 return true; 874 } 875 876 877 // If it was not the null DN, then iterate through the associated 878 // subordinate backends to make the determination. 879 Map<DN,Backend> baseMap; 880 if (subordinateBaseDNs == null) 881 { 882 baseMap = DirectoryServer.getPublicNamingContexts(); 883 } 884 else 885 { 886 baseMap = subordinateBaseDNs; 887 } 888 889 for (DN baseDN : baseMap.keySet()) 890 { 891 if (entryDN.isDescendantOf(baseDN)) 892 { 893 Backend b = baseMap.get(baseDN); 894 if (b.entryExists(entryDN)) 895 { 896 return true; 897 } 898 } 899 } 900 901 return false; 902 } 903 904 905 906 /** 907 * {@inheritDoc} 908 */ 909 @Override() 910 public void addEntry(Entry entry, AddOperation addOperation) 911 throws DirectoryException 912 { 913 Message message = 914 ERR_ROOTDSE_ADD_NOT_SUPPORTED.get(String.valueOf(entry.getDN())); 915 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 916 } 917 918 919 920 /** 921 * {@inheritDoc} 922 */ 923 @Override() 924 public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) 925 throws DirectoryException 926 { 927 Message message = 928 ERR_ROOTDSE_DELETE_NOT_SUPPORTED.get(String.valueOf(entryDN)); 929 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 930 } 931 932 933 934 /** 935 * {@inheritDoc} 936 */ 937 @Override() 938 public void replaceEntry(Entry entry, ModifyOperation modifyOperation) 939 throws DirectoryException 940 { 941 Message message = ERR_ROOTDSE_MODIFY_NOT_SUPPORTED.get( 942 String.valueOf(entry.getDN()), String.valueOf(configEntryDN)); 943 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 944 } 945 946 947 948 /** 949 * {@inheritDoc} 950 */ 951 @Override() 952 public void renameEntry(DN currentDN, Entry entry, 953 ModifyDNOperation modifyDNOperation) 954 throws DirectoryException 955 { 956 Message message = 957 ERR_ROOTDSE_MODIFY_DN_NOT_SUPPORTED.get(String.valueOf(currentDN)); 958 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 959 } 960 961 962 963 /** 964 * {@inheritDoc} 965 */ 966 @Override() 967 public void search(SearchOperation searchOperation) 968 throws DirectoryException, CanceledOperationException { 969 DN baseDN = searchOperation.getBaseDN(); 970 if (! baseDN.isNullDN()) 971 { 972 Message message = ERR_ROOTDSE_INVALID_SEARCH_BASE. 973 get(searchOperation.getConnectionID(), 974 searchOperation.getOperationID(), String.valueOf(baseDN)); 975 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 976 } 977 978 979 SearchFilter filter = searchOperation.getFilter(); 980 switch (searchOperation.getScope()) 981 { 982 case BASE_OBJECT: 983 Entry dseEntry = getRootDSE(); 984 if (filter.matchesEntry(dseEntry)) 985 { 986 searchOperation.returnEntry(dseEntry, null); 987 } 988 break; 989 990 991 case SINGLE_LEVEL: 992 Map<DN,Backend> baseMap; 993 if (subordinateBaseDNs == null) 994 { 995 baseMap = DirectoryServer.getPublicNamingContexts(); 996 } 997 else 998 { 999 baseMap = subordinateBaseDNs; 1000 } 1001 1002 for (DN subBase : baseMap.keySet()) 1003 { 1004 searchOperation.checkIfCanceled(false); 1005 1006 Backend b = baseMap.get(subBase); 1007 Entry subBaseEntry = b.getEntry(subBase); 1008 if ((subBaseEntry != null) && filter.matchesEntry(subBaseEntry)) 1009 { 1010 searchOperation.returnEntry(subBaseEntry, null); 1011 } 1012 } 1013 break; 1014 1015 1016 case WHOLE_SUBTREE: 1017 case SUBORDINATE_SUBTREE: 1018 if (subordinateBaseDNs == null) 1019 { 1020 baseMap = DirectoryServer.getPublicNamingContexts(); 1021 } 1022 else 1023 { 1024 baseMap = subordinateBaseDNs; 1025 } 1026 1027 try 1028 { 1029 for (DN subBase : baseMap.keySet()) 1030 { 1031 searchOperation.checkIfCanceled(false); 1032 1033 Backend b = baseMap.get(subBase); 1034 searchOperation.setBaseDN(subBase); 1035 1036 try 1037 { 1038 b.search(searchOperation); 1039 } 1040 catch (DirectoryException de) 1041 { 1042 // If it's a "no such object" exception, then the base entry for 1043 // the backend doesn't exist. This isn't an error, so ignore it. 1044 // We'll propogate all other errors, though. 1045 if (de.getResultCode() != ResultCode.NO_SUCH_OBJECT) 1046 { 1047 throw de; 1048 } 1049 } 1050 } 1051 } 1052 catch (DirectoryException de) 1053 { 1054 if (debugEnabled()) 1055 { 1056 TRACER.debugCaught(DebugLogLevel.ERROR, de); 1057 } 1058 1059 throw de; 1060 } 1061 catch (Exception e) 1062 { 1063 if (debugEnabled()) 1064 { 1065 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1066 } 1067 1068 Message message = ERR_ROOTDSE_UNEXPECTED_SEARCH_FAILURE. 1069 get(searchOperation.getConnectionID(), 1070 searchOperation.getOperationID(), 1071 stackTraceToSingleLineString(e)); 1072 throw new DirectoryException( 1073 DirectoryServer.getServerErrorResultCode(), message, 1074 e); 1075 } 1076 finally 1077 { 1078 searchOperation.setBaseDN(rootDSEDN); 1079 } 1080 break; 1081 1082 default: 1083 Message message = ERR_ROOTDSE_INVALID_SEARCH_SCOPE. 1084 get(searchOperation.getConnectionID(), 1085 searchOperation.getOperationID(), 1086 String.valueOf(searchOperation.getScope())); 1087 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 1088 } 1089 } 1090 1091 1092 1093 /** 1094 * {@inheritDoc} 1095 */ 1096 @Override() 1097 public HashSet<String> getSupportedControls() 1098 { 1099 return supportedControls; 1100 } 1101 1102 1103 1104 /** 1105 * {@inheritDoc} 1106 */ 1107 @Override() 1108 public HashSet<String> getSupportedFeatures() 1109 { 1110 return supportedFeatures; 1111 } 1112 1113 1114 1115 /** 1116 * {@inheritDoc} 1117 */ 1118 @Override() 1119 public boolean supportsLDIFExport() 1120 { 1121 // We will only export the DSE entry itself. 1122 return true; 1123 } 1124 1125 1126 1127 /** 1128 * {@inheritDoc} 1129 */ 1130 @Override() 1131 public void exportLDIF(LDIFExportConfig exportConfig) 1132 throws DirectoryException 1133 { 1134 // Create the LDIF writer. 1135 LDIFWriter ldifWriter; 1136 try 1137 { 1138 ldifWriter = new LDIFWriter(exportConfig); 1139 } 1140 catch (Exception e) 1141 { 1142 if (debugEnabled()) 1143 { 1144 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1145 } 1146 1147 Message message = ERR_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER.get( 1148 stackTraceToSingleLineString(e)); 1149 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 1150 message); 1151 } 1152 1153 1154 // Write the root DSE entry itself to it. Make sure to close the LDIF 1155 // writer when we're done. 1156 try 1157 { 1158 ldifWriter.writeEntry(getRootDSE()); 1159 } 1160 catch (Exception e) 1161 { 1162 if (debugEnabled()) 1163 { 1164 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1165 } 1166 1167 Message message = 1168 ERR_ROOTDSE_UNABLE_TO_EXPORT_DSE.get(stackTraceToSingleLineString(e)); 1169 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 1170 message); 1171 } 1172 finally 1173 { 1174 try 1175 { 1176 ldifWriter.close(); 1177 } 1178 catch (Exception e) 1179 { 1180 if (debugEnabled()) 1181 { 1182 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1183 } 1184 } 1185 } 1186 } 1187 1188 1189 1190 /** 1191 * {@inheritDoc} 1192 */ 1193 @Override() 1194 public boolean supportsLDIFImport() 1195 { 1196 // This backend does not support LDIF imports. 1197 return false; 1198 } 1199 1200 1201 1202 /** 1203 * {@inheritDoc} 1204 */ 1205 @Override() 1206 public LDIFImportResult importLDIF(LDIFImportConfig importConfig) 1207 throws DirectoryException 1208 { 1209 // This backend does not support LDIF imports. 1210 Message message = ERR_ROOTDSE_IMPORT_NOT_SUPPORTED.get(); 1211 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1212 } 1213 1214 1215 1216 /** 1217 * {@inheritDoc} 1218 */ 1219 @Override() 1220 public boolean supportsBackup() 1221 { 1222 // This backend does not provide a backup/restore mechanism. 1223 return false; 1224 } 1225 1226 1227 1228 /** 1229 * {@inheritDoc} 1230 */ 1231 @Override() 1232 public boolean supportsBackup(BackupConfig backupConfig, 1233 StringBuilder unsupportedReason) 1234 { 1235 // This backend does not provide a backup/restore mechanism. 1236 return false; 1237 } 1238 1239 1240 1241 /** 1242 * {@inheritDoc} 1243 */ 1244 @Override() 1245 public void createBackup(BackupConfig backupConfig) 1246 throws DirectoryException 1247 { 1248 // This backend does not provide a backup/restore mechanism. 1249 Message message = ERR_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1250 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1251 } 1252 1253 1254 1255 /** 1256 * {@inheritDoc} 1257 */ 1258 @Override() 1259 public void removeBackup(BackupDirectory backupDirectory, 1260 String backupID) 1261 throws DirectoryException 1262 { 1263 // This backend does not provide a backup/restore mechanism. 1264 Message message = ERR_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1265 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1266 } 1267 1268 1269 1270 /** 1271 * {@inheritDoc} 1272 */ 1273 @Override() 1274 public boolean supportsRestore() 1275 { 1276 // This backend does not provide a backup/restore mechanism. 1277 return false; 1278 } 1279 1280 1281 1282 /** 1283 * {@inheritDoc} 1284 */ 1285 @Override() 1286 public void restoreBackup(RestoreConfig restoreConfig) 1287 throws DirectoryException 1288 { 1289 // This backend does not provide a backup/restore mechanism. 1290 Message message = ERR_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(); 1291 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1292 } 1293 1294 1295 1296 /** 1297 * {@inheritDoc} 1298 */ 1299 @Override() 1300 public boolean isConfigurationAcceptable(Configuration configuration, 1301 List<Message> unacceptableReasons) 1302 { 1303 RootDSEBackendCfg config = (RootDSEBackendCfg) configuration; 1304 return isConfigurationChangeAcceptable(config, unacceptableReasons); 1305 } 1306 1307 1308 1309 /** 1310 * {@inheritDoc} 1311 */ 1312 public boolean isConfigurationChangeAcceptable( 1313 RootDSEBackendCfg cfg, 1314 List<Message> unacceptableReasons) 1315 { 1316 boolean configIsAcceptable = true; 1317 1318 1319 try 1320 { 1321 Set<DN> subDNs = cfg.getSubordinateBaseDN(); 1322 if (subDNs.isEmpty()) 1323 { 1324 // This is fine -- we'll just use the set of user-defined suffixes. 1325 } 1326 else 1327 { 1328 for (DN baseDN : subDNs) 1329 { 1330 Backend backend = DirectoryServer.getBackend(baseDN); 1331 if (backend == null) 1332 { 1333 Message message = WARN_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE.get( 1334 String.valueOf(baseDN)); 1335 unacceptableReasons.add(message); 1336 configIsAcceptable = false; 1337 } 1338 } 1339 } 1340 } 1341 catch (Exception e) 1342 { 1343 if (debugEnabled()) 1344 { 1345 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1346 } 1347 1348 Message message = WARN_ROOTDSE_SUBORDINATE_BASE_EXCEPTION.get( 1349 stackTraceToSingleLineString(e)); 1350 unacceptableReasons.add(message); 1351 configIsAcceptable = false; 1352 } 1353 1354 1355 return configIsAcceptable; 1356 } 1357 1358 1359 1360 /** 1361 * {@inheritDoc} 1362 */ 1363 public ConfigChangeResult applyConfigurationChange(RootDSEBackendCfg cfg) 1364 { 1365 ResultCode resultCode = ResultCode.SUCCESS; 1366 boolean adminActionRequired = false; 1367 ArrayList<Message> messages = new ArrayList<Message>(); 1368 1369 1370 // Check to see if we should apply a new set of base DNs. 1371 ConcurrentHashMap<DN,Backend> subBases; 1372 try 1373 { 1374 Set<DN> subDNs = cfg.getSubordinateBaseDN(); 1375 if (subDNs.isEmpty()) 1376 { 1377 // This is fine -- we'll just use the set of user-defined suffixes. 1378 subBases = null; 1379 } 1380 else 1381 { 1382 subBases = new ConcurrentHashMap<DN,Backend>(); 1383 for (DN baseDN : subDNs) 1384 { 1385 Backend backend = DirectoryServer.getBackend(baseDN); 1386 if (backend == null) 1387 { 1388 // This is not fine. We can't use a suffix that doesn't exist. 1389 Message message = WARN_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE.get( 1390 String.valueOf(baseDN)); 1391 messages.add(message); 1392 1393 if (resultCode == ResultCode.SUCCESS) 1394 { 1395 resultCode = DirectoryServer.getServerErrorResultCode(); 1396 } 1397 } 1398 else 1399 { 1400 subBases.put(baseDN, backend); 1401 } 1402 } 1403 } 1404 } 1405 catch (Exception e) 1406 { 1407 if (debugEnabled()) 1408 { 1409 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1410 } 1411 1412 Message message = WARN_ROOTDSE_SUBORDINATE_BASE_EXCEPTION.get( 1413 stackTraceToSingleLineString(e)); 1414 messages.add(message); 1415 1416 if (resultCode == ResultCode.SUCCESS) 1417 { 1418 resultCode = DirectoryServer.getServerErrorResultCode(); 1419 } 1420 1421 subBases = null; 1422 } 1423 1424 1425 boolean newShowAll = cfg.isShowAllAttributes(); 1426 1427 1428 // Check to see if there is a new set of user-defined attributes. 1429 ArrayList<Attribute> userAttrs = new ArrayList<Attribute>(); 1430 try 1431 { 1432 ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN); 1433 1434 for (List<Attribute> attrs : 1435 configEntry.getEntry().getUserAttributes().values()) 1436 { 1437 for (Attribute a : attrs) 1438 { 1439 if (! isDSEConfigAttribute(a)) 1440 { 1441 userAttrs.add(a); 1442 } 1443 } 1444 } 1445 for (List<Attribute> attrs : 1446 configEntry.getEntry().getOperationalAttributes().values()) 1447 { 1448 for (Attribute a : attrs) 1449 { 1450 if (! isDSEConfigAttribute(a)) 1451 { 1452 userAttrs.add(a); 1453 } 1454 } 1455 } 1456 } 1457 catch (ConfigException e) 1458 { 1459 if (debugEnabled()) 1460 { 1461 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1462 } 1463 1464 messages.add(ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY.get( 1465 String.valueOf(configEntryDN), 1466 stackTraceToSingleLineString(e))); 1467 resultCode = DirectoryServer.getServerErrorResultCode(); 1468 } 1469 1470 1471 if (resultCode == ResultCode.SUCCESS) 1472 { 1473 subordinateBaseDNs = subBases; 1474 1475 if (subordinateBaseDNs == null) 1476 { 1477 Message message = INFO_ROOTDSE_USING_SUFFIXES_AS_BASE_DNS.get(); 1478 messages.add(message); 1479 } 1480 else 1481 { 1482 StringBuilder basesStr = new StringBuilder(); 1483 for (DN dn : subordinateBaseDNs.keySet()) 1484 { 1485 if (basesStr.length() > 0) 1486 { 1487 basesStr.append(", "); 1488 } 1489 else 1490 { 1491 basesStr.append("{ "); 1492 } 1493 1494 basesStr.append(dn); 1495 } 1496 1497 basesStr.append(" }"); 1498 1499 Message message = INFO_ROOTDSE_USING_NEW_SUBORDINATE_BASE_DNS.get( 1500 basesStr.toString()); 1501 messages.add(message); 1502 } 1503 1504 1505 if (showAllAttributes != newShowAll) 1506 { 1507 showAllAttributes = newShowAll; 1508 Message message = INFO_ROOTDSE_UPDATED_SHOW_ALL_ATTRS.get( 1509 ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES, 1510 String.valueOf(showAllAttributes)); 1511 messages.add(message); 1512 } 1513 1514 1515 userDefinedAttributes = userAttrs; 1516 Message message = INFO_ROOTDSE_USING_NEW_USER_ATTRS.get(); 1517 messages.add(message); 1518 } 1519 1520 1521 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 1522 } 1523 1524 1525 1526 /** 1527 * {@inheritDoc} 1528 */ 1529 public void preloadEntryCache() throws UnsupportedOperationException { 1530 throw new UnsupportedOperationException("Operation not supported."); 1531 } 1532 } 1533