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