/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying * information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2006 Sun Microsystems, Inc.
*/
package org.opends.server.backends;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.CancelledOperationException;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.util.LDIFWriter;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines a backend to hold the Directory Server root DSE. It is a
* kind of meta-backend in that it will dynamically generate the root DSE entry
* (although there will be some caching) for base-level searches, and will
* simply redirect to other backends for operations in other scopes.
*
* This should not be treated like a regular backend when it comes to
* initializing the server configuration. It should only be initialized after
* all other backends are configured. As such, it should have a special entry
* in the configuration rather than being placed under the cn=Backends branch
* with the other backends.
*/
public class RootDSEBackend
extends Backend
implements ConfigurableComponent
{
/**
* The fully-qualified name of this class for debugging purposes.
*/
private static final String CLASS_NAME =
"org.opends.server.backends.RootDSEBackend";
// The set of standard "static" attributes that we will always include in the
// root DSE entry and won't change while the server is running.
private ArrayList staticDSEAttributes;
// The set of user-defined attributes that will be included in the root DSE
// entry.
private ArrayList userDefinedAttributes;
// Indicates whether the attributes of the root DSE should always be treated
// as user attributes even if they are defined as operational in the schema.
private boolean showAllAttributes;
// The set of subordinate base DNs and their associated backends that will be
// used for non-base searches.
private ConcurrentHashMap subordinateBaseDNs;
// The set of objectclasses that will be used in the root DSE entry.
private HashMap dseObjectClasses;
// The DN of the configuration entry for this backend.
private DN configEntryDN;
// The DN for the root DSE.
private DN rootDSEDN;
// The set of base DNs for this backend.
private DN[] baseDNs;
// The set of supported controls for this backend.
private HashSet supportedControls;
// The set of supported features for this backend.
private HashSet supportedFeatures;
/**
* Creates a new backend with the provided information. All backend
* implementations must implement a default constructor that use
* super() to invoke this constructor.
*/
public RootDSEBackend()
{
super();
assert debugConstructor(CLASS_NAME);
// Perform all initialization in initializeBackend.
}
/**
* Initializes this backend based on the information in the provided
* configuration entry.
*
* @param configEntry The configuration entry that contains the information
* to use to initialize this backend.
* @param baseDNs The set of base DNs that have been configured for this
* backend.
*
* @throws ConfigException If an unrecoverable problem arises in the
* process of performing the initialization.
*
* @throws InitializationException If a problem occurs during initialization
* that is not related to the server
* configuration.
*/
public void initializeBackend(ConfigEntry configEntry, DN[] baseDNs)
throws ConfigException, InitializationException
{
assert debugEnter(CLASS_NAME, "initializeBackend",
String.valueOf(configEntry));
// Make sure that a configuration entry was provided. If not, then we will
// not be able to complete initialization.
if (configEntry == null)
{
int msgID = MSGID_ROOTDSE_CONFIG_ENTRY_NULL;
String message = getMessage(msgID);
throw new ConfigException(msgID, message);
}
configEntryDN = configEntry.getDN();
// Get the set of user-defined attributes for the configuration entry. Any
// attributes that we don't recognize will be included directly in the root
// DSE.
userDefinedAttributes = new ArrayList();
for (List attrs :
configEntry.getEntry().getUserAttributes().values())
{
for (Attribute a : attrs)
{
if (! isDSEConfigAttribute(a))
{
userDefinedAttributes.add(a);
}
}
}
for (List attrs :
configEntry.getEntry().getOperationalAttributes().values())
{
for (Attribute a : attrs)
{
if (! isDSEConfigAttribute(a))
{
userDefinedAttributes.add(a);
}
}
}
// Create the set of base DNs that we will handle. In this case, it's just
// the root DSE.
rootDSEDN = new DN(new ArrayList(0));
this.baseDNs = new DN[] { rootDSEDN };
// Create the set of subordinate base DNs. If this is specified in the
// configuration, then use that set. Otherwise, use the set of non-private
// backends defined in the server.
String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
DNConfigAttribute subDNsStub =
new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
false, true, false);
try
{
DNConfigAttribute subDNsAttr =
(DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
if (subDNsAttr == null)
{
// This is fine -- we'll just use the set of user-defined suffixes.
subordinateBaseDNs = null;
}
else
{
subordinateBaseDNs = new ConcurrentHashMap();
for (DN baseDN : subDNsAttr.activeValues())
{
Backend backend = DirectoryServer.getBackend(baseDN);
if (backend == null)
{
int msgID = MSGID_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE;
String message = getMessage(msgID, String.valueOf(baseDN));
logError(ErrorLogCategory.CONFIGURATION,
ErrorLogSeverity.SEVERE_WARNING, message, msgID);
}
else
{
subordinateBaseDNs.put(baseDN, backend);
}
}
}
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "initializeBackend", e);
int msgID = MSGID_ROOTDSE_SUBORDINATE_BASE_EXCEPTION;
String message = getMessage(msgID, stackTraceToSingleLineString(e));
throw new InitializationException(msgID, message,e);
}
// Determine whether all root DSE attributes should be treated as user
// attributes.
showAllAttributes = DEFAULT_ROOTDSE_SHOW_ALL_ATTRIBUTES;
int msgID = MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES;
BooleanConfigAttribute showAllStub =
new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
getMessage(msgID), false);
try
{
BooleanConfigAttribute showAllAttr =
(BooleanConfigAttribute)
configEntry.getConfigAttribute(showAllStub);
if (showAllAttr != null)
{
showAllAttributes = showAllAttr.activeValue();
}
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "initializeBackend", e);
msgID = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
stackTraceToSingleLineString(e));
logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_ERROR, message,
msgID);
}
// Construct the set of "static" attributes that will always be present in
// the root DSE.
staticDSEAttributes = new ArrayList();
staticDSEAttributes.add(createAttribute(ATTR_VENDOR_NAME,
ATTR_VENDOR_NAME_LC,
SERVER_VENDOR_NAME));
staticDSEAttributes.add(createAttribute(ATTR_VENDOR_VERSION,
ATTR_VENDOR_VERSION_LC,
DirectoryServer.getVersionString()));
// Construct the set of objectclasses to include in the root DSE entry.
dseObjectClasses = new HashMap(2);
ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP);
if (topOC == null)
{
topOC = DirectoryServer.getDefaultObjectClass(OC_TOP);
}
dseObjectClasses.put(topOC, OC_TOP);
ObjectClass rootDSEOC =
DirectoryServer.getObjectClass(OC_ROOT_DSE_LC);
if (rootDSEOC == null)
{
rootDSEOC = DirectoryServer.getDefaultObjectClass(OC_ROOT_DSE);
}
dseObjectClasses.put(rootDSEOC, OC_ROOT_DSE);
// Define an empty sets for the supported controls and features.
supportedControls = new HashSet(0);
supportedFeatures = new HashSet(0);
// Register with the Directory Server as a configurable component.
DirectoryServer.registerConfigurableComponent(this);
}
/**
* Performs any necessary work to finalize this backend, including closing any
* underlying databases or connections and deregistering any suffixes that it
* manages with the Directory Server. This may be called during the
* Directory Server shutdown process or if a backend is disabled with the
* server online. It must not return until the backend is closed.
*
* This method may not throw any exceptions. If any problems are encountered,
* then they may be logged but the closure should progress as completely as
* possible.
*/
public void finalizeBackend()
{
assert debugEnter(CLASS_NAME, "finalizeBackend");
DirectoryServer.deregisterConfigurableComponent(this);
}
/**
* Indicates whether the provided attribute is one that is used in the
* configuration of this backend.
*
* @param attribute The attribute for which to make the determination.
*
* @return true if the provided attribute is one that is used in
* the configuration of this backend, false if not.
*/
private boolean isDSEConfigAttribute(Attribute attribute)
{
assert debugEnter(CLASS_NAME, "isConfigAttribute",
String.valueOf(attribute));
AttributeType attrType = attribute.getAttributeType();
if (attrType.hasName(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN.toLowerCase()) ||
attrType.hasName(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES.toLowerCase()) ||
attrType.hasName(ATTR_COMMON_NAME))
{
return true;
}
return false;
}
/**
* Retrieves the set of base-level DNs that may be used within this backend.
*
* @return The set of base-level DNs that may be used within this backend.
*/
public DN[] getBaseDNs()
{
assert debugEnter(CLASS_NAME, "getBaseDNs");
return baseDNs;
}
/**
* Indicates whether the data associated with this backend may be considered
* local (i.e., in a repository managed by the Directory Server) rather than
* remote (i.e., in an external repository accessed by the Directory Server
* but managed through some other means).
*
* @return true if the data associated with this backend may be
* considered local, or false if it is remote.
*/
public boolean isLocal()
{
assert debugEnter(CLASS_NAME, "isLocal");
// For the purposes of this method, this is a local backend.
return true;
}
/**
* Retrieves the requested entry from this backend.
*
* @param entryDN The distinguished name of the entry to retrieve.
*
* @return The requested entry, or null if the entry does not
* exist.
*
* @throws DirectoryException If a problem occurs while trying to retrieve
* the entry.
*/
public Entry getEntry(DN entryDN)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "getEntry", String.valueOf(entryDN));
// If the requested entry was the root DSE, then create and return it.
if ((entryDN == null) || entryDN.isNullDN())
{
return getRootDSE();
}
// This method should never be used to get anything other than the root DSE.
// If we got here, then that appears to be the case, so log a message.
int msgID = MSGID_ROOTDSE_GET_ENTRY_NONROOT;
String message = getMessage(msgID, String.valueOf(entryDN));
logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.MILD_WARNING,
message, msgID);
// Go ahead and check the subordinate backends to see if we can find the
// entry there. Note that in order to avoid potential loop conditions, this
// will only work if the set of subordinate bases has been explicitly
// specified.
if (subordinateBaseDNs != null)
{
for (Backend b : subordinateBaseDNs.values())
{
if (b.handlesEntry(entryDN))
{
return b.getEntry(entryDN);
}
}
}
// If we've gotten here, then we couldn't find the entry so return null.
return null;
}
/**
* Retrieves the root DSE entry for the Directory Server.
*
* @return The root DSE entry for the Directory Server.
*/
public Entry getRootDSE()
{
assert debugEnter(CLASS_NAME, "getRootDSE");
HashMap> dseUserAttrs =
new HashMap>();
HashMap> dseOperationalAttrs =
new HashMap>();
// Add the "namingContexts" attribute.
Attribute namingContextAttr =
createDNAttribute(ATTR_NAMING_CONTEXTS, ATTR_NAMING_CONTEXTS_LC,
DirectoryServer.getSuffixes().keySet());
ArrayList namingContextAttrs = new ArrayList(1);
namingContextAttrs.add(namingContextAttr);
if (showAllAttributes ||
(! namingContextAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(namingContextAttr.getAttributeType(),
namingContextAttrs);
}
else
{
dseOperationalAttrs.put(namingContextAttr.getAttributeType(),
namingContextAttrs);
}
// Add the "supportedControl" attribute.
Attribute supportedControlAttr =
createAttribute(ATTR_SUPPORTED_CONTROL, ATTR_SUPPORTED_CONTROL_LC,
DirectoryServer.getSupportedControls());
ArrayList supportedControlAttrs = new ArrayList(1);
supportedControlAttrs.add(supportedControlAttr);
if (showAllAttributes ||
(! supportedControlAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(supportedControlAttr.getAttributeType(),
supportedControlAttrs);
}
else
{
dseOperationalAttrs.put(supportedControlAttr.getAttributeType(),
supportedControlAttrs);
}
// Add the "supportedExtension" attribute.
Attribute supportedExtensionAttr =
createAttribute(ATTR_SUPPORTED_EXTENSION, ATTR_SUPPORTED_EXTENSION_LC,
DirectoryServer.getSupportedExtensions().keySet());
ArrayList supportedExtensionAttrs = new ArrayList(1);
supportedExtensionAttrs.add(supportedExtensionAttr);
if (showAllAttributes ||
(! supportedExtensionAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(supportedExtensionAttr.getAttributeType(),
supportedExtensionAttrs);
}
else
{
dseOperationalAttrs.put(supportedExtensionAttr.getAttributeType(),
supportedExtensionAttrs);
}
// Add the "supportedFeature" attribute.
Attribute supportedFeatureAttr =
createAttribute(ATTR_SUPPORTED_FEATURE, ATTR_SUPPORTED_FEATURE_LC,
DirectoryServer.getSupportedFeatures());
ArrayList supportedFeatureAttrs = new ArrayList(1);
supportedFeatureAttrs.add(supportedFeatureAttr);
if (showAllAttributes ||
(! supportedFeatureAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(supportedFeatureAttr.getAttributeType(),
supportedFeatureAttrs);
}
else
{
dseOperationalAttrs.put(supportedFeatureAttr.getAttributeType(),
supportedFeatureAttrs);
}
// Add the "supportedSASLMechanisms" attribute.
Attribute supportedSASLMechAttr =
createAttribute(ATTR_SUPPORTED_SASL_MECHANISMS,
ATTR_SUPPORTED_SASL_MECHANISMS_LC,
DirectoryServer.getSupportedSASLMechanisms().keySet());
ArrayList supportedSASLMechAttrs = new ArrayList(1);
supportedSASLMechAttrs.add(supportedSASLMechAttr);
if (showAllAttributes ||
(! supportedSASLMechAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(supportedSASLMechAttr.getAttributeType(),
supportedSASLMechAttrs);
}
else
{
dseOperationalAttrs.put(supportedSASLMechAttr.getAttributeType(),
supportedSASLMechAttrs);
}
// Add the "supportedAuthPasswordSchemes" attribute.
Set authPWSchemes =
DirectoryServer.getAuthPasswordStorageSchemes().keySet();
if (! authPWSchemes.isEmpty())
{
Attribute supportedAuthPWSchemesAttr =
createAttribute(ATTR_SUPPORTED_AUTH_PW_SCHEMES,
ATTR_SUPPORTED_AUTH_PW_SCHEMES_LC, authPWSchemes);
ArrayList supportedAuthPWSchemesAttrs =
new ArrayList(1);
supportedAuthPWSchemesAttrs.add(supportedAuthPWSchemesAttr);
if (showAllAttributes ||
(! supportedSASLMechAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(supportedAuthPWSchemesAttr.getAttributeType(),
supportedAuthPWSchemesAttrs);
}
else
{
dseOperationalAttrs.put(supportedAuthPWSchemesAttr.getAttributeType(),
supportedAuthPWSchemesAttrs);
}
}
// Add the "subschemaSubentry" attribute.
DN schemaDN = DirectoryServer.getSchemaDN();
if (schemaDN != null)
{
Attribute subschemaSubentryAttr =
createAttribute(ATTR_SUBSCHEMA_SUBENTRY, ATTR_SUBSCHEMA_SUBENTRY_LC,
String.valueOf(schemaDN));
ArrayList subschemaSubentryAttrs = new ArrayList(1);
subschemaSubentryAttrs.add(subschemaSubentryAttr);
if (showAllAttributes ||
(! subschemaSubentryAttr.getAttributeType().isOperational()))
{
dseUserAttrs.put(subschemaSubentryAttr.getAttributeType(),
subschemaSubentryAttrs);
}
else
{
dseOperationalAttrs.put(subschemaSubentryAttr.getAttributeType(),
subschemaSubentryAttrs);
}
}
// Add all the standard "static" attributes.
for (Attribute a : staticDSEAttributes)
{
AttributeType type = a.getAttributeType();
if (type.isOperational() && (! showAllAttributes))
{
List attrs = dseOperationalAttrs.get(type);
if (attrs == null)
{
attrs = new ArrayList();
attrs.add(a);
dseOperationalAttrs.put(type, attrs);
}
else
{
attrs.add(a);
}
}
else
{
List attrs = dseUserAttrs.get(type);
if (attrs == null)
{
attrs = new ArrayList();
attrs.add(a);
dseUserAttrs.put(type, attrs);
}
else
{
attrs.add(a);
}
}
}
// Add all the user-defined attributes.
for (Attribute a : userDefinedAttributes)
{
AttributeType type = a.getAttributeType();
if (type.isOperational() && (! showAllAttributes))
{
List attrs = dseOperationalAttrs.get(type);
if (attrs == null)
{
attrs = new ArrayList();
attrs.add(a);
dseOperationalAttrs.put(type, attrs);
}
else
{
attrs.add(a);
}
}
else
{
List attrs = dseUserAttrs.get(type);
if (attrs == null)
{
attrs = new ArrayList();
attrs.add(a);
dseUserAttrs.put(type, attrs);
}
else
{
attrs.add(a);
}
}
}
// Construct and return the entry.
return new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs,
dseOperationalAttrs);
}
/**
* Creates an attribute for the root DSE with the following criteria.
*
* @param name The name for the attribute.
* @param lowerName The name for the attribute formatted in all lowercase
* characters.
* @param value The value to use for the attribute.
*
* @return The constructed attribute.
*/
private Attribute createAttribute(String name, String lowerName,
String value)
{
assert debugEnter(CLASS_NAME, "createAttribute", String.valueOf(name),
String.valueOf(lowerName), String.valueOf(value));
AttributeType type = DirectoryServer.getAttributeType(lowerName);
if (type == null)
{
type = DirectoryServer.getDefaultAttributeType(name);
}
LinkedHashSet attrValues =
new LinkedHashSet(1);
attrValues.add(new AttributeValue(type, new ASN1OctetString(value)));
return new Attribute(type, name, attrValues);
}
/**
* Creates an attribute for the root DSE meant to hold a set of DNs.
*
* @param name The name for the attribute.
* @param lowerName The name for the attribute formatted in all lowercase
* characters.
* @param values The set of DN values to use for the attribute.
*
* @return The constructed attribute.
*/
private Attribute createDNAttribute(String name, String lowerName,
Collection values)
{
assert debugEnter(CLASS_NAME, "createDNAttribute", String.valueOf(name),
String.valueOf(lowerName), String.valueOf(values));
AttributeType type = DirectoryServer.getAttributeType(lowerName);
if (type == null)
{
type = DirectoryServer.getDefaultAttributeType(name);
}
LinkedHashSet attrValues =
new LinkedHashSet();
for (DN dn : values)
{
attrValues.add(new AttributeValue(type,
new ASN1OctetString(dn.toString())));
}
return new Attribute(type, name, attrValues);
}
/**
* Creates an attribute for the root DSE with the following criteria.
*
* @param name The name for the attribute.
* @param lowerName The name for the attribute formatted in all lowercase
* characters.
* @param values The set of values to use for the attribute.
*
* @return The constructed attribute.
*/
private Attribute createAttribute(String name, String lowerName,
Collection values)
{
assert debugEnter(CLASS_NAME, "createAttribute", String.valueOf(name),
String.valueOf(lowerName), String.valueOf(values));
AttributeType type = DirectoryServer.getAttributeType(lowerName);
if (type == null)
{
type = DirectoryServer.getDefaultAttributeType(name);
}
LinkedHashSet attrValues =
new LinkedHashSet();
for (String s : values)
{
attrValues.add(new AttributeValue(type, new ASN1OctetString(s)));
}
return new Attribute(type, name, attrValues);
}
/**
* Indicates whether an entry with the specified DN exists in the backend.
* The default implementation obtains a read lock and calls
* getEntry, but backend implementations may override this with a
* more efficient version that does not require a lock. The caller is not
* required to hold any locks on the specified DN.
*
* @param entryDN The DN of the entry for which to determine existence.
*
* @return true if the specified entry exists in this backend,
* or false if it does not.
*
* @throws DirectoryException If a problem occurs while trying to make the
* determination.
*/
public boolean entryExists(DN entryDN)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "entryExists", String.valueOf(entryDN));
// If the specified DN was the null DN, then it exists.
if (entryDN.isNullDN())
{
return true;
}
// If it was not the null DN, then iterate through the associated
// subordinate backends to make the determination.
Map baseMap;
if (subordinateBaseDNs == null)
{
baseMap = DirectoryServer.getSuffixes();
}
else
{
baseMap = subordinateBaseDNs;
}
for (DN baseDN : baseMap.keySet())
{
if (entryDN.isDescendantOf(baseDN))
{
Backend b = baseMap.get(baseDN);
if (b.entryExists(entryDN))
{
return true;
}
}
}
return false;
}
/**
* Adds the provided entry to this backend. This method must ensure that the
* entry is appropriate for the backend and that no entry already exists with
* the same DN.
*
* @param entry The entry to add to this backend.
* @param addOperation The add operation with which the new entry is
* associated. This may be null for adds
* performed internally.
*
* @throws DirectoryException If a problem occurs while trying to add the
* entry.
*/
public void addEntry(Entry entry, AddOperation addOperation)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "addEntry", String.valueOf(entry),
String.valueOf(addOperation));
int msgID = MSGID_ROOTDSE_ADD_NOT_SUPPORTED;
String message = getMessage(msgID, String.valueOf(entry.getDN()));
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Removes the specified entry from this backend. This method must ensure
* that the entry exists and that it does not have any subordinate entries
* (unless the backend supports a subtree delete operation and the client
* included the appropriate information in the request).
*
* @param entryDN The DN of the entry to remove from this backend.
* @param deleteOperation The delete operation with which this action is
* associated. This may be null for
* deletes performed internally.
*
* @throws DirectoryException If a problem occurs while trying to remove the
* entry.
*/
public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "deleteEntry", String.valueOf(entryDN),
String.valueOf(deleteOperation));
int msgID = MSGID_ROOTDSE_DELETE_NOT_SUPPORTED;
String message = getMessage(msgID, String.valueOf(entryDN));
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Replaces the specified entry with the provided entry in this backend. The
* backend must ensure that an entry already exists with the same DN as the
* provided entry.
*
* @param entry The new entry to use in place of the existing
* entry with the same DN.
* @param modifyOperation The modify operation with which this action is
* associated. This may be null for
* modifications performed internally.
*
* @throws DirectoryException If a problem occurs while trying to replace
* the entry.
*/
public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "replaceEntry", String.valueOf(entry),
String.valueOf(modifyOperation));
int msgID = MSGID_ROOTDSE_MODIFY_NOT_SUPPORTED;
String message = getMessage(msgID, String.valueOf(entry.getDN()),
String.valueOf(configEntryDN));
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Moves and/or renames the provided entry in this backend, altering any
* subordinate entries as necessary. This must ensure that an entry already
* exists with the provided current DN, and that no entry exists with the
* target DN of the provided entry.
*
* @param currentDN The current DN of the entry to be replaced.
* @param entry The new content to use for the entry.
* @param modifyDNOperation The modify DN operation with which this action
* is associated. This may be null
* for modify DN operations performed internally.
*
* @throws DirectoryException If a problem occurs while trying to perform
* the rename.
*/
public void renameEntry(DN currentDN, Entry entry,
ModifyDNOperation modifyDNOperation)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "renameEntry", String.valueOf(currentDN),
String.valueOf(entry), String.valueOf(modifyDNOperation));
int msgID = MSGID_ROOTDSE_MODIFY_DN_NOT_SUPPORTED;
String message = getMessage(msgID, String.valueOf(currentDN));
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Processes the specified search in this backend. Matching entries should be
* provided back to the core server using the
* SearchOperation.returnEntry method.
*
* @param searchOperation The search operation to be processed.
*
* @throws DirectoryException If a problem occurs while processing the
* search.
*
* @throws CancelledOperationException If this backend noticed and reacted
* to a request to cancel or abandon the
* add operation.
*/
public void search(SearchOperation searchOperation)
throws DirectoryException, CancelledOperationException
{
assert debugEnter(CLASS_NAME, "search", String.valueOf(searchOperation));
DN baseDN = searchOperation.getBaseDN();
if (! baseDN.isNullDN())
{
int msgID = MSGID_ROOTDSE_INVALID_SEARCH_BASE;
String message = getMessage(msgID, searchOperation.getConnectionID(),
searchOperation.getOperationID(),
String.valueOf(baseDN));
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
SearchFilter filter = searchOperation.getFilter();
switch (searchOperation.getScope())
{
case BASE_OBJECT:
Entry dseEntry = getRootDSE();
if (filter.matchesEntry(dseEntry))
{
searchOperation.returnEntry(dseEntry, null);
}
break;
case SINGLE_LEVEL:
Map baseMap;
if (subordinateBaseDNs == null)
{
baseMap = DirectoryServer.getSuffixes();
}
else
{
baseMap = subordinateBaseDNs;
}
for (DN subBase : baseMap.keySet())
{
CancelRequest cancelRequest = searchOperation.getCancelRequest();
if (cancelRequest != null)
{
throw new CancelledOperationException(CancelResult.CANCELED);
}
Backend b = baseMap.get(subBase);
Entry subBaseEntry = b.getEntry(subBase);
if ((subBaseEntry != null) && filter.matchesEntry(subBaseEntry))
{
searchOperation.returnEntry(subBaseEntry, null);
}
}
break;
case WHOLE_SUBTREE:
case SUBORDINATE_SUBTREE:
if (subordinateBaseDNs == null)
{
baseMap = DirectoryServer.getSuffixes();
}
else
{
baseMap = subordinateBaseDNs;
}
try
{
for (DN subBase : baseMap.keySet())
{
CancelRequest cancelRequest = searchOperation.getCancelRequest();
if (cancelRequest != null)
{
throw new CancelledOperationException(CancelResult.CANCELED);
}
Backend b = baseMap.get(subBase);
searchOperation.setBaseDN(subBase);
try
{
b.search(searchOperation);
}
catch (DirectoryException de)
{
// If it's a "no such object" exception, then the base entry for
// the backend doesn't exist. This isn't an error, so ignore it.
// We'll propogate all other errors, though.
if (de.getResultCode() != ResultCode.NO_SUCH_OBJECT)
{
throw de;
}
}
}
}
catch (DirectoryException de)
{
assert debugException(CLASS_NAME, "search", de);
throw de;
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "search", e);
int msgID = MSGID_ROOTDSE_UNEXPECTED_SEARCH_FAILURE;
String message = getMessage(msgID, searchOperation.getConnectionID(),
searchOperation.getOperationID(),
stackTraceToSingleLineString(e));
throw new DirectoryException(
DirectoryServer.getServerErrorResultCode(), message,
msgID, e);
}
finally
{
searchOperation.setBaseDN(rootDSEDN);
}
break;
default:
int msgID = MSGID_ROOTDSE_INVALID_SEARCH_SCOPE;
String message = getMessage(msgID, searchOperation.getConnectionID(),
searchOperation.getOperationID(),
String.valueOf(searchOperation.getScope()));
throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, msgID);
}
}
/**
* Retrieves the OIDs of the controls that may be supported by this backend.
*
* @return The OIDs of the controls that may be supported by this backend.
*/
public HashSet getSupportedControls()
{
assert debugEnter(CLASS_NAME, "getSupportedControls");
return supportedControls;
}
/**
* Indicates whether this backend supports the specified control.
*
* @param controlOID The OID of the control for which to make the
* determination.
*
* @return true if this backend does support the requested
* control, or false
*/
public boolean supportsControl(String controlOID)
{
assert debugEnter(CLASS_NAME, "supportsControl",
String.valueOf(controlOID));
// This backend does not provide any special control support.
return false;
}
/**
* Retrieves the OIDs of the features that may be supported by this backend.
*
* @return The OIDs of the features that may be supported by this backend.
*/
public HashSet getSupportedFeatures()
{
assert debugEnter(CLASS_NAME, "getSupportedFeatures");
return supportedFeatures;
}
/**
* Indicates whether this backend supports the specified feature.
*
* @param featureOID The OID of the feature for which to make the
* determination.
*
* @return true if this backend does support the requested
* feature, or false
*/
public boolean supportsFeature(String featureOID)
{
assert debugEnter(CLASS_NAME, "supportsFeature",
String.valueOf(featureOID));
// This backend does not provide any special feature support.
return false;
}
/**
* Indicates whether this backend provides a mechanism to export the data it
* contains to an LDIF file.
*
* @return true if this backend provides an LDIF export
* mechanism, or false if not.
*/
public boolean supportsLDIFExport()
{
assert debugEnter(CLASS_NAME, "supportsLDIFExport");
// We will only export the DSE entry itself.
return true;
}
/**
* Exports the contents of this backend to LDIF. This method should only be
* called if supportsLDIFExport returns true. Note
* that the server will not explicitly initialize this backend before calling
* this method.
*
* @param configEntry The configuration entry for this backend.
* @param baseDNs The set of base DNs configured for this backend.
* @param exportConfig The configuration to use when performing the export.
*
* @throws DirectoryException If a problem occurs while performing the LDIF
* export.
*/
public void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
LDIFExportConfig exportConfig)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "exportLDIF", String.valueOf(exportConfig));
// Create the LDIF writer.
LDIFWriter ldifWriter;
try
{
ldifWriter = new LDIFWriter(exportConfig);
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "exportLDIF", e);
int msgID = MSGID_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER;
String message = getMessage(msgID, stackTraceToSingleLineString(e));
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
message, msgID);
}
// Write the root DSE entry itself to it. Make sure to close the LDIF
// writer when we're done.
try
{
ldifWriter.writeEntry(getRootDSE());
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "exportLDIF", e);
int msgID = MSGID_ROOTDSE_UNABLE_TO_EXPORT_DSE;
String message = getMessage(msgID, stackTraceToSingleLineString(e));
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
message, msgID);
}
finally
{
try
{
ldifWriter.close();
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "exportLDIF", e);
}
}
}
/**
* Indicates whether this backend provides a mechanism to import its data from
* an LDIF file.
*
* @return true if this backend provides an LDIF import
* mechanism, or false if not.
*/
public boolean supportsLDIFImport()
{
assert debugEnter(CLASS_NAME, "supportsLDIFImport");
// This backend does not support LDIF imports.
return false;
}
/**
* Imports information from an LDIF file into this backend. This method
* should only be called if supportsLDIFImport returns
* true. Note that the server will not explicitly initialize
* this backend before calling this method.
*
* @param configEntry The configuration entry for this backend.
* @param baseDNs The set of base DNs configured for this backend.
* @param importConfig The configuration to use when performing the import.
*
* @throws DirectoryException If a problem occurs while performing the LDIF
* import.
*/
public void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
LDIFImportConfig importConfig)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "importLDIF", String.valueOf(importConfig));
// This backend does not support LDIF imports.
int msgID = MSGID_ROOTDSE_IMPORT_NOT_SUPPORTED;
String message = getMessage(msgID);
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Indicates whether this backend provides a backup mechanism of any kind.
* This method is used by the backup process when backing up all backends to
* determine whether this backend is one that should be skipped. It should
* only return true for backends that it is not possible to
* archive directly (e.g., those that don't store their data locally, but
* rather pass through requests to some other repository).
*
* @return true if this backend provides any kind of backup
* mechanism, or false if it does not.
*/
public boolean supportsBackup()
{
assert debugEnter(CLASS_NAME, "supportsBackup");
// This backend does not provide a backup/restore mechanism.
return false;
}
/**
* Indicates whether this backend provides a mechanism to perform a backup of
* its contents in a form that can be restored later, based on the provided
* configuration.
*
* @param backupConfig The configuration of the backup for which to
* make the determination.
* @param unsupportedReason A buffer to which a message can be appended
* explaining why the requested backup is not
* supported.
*
* @return true if this backend provides a mechanism for
* performing backups with the provided configuration, or
* false if not.
*/
public boolean supportsBackup(BackupConfig backupConfig,
StringBuilder unsupportedReason)
{
assert debugEnter(CLASS_NAME, "supportsBackup");
// This backend does not provide a backup/restore mechanism.
return false;
}
/**
* Creates a backup of the contents of this backend in a form that may be
* restored at a later date if necessary. This method should only be called
* if supportsBackup returns true. Note that the
* server will not explicitly initialize this backend before calling this
* method.
*
* @param configEntry The configuration entry for this backend.
* @param backupConfig The configuration to use when performing the backup.
*
* @throws DirectoryException If a problem occurs while performing the
* backup.
*/
public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "createBackup", String.valueOf(backupConfig));
// This backend does not provide a backup/restore mechanism.
int msgID = MSGID_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED;
String message = getMessage(msgID);
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Removes the specified backup if it is possible to do so.
*
* @param backupDirectory The backup directory structure with which the
* specified backup is associated.
* @param backupID The backup ID for the backup to be removed.
*
* @throws DirectoryException If it is not possible to remove the specified
* backup for some reason (e.g., no such backup
* exists or there are other backups that are
* dependent upon it).
*/
public void removeBackup(BackupDirectory backupDirectory,
String backupID)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "removeBackup",
String.valueOf(backupDirectory),
String.valueOf(backupID));
// This backend does not provide a backup/restore mechanism.
int msgID = MSGID_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED;
String message = getMessage(msgID);
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Indicates whether this backend provides a mechanism to restore a backup.
*
* @return true if this backend provides a mechanism for
* restoring backups, or false if not.
*/
public boolean supportsRestore()
{
assert debugEnter(CLASS_NAME, "supportsRestore");
// This backend does not provide a backup/restore mechanism.
return false;
}
/**
* Restores a backup of the contents of this backend. This method should only
* be called if supportsRestore returns true. Note
* that the server will not explicitly initialize this backend before calling
* this method.
*
* @param configEntry The configuration entry for this backend.
* @param restoreConfig The configuration to use when performing the
* restore.
*
* @throws DirectoryException If a problem occurs while performing the
* restore.
*/
public void restoreBackup(ConfigEntry configEntry,
RestoreConfig restoreConfig)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "restoreBackup",
String.valueOf(restoreConfig));
// This backend does not provide a backup/restore mechanism.
int msgID = MSGID_ROOTDSE_BACKUP_AND_RESTORE_NOT_SUPPORTED;
String message = getMessage(msgID);
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
msgID);
}
/**
* Retrieves the DN of the configuration entry with which this component is
* associated.
*
* @return The DN of the configuration entry with which this component is
* associated.
*/
public DN getConfigurableComponentEntryDN()
{
assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN");
return configEntryDN;
}
/**
* Retrieves the set of configuration attributes that are associated with this
* configurable component.
*
* @return The set of configuration attributes that are associated with this
* configurable component.
*/
public List getConfigurationAttributes()
{
assert debugEnter(CLASS_NAME, "getConfigurationAttributes");
LinkedList attrList = new LinkedList();
String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
ArrayList values = new ArrayList();
if (subordinateBaseDNs != null)
{
values.addAll(subordinateBaseDNs.keySet());
}
attrList.add(new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN,
description, false, true, false,
values));
description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
attrList.add(new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
description, showAllAttributes));
return attrList;
}
/**
* Indicates whether the provided configuration entry has an acceptable
* configuration for this component. If it does not, then detailed
* information about the problem(s) should be added to the provided list.
*
* @param configEntry The configuration entry for which to make the
* determination.
* @param unacceptableReasons A list that can be used to hold messages about
* why the provided entry does not have an
* acceptable configuration.
*
* @return true if the provided entry has an acceptable
* configuration for this component, or false if not.
*/
public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
List unacceptableReasons)
{
assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration",
String.valueOf(configEntry), "java.util.List");
boolean configIsAcceptable = true;
String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
DNConfigAttribute subDNsStub =
new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
false, true, false);
try
{
DNConfigAttribute subDNsAttr =
(DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
if (subDNsAttr == null)
{
// This is fine -- we'll just use the set of user-defined suffixes.
}
else
{
for (DN baseDN : subDNsAttr.activeValues())
{
Backend backend = DirectoryServer.getBackend(baseDN);
if (backend == null)
{
int msgID = MSGID_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE;
String message = getMessage(msgID, String.valueOf(baseDN));
unacceptableReasons.add(message);
configIsAcceptable = false;
}
}
}
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
int msgID = MSGID_ROOTDSE_SUBORDINATE_BASE_EXCEPTION;
String message = getMessage(msgID, stackTraceToSingleLineString(e));
unacceptableReasons.add(message);
configIsAcceptable = false;
}
description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
BooleanConfigAttribute showAllStub =
new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
description, false);
try
{
BooleanConfigAttribute showAllAttr =
(BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "hasAcceptableConfiguration", e);
int msgID = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
stackTraceToSingleLineString(e));
unacceptableReasons.add(message);
configIsAcceptable = false;
}
return configIsAcceptable;
}
/**
* Makes a best-effort attempt to apply the configuration contained in the
* provided entry. Information about the result of this processing should be
* added to the provided message list. Information should always be added to
* this list if a configuration change could not be applied. If detailed
* results are requested, then information about the changes applied
* successfully (and optionally about parameters that were not changed) should
* also be included.
*
* @param configEntry The entry containing the new configuration to
* apply for this component.
* @param detailedResults Indicates whether detailed information about the
* processing should be added to the list.
*
* @return Information about the result of the configuration update.
*/
public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
boolean detailedResults)
{
assert debugEnter(CLASS_NAME, "applyNewConfiguration",
String.valueOf(configEntry),
String.valueOf(detailedResults));
ResultCode resultCode = ResultCode.SUCCESS;
boolean adminActionRequired = false;
ArrayList messages = new ArrayList();
// Check to see if we should apply a new set of base DNs.
ConcurrentHashMap subBases;
String description = getMessage(MSGID_ROOTDSE_SUBORDINATE_BASE_DESCRIPTION);
DNConfigAttribute subDNsStub =
new DNConfigAttribute(ATTR_ROOT_DSE_SUBORDINATE_BASE_DN, description,
false, true, false);
try
{
DNConfigAttribute subDNsAttr =
(DNConfigAttribute) configEntry.getConfigAttribute(subDNsStub);
if (subDNsAttr == null)
{
// This is fine -- we'll just use the set of user-defined suffixes.
subBases = null;
}
else
{
subBases = new ConcurrentHashMap();
for (DN baseDN : subDNsAttr.activeValues())
{
Backend backend = DirectoryServer.getBackend(baseDN);
if (backend == null)
{
// This is not fine. We can't use a suffix that doesn't exist.
int msgID = MSGID_ROOTDSE_NO_BACKEND_FOR_SUBORDINATE_BASE;
String message = getMessage(msgID, String.valueOf(baseDN));
messages.add(message);
if (resultCode == ResultCode.SUCCESS)
{
resultCode = DirectoryServer.getServerErrorResultCode();
}
}
else
{
subBases.put(baseDN, backend);
}
}
}
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "applyNewConfiguration", e);
int msgID = MSGID_ROOTDSE_SUBORDINATE_BASE_EXCEPTION;
String message = getMessage(msgID, stackTraceToSingleLineString(e));
messages.add(message);
if (resultCode == ResultCode.SUCCESS)
{
resultCode = DirectoryServer.getServerErrorResultCode();
}
subBases = null;
}
boolean newShowAll = DEFAULT_ROOTDSE_SHOW_ALL_ATTRIBUTES;
description = getMessage(MSGID_ROOTDSE_DESCRIPTION_SHOW_ALL_ATTRIBUTES);
BooleanConfigAttribute showAllStub =
new BooleanConfigAttribute(ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
description, false);
try
{
BooleanConfigAttribute showAllAttr =
(BooleanConfigAttribute) configEntry.getConfigAttribute(showAllStub);
if (showAllAttr != null)
{
newShowAll = showAllAttr.pendingValue();
}
}
catch (Exception e)
{
assert debugException(CLASS_NAME, "applyNewConfiguration", e);
int msgID = MSGID_ROOTDSE_CANNOT_DETERMINE_ALL_USER_ATTRIBUTES;
String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
stackTraceToSingleLineString(e));
messages.add(message);
if (resultCode == ResultCode.SUCCESS)
{
resultCode = DirectoryServer.getServerErrorResultCode();
}
}
// Check to see if there is a new set of user-defined attributes.
ArrayList userAttrs = new ArrayList();
for (List attrs :
configEntry.getEntry().getUserAttributes().values())
{
for (Attribute a : attrs)
{
if (! isDSEConfigAttribute(a))
{
userAttrs.add(a);
}
}
}
for (List attrs :
configEntry.getEntry().getOperationalAttributes().values())
{
for (Attribute a : attrs)
{
if (! isDSEConfigAttribute(a))
{
userAttrs.add(a);
}
}
}
if (resultCode == ResultCode.SUCCESS)
{
subordinateBaseDNs = subBases;
if (detailedResults)
{
if (subordinateBaseDNs == null)
{
int msgID = MSGID_ROOTDSE_USING_SUFFIXES_AS_BASE_DNS;
String message = getMessage(msgID);
messages.add(message);
}
else
{
StringBuilder basesStr = new StringBuilder();
Iterator iterator = subordinateBaseDNs.keySet().iterator();
while (iterator.hasNext())
{
if (basesStr.length() > 0)
{
basesStr.append(", ");
}
else
{
basesStr.append("{ ");
}
basesStr.append(iterator.next());
}
basesStr.append(" }");
int msgID = MSGID_ROOTDSE_USING_NEW_SUBORDINATE_BASE_DNS;
String message = getMessage(msgID, basesStr.toString());
messages.add(message);
}
}
if (showAllAttributes != newShowAll)
{
showAllAttributes = newShowAll;
if (detailedResults)
{
int msgID = MSGID_ROOTDSE_UPDATED_SHOW_ALL_ATTRS;
String message = getMessage(msgID, ATTR_ROOTDSE_SHOW_ALL_ATTRIBUTES,
showAllAttributes);
messages.add(message);
}
}
userDefinedAttributes = userAttrs;
if (detailedResults)
{
int msgID = MSGID_ROOTDSE_USING_NEW_USER_ATTRS;
String message = getMessage(msgID);
messages.add(message);
}
}
return new ConfigChangeResult(resultCode, adminActionRequired, messages);
}
}