From 5be072c20e46f0921bb00401ff26d0defb3e8991 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 05 Dec 2006 21:41:50 +0000
Subject: [PATCH] Update the base DN registration process to address a number of issues:
---
opends/resource/schema/02-config.ldif | 3
opends/src/server/org/opends/server/core/BackendConfigManager.java | 163 +++
opends/src/server/org/opends/server/backends/BackupBackend.java | 63
opends/src/server/org/opends/server/api/Backend.java | 92 +
opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java | 783 ++++++++++++++++++
opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java | 4
opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java | 11
opends/src/server/org/opends/server/backends/MemoryBackend.java | 57
opends/src/server/org/opends/server/messages/CoreMessages.java | 136 +++
opends/src/server/org/opends/server/util/ServerConstants.java | 17
opends/src/server/org/opends/server/core/DirectoryServer.java | 710 ++++++++++------
opends/src/server/org/opends/server/backends/MonitorBackend.java | 63
opends/src/server/org/opends/server/messages/ConfigMessages.java | 14
opends/src/server/org/opends/server/messages/BackendMessages.java | 14
opends/src/server/org/opends/server/backends/jeb/BackendImpl.java | 82 -
opends/src/server/org/opends/server/core/AddOperation.java | 2
opends/src/server/org/opends/server/types/DN.java | 4
opends/src/server/org/opends/server/backends/RootDSEBackend.java | 85 -
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java | 39
opends/src/server/org/opends/server/backends/SchemaBackend.java | 73 -
opends/src/server/org/opends/server/backends/task/TaskBackend.java | 63
21 files changed, 1,856 insertions(+), 622 deletions(-)
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index c510bca..4eea314 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -995,6 +995,9 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.289 NAME 'ds-pwp-password-policy-dn'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE directoryOperation
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.291 NAME 'ds-private-naming-contexts'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE directoryOperation
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
diff --git a/opends/src/server/org/opends/server/api/Backend.java b/opends/src/server/org/opends/server/api/Backend.java
index a97914c..eba5623 100644
--- a/opends/src/server/org/opends/server/api/Backend.java
+++ b/opends/src/server/org/opends/server/api/Backend.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
@@ -400,10 +401,18 @@
* @param controlOID The OID of the control for which to make the
* determination.
*
- * @return <CODE>true</CODE> if this backend does support the
- * requested control, or <CODE>false</CODE>
+ * @return {@code true} if this backends supports the control with
+ * the specified OID, or {@code false} if it does not.
*/
- public abstract boolean supportsControl(String controlOID);
+ public final boolean supportsControl(String controlOID)
+ {
+ assert debugEnter(CLASS_NAME, "supportsControl",
+ String.valueOf(controlOID));
+
+ Set<String> supportedControls = getSupportedControls();
+ return ((supportedControls != null) &&
+ supportedControls.contains(controlOID));
+ }
@@ -424,10 +433,18 @@
* @param featureOID The OID of the feature for which to make the
* determination.
*
- * @return <CODE>true</CODE> if this backend does support the
- * requested feature, or <CODE>false</CODE>
+ * @return {@code true} if this backend supports the feature with
+ * the specified OID, or {@code false} if it does not.
*/
- public abstract boolean supportsFeature(String featureOID);
+ public final boolean supportsFeature(String featureOID)
+ {
+ assert debugEnter(CLASS_NAME, "supportsFeature",
+ String.valueOf(featureOID));
+
+ Set<String> supportedFeatures = getSupportedFeatures();
+ return ((supportedFeatures != null) &&
+ supportedFeatures.contains(featureOID));
+ }
@@ -892,15 +909,64 @@
synchronized (this)
{
- Backend[] newSubordinateBackends =
- new Backend[subordinateBackends.length+1];
+ LinkedHashSet<Backend> backendSet =
+ new LinkedHashSet<Backend>();
- System.arraycopy(subordinateBackends, 0, newSubordinateBackends,
- 0, subordinateBackends.length);
- newSubordinateBackends[subordinateBackends.length] =
- subordinateBackend;
+ for (Backend b : subordinateBackends)
+ {
+ backendSet.add(b);
+ }
- subordinateBackends = newSubordinateBackends;
+ if (backendSet.add(subordinateBackend))
+ {
+ Backend[] newSubordinateBackends =
+ new Backend[backendSet.size()];
+ backendSet.toArray(newSubordinateBackends);
+ subordinateBackends = newSubordinateBackends;
+ }
+ }
+ }
+
+
+
+ /**
+ * Removes the provided backend from the set of subordinate backends
+ * for this backend.
+ *
+ * @param subordinateBackend The backend to remove from the set of
+ * subordinate backends for this
+ * backend.
+ */
+ public void removeSubordinateBackend(Backend subordinateBackend)
+ {
+ assert debugEnter(CLASS_NAME, "removeSubordinateBackend",
+ String.valueOf(subordinateBackend));
+
+ synchronized (this)
+ {
+ ArrayList<Backend> backendList =
+ new ArrayList<Backend>(subordinateBackends.length);
+
+ boolean found = false;
+ for (Backend b : subordinateBackends)
+ {
+ if (b.equals(subordinateBackend))
+ {
+ found = true;
+ }
+ else
+ {
+ backendList.add(b);
+ }
+ }
+
+ if (found)
+ {
+ Backend[] newSubordinateBackends =
+ new Backend[backendList.size()];
+ backendList.toArray(newSubordinateBackends);
+ subordinateBackends = newSubordinateBackends;
+ }
}
}
diff --git a/opends/src/server/org/opends/server/backends/BackupBackend.java b/opends/src/server/org/opends/server/backends/BackupBackend.java
index fda38ea..0576804 100644
--- a/opends/src/server/org/opends/server/backends/BackupBackend.java
+++ b/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -275,7 +275,19 @@
// Register the backup base as a private suffix.
- DirectoryServer.registerPrivateSuffix(backupBaseDN, this);
+ try
+ {
+ DirectoryServer.registerBaseDN(backupBaseDN, this, true, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, backupBaseDN.toString(),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
@@ -296,6 +308,15 @@
assert debugEnter(CLASS_NAME, "finalizeBackend");
DirectoryServer.deregisterConfigurableComponent(this);
+
+ try
+ {
+ DirectoryServer.deregisterBaseDN(backupBaseDN, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "finalizeBackend", e);
+ }
}
@@ -1029,26 +1050,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- 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.
@@ -1063,26 +1064,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- 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.
*
diff --git a/opends/src/server/org/opends/server/backends/MemoryBackend.java b/opends/src/server/org/opends/server/backends/MemoryBackend.java
index a2587c4..10bd317 100644
--- a/opends/src/server/org/opends/server/backends/MemoryBackend.java
+++ b/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -48,6 +48,7 @@
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.RestoreConfig;
@@ -62,6 +63,7 @@
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.*;
@@ -148,7 +150,7 @@
*/
public synchronized void initializeBackend(ConfigEntry configEntry,
DN[] baseDNs)
- throws ConfigException
+ throws ConfigException, InitializationException
{
assert debugEnter(CLASS_NAME, "initializeBackend",
String.valueOf(configEntry), String.valueOf(baseDNs));
@@ -184,7 +186,19 @@
for (DN dn : baseDNs)
{
- DirectoryServer.registerSuffix(dn, this);
+ try
+ {
+ DirectoryServer.registerBaseDN(dn, this, false, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, dn.toString(),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
}
@@ -209,6 +223,18 @@
assert debugEnter(CLASS_NAME, "finalizeBackend");
clearMemoryBackend();
+
+ for (DN dn : baseDNs)
+ {
+ try
+ {
+ DirectoryServer.deregisterBaseDN(dn, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "finalizeBackend", e);
+ }
+ }
}
@@ -585,19 +611,6 @@
/**
* {@inheritDoc}
*/
- public boolean supportsControl(String controlOID)
- {
- assert debugEnter(CLASS_NAME, "supportsControl",
- String.valueOf(controlOID));
-
- return supportedControls.contains(controlOID);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
public HashSet<String> getSupportedFeatures()
{
assert debugEnter(CLASS_NAME, "getSupportedFeatures");
@@ -610,20 +623,6 @@
/**
* {@inheritDoc}
*/
- public boolean supportsFeature(String featureOID)
- {
- assert debugEnter(CLASS_NAME, "supportsFeature",
- String.valueOf(featureOID));
-
- // This backend does not provide any special feature support.
- return false;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
public boolean supportsLDIFExport()
{
assert debugEnter(CLASS_NAME, "supportsLDIFExport");
diff --git a/opends/src/server/org/opends/server/backends/MonitorBackend.java b/opends/src/server/org/opends/server/backends/MonitorBackend.java
index 206a778..fd716a9 100644
--- a/opends/src/server/org/opends/server/backends/MonitorBackend.java
+++ b/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -250,7 +250,19 @@
// Register the monitor base as a private suffix.
- DirectoryServer.registerPrivateSuffix(baseMonitorDN, this);
+ try
+ {
+ DirectoryServer.registerBaseDN(baseMonitorDN, this, true, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, baseMonitorDN.toString(),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
@@ -271,6 +283,15 @@
assert debugEnter(CLASS_NAME, "finalizeBackend");
DirectoryServer.deregisterConfigurableComponent(this);
+
+ try
+ {
+ DirectoryServer.deregisterBaseDN(baseMonitorDN, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "finalizeBackend", e);
+ }
}
@@ -929,26 +950,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- 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.
@@ -963,26 +964,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- 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.
*
diff --git a/opends/src/server/org/opends/server/backends/RootDSEBackend.java b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
index 5611e60..17aac64 100644
--- a/opends/src/server/org/opends/server/backends/RootDSEBackend.java
+++ b/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -502,21 +502,42 @@
// Add the "namingContexts" attribute.
- Attribute namingContextAttr =
+ Attribute publicNamingContextAttr =
createDNAttribute(ATTR_NAMING_CONTEXTS, ATTR_NAMING_CONTEXTS_LC,
- DirectoryServer.getSuffixes().keySet());
- ArrayList<Attribute> namingContextAttrs = new ArrayList<Attribute>(1);
- namingContextAttrs.add(namingContextAttr);
+ DirectoryServer.getPublicNamingContexts().keySet());
+ ArrayList<Attribute> publicNamingContextAttrs = new ArrayList<Attribute>(1);
+ publicNamingContextAttrs.add(publicNamingContextAttr);
if (showAllAttributes ||
- (! namingContextAttr.getAttributeType().isOperational()))
+ (! publicNamingContextAttr.getAttributeType().isOperational()))
{
- dseUserAttrs.put(namingContextAttr.getAttributeType(),
- namingContextAttrs);
+ dseUserAttrs.put(publicNamingContextAttr.getAttributeType(),
+ publicNamingContextAttrs);
}
else
{
- dseOperationalAttrs.put(namingContextAttr.getAttributeType(),
- namingContextAttrs);
+ dseOperationalAttrs.put(publicNamingContextAttr.getAttributeType(),
+ publicNamingContextAttrs);
+ }
+
+
+ // Add the "ds-private-naming-contexts" attribute.
+ Attribute privateNamingContextAttr =
+ createDNAttribute(ATTR_PRIVATE_NAMING_CONTEXTS,
+ ATTR_PRIVATE_NAMING_CONTEXTS,
+ DirectoryServer.getPrivateNamingContexts().keySet());
+ ArrayList<Attribute> privateNamingContextAttrs =
+ new ArrayList<Attribute>(1);
+ privateNamingContextAttrs.add(privateNamingContextAttr);
+ if (showAllAttributes ||
+ (! privateNamingContextAttr.getAttributeType().isOperational()))
+ {
+ dseUserAttrs.put(privateNamingContextAttr.getAttributeType(),
+ privateNamingContextAttrs);
+ }
+ else
+ {
+ dseOperationalAttrs.put(privateNamingContextAttr.getAttributeType(),
+ privateNamingContextAttrs);
}
@@ -858,7 +879,7 @@
Map<DN,Backend> baseMap;
if (subordinateBaseDNs == null)
{
- baseMap = DirectoryServer.getSuffixes();
+ baseMap = DirectoryServer.getPublicNamingContexts();
}
else
{
@@ -1043,7 +1064,7 @@
Map<DN,Backend> baseMap;
if (subordinateBaseDNs == null)
{
- baseMap = DirectoryServer.getSuffixes();
+ baseMap = DirectoryServer.getPublicNamingContexts();
}
else
{
@@ -1072,7 +1093,7 @@
case SUBORDINATE_SUBTREE:
if (subordinateBaseDNs == null)
{
- baseMap = DirectoryServer.getSuffixes();
+ baseMap = DirectoryServer.getPublicNamingContexts();
}
else
{
@@ -1158,26 +1179,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- 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.
@@ -1192,26 +1193,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- 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.
*
diff --git a/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opends/src/server/org/opends/server/backends/SchemaBackend.java
index ac62d24..1bf567d 100644
--- a/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -251,11 +251,22 @@
// Register each of the suffixes with the Directory Server. Also, register
// the first one as the schema base.
this.baseDNs = baseDNs;
- DirectoryServer.registerPrivateSuffix(baseDNs[0], this);
DirectoryServer.setSchemaDN(baseDNs[0]);
- for (int i=1; i < baseDNs.length; i++)
+ for (int i=0; i < baseDNs.length; i++)
{
- DirectoryServer.registerPrivateSuffix(baseDNs[i], this);
+ try
+ {
+ DirectoryServer.registerBaseDN(baseDNs[i], this, true, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, baseDNs[i].toString(),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
@@ -306,6 +317,18 @@
assert debugEnter(CLASS_NAME, "finalizeBackend");
DirectoryServer.deregisterConfigurableComponent(this);
+
+ for (DN baseDN : baseDNs)
+ {
+ try
+ {
+ DirectoryServer.deregisterBaseDN(baseDN, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "finalizeBackend", e);
+ }
+ }
}
@@ -801,26 +824,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- 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.
@@ -835,26 +838,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- 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.
*
@@ -2227,7 +2210,7 @@
{
try
{
- DirectoryServer.deregisterSuffix(dn);
+ DirectoryServer.deregisterBaseDN(dn, false);
if (detailedResults)
{
msgID = MSGID_SCHEMA_DEREGISTERED_BASE_DN;
@@ -2250,7 +2233,7 @@
{
try
{
- DirectoryServer.registerPrivateSuffix(dn, this);
+ DirectoryServer.registerBaseDN(dn, this, true, false);
if (detailedResults)
{
msgID = MSGID_SCHEMA_REGISTERED_BASE_DN;
diff --git a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
index a1b3094..98742e2 100644
--- a/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
+++ b/opends/src/server/org/opends/server/backends/jeb/BackendImpl.java
@@ -63,6 +63,7 @@
import org.opends.server.types.ResultCode;
import org.opends.server.util.LDIFException;
+import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.JebMessages.*;
import static org.opends.server.loggers.Error.logError;
@@ -293,10 +294,21 @@
config = new Config();
config.initializeConfig(configEntry, baseDNs);
- // FIXME: Currently assuming every base DN is also a suffix.
for (DN dn : baseDNs)
{
- DirectoryServer.registerSuffix(dn, this);
+ try
+ {
+ DirectoryServer.registerBaseDN(dn, this, false, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, String.valueOf(dn),
+ String.valueOf(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
/*
@@ -402,9 +414,9 @@
{
try
{
- DirectoryServer.deregisterSuffix(dn);
+ DirectoryServer.deregisterBaseDN(dn, false);
}
- catch (ConfigException e)
+ catch (Exception e)
{
assert debugException(CLASS_NAME, "finalizeBackend", e);
}
@@ -571,23 +583,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- public boolean supportsFeature(String featureOID)
- {
- assert debugEnter(CLASS_NAME, "supportsFeature");
-
- return false; //NYI
- }
-
-
-
- /**
* 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.
@@ -602,23 +597,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- public boolean supportsControl(String controlOID)
- {
- assert debugEnter(CLASS_NAME, "supportsControl");
-
- return supportedControls.contains(controlOID);
- }
-
-
-
- /**
* 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.
@@ -1365,6 +1343,8 @@
assert debugEnter(CLASS_NAME, "applyNewConfiguration");
ConfigChangeResult ccr;
+ ResultCode resultCode = ResultCode.SUCCESS;
+ ArrayList<String> messages = new ArrayList<String>();
try
{
@@ -1397,7 +1377,7 @@
// Even though access to the entry container map is safe, there may be
// operation threads with a handle on the entry container being
// closed.
- DirectoryServer.deregisterSuffix(baseDN);
+ DirectoryServer.deregisterBaseDN(baseDN, false);
rootContainer.removeEntryContainer(baseDN);
}
}
@@ -1406,9 +1386,24 @@
{
if (!rootContainer.getBaseDNs().contains(baseDN))
{
- // The base DN was added.
- rootContainer.openEntryContainer(baseDN);
- DirectoryServer.registerSuffix(baseDN, this);
+ try
+ {
+ // The base DN was added.
+ rootContainer.openEntryContainer(baseDN);
+ DirectoryServer.registerBaseDN(baseDN, this, false, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyNewConfiguration", e);
+
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ messages.add(getMessage(msgID, String.valueOf(baseDN),
+ String.valueOf(e)));
+ ccr = new ConfigChangeResult(resultCode, false, messages);
+ return ccr;
+ }
}
}
@@ -1420,14 +1415,13 @@
}
catch (Exception e)
{
- ArrayList<String> messages = new ArrayList<String>();
messages.add(e.getMessage());
ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(),
false, messages);
return ccr;
}
- ccr = new ConfigChangeResult(ResultCode.SUCCESS, false);
+ ccr = new ConfigChangeResult(resultCode, false, messages);
return ccr;
}
diff --git a/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index 8e4783d..5fcbf15 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -336,7 +336,19 @@
// Register the task base as a private suffix.
- DirectoryServer.registerPrivateSuffix(baseDNs[0], this);
+ try
+ {
+ DirectoryServer.registerBaseDN(taskRootDN, this, true, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackend", e);
+
+ msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN;
+ String message = getMessage(msgID, taskRootDN.toString(),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
}
@@ -381,6 +393,15 @@
{
assert debugException(CLASS_NAME, "finalizeBackend", e);
}
+
+ try
+ {
+ DirectoryServer.deregisterBaseDN(taskRootDN, false);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "finalizeBackend", e);
+ }
}
@@ -971,26 +992,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- 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.
@@ -1005,26 +1006,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- 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.
*
diff --git a/opends/src/server/org/opends/server/core/AddOperation.java b/opends/src/server/org/opends/server/core/AddOperation.java
index fdc2f56..54f9e02 100644
--- a/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opends/src/server/org/opends/server/core/AddOperation.java
@@ -1018,7 +1018,7 @@
if (parentDN == null)
{
// Either this entry is a suffix or doesn't belong in the directory.
- if (DirectoryServer.isSuffix(entryDN))
+ if (DirectoryServer.isNamingContext(entryDN))
{
// This is fine. This entry is one of the configured suffixes.
parentLock = null;
diff --git a/opends/src/server/org/opends/server/core/BackendConfigManager.java b/opends/src/server/org/opends/server/core/BackendConfigManager.java
index f5cb012..32b13ed 100644
--- a/opends/src/server/org/opends/server/core/BackendConfigManager.java
+++ b/opends/src/server/org/opends/server/core/BackendConfigManager.java
@@ -48,6 +48,7 @@
import org.opends.server.config.MultiChoiceConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
@@ -542,7 +543,21 @@
// Register the backend with the server.
- DirectoryServer.registerBackend(backend);
+ try
+ {
+ DirectoryServer.registerBackend(backend);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "initializeBackendConfig", e);
+
+ msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+ String message = getMessage(msgID, backendID,
+ stackTraceToSingleLineString(e));
+ logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+ message, msgID);
+ // FIXME -- Do we need to send an admin alert?
+ }
// Put this backend in the hash so that we will be able to find it if it
@@ -711,6 +726,64 @@
unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
return false;
}
+
+ // See if the backend is registered with the server. If it is, then
+ // see what's changed and whether those changes are acceptable.
+ Backend backend = registeredBackends.get(configEntryDN);
+ if (backend != null)
+ {
+ LinkedHashSet<DN> removedDNs = new LinkedHashSet<DN>();
+ for (DN dn : backend.getBaseDNs())
+ {
+ removedDNs.add(dn);
+ }
+
+ LinkedHashSet<DN> addedDNs = new LinkedHashSet<DN>();
+ for (DN dn : baseDNAttr.pendingValues())
+ {
+ addedDNs.add(dn);
+ }
+
+ Iterator<DN> iterator = removedDNs.iterator();
+ while (iterator.hasNext())
+ {
+ DN dn = iterator.next();
+ if (addedDNs.remove(dn))
+ {
+ iterator.remove();
+ }
+ }
+
+ for (DN dn : addedDNs)
+ {
+ try
+ {
+ DirectoryServer.registerBaseDN(dn, backend, false, true);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "configChangeIsAcceptable", de);
+
+ unacceptableReason.append(de.getMessage());
+ return false;
+ }
+ }
+
+ for (DN dn : removedDNs)
+ {
+ try
+ {
+ DirectoryServer.deregisterBaseDN(dn, true);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "configChangeIsAcceptable", de);
+
+ unacceptableReason.append(de.getMessage());
+ return false;
+ }
+ }
+ }
}
catch (Exception e)
{
@@ -1266,7 +1339,28 @@
}
// Register the backend with the server.
- DirectoryServer.registerBackend(backend);
+ try
+ {
+ DirectoryServer.registerBackend(backend);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyConfigurationChange", e);
+
+ msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+ String message = getMessage(msgID, backendID,
+ stackTraceToSingleLineString(e));
+
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ messages.add(message);
+
+ logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+ message, msgID);
+ // FIXME -- Do we need to send an admin alert?
+
+ return new ConfigChangeResult(resultCode, adminActionRequired,
+ messages);
+ }
registeredBackends.put(backendDN, backend);
@@ -1443,6 +1537,7 @@
// See if the entry contains an attribute that specifies the set of base DNs
// for the backend. If it does not, then skip it.
+ List<DN> baseDNs = null;
msgID = MSGID_CONFIG_BACKEND_ATTR_DESCRIPTION_BASE_DNS;
DNConfigAttribute baseDNStub =
new DNConfigAttribute(ATTR_BACKEND_BASE_DN, getMessage(msgID), true,
@@ -1458,6 +1553,10 @@
unacceptableReason.append(getMessage(msgID, String.valueOf(backendDN)));
return false;
}
+ else
+ {
+ baseDNs = baseDNAttr.pendingValues();
+ }
}
catch (Exception e)
{
@@ -1554,6 +1653,25 @@
}
+ // Make sure that all of the base DNs are acceptable for use in the server.
+ for (DN baseDN : baseDNs)
+ {
+ try
+ {
+ DirectoryServer.registerBaseDN(baseDN, backend, false, true);
+ }
+ catch (DirectoryException de)
+ {
+ unacceptableReason.append(de.getMessage());
+ return false;
+ }
+ catch (Exception e)
+ {
+ unacceptableReason.append(stackTraceToSingleLineString(e));
+ return false;
+ }
+ }
+
// If we've gotten to this point, then it is acceptable as far as we are
// concerned. If it is unacceptable according to the configuration for that
@@ -1960,7 +2078,29 @@
// At this point, the backend should be online. Add it as one of the
// registered backends for this backend config manager.
- DirectoryServer.registerBackend(backend);
+ try
+ {
+ DirectoryServer.registerBackend(backend);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyConfigurationAdd", e);
+
+ msgID = MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND;
+ String message = getMessage(msgID, backendID,
+ stackTraceToSingleLineString(e));
+
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ messages.add(message);
+
+ logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+ message, msgID);
+ // FIXME -- Do we need to send an admin alert?
+
+ return new ConfigChangeResult(resultCode, adminActionRequired,
+ messages);
+ }
+
registeredBackends.put(backendDN, backend);
return new ConfigChangeResult(resultCode, adminActionRequired, messages);
}
@@ -1995,7 +2135,7 @@
// do know about it, then that means that it is enabled and we will not
// allow removing a backend that is enabled.
Backend backend = registeredBackends.get(backendDN);
- if (backendDN == null)
+ if (backend == null)
{
return true;
}
@@ -2041,7 +2181,7 @@
// See if this backend config manager has a backend registered with the
// provided DN. If not, then we don't care if the entry is deleted.
Backend backend = registeredBackends.get(backendDN);
- if (backendDN == null)
+ if (backend == null)
{
return new ConfigChangeResult(resultCode, adminActionRequired,
messages);
@@ -2053,6 +2193,19 @@
Backend[] subBackends = backend.getSubordinateBackends();
if ((subBackends == null) || (subBackends.length == 0))
{
+ registeredBackends.remove(backendDN);
+
+ try
+ {
+ backend.finalizeBackend();
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "applyConfigurationDelete", e);
+ }
+
+ DirectoryServer.deregisterBackend(backend);
+
return new ConfigChangeResult(resultCode, adminActionRequired,
messages);
}
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index e255767..e113da4 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -34,8 +34,11 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -161,6 +164,7 @@
import static org.opends.server.util.DynamicConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.Validator.*;
@@ -398,19 +402,6 @@
// The key manager provider configuration manager for the Directory Server.
private KeyManagerProviderConfigManager keyManagerProviderConfigManager;
- // The set of "private" suffixes that will be used to provide server-generated
- // data (e.g., monitor information, schema, etc.) to clients but will not be
- // searchable by default.
- private LinkedHashMap<DN,Backend> privateSuffixes;
-
- // The set of backends that have been registered with the server (mapped
- // between the normalized suffix and the backend).
- private LinkedHashMap<DN,Backend> suffixes;
-
- // The set of backends that have been registered with the server (mapped
- // between their backend ID and the backend).
- private LinkedHashMap<String,Backend> backends;
-
// The set of connections that are currently established.
private LinkedHashSet<ClientConnection> establishedConnections;
@@ -498,6 +489,18 @@
// The thread group for all threads associated with the Directory Server.
private ThreadGroup directoryThreadGroup;
+ // The set of base DNs registered with the server.
+ private TreeMap<DN,Backend> baseDNs;
+
+ // The set of private naming contexts registered with the server.
+ private TreeMap<DN,Backend> privateNamingContexts;
+
+ // The set of public naming contexts registered with the server.
+ private TreeMap<DN,Backend> publicNamingContexts;
+
+ // The set of backends registered with the server.
+ private TreeMap<String,Backend> backends;
+
// The set of supported controls registered with the Directory Server.
private TreeSet<String> supportedControls;
@@ -606,9 +609,10 @@
directoryServer.defaultPasswordPolicy = null;
directoryServer.monitorProviders =
new ConcurrentHashMap<String,MonitorProvider>();
- directoryServer.privateSuffixes = new LinkedHashMap<DN,Backend>();
- directoryServer.suffixes = new LinkedHashMap<DN,Backend>();
- directoryServer.backends = new LinkedHashMap<String,Backend>();
+ directoryServer.backends = new TreeMap<String,Backend>();
+ directoryServer.baseDNs = new TreeMap<DN,Backend>();
+ directoryServer.publicNamingContexts = new TreeMap<DN,Backend>();
+ directoryServer.privateNamingContexts = new TreeMap<DN,Backend>();
directoryServer.changeNotificationListeners =
new CopyOnWriteArrayList<ChangeNotificationListener>();
directoryServer.persistentSearches =
@@ -5210,12 +5214,12 @@
/**
* Retrieves the set of backends that have been registered with the Directory
- * Server.
+ * Server, as a mapping between the backend ID and the corresponding backend.
*
* @return The set of backends that have been registered with the Directory
* Server.
*/
- public static LinkedHashMap<String,Backend> getBackends()
+ public static Map<String,Backend> getBackends()
{
assert debugEnter(CLASS_NAME, "getBackends");
@@ -5229,7 +5233,7 @@
*
* @param backendID The backend ID of the backend to retrieve.
*
- * @return The backend with the specified backend ID, or <CODE>null</CODE> if
+ * @return The backend with the specified backend ID, or {@code null} if
* there is none.
*/
public static Backend getBackend(String backendID)
@@ -5247,8 +5251,8 @@
*
* @param backendID The backend ID for which to make the determination.
*
- * @return <CODE>true</CODE> if the Directory Server has a backend with the
- * specified backend ID, or <CODE>false</CODE> if not.
+ * @return {@code true} if the Directory Server has a backend with the
+ * specified backend ID, or {@code false} if not.
*/
public static boolean hasBackend(String backendID)
{
@@ -5264,13 +5268,40 @@
* will not register the set of configured suffixes with the server, as that
* must be done by the backend itself.
*
- * @param backend The backend to register with the server.
+ * @param backend The backend to register with the server. Neither the
+ * backend nor its backend ID may be null.
+ *
+ * @throws DirectoryException If the backend ID for the provided backend
+ * conflicts with the backend ID of an existing
+ * backend.
*/
public static void registerBackend(Backend backend)
+ throws DirectoryException
{
assert debugEnter(CLASS_NAME, "registerBackend", String.valueOf(backend));
- directoryServer.backends.put(backend.getBackendID(), backend);
+ ensureNotNull(backend);
+
+ String backendID = backend.getBackendID();
+ ensureNotNull(backendID);
+
+ synchronized (directoryServer)
+ {
+ TreeMap<String,Backend> newBackends =
+ new TreeMap<String,Backend>(directoryServer.backends);
+ if (newBackends.containsKey(backendID))
+ {
+ int msgID = MSGID_REGISTER_BACKEND_ALREADY_EXISTS;
+ String message = getMessage(msgID, backendID);
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+ }
+ else
+ {
+ newBackends.put(backendID, backend);
+ directoryServer.backends = newBackends;
+ }
+ }
}
@@ -5280,370 +5311,525 @@
* will not deregister the set of configured suffixes with the server, as that
* must be done by the backend itself.
*
- * @param backend the backend to deregister with the server.
+ * @param backend The backend to deregister with the server. It must not be
+ * {@code null}.
*/
public static void deregisterBackend(Backend backend)
{
assert debugEnter(CLASS_NAME, "deregisterBackend", String.valueOf(backend));
- directoryServer.backends.remove(backend.getBackendID());
+ ensureNotNull(backend);
+
+ synchronized (directoryServer)
+ {
+ TreeMap<String,Backend> newBackends =
+ new TreeMap<String,Backend>(directoryServer.backends);
+ newBackends.remove(backend.getBackendID());
+
+ directoryServer.backends = newBackends;
+ }
}
/**
- * Retrieves the set of suffixes that have been registered with the Directory
- * Server.
+ * Retrieves the entire set of base DNs registered with the Directory Server,
+ * mapped from the base DN to the backend responsible for that base DN. The
+ * same backend may be present multiple times, mapped from different base DNs.
*
- * @return The set of suffixes that have been registered with the Directory
- * Server.
+ * @return The entire set of base DNs registered with the Directory Server.
*/
- public static LinkedHashMap<DN,Backend> getSuffixes()
+ public static Map<DN,Backend> getBaseDNs()
{
- assert debugEnter(CLASS_NAME, "getSuffixes");
+ assert debugEnter(CLASS_NAME, "getBaseDNs");
- return directoryServer.suffixes;
+ return directoryServer.baseDNs;
}
/**
- * Retrieves the set of "private" suffixes that have been registered with the
- * server that will provide server-specific data to clients (e.g., monitor
- * data, schema, etc.) but should not be considered for normal operations
- * that may target all "user" suffixes.
+ * Retrieves the backend with the specified base DN.
*
- * @return The set of "private" suffixes that have been registered with the
- * server.
+ * @param baseDN The DN that is registered as one of the base DNs for the
+ * backend to retrieve.
+ *
+ * @return The backend with the specified base DN, or {@code null} if there
+ * is no backend registered with the specified base DN.
*/
- public static LinkedHashMap<DN,Backend> getPrivateSuffixes()
+ public static Backend getBackendWithBaseDN(DN baseDN)
{
- assert debugEnter(CLASS_NAME, "getPrivateSuffixes");
+ assert debugEnter(CLASS_NAME, "getBackendWithBaseDN",
+ String.valueOf(baseDN));
- return directoryServer.privateSuffixes;
+ return directoryServer.baseDNs.get(baseDN);
}
/**
- * Indicates whether the provided DN is one of the suffixes defined in the
- * Directory Server.
+ * Retrieves the backend that should be used to handle operations on the
+ * specified entry.
*
- * @param dn The DN for which to make the determination.
+ * @param entryDN The DN of the entry for which to retrieve the
+ * corresponding backend.
*
- * @return <CODE>true</CODE> if the provided DN is one of the suffixes
- * defined in the Directory Server, or <CODE>false</CODE> if not.
+ * @return The backend that should be used to handle operations on the
+ * specified entry, or {@code null} if no appropriate backend is
+ * registered with the server.
*/
- public static boolean isSuffix(DN dn)
+ public static Backend getBackend(DN entryDN)
{
- assert debugEnter(CLASS_NAME, "isSuffix", String.valueOf(dn));
+ assert debugEnter(CLASS_NAME, "getBackendForEntry",
+ String.valueOf(entryDN));
- return (directoryServer.suffixes.containsKey(dn) ||
- directoryServer.privateSuffixes.containsKey(dn));
- }
-
-
-
- /**
- * Retrieves the backend that should be used to handle operations for the
- * provided entry DN.
- *
- * @param dn The DN of the entry for which to retrieve the appropriate
- * backend.
- *
- * @return The backend that should be used to handle the provided DN, or
- * <CODE>null</CODE> if there is no backend for the provided DN.
- */
- public static Backend getBackend(DN dn)
- {
- assert debugEnter(CLASS_NAME, "getBackend", String.valueOf(dn));
-
- if (dn.isNullDN())
+ if (entryDN.isNullDN())
{
return directoryServer.rootDSEBackend;
}
- Backend backend = directoryServer.suffixes.get(dn);
- if (backend == null)
+ TreeMap<DN,Backend> baseDNs = directoryServer.baseDNs;
+ Backend b = baseDNs.get(entryDN);
+ while (b == null)
{
- backend = directoryServer.privateSuffixes.get(dn);
- }
-
- while (backend == null)
- {
- dn = dn.getParentDNInSuffix();
- if (dn == null)
+ entryDN = entryDN.getParent();
+ if (entryDN == null)
{
- break;
+ return null;
}
- backend = directoryServer.suffixes.get(dn);
- if (backend == null)
- {
- backend = directoryServer.privateSuffixes.get(dn);
- }
+ b = baseDNs.get(entryDN);
}
- return backend;
+ return b;
}
/**
- * Registers the specified suffix to be handled by the provided backend.
+ * Registers the provided base DN with the server.
*
- * @param suffixDN The base DN for this suffix.
- * @param backend The backend to handle operations for the provided base.
+ * @param baseDN The base DN to register with the server. It must not be
+ * {@code null}.
+ * @param backend The backend responsible for the provided base DN. It
+ * must not be {@code null}.
+ * @param isPrivate Indicates whether the base DN should be considered a
+ * private base DN. If the provided base DN is a naming
+ * context, then this controls whether it is public or
+ * private.
+ * @param testOnly Indicates whether to only test whether the new base DN
+ * registration would be successful without actually
+ * applying any changes.
*
- * @throws ConfigException If the specified suffix is already registered
- * with the Directory Server.
+ * @throws DirectoryException If a problem occurs while attempting to
+ * register the provided base DN.
*/
- public static void registerSuffix(DN suffixDN, Backend backend)
- throws ConfigException
+ public static void registerBaseDN(DN baseDN, Backend backend,
+ boolean isPrivate, boolean testOnly)
+ throws DirectoryException
{
- assert debugEnter(CLASS_NAME, "registerSuffix", String.valueOf(suffixDN),
- String.valueOf(backend));
+ assert debugEnter(CLASS_NAME, "registerBaseDN", String.valueOf(baseDN),
+ String.valueOf(backend), String.valueOf(isPrivate),
+ String.valueOf(testOnly));
- backend.setPrivateBackend(false);
+ ensureNotNull(baseDN, backend);
- synchronized (directoryServer.suffixes)
+ synchronized (directoryServer)
{
- // Check to see if this suffix is already in use. It may be a suffix, or
- // it may be a sub-suffix of an existing suffix.
- Backend b = directoryServer.suffixes.get(suffixDN);
- if (b != null)
- {
- int msgID = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- b.getClass().getName());
+ TreeMap<DN,Backend> newBaseDNs =
+ new TreeMap<DN,Backend>(directoryServer.baseDNs);
+ TreeMap<DN,Backend> newPublicNamingContexts =
+ new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
+ TreeMap<DN,Backend> newPrivateNamingContexts =
+ new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
- throw new ConfigException(msgID, message);
+
+ // Check to see if the base DN is already registered with the server.
+ Backend existingBackend = newBaseDNs.get(baseDN);
+ if (existingBackend != null)
+ {
+ int msgID = MSGID_REGISTER_BASEDN_ALREADY_EXISTS;
+ String message = getMessage(msgID, String.valueOf(baseDN),
+ backend.getBackendID(),
+ existingBackend.getBackendID());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
}
- boolean found = false;
- DN parentDN = suffixDN.getParentDNInSuffix();
+
+ // Check to see if the backend is already registered with the server for
+ // any other base DN(s). The new base DN must not have any hierarchical
+ // relationship with any other base Dns for the same backend.
+ LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+ for (DN dn : newBaseDNs.keySet())
+ {
+ Backend b = newBaseDNs.get(dn);
+ if (b.equals(backend))
+ {
+ otherBaseDNs.add(dn);
+
+ if (baseDN.isAncestorOf(dn) || baseDN.isDescendantOf(dn))
+ {
+ int msgID = MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT;
+ String message = getMessage(msgID, String.valueOf(baseDN),
+ backend.getBackendID(),
+ String.valueOf(dn));
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
+ }
+ }
+
+
+ // Check to see if the new base DN is subordinate to any other base DN
+ // already defined. If it is, then any other base DN(s) for the same
+ // backend must also be subordinate to the same base DN.
+ Backend superiorBackend = null;
+ DN superiorBaseDN = null;
+ DN parentDN = baseDN.getParent();
while (parentDN != null)
{
- b = directoryServer.suffixes.get(suffixDN);
- if (b != null)
+ if (newBaseDNs.containsKey(parentDN))
{
- if (b.hasSubSuffix(suffixDN))
+ superiorBaseDN = parentDN;
+ superiorBackend = newBaseDNs.get(parentDN);
+
+ for (DN dn : otherBaseDNs)
{
- int msgID = MSGID_CANNOT_REGISTER_DUPLICATE_SUBSUFFIX;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- String.valueOf(parentDN));
-
- throw new ConfigException(msgID, message);
+ if (! dn.isDescendantOf(superiorBaseDN))
+ {
+ int msgID = MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES;
+ String message = getMessage(msgID, String.valueOf(baseDN),
+ backend.getBackendID(),
+ String.valueOf(dn));
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
}
- else
- {
- b.addSubordinateBackend(backend);
- found = true;
- break;
- }
+
+ break;
}
- parentDN = parentDN.getParentDNInSuffix();
+ parentDN = parentDN.getParent();
}
-
- if (! found)
+ if (superiorBackend == null)
{
- // If we've gotten here, then it is not in use. Register it.
- directoryServer.suffixes.put(suffixDN, backend);
- }
-
-
- // See if there are any supported controls or features that we want to
- // advertise.
- Set<String> supportedControls = backend.getSupportedControls();
- if (supportedControls != null)
- {
- for (String controlOID : supportedControls)
+ if (backend.getParentBackend() != null)
{
- registerSupportedControl(controlOID);
+ int msgID = MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE;
+ String message = getMessage(msgID, String.valueOf(baseDN),
+ backend.getBackendID(),
+ backend.getParentBackend().getBackendID());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
}
}
- Set<String> supportedFeatures = backend.getSupportedFeatures();
- if (supportedFeatures != null)
+
+ // Check to see if the new base DN should be the superior base DN for any
+ // other base DN(s) already defined.
+ LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+ LinkedList<DN> subordinateBaseDNs = new LinkedList<DN>();
+ for (DN dn : newBaseDNs.keySet())
{
- for (String featureOID : supportedFeatures)
- {
- registerSupportedFeature(featureOID);
- }
- }
- }
- }
-
-
-
- /**
- * Registers the specified private suffix to be handled by the provided
- * backend.
- *
- * @param suffixDN The base DN for this suffix.
- * @param backend The backend to handle operations for the provided base.
- *
- * @throws ConfigException If the specified suffix is already registered
- * with the Directory Server.
- */
- public static void registerPrivateSuffix(DN suffixDN, Backend backend)
- throws ConfigException
- {
- assert debugEnter(CLASS_NAME, "registerPrivateSuffix",
- String.valueOf(suffixDN), String.valueOf(backend));
-
- backend.setPrivateBackend(true);
-
- synchronized (directoryServer.privateSuffixes)
- {
- // Check to see if this suffix is already in use for a "user" suffix. It
- // may be a suffix, or it may be a sub-suffix of an existing suffix.
- synchronized (directoryServer.suffixes)
- {
- Backend b = directoryServer.suffixes.get(suffixDN);
- if (b != null)
- {
- int msgID = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- b.getClass().getName());
-
- throw new ConfigException(msgID, message);
- }
-
- DN parentDN = suffixDN.getParentDNInSuffix();
+ Backend b = newBaseDNs.get(dn);
+ parentDN = dn.getParent();
while (parentDN != null)
{
- b = directoryServer.suffixes.get(suffixDN);
- if (b != null)
+ if (parentDN.equals(baseDN))
{
- int msgID = MSGID_CANNOT_REGISTER_PRIVATE_SUFFIX_BELOW_USER_PARENT;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- String.valueOf(parentDN));
- throw new ConfigException(msgID, message);
+ subordinateBaseDNs.add(dn);
+ subordinateBackends.add(b);
+ break;
+ }
+ else if (newBaseDNs.containsKey(parentDN))
+ {
+ break;
}
- parentDN = suffixDN.getParentDNInSuffix();
+ parentDN = parentDN.getParent();
}
}
- // Check to see if this suffix is already registered as a private suffix
- // or sub-suffix.
- Backend b = directoryServer.privateSuffixes.get(suffixDN);
- if (b != null)
+ // If we've gotten here, then the new base DN is acceptable. If we should
+ // actually apply the changes then do so now.
+ if (! testOnly)
{
- int msgID = MSGID_CANNOT_REGISTER_DUPLICATE_SUFFIX;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- b.getClass().getName());
-
- throw new ConfigException(msgID, message);
- }
-
- DN parentDN = suffixDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- b = directoryServer.privateSuffixes.get(suffixDN);
- if (b != null)
+ // Check to see if any of the registered backends already contain an
+ // entry with the DN specified as the base DN. This could happen if
+ // we're creating a new subordinate backend in an existing directory
+ // (e.g., moving the "ou=People,dc=example,dc=com" branch to its own
+ // backend when that data already exists under the "dc=example,dc=com"
+ // backend). This condition shouldn't prevent the new base DN from
+ // being registered, but it's definitely important enough that we let
+ // the administrator know about it and remind them that the existing
+ // backend will need to be reinitialized.
+ if (superiorBackend != null)
{
- if (b.hasSubSuffix(suffixDN))
+ if (superiorBackend.entryExists(baseDN))
{
- int msgID = MSGID_CANNOT_REGISTER_DUPLICATE_SUBSUFFIX;
- String message = getMessage(msgID, String.valueOf(suffixDN),
- String.valueOf(parentDN));
+ int msgID = MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS;
+ String message = getMessage(msgID, superiorBackend.getBackendID(),
+ String.valueOf(baseDN),
+ backend.getBackendID());
+ logError(ErrorLogCategory.CONFIGURATION,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+ }
+ }
- throw new ConfigException(msgID, message);
+
+ newBaseDNs.put(baseDN, backend);
+
+ if (superiorBackend == null)
+ {
+ if (isPrivate)
+ {
+ backend.setPrivateBackend(true);
+ newPrivateNamingContexts.put(baseDN, backend);
}
else
{
- b.addSubordinateBackend(backend);
- return;
+ backend.setPrivateBackend(false);
+ newPublicNamingContexts.put(baseDN, backend);
}
}
+ else if (otherBaseDNs.isEmpty())
+ {
+ backend.setParentBackend(superiorBackend);
+ superiorBackend.addSubordinateBackend(backend);
+ }
- parentDN = suffixDN.getParentDNInSuffix();
+ for (Backend b : subordinateBackends)
+ {
+ Backend oldParentBackend = b.getParentBackend();
+ if (oldParentBackend != null)
+ {
+ oldParentBackend.removeSubordinateBackend(b);
+ }
+
+ b.setParentBackend(backend);
+ backend.addSubordinateBackend(b);
+ }
+
+ for (DN dn : subordinateBaseDNs)
+ {
+ newPublicNamingContexts.remove(dn);
+ newPrivateNamingContexts.remove(dn);
+ }
+
+ directoryServer.baseDNs = newBaseDNs;
+ directoryServer.publicNamingContexts = newPublicNamingContexts;
+ directoryServer.privateNamingContexts = newPrivateNamingContexts;
}
-
-
- // If we've gotten here, then it is not in use. Register it.
- directoryServer.privateSuffixes.put(suffixDN, backend);
}
}
/**
- * Deregisters the specified suffix with the Directory Server. This should
- * work regardless of whether the specified DN is a normal suffix or a private
- * suffix.
+ * Deregisters the provided base DN with the server.
*
- * @param suffixDN The suffix DN to deregister with the server.
+ * @param baseDN The base DN to deregister with the server. It must not
+ * be {@code null}.
+ * @param testOnly Indicates whether to only test whether the new base DN
+ * registration would be successful without actually
+ * applying any changes.
*
- * @throws ConfigException If a problem occurs while attempting to
- * deregister the specified suffix.
+ * @throws DirectoryException If a problem occurs while attempting to
+ * deregister the provided base DN.
*/
- public static void deregisterSuffix(DN suffixDN)
- throws ConfigException
+ public static void deregisterBaseDN(DN baseDN, boolean testOnly)
+ throws DirectoryException
{
- assert debugEnter(CLASS_NAME, "deregisterSuffix", String.valueOf(suffixDN));
+ assert debugEnter(CLASS_NAME, "deregisterBaseDN", String.valueOf(baseDN));
+ ensureNotNull(baseDN);
- // First, check to see if it is a "user" suffix or sub-suffix.
- synchronized (directoryServer.suffixes)
+ synchronized (directoryServer)
{
- Backend b = directoryServer.suffixes.remove(suffixDN);
- if (b != null)
+ TreeMap<DN,Backend> newBaseDNs =
+ new TreeMap<DN,Backend>(directoryServer.baseDNs);
+ TreeMap<DN,Backend> newPublicNamingContexts =
+ new TreeMap<DN,Backend>(directoryServer.publicNamingContexts);
+ TreeMap<DN,Backend> newPrivateNamingContexts =
+ new TreeMap<DN,Backend>(directoryServer.privateNamingContexts);
+
+
+ // Make sure that the Directory Server actually contains a backend with
+ // the specified base DN.
+ Backend backend = newBaseDNs.get(baseDN);
+ if (backend == null)
{
- return;
+ int msgID = MSGID_DEREGISTER_BASEDN_NOT_REGISTERED;
+ String message = getMessage(msgID, String.valueOf(baseDN));
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
}
- DN parentDN = suffixDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- b = directoryServer.suffixes.get(parentDN);
- if (b != null)
- {
- if (b.hasSubSuffix(suffixDN))
- {
- b.removeSubSuffix(suffixDN, parentDN);
- }
- return;
+ // Check to see if the backend has a parent backend, and whether it has
+ // any subordinates with base DNs that are below the base DN to remove.
+ Backend superiorBackend = backend.getParentBackend();
+ LinkedList<Backend> subordinateBackends = new LinkedList<Backend>();
+ if (backend.getSubordinateBackends() != null)
+ {
+ for (Backend b : backend.getSubordinateBackends())
+ {
+ for (DN dn : b.getBaseDNs())
+ {
+ if (dn.isDescendantOf(baseDN))
+ {
+ subordinateBackends.add(b);
+ break;
+ }
+ }
}
}
- }
- // Check the set of private suffixes and sub-suffixes.
- synchronized (directoryServer.privateSuffixes)
- {
- Backend b = directoryServer.privateSuffixes.remove(suffixDN);
- if (b != null)
+ // See if there are any other base DNs registered within the same backend.
+ LinkedList<DN> otherBaseDNs = new LinkedList<DN>();
+ for (DN dn : newBaseDNs.keySet())
{
- return;
- }
-
- DN parentDN = suffixDN.getParentDNInSuffix();
- while (parentDN != null)
- {
- b = directoryServer.privateSuffixes.get(parentDN);
- if (b != null)
+ if (dn.equals(baseDN))
{
- if (b.hasSubSuffix(suffixDN))
- {
- b.removeSubSuffix(suffixDN, parentDN);
- }
+ continue;
+ }
- return;
+ Backend b = newBaseDNs.get(dn);
+ if (backend.equals(b))
+ {
+ otherBaseDNs.add(dn);
}
}
+
+
+ // If we've gotten here, then it's OK to make the changes.
+ if (! testOnly)
+ {
+ // Get rid of the references to this base DN in the mapping tree
+ // information.
+ newBaseDNs.remove(baseDN);
+ newPublicNamingContexts.remove(baseDN);
+ newPrivateNamingContexts.remove(baseDN);
+
+
+ if (superiorBackend == null)
+ {
+ // If there were any subordinate backends, then all of their base DNs
+ // will now be promoted to naming contexts.
+ for (Backend b : subordinateBackends)
+ {
+ b.setParentBackend(null);
+ backend.removeSubordinateBackend(b);
+
+ for (DN dn : b.getBaseDNs())
+ {
+ if (b.isPrivateBackend())
+ {
+ newPrivateNamingContexts.put(dn, b);
+ }
+ else
+ {
+ newPublicNamingContexts.put(dn, b);
+ }
+ }
+ }
+ }
+ else
+ {
+ // If there are no other base DNs for the associated backend, then
+ // remove this backend as a subordinate of the parent backend.
+ if (otherBaseDNs.isEmpty())
+ {
+ superiorBackend.removeSubordinateBackend(backend);
+ }
+
+
+ // If there are any subordinate backends, then they need to be made
+ // subordinate to the parent backend. Also, we should log a warning
+ // message indicating that there may be inconsistent search results
+ // because some of the structural entries will be missing.
+ if (! subordinateBackends.isEmpty())
+ {
+ int msgID = MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY;
+ String message = getMessage(msgID, String.valueOf(baseDN),
+ backend.getBackendID());
+ logError(ErrorLogCategory.CONFIGURATION,
+ ErrorLogSeverity.SEVERE_WARNING, message, msgID);
+
+ for (Backend b : subordinateBackends)
+ {
+ backend.removeSubordinateBackend(b);
+ superiorBackend.addSubordinateBackend(b);
+ b.setParentBackend(superiorBackend);
+ }
+ }
+ }
+
+
+ directoryServer.baseDNs = newBaseDNs;
+ directoryServer.publicNamingContexts = newPublicNamingContexts;
+ directoryServer.privateNamingContexts = newPrivateNamingContexts;
+ }
}
}
/**
+ * Retrieves the set of public naming contexts defined in the Directory
+ * Server, mapped from the naming context DN to the corresponding backend.
+ *
+ * @return The set of public naming contexts defined in the Directory Server.
+ */
+ public static Map<DN,Backend> getPublicNamingContexts()
+ {
+ assert debugEnter(CLASS_NAME, "getPublicNamingContexts");
+
+ return directoryServer.publicNamingContexts;
+ }
+
+
+
+ /**
+ * Retrieves the set of private naming contexts defined in the Directory
+ * Server, mapped from the naming context DN to the corresponding backend.
+ *
+ * @return The set of private naming contexts defined in the Directory
+ * Server.
+ */
+ public static Map<DN,Backend> getPrivateNamingContexts()
+ {
+ assert debugEnter(CLASS_NAME, "getPrivateNamingContexts");
+
+ return directoryServer.privateNamingContexts;
+ }
+
+
+
+ /**
+ * Indicates whether the specified DN is one of the Directory Server naming
+ * contexts.
+ *
+ * @param dn The DN for which to make the determination.
+ *
+ * @return {@code true} if the specified DN is a naming context for the
+ * Directory Server, or {@code false} if it is not.
+ */
+ public static boolean isNamingContext(DN dn)
+ {
+ assert debugEnter(CLASS_NAME, "isNamingContext");
+
+ return (directoryServer.publicNamingContexts.containsKey(dn) ||
+ directoryServer.privateNamingContexts.containsKey(dn));
+ }
+
+
+
+ /**
* Retrieves the root DSE entry for the Directory Server.
*
* @return The root DSE entry for the Directory Server.
diff --git a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index 9e045a1..f377127 100644
--- a/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -598,7 +598,8 @@
try
{
- DirectoryServer.registerPrivateSuffix(configRootEntry.getDN(), this);
+ DirectoryServer.registerBaseDN(configRootEntry.getDN(), this, true,
+ false);
}
catch (Exception e)
{
@@ -729,7 +730,7 @@
try
{
- DirectoryServer.deregisterSuffix(configRootEntry.getDN());
+ DirectoryServer.deregisterBaseDN(configRootEntry.getDN(), false);
}
catch (Exception e)
{
@@ -1796,23 +1797,6 @@
/**
- * Indicates whether this backend supports the specified control.
- *
- * @param controlOID The OID of the control for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * control, or <CODE>false</CODE>
- */
- public boolean supportsControl(String controlOID)
- {
- // NYI
- 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.
@@ -1826,23 +1810,6 @@
/**
- * Indicates whether this backend supports the specified feature.
- *
- * @param featureOID The OID of the feature for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if this backend does support the requested
- * feature, or <CODE>false</CODE>
- */
- public boolean supportsFeature(String featureOID)
- {
- // NYI
- return false;
- }
-
-
-
- /**
* Indicates whether this backend provides a mechanism to export the data it
* contains to an LDIF file.
*
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index db68ea0..f3d3c70 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -35,9 +35,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -309,7 +309,7 @@
// use it. Otherwise, add a realm for each suffix defined in the server.
if (realm == null)
{
- LinkedHashMap<DN,Backend> suffixes = DirectoryServer.getSuffixes();
+ Map<DN,Backend> suffixes = DirectoryServer.getPublicNamingContexts();
if (! suffixes.isEmpty())
{
Iterator<DN> iterator = suffixes.keySet().iterator();
diff --git a/opends/src/server/org/opends/server/messages/BackendMessages.java b/opends/src/server/org/opends/server/messages/BackendMessages.java
index d1ecddb..3691cce 100644
--- a/opends/src/server/org/opends/server/messages/BackendMessages.java
+++ b/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -2233,6 +2233,17 @@
/**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to register a base DN for use in the server. This takes two
+ * arguments, which are the base DN and a string representation of the
+ * exception that was caught.
+ */
+ public static final int MSGID_BACKEND_CANNOT_REGISTER_BASEDN =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_FATAL_ERROR | 210;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -2252,6 +2263,9 @@
"the entry is already locked by a long-running operation " +
"or that the entry has previously been locked but was " +
"not properly unlocked.");
+ registerMessage(MSGID_BACKEND_CANNOT_REGISTER_BASEDN,
+ "An error occurred while attempting to register base DN " +
+ "in the Directory Server: %s.");
registerMessage(MSGID_ROOTDSE_CONFIG_ENTRY_NULL,
diff --git a/opends/src/server/org/opends/server/messages/ConfigMessages.java b/opends/src/server/org/opends/server/messages/ConfigMessages.java
index 237e237..744d98b 100644
--- a/opends/src/server/org/opends/server/messages/ConfigMessages.java
+++ b/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6153,6 +6153,17 @@
/**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to register a backend with the Directory Server. This takes two
+ * arguments, which are the backend ID and a string representation of the
+ * exception that was caught.
+ */
+ public static final int MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 572;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -7020,6 +7031,9 @@
"lock for backend %s: %s. This may interfere with " +
"operations that require exclusive access, including " +
"LDIF import and restoring a backup.");
+ registerMessage(MSGID_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND,
+ "An error occurred while attempting to register backend " +
+ "%s with the Directory Server: %s.");
registerMessage(MSGID_CONFIG_BACKEND_CLASS_NOT_BACKEND,
"The class %s specified in configuration entry %s does " +
"not contain a valid Directory Server backend " +
diff --git a/opends/src/server/org/opends/server/messages/CoreMessages.java b/opends/src/server/org/opends/server/messages/CoreMessages.java
index b497d0a..8d29a84 100644
--- a/opends/src/server/org/opends/server/messages/CoreMessages.java
+++ b/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -5967,6 +5967,98 @@
/**
+ * The message ID for the message that will be used if an attempt is made to
+ * register a backend with an ID that is already registered. This takes a
+ * single argument, which is the conflicting backend ID.
+ */
+ public static final int MSGID_REGISTER_BACKEND_ALREADY_EXISTS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 571;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * register a base DN that is already registered. This takes three arguments,
+ * which are the base DN, the backend ID for the new backend, and the backend
+ * ID for the already-registered backend.
+ */
+ public static final int MSGID_REGISTER_BASEDN_ALREADY_EXISTS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 572;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * register a base DN with a backend that already has another base DN that
+ * contains conflicting hierarchy. This takes three arguments, which are the
+ * base DN to be registered, the backend ID, and the already-registered base
+ * DN.
+ */
+ public static final int MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 573;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * register a base DN with a backend that already contains other base DNs
+ * that do not share the same parent base DN. This takes three arguments,
+ * which are the base DN to be registered, the backend ID, and the
+ * already-registered base DN.
+ */
+ public static final int MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 574;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * register a base DN with a backend that already contains other base DNs
+ * that are subordinate to a backend whereas the new base DN is not
+ * subordinate to any backend. This takes three arguments, which are the base
+ * DN to be registered, the backend ID, and the backend ID of the parent
+ * backend.
+ */
+ public static final int MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 575;
+
+
+
+ /**
+ * The message ID for the message that will be used if a newly-registered
+ * backend has a base DN for which a corresponding entry already exists in the
+ * server. This takes three arguments, which are the backend ID for the
+ * existing backend, the base DN for the new backend, and the backend ID for
+ * the new backend.
+ */
+ public static final int MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 576;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * deregister a base DN that is not registered. This takes a single argument,
+ * which is the base DN to deregister.
+ */
+ public static final int MSGID_DEREGISTER_BASEDN_NOT_REGISTERED =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 577;
+
+
+
+ /**
+ * The message ID for the message that will be used if a base DN containing
+ * both superior and subordinate backends is deregistered, leaving the
+ * possibility for missing entries in the data hierarchy. This takes two
+ * arguments, which are the base DN that has been deregistered and the backend
+ * ID of the associated backend.
+ */
+ public static final int MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY =
+ CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 578;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined
* in this class.
*/
@@ -8057,6 +8149,50 @@
"The user-specific lookthrough limit value %s contained " +
"in user entry %s could not be parsed as an integer. " +
"The default server lookthrough limit will be used.");
+
+
+ registerMessage(MSGID_REGISTER_BACKEND_ALREADY_EXISTS,
+ "Unable to register backend %s with the Directory Server " +
+ "because another backend with the same backend ID is " +
+ "already registered.");
+ registerMessage(MSGID_REGISTER_BASEDN_ALREADY_EXISTS,
+ "Unable to register base DN %s with the Directory Server " +
+ "for backend %s because that base DN is already " +
+ "registered for backend %s.");
+ registerMessage(MSGID_REGISTER_BASEDN_HIERARCHY_CONFLICT,
+ "Unable to register base DN %s with the Directory Server " +
+ "for backend %s because that backend already contains " +
+ "another base DN %s that is within the same hierarchical " +
+ "path.");
+ registerMessage(MSGID_REGISTER_BASEDN_DIFFERENT_PARENT_BASES,
+ "Unable to register base DN %s with the Directory Server " +
+ "for backend %s because that backend already contains " +
+ "another base DN %s that is not subordinate to the same " +
+ "base DN in the parent backend.");
+ registerMessage(MSGID_REGISTER_BASEDN_NEW_BASE_NOT_SUBORDINATE,
+ "Unable to register base DN %s with the Directory Server " +
+ "for backend %s because that backend already contains " +
+ "one or more other base DNs that are subordinate to " +
+ "backend %s but the new base DN is not.");
+ registerMessage(MSGID_REGISTER_BASEDN_ENTRIES_IN_MULTIPLE_BACKENDS,
+ "Backend %s already contains entry %s which has just " +
+ "been registered as the base DN for backend %s. " +
+ "These conflicting entries may cause unexpected or " +
+ "errant search results, and both backends should be " +
+ "reinitialized to ensure that each has the correct " +
+ "content.");
+ registerMessage(MSGID_DEREGISTER_BASEDN_NOT_REGISTERED,
+ "Unable to de-register base DN %s with the Directory " +
+ "Server because that base DN is not registered for any " +
+ "active backend.");
+ registerMessage(MSGID_DEREGISTER_BASEDN_MISSING_HIERARCHY,
+ "Base DN %s has been deregistered from the Directory " +
+ "Server for backend %s. This base DN had both superior " +
+ "and subordinate entries in other backends, and there " +
+ "may be inconsistent or unexpected behavior when " +
+ "accessing entries in this portion of the hierarchy " +
+ "because of the missing entries that had been held in " +
+ "the de-registered backend.");
}
}
diff --git a/opends/src/server/org/opends/server/types/DN.java b/opends/src/server/org/opends/server/types/DN.java
index 45de88c..242e0cd 100644
--- a/opends/src/server/org/opends/server/types/DN.java
+++ b/opends/src/server/org/opends/server/types/DN.java
@@ -507,7 +507,9 @@
public DN getParentDNInSuffix() {
assert debugEnter(CLASS_NAME, "getParentDNInSuffix");
- if ((numComponents <= 1) || (DirectoryServer.isSuffix(this))) {
+ if ((numComponents <= 1) ||
+ DirectoryServer.isNamingContext(this))
+ {
return null;
}
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 4190996..34bf7c6 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -186,8 +186,8 @@
/**
* The name of the standard attribute that is used to specify the set of
- * naming contexts (suffixes) for the Directory Server, formatted in camel
- * case.
+ * public naming contexts (suffixes) for the Directory Server, formatted in
+ * camel case.
*/
public static final String ATTR_NAMING_CONTEXTS = "namingContexts";
@@ -195,14 +195,23 @@
/**
* The name of the standard attribute that is used to specify the set of
- * naming contexts (suffixes) for the Directory Server, formatted in all
- * lowercase.
+ * public naming contexts (suffixes) for the Directory Server, formatted in
+ * all lowercase.
*/
public static final String ATTR_NAMING_CONTEXTS_LC = "namingcontexts";
/**
+ * The name of the attribute used to hold the DNs that constitute the set of
+ * "private" naming contexts registered with the server.
+ */
+ public static final String ATTR_PRIVATE_NAMING_CONTEXTS =
+ "ds-private-naming-contexts";
+
+
+
+ /**
* The name of the standard attribute that is used to hold organization names,
* formatted in all lowercase.
*/
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
index e4b4266..b3b884c 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -76,7 +76,7 @@
*/
public static final String PROPERTY_LDAP_PORT =
"org.opends.server.LdapPort";
-
+
/**
* The string representation of the DN that will be used as the base entry for
* the test backend. This must not be changed, as there are a number of test
@@ -151,7 +151,7 @@
testRoot.mkdirs();
//db_verify is second jeb backend used by the jeb verify test cases
String[] subDirectories = { "bak", "bin", "changelogDb", "classes",
- "config", "db", "db_verify", "ldif", "lib",
+ "config", "db", "db_verify", "ldif", "lib",
"locks", "logs" };
for (String s : subDirectories)
{
@@ -348,6 +348,7 @@
if (memoryBackend == null)
{
memoryBackend = new MemoryBackend();
+ memoryBackend.setBackendID("test");
memoryBackend.initializeBackend(null, new DN[] { baseDN });
DirectoryServer.registerBackend(memoryBackend);
}
@@ -367,12 +368,12 @@
* @param createBaseEntry Indicate whether to automatically create the base
* entry and add it to the backend.
- *
+ *
* @param beID The be id to clear.
- *
+ *
* @param dn The suffix of the backend to create if the the createBaseEntry
* boolean is true.
- *
+ *
* @throws Exception If an unexpected problem occurs.
*/
public static void clearJEBackend(boolean createBaseEntry, String beID, String dn)
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
new file mode 100644
index 0000000..66e4204
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
@@ -0,0 +1,783 @@
+/*
+ * 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.core;
+
+
+
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.api.Backend;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * A set of generic test cases that cover adding, modifying, and removing
+ * Directory Server backends.
+ */
+public class BackendConfigManagerTestCase
+ extends CoreTestCase
+{
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+ }
+
+
+
+ /**
+ * Tests that the server will reject an attempt to register a base DN that is
+ * already defined in the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRegisterBaseThatAlreadyExists()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(false);
+
+ DN baseDN = DN.decode("o=test");
+ String backendID = createBackendID(baseDN);
+ Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+ backendEntry.getUserAttributes(),
+ backendEntry.getOperationalAttributes());
+ assertFalse(addOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests that the server will reject an attempt to deregister a base DN that
+ * is not defined in the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { DirectoryException.class })
+ public void testDeregisterNonExistentBaseDN()
+ throws Exception
+ {
+ DirectoryServer.deregisterBaseDN(DN.decode("o=unregistered"), false);
+ }
+
+
+
+ /**
+ * Tests that the server will reject an attempt to register a base DN using a
+ * backend with a backend ID that is already defined in the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testRegisterBackendIDThatAlreadyExists()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(false);
+
+ DN baseDN = DN.decode("o=test");
+ String backendID = "test";
+ Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+ backendEntry.getUserAttributes(),
+ backendEntry.getOperationalAttributes());
+ assertFalse(addOperation.getResultCode() == ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the ability of the server to create and remove a backend that is
+ * never enabled.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddAndRemoveDisabledBackend()
+ throws Exception
+ {
+ DN baseDN = DN.decode("o=bcmtest");
+ String backendID = createBackendID(baseDN);
+ Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+ backendEntry.getUserAttributes(),
+ backendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(backendID));
+ assertNull(DirectoryServer.getBackendWithBaseDN(baseDN));
+
+ DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the ability of the server to create and remove a backend that is
+ * enabled. It will also test the ability of that backend to hold entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddAndRemoveEnabledBackend()
+ throws Exception
+ {
+ DN baseDN = DN.decode("o=bcmtest");
+ String backendID = createBackendID(baseDN);
+ Entry backendEntry = createBackendEntry(backendID, true, baseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+ backendEntry.getUserAttributes(),
+ backendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend backend = DirectoryServer.getBackend(backendID);
+ assertNotNull(backend);
+ assertEquals(backend, DirectoryServer.getBackendWithBaseDN(baseDN));
+ assertNull(backend.getParentBackend());
+ assertTrue(backend.getSubordinateBackends().length == 0);
+ assertFalse(backend.entryExists(baseDN));
+ assertTrue(DirectoryServer.isNamingContext(baseDN));
+
+ Entry e = createEntry(baseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(backend.entryExists(baseDN));
+
+ DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(backendID));
+ }
+
+
+
+ /**
+ * Tests the ability of the server to create a backend that is disabled and
+ * then enable it through a configuration change, and then subsequently
+ * disable it.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testEnableAndDisableBackend()
+ throws Exception
+ {
+ // Create the backend and make it disabled.
+ DN baseDN = DN.decode("o=bcmtest");
+ String backendID = createBackendID(baseDN);
+ Entry backendEntry = createBackendEntry(backendID, false, baseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(backendEntry.getDN(), backendEntry.getObjectClasses(),
+ backendEntry.getUserAttributes(),
+ backendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(backendID));
+ assertFalse(DirectoryServer.isNamingContext(baseDN));
+
+
+ // Modify the backend to enable it.
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-backend-enabled", "true")));
+ ModifyOperation modifyOperation =
+ conn.processModify(backendEntry.getDN(), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend backend = DirectoryServer.getBackend(backendID);
+ assertNotNull(backend);
+ assertEquals(backend, DirectoryServer.getBackendWithBaseDN(baseDN));
+ assertNull(backend.getParentBackend());
+ assertTrue(backend.getSubordinateBackends().length == 0);
+ assertFalse(backend.entryExists(baseDN));
+ assertTrue(DirectoryServer.isNamingContext(baseDN));
+
+ Entry e = createEntry(baseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(backend.entryExists(baseDN));
+
+
+ // Modify the backend to disable it.
+ mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-backend-enabled", "false")));
+ modifyOperation =
+ conn.processModify(backendEntry.getDN(), mods);
+ assertNull(DirectoryServer.getBackend(backendID));
+ assertFalse(DirectoryServer.entryExists(baseDN));
+ assertFalse(DirectoryServer.isNamingContext(baseDN));
+
+
+ // Delete the disabled backend.
+ DeleteOperation deleteOperation = conn.processDelete(backendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the ability of the Directory Server to work properly when adding
+ * nested backends in which the parent is added first and the child second.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddNestedBackendParentFirst()
+ throws Exception
+ {
+ // Create the parent backend and the corresponding base entry.
+ DN parentBaseDN = DN.decode("o=parent");
+ String parentBackendID = createBackendID(parentBaseDN);
+ Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+ parentBaseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(parentBackendEntry.getDN(),
+ parentBackendEntry.getObjectClasses(),
+ parentBackendEntry.getUserAttributes(),
+ parentBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+ assertNotNull(parentBackend);
+ assertEquals(parentBackend,
+ DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+ assertNull(parentBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 0);
+ assertFalse(parentBackend.entryExists(parentBaseDN));
+ assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+
+ Entry e = createEntry(parentBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(parentBackend.entryExists(parentBaseDN));
+
+
+ // Create the child backend and the corresponding base entry.
+ DN childBaseDN = DN.decode("ou=child,o=parent");
+ String childBackendID = createBackendID(childBaseDN);
+ Entry childBackendEntry = createBackendEntry(childBackendID, true,
+ childBaseDN);
+
+ addOperation =
+ conn.processAdd(childBackendEntry.getDN(),
+ childBackendEntry.getObjectClasses(),
+ childBackendEntry.getUserAttributes(),
+ childBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend childBackend = DirectoryServer.getBackend(childBackendID);
+ assertNotNull(childBackend);
+ assertEquals(childBackend,
+ DirectoryServer.getBackendWithBaseDN(childBaseDN));
+ assertNotNull(childBackend.getParentBackend());
+ assertEquals(parentBackend, childBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+ assertFalse(childBackend.entryExists(childBaseDN));
+ assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+
+ e = createEntry(childBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(childBackend.entryExists(childBaseDN));
+
+
+ // Make sure that both entries exist.
+ InternalSearchOperation internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+
+ // Make sure that we can't remove the parent backend with the child still
+ // in place.
+ DeleteOperation deleteOperation =
+ conn.processDelete(parentBackendEntry.getDN());
+ assertFalse(deleteOperation.getResultCode() == ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getBackend(parentBackendID));
+
+ // Delete the child and then delete the parent.
+ deleteOperation = conn.processDelete(childBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(childBackendID));
+ assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+ deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(parentBackendID));
+ }
+
+
+
+ /**
+ * Tests the ability of the Directory Server to work properly when adding
+ * nested backends in which the child is added first and the parent second.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddNestedBackendChildFirst()
+ throws Exception
+ {
+ // Create the child backend and the corresponding base entry (at the time
+ // of the creation, it will be a naming context).
+ DN childBaseDN = DN.decode("ou=child,o=parent");
+ String childBackendID = createBackendID(childBaseDN);
+ Entry childBackendEntry = createBackendEntry(childBackendID, true,
+ childBaseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(childBackendEntry.getDN(),
+ childBackendEntry.getObjectClasses(),
+ childBackendEntry.getUserAttributes(),
+ childBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend childBackend = DirectoryServer.getBackend(childBackendID);
+ assertNotNull(childBackend);
+ assertEquals(childBackend,
+ DirectoryServer.getBackendWithBaseDN(childBaseDN));
+ assertFalse(childBackend.entryExists(childBaseDN));
+ assertNull(childBackend.getParentBackend());
+ assertTrue(childBackend.getSubordinateBackends().length == 0);
+ assertFalse(childBackend.entryExists(childBaseDN));
+
+ Entry e = createEntry(childBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(childBackend.entryExists(childBaseDN));
+ assertTrue(DirectoryServer.isNamingContext(childBaseDN));
+
+
+ // Create the parent backend and the corresponding entry (and verify that
+ // its DN is now a naming context and the child's is not).
+ DN parentBaseDN = DN.decode("o=parent");
+ String parentBackendID = createBackendID(parentBaseDN);
+ Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+ parentBaseDN);
+
+ addOperation =
+ conn.processAdd(parentBackendEntry.getDN(),
+ parentBackendEntry.getObjectClasses(),
+ parentBackendEntry.getUserAttributes(),
+ parentBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+ assertNotNull(parentBackend);
+ assertEquals(parentBackend,
+ DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+ assertNotNull(childBackend.getParentBackend());
+ assertEquals(parentBackend, childBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+
+ e = createEntry(parentBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(parentBackend.entryExists(parentBaseDN));
+ assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+ assertFalse(DirectoryServer.isNamingContext(childBaseDN));
+
+
+ // Verify that we can see both entries with a subtree search.
+ InternalSearchOperation internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+ // Delete the backends from the server.
+ DeleteOperation deleteOperation =
+ conn.processDelete(childBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(childBackendID));
+ assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+ deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(parentBackendID));
+ }
+
+
+
+ /**
+ * Tests the ability of the Directory Server to work properly when inserting
+ * an intermediate backend between a parent backend and an existing nested
+ * backend.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testInsertIntermediateBackend()
+ throws Exception
+ {
+ // Add the parent backend to the server and its corresponding base entry.
+ DN parentBaseDN = DN.decode("o=parent");
+ String parentBackendID = createBackendID(parentBaseDN);
+ Entry parentBackendEntry = createBackendEntry(parentBackendID, true,
+ parentBaseDN);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ conn.processAdd(parentBackendEntry.getDN(),
+ parentBackendEntry.getObjectClasses(),
+ parentBackendEntry.getUserAttributes(),
+ parentBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend parentBackend = DirectoryServer.getBackend(parentBackendID);
+ assertNotNull(parentBackend);
+ assertEquals(parentBackend,
+ DirectoryServer.getBackendWithBaseDN(parentBaseDN));
+ assertNull(parentBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 0);
+ assertFalse(parentBackend.entryExists(parentBaseDN));
+
+ Entry e = createEntry(parentBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(parentBackend.entryExists(parentBaseDN));
+ assertTrue(DirectoryServer.isNamingContext(parentBaseDN));
+
+
+ // Add the grandchild backend to the server.
+ DN grandchildBaseDN = DN.decode("ou=grandchild,ou=child,o=parent");
+ String grandchildBackendID = createBackendID(grandchildBaseDN);
+ Entry grandchildBackendEntry = createBackendEntry(grandchildBackendID, true,
+ grandchildBaseDN);
+
+ addOperation =
+ conn.processAdd(grandchildBackendEntry.getDN(),
+ grandchildBackendEntry.getObjectClasses(),
+ grandchildBackendEntry.getUserAttributes(),
+ grandchildBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend grandchildBackend = DirectoryServer.getBackend(grandchildBackendID);
+ assertNotNull(grandchildBackend);
+ assertEquals(grandchildBackend,
+ DirectoryServer.getBackendWithBaseDN(grandchildBaseDN));
+ assertNotNull(grandchildBackend.getParentBackend());
+ assertEquals(grandchildBackend.getParentBackend(), parentBackend);
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+ assertFalse(grandchildBackend.entryExists(grandchildBaseDN));
+
+ // Verify that we can't create the grandchild base entry because its parent
+ // doesn't exist.
+ e = createEntry(grandchildBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.NO_SUCH_OBJECT);
+ assertFalse(grandchildBackend.entryExists(grandchildBaseDN));
+
+
+ // Add the child backend to the server and create its base entry.
+ DN childBaseDN = DN.decode("ou=child,o=parent");
+ String childBackendID = createBackendID(childBaseDN);
+ Entry childBackendEntry = createBackendEntry(childBackendID, true,
+ childBaseDN);
+
+ addOperation =
+ conn.processAdd(childBackendEntry.getDN(),
+ childBackendEntry.getObjectClasses(),
+ childBackendEntry.getUserAttributes(),
+ childBackendEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+ Backend childBackend = DirectoryServer.getBackend(childBackendID);
+ assertNotNull(childBackend);
+ assertEquals(childBackend,
+ DirectoryServer.getBackendWithBaseDN(childBaseDN));
+ assertNotNull(childBackend.getParentBackend());
+ assertEquals(parentBackend, childBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+ assertFalse(childBackend.entryExists(childBaseDN));
+ assertTrue(childBackend.getSubordinateBackends().length == 1);
+ assertEquals(childBackend.getSubordinateBackends()[0], grandchildBackend);
+ assertEquals(grandchildBackend.getParentBackend(), childBackend);
+
+ e = createEntry(childBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(childBackend.entryExists(childBaseDN));
+
+ // Now we can create the grandchild base entry.
+ e = createEntry(grandchildBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(grandchildBackend.entryExists(grandchildBaseDN));
+
+
+ // Verify that a subtree search can see all three entries.
+ InternalSearchOperation internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 3);
+
+
+ // Disable the intermediate (child) backend. This should be allowed.
+ ArrayList<Modification> mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-backend-enabled",
+ "false")));
+ ModifyOperation modifyOperation =
+ conn.processModify(childBackendEntry.getDN(), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ // Make sure that we now only see two entries with the subtree search
+ // (and those two entries should be the parent and grandchild base entries).
+ internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+ // Re-enable the intermediate backend.
+ mods = new ArrayList<Modification>();
+ mods.add(new Modification(ModificationType.REPLACE,
+ new Attribute("ds-cfg-backend-enabled", "true")));
+ modifyOperation = conn.processModify(childBackendEntry.getDN(), mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+ // Update our reference to the child backend since the old one is no longer
+ // valid, and make sure that it got re-inserted back into the same place in
+ // the hierarchy.
+ childBackend = DirectoryServer.getBackend(childBackendID);
+ assertNotNull(childBackend);
+ assertEquals(childBackend,
+ DirectoryServer.getBackendWithBaseDN(childBaseDN));
+ assertNotNull(childBackend.getParentBackend());
+ assertEquals(parentBackend, childBackend.getParentBackend());
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+ assertFalse(childBackend.entryExists(childBaseDN));
+ assertTrue(childBackend.getSubordinateBackends().length == 1);
+ assertEquals(childBackend.getSubordinateBackends()[0], grandchildBackend);
+ assertEquals(grandchildBackend.getParentBackend(), childBackend);
+
+
+ // Since the memory backend that we're using for this test doesn't retain
+ // entries across stops and restarts, a subtree search below the parent
+ // should still only return two entries, which means that it's going through
+ // the entire chain of backends.
+ internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 2);
+
+
+ // Add the child entry back into the server to get things back to the way
+ // they were before we disabled the backend.
+ e = createEntry(childBaseDN);
+ addOperation = conn.processAdd(e.getDN(), e.getObjectClasses(),
+ e.getUserAttributes(),
+ e.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(childBackend.entryExists(childBaseDN));
+
+
+ // We should again be able to see all three entries when performing a
+ // search.
+ internalSearch =
+ conn.processSearch(parentBaseDN, SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ assertEquals(internalSearch.getResultCode(), ResultCode.SUCCESS);
+ assertEquals(internalSearch.getSearchEntries().size(), 3);
+
+
+ // Get rid of the entries in the proper order.
+ DeleteOperation deleteOperation =
+ conn.processDelete(grandchildBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(grandchildBackendID));
+ assertTrue(childBackend.getSubordinateBackends().length == 0);
+ assertTrue(parentBackend.getSubordinateBackends().length == 1);
+
+ deleteOperation = conn.processDelete(childBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(childBackendID));
+ assertTrue(parentBackend.getSubordinateBackends().length == 0);
+
+ deleteOperation = conn.processDelete(parentBackendEntry.getDN());
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNull(DirectoryServer.getBackend(parentBackendID));
+ }
+
+
+
+ /**
+ * Creates an entry that may be used to add a new backend to the server. It
+ * will be an instance of the memory backend.
+ *
+ * @param backendID The backend ID to use for the backend.
+ * @param enabled Indicates whether the backend should be enabled.
+ * @param baseDNs The set of base DNs to use for the new backend.
+ *
+ * @return An entry that may be used to add a new backend to the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private Entry createBackendEntry(String backendID, boolean enabled,
+ DN... baseDNs)
+ throws Exception
+ {
+ assertNotNull(baseDNs);
+ assertFalse(baseDNs.length == 0);
+
+ ArrayList<String> lines = new ArrayList<String>();
+ lines.add("dn: ds-cfg-backend-id=" + backendID + ",cn=Backends,cn=config");
+ lines.add("objectClass: top");
+ lines.add("objectClass: ds-cfg-backend");
+ lines.add("ds-cfg-backend-id: " + backendID);
+ lines.add("ds-cfg-backend-class: org.opends.server.backends.MemoryBackend");
+ lines.add("ds-cfg-backend-enabled: " + String.valueOf(enabled));
+ lines.add("ds-cfg-backend-writability-mode: enabled");
+
+ for (DN dn : baseDNs)
+ {
+ lines.add("ds-cfg-backend-base-dn: " + dn.toString());
+ }
+
+ String[] lineArray = new String[lines.size()];
+ lines.toArray(lineArray);
+ return TestCaseUtils.makeEntry(lineArray);
+ }
+
+
+
+ /**
+ * Constructs a backend ID to use for a backend with the provided set of base
+ * DNs.
+ *
+ * @param baseDNs The set of base DNs to use when creating the backend ID.
+ *
+ * @return The constructed backend ID based on the given base DNs.
+ */
+ private String createBackendID(DN... baseDNs)
+ {
+ StringBuilder buffer = new StringBuilder();
+
+ for (DN dn : baseDNs)
+ {
+ if (buffer.length() > 0)
+ {
+ buffer.append("___");
+ }
+
+ String ndn = dn.toNormalizedString();
+ for (int i=0; i < ndn.length(); i++)
+ {
+ char c = ndn.charAt(i);
+ if (Character.isLetterOrDigit(c))
+ {
+ buffer.append(c);
+ }
+ else
+ {
+ buffer.append('_');
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
+}
+
--
Gitblit v1.10.0