From cd068f12d9ab47bd1a8bb924dd62cc6de0f8c46b Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Fri, 15 Dec 2006 22:03:46 +0000
Subject: [PATCH] Add initial support for updating the Directory Server schema over protocol. In particular, this commit makes it possible to add new attribute types and objectclasses and have them written to the 99-user.ldif file. This implementation does not yet support interacting with any other schema element types, removing or replacing existing schema elements, or interacting with any files other than 99-user.ldif. Additional functionality will be added in the future to enable these capabilities.
---
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java | 14
opendj-sdk/opends/src/server/org/opends/server/types/Schema.java | 205 +++++++
opendj-sdk/opends/src/server/org/opends/server/messages/SchemaMessages.java | 4
opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java | 8
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java | 438 +++++++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java | 748 +++++++++++++++++++++++++++
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java | 173 ++++++
7 files changed, 1,570 insertions(+), 20 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
index 4eae6b5..8faf757 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -65,6 +65,8 @@
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
+import org.opends.server.schema.AttributeTypeSyntax;
+import org.opends.server.schema.ObjectClassSyntax;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
@@ -78,16 +80,20 @@
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.Modification;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
+import org.opends.server.types.Schema;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.util.DynamicConstants;
+import org.opends.server.util.LDIFReader;
import org.opends.server.util.LDIFWriter;
import static org.opends.server.config.ConfigConstants.*;
@@ -238,7 +244,7 @@
DirectoryServer.getAttributeType(ATTR_DIT_STRUCTURE_RULES_LC, true);
matchingRuleUsesType =
DirectoryServer.getAttributeType(ATTR_MATCHING_RULE_USE_LC, true);
- nameFormsType = DirectoryServer.getAttributeType(ATTR_NAME_FORMS, true);
+ nameFormsType = DirectoryServer.getAttributeType(ATTR_NAME_FORMS_LC, true);
// Get the set of user-defined attributes for the configuration entry. Any
@@ -321,19 +327,12 @@
schemaObjectClasses = new LinkedHashMap<ObjectClass,String>(3);
schemaObjectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
- ObjectClass subentryOC =
- DirectoryServer.getObjectClass(OC_LDAP_SUBENTRY_LC);
- if (subentryOC == null)
- {
- subentryOC = DirectoryServer.getDefaultObjectClass(OC_LDAP_SUBENTRY);
- }
+ ObjectClass subentryOC = DirectoryServer.getObjectClass(OC_LDAP_SUBENTRY_LC,
+ true);
schemaObjectClasses.put(subentryOC, OC_LDAP_SUBENTRY);
- ObjectClass subschemaOC = DirectoryServer.getObjectClass(OC_SUBSCHEMA);
- if (subschemaOC == null)
- {
- subschemaOC = DirectoryServer.getDefaultObjectClass(OC_SUBSCHEMA);
- }
+ ObjectClass subschemaOC = DirectoryServer.getObjectClass(OC_SUBSCHEMA,
+ true);
schemaObjectClasses.put(subschemaOC, OC_SUBSCHEMA);
@@ -837,12 +836,381 @@
assert debugEnter(CLASS_NAME, "replaceEntry", String.valueOf(entry),
String.valueOf(modifyOperation));
- // FIXME -- We need to allow this.
- int msgID = MSGID_SCHEMA_MODIFY_NOT_SUPPORTED;
- String message = getMessage(msgID, String.valueOf(entry.getDN()),
- String.valueOf(configEntryDN));
- throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
- msgID);
+
+ // At present, we only allow the addition of new attribute types,
+ // object classes, name forms, DIT content rules, DIT structure rules, and
+ // matching rule uses. We will not support removing or replacing existing
+ // elements, nor will we allow modification of any other attributes. Make
+ // sure that the included modify operation is acceptable within these
+ // constraints.
+ List<Modification> mods = modifyOperation.getModifications();
+ if (mods.isEmpty())
+ {
+ // There aren't any modifications, so we don't need to do anything.
+ return;
+ }
+
+ Schema newSchema = DirectoryServer.getSchema().duplicate();
+ LinkedList<AttributeType> newAttrTypes = new LinkedList<AttributeType>();
+ LinkedList<ObjectClass> newObjectClasses = new LinkedList<ObjectClass>();
+
+ for (Modification m : mods)
+ {
+ if (m.isInternal())
+ {
+ // We don't need to do anything for internal modifications (e.g., like
+ // those that set modifiersName and modifyTimestamp).
+ continue;
+ }
+
+ switch (m.getModificationType())
+ {
+ case ADD:
+ // This is fine, as long as there aren't any conflicts later.
+ break;
+
+ case DELETE:
+ // FIXME -- We need to support this.
+ int msgID = MSGID_SCHEMA_DELETE_MODTYPE_NOT_SUPPORTED;
+ String message = getMessage(msgID);
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+
+ case REPLACE:
+ // FIXME -- Should we support this?
+ case INCREMENT:
+ default:
+ // FIXME -- Make sure to update this message once we support
+ // schema deletes and possibly replace.
+ msgID = MSGID_SCHEMA_INVALID_MODIFICATION_TYPE;
+ message = getMessage(msgID, m.getModificationType());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+ }
+
+
+ // At the present time, we will only allow modification of the
+ // attributeTypes and objectClasses attributes.
+ Attribute a = m.getAttribute();
+ AttributeType t = a.getAttributeType();
+ if (t.equals(attributeTypesType))
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ AttributeType newType;
+ try
+ {
+ newType = AttributeTypeSyntax.decodeAttributeType(v.getValue(),
+ newSchema);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", de);
+
+ int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE;
+ String message = getMessage(msgID, v.getStringValue(),
+ de.getErrorMessage());
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message, msgID, de);
+ }
+
+ try
+ {
+ newSchema.registerAttributeType(newType, false);
+ newAttrTypes.add(newType);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", de);
+
+ int msgID = MSGID_SCHEMA_MODIFY_ATTRTYPE_ALREADY_EXISTS;
+ String message = getMessage(msgID, newType.getNameOrOID(),
+ de.getErrorMessage());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID, de);
+ }
+ }
+ }
+ else if (t.equals(objectClassesType))
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ ObjectClass newClass;
+ try
+ {
+ newClass = ObjectClassSyntax.decodeObjectClass(v.getValue(),
+ newSchema);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", de);
+
+ int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS;
+ String message = getMessage(msgID, v.getStringValue(),
+ de.getErrorMessage());
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message, msgID, de);
+ }
+
+ // If there is a superior class, then make sure it is defined.
+ ObjectClass superiorClass = newClass.getSuperiorClass();
+ if (superiorClass != null)
+ {
+ String lowerName = toLowerCase(superiorClass.getNameOrOID());
+ if (! newSchema.hasObjectClass(lowerName))
+ {
+ int msgID = MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS;
+ String message = getMessage(msgID, newClass.getNameOrOID(),
+ superiorClass.getNameOrOID());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
+ }
+
+ // Make sure that all the associated attribute types are defined.
+ for (AttributeType at : newClass.getRequiredAttributes())
+ {
+ String lowerName = toLowerCase(at.getNameOrOID());
+ if (! newSchema.hasAttributeType(lowerName))
+ {
+ int msgID = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_REQUIRED_ATTR;
+ String message = getMessage(msgID, newClass.getNameOrOID(),
+ at.getNameOrOID());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
+ }
+
+ for (AttributeType at : newClass.getOptionalAttributes())
+ {
+ String lowerName = toLowerCase(at.getNameOrOID());
+ if (! newSchema.hasAttributeType(lowerName))
+ {
+ int msgID = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_OPTIONAL_ATTR;
+ String message = getMessage(msgID, newClass.getNameOrOID(),
+ at.getNameOrOID());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID);
+ }
+ }
+
+ try
+ {
+ newSchema.registerObjectClass(newClass, false);
+ newObjectClasses.add(newClass);
+ }
+ catch (DirectoryException de)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", de);
+
+ int msgID = MSGID_SCHEMA_MODIFY_OBJECTCLASS_ALREADY_EXISTS;
+ String message = getMessage(msgID, newClass.getNameOrOID(),
+ de.getErrorMessage());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ message, msgID, de);
+ }
+ }
+ }
+ else
+ {
+ int msgID = MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE;
+ String message = getMessage(msgID, a.getName());
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+ msgID);
+ }
+ }
+
+
+ // If we've gotten here, then everything looks OK. Add the new schema
+ // elements to the 99-user.ldif file and swing the new schema into place.
+ String schemaDirPath = DirectoryServer.getServerRoot() + File.separator +
+ PATH_SCHEMA_DIR;
+ File userSchemaFile = new File(schemaDirPath, FILE_USER_SCHEMA_ELEMENTS);
+ Entry userSchemaEntry = null;
+
+ if (userSchemaFile.exists())
+ {
+ // There's already a set of user-defined schema elements, so we'll need to
+ // add these new elements to that set.
+ LDIFReader ldifReader = null;
+ try
+ {
+ LDIFImportConfig importConfig =
+ new LDIFImportConfig(userSchemaFile.getAbsolutePath());
+ ldifReader = new LDIFReader(importConfig);
+
+ userSchemaEntry = ldifReader.readEntry(true);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", e);
+
+ int msgID = MSGID_SCHEMA_MODIFY_CANNOT_READ_EXISTING_USER_SCHEMA;
+ String message = getMessage(msgID, userSchemaFile.getAbsolutePath(),
+ stackTraceToSingleLineString(e));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID, e);
+ }
+ finally
+ {
+ if (ldifReader != null)
+ {
+ ldifReader.close();
+ }
+ }
+ }
+
+ if (userSchemaEntry == null)
+ {
+ // This could happen if there was no user schema file or if it was there
+ // but didn't have any entries. At any rate, create a new, empty entry.
+ userSchemaEntry = createEmptySchemaEntry();
+ }
+
+
+ // Add all of the new schema elements to the entry.
+ if (! newAttrTypes.isEmpty())
+ {
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ for (AttributeType t : newAttrTypes)
+ {
+ StringBuilder buffer = new StringBuilder();
+ t.toString(buffer, false);
+ values.add(new AttributeValue(attributeTypesType, buffer.toString()));
+ }
+
+ Attribute attrTypeAttribute = new Attribute(attributeTypesType,
+ ATTR_ATTRIBUTE_TYPES, values);
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ userSchemaEntry.addAttribute(attrTypeAttribute, duplicateValues);
+ }
+
+ if (! newObjectClasses.isEmpty())
+ {
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>();
+ for (ObjectClass oc : newObjectClasses)
+ {
+ StringBuilder buffer = new StringBuilder();
+ oc.toString(buffer, false);
+ values.add(new AttributeValue(attributeTypesType, buffer.toString()));
+ }
+
+ Attribute ocAttribute = new Attribute(objectClassesType,
+ ATTR_OBJECTCLASSES, values);
+ LinkedList<AttributeValue> duplicateValues =
+ new LinkedList<AttributeValue>();
+ userSchemaEntry.addAttribute(ocAttribute, duplicateValues);
+ }
+
+
+ // Swing the new schema into place.
+ try
+ {
+ File tempSchemaFile = new File(userSchemaFile.getAbsolutePath() + ".tmp");
+ LDIFExportConfig exportConfig =
+ new LDIFExportConfig(tempSchemaFile.getAbsolutePath(),
+ ExistingFileBehavior.OVERWRITE);
+
+ LDIFWriter writer = null;
+ try
+ {
+ writer = new LDIFWriter(exportConfig);
+ writer.writeEntry(userSchemaEntry);
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+
+ File oldSchemaFile = null;
+ if (userSchemaFile.exists())
+ {
+ oldSchemaFile = new File(userSchemaFile.getAbsolutePath() + ".old");
+ if (oldSchemaFile.exists())
+ {
+ oldSchemaFile.delete();
+ }
+
+ userSchemaFile.renameTo(oldSchemaFile);
+ }
+
+ tempSchemaFile.renameTo(userSchemaFile);
+
+ if (oldSchemaFile != null)
+ {
+ oldSchemaFile.delete();
+ }
+
+ DirectoryServer.setSchema(newSchema);
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "replaceEntry", e);
+
+ int msgID = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA;
+ String message = getMessage(msgID, userSchemaFile.getAbsolutePath(),
+ stackTraceToSingleLineString(e));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID, e);
+ }
+ }
+
+
+
+ /**
+ * Creates an empty entry that may be used as the basis for a new schema file.
+ *
+ * @return An empty entry that may be used as the basis for a new schema
+ * file.
+ */
+ private Entry createEmptySchemaEntry()
+ {
+ assert debugEnter(CLASS_NAME, "createEmptySchemaEntry");
+
+ LinkedHashMap<ObjectClass,String> objectClasses =
+ new LinkedHashMap<ObjectClass,String>();
+ objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+ objectClasses.put(DirectoryServer.getObjectClass(OC_LDAP_SUBENTRY_LC, true),
+ OC_LDAP_SUBENTRY);
+ objectClasses.put(DirectoryServer.getObjectClass(OC_SUBSCHEMA, true),
+ OC_SUBSCHEMA);
+
+ LinkedHashMap<AttributeType,List<Attribute>> userAttributes =
+ new LinkedHashMap<AttributeType,List<Attribute>>();
+
+ LinkedHashMap<AttributeType,List<Attribute>> operationalAttributes =
+ new LinkedHashMap<AttributeType,List<Attribute>>();
+
+ DN dn = DirectoryServer.getSchemaDN();
+ RDN rdn = dn.getRDN();
+ for (int i=0; i < rdn.getNumValues(); i++)
+ {
+ AttributeType type = rdn.getAttributeType(i);
+ String name = rdn.getAttributeName(i);
+
+ LinkedHashSet<AttributeValue> values =
+ new LinkedHashSet<AttributeValue>(1);
+ values.add(rdn.getAttributeValue(i));
+
+ LinkedList<Attribute> attrList = new LinkedList<Attribute>();
+ attrList.add(new Attribute(type, name, values));
+ if (type.isOperational())
+ {
+ operationalAttributes.put(type, attrList);
+ }
+ else
+ {
+ userAttributes.put(type, attrList);
+ }
+ }
+
+ return new Entry(dn, objectClasses, userAttributes, operationalAttributes);
}
@@ -2395,5 +2763,39 @@
return new ConfigChangeResult(resultCode, adminActionRequired, messages);
}
+
+
+
+ /**
+ * Indicates whether to treat common schema attributes like user attributes
+ * rather than operational attributes.
+ *
+ * @return {@code true} if common attributes should be treated like user
+ * attributes, or {@code false} if not.
+ */
+ boolean showAllAttributes()
+ {
+ assert debugEnter(CLASS_NAME, "showAllAttributes");
+
+ return showAllAttributes;
+ }
+
+
+
+ /**
+ * Specifies whether to treat common schema attributes like user attributes
+ * rather than operational attributes.
+ *
+ * @param showAllAttributes Specifies whether to treat common schema
+ * attributes like user attributes rather than
+ * operational attributes.
+ */
+ void setShowAllAttributes(boolean showAllAttributes)
+ {
+ assert debugEnter(CLASS_NAME, "setShowAllAttributes",
+ String.valueOf(showAllAttributes));
+
+ this.showAllAttributes = showAllAttributes;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
index 8bb712d..3247efa 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -3262,6 +3262,14 @@
/**
+ * The name (with no path information) of the file in the schema directory
+ * that will contain user-defined schema definitions.
+ */
+ public static final String FILE_USER_SCHEMA_ELEMENTS = "99-user.ldif";
+
+
+
+ /**
* The name of the configuration attribute that indicates the log file
* where the loggers will log the information.
*/
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index 1d60a8e..6817cbc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2326,6 +2326,20 @@
/**
+ * Replaces the Directory Server schema with the provided schema.
+ *
+ * @param schema The new schema to use for the Directory Server.
+ */
+ public static void setSchema(Schema schema)
+ {
+ assert debugEnter(CLASS_NAME, "setSchema", String.valueOf(schema));
+
+ directoryServer.schema = schema;
+ }
+
+
+
+ /**
* Retrieves the set of matching rules registered with the Directory Server.
* The mapping will be between the lowercase name or OID for each matching
* rule and the matching rule implementation. The same matching rule instance
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
index 3691cce..2c1f81c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -2244,6 +2244,136 @@
/**
+ * The message ID for the message that will be used if a modify operation
+ * attempts to delete existing schema elements, which is not currently
+ * supported. This does not take any arguments.
+ */
+ public static final int MSGID_SCHEMA_DELETE_MODTYPE_NOT_SUPPORTED =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 211;
+
+
+
+ /**
+ * The message ID for the message that will be used if a modify operation
+ * attempts to replace or increment schema elements, which is not allowed.
+ * This takes a single argument, which is the name of the attempted
+ * modification type.
+ */
+ public static final int MSGID_SCHEMA_INVALID_MODIFICATION_TYPE =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 212;
+
+
+
+ /**
+ * The message ID for the message that will be used if a modify operation
+ * attempts to modify an attribute type that cannot be changed. This takes a
+ * single argument, which is the name of the target attribute type.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 213;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to decode a new attribute type. This takes two arguments, which
+ * are the attribute type string and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 214;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * add a new attribute type with a name or OID that conflicts with an existing
+ * attribute type. This takes two arguments, which are the name of the new
+ * attribute type and a message explaining the problem that occurred.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_ATTRTYPE_ALREADY_EXISTS =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 215;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to decode a new objectclass. This takes two arguments, which
+ * are the objectclass string and a message explaining the problem that
+ * occurred.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 216;
+
+
+
+ /**
+ * The message ID for the message that will be used if a new objectclass
+ * references an undefined superior class. This takes two arguments, which
+ * are the name of the new objectclass and the name of the undefined superior
+ * class.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 217;
+
+
+
+ /**
+ * The message ID for the message that will be used if a new objectclass
+ * references an undefined required attribute. This takes two arguments,
+ * which are the name of the new objectclass and the name of the undefined
+ * attribute type.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_OC_UNDEFINED_REQUIRED_ATTR =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 218;
+
+
+
+ /**
+ * The message ID for the message that will be used if a new objectclass
+ * references an undefined optional attribute. This takes two arguments,
+ * which are the name of the new objectclass and the name of the undefined
+ * attribute type.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_OC_UNDEFINED_OPTIONAL_ATTR =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 219;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * add a new objectclass type with a name or OID that conflicts with an
+ * existing objectclass. This takes two arguments, which are the name of the
+ * new objectclass and a message explaining the problem that occurred.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_OBJECTCLASS_ALREADY_EXISTS =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 220;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * attempting to read the contents of an existing schema file so that it may
+ * be updated. This takes two arguments, which are the path to the schema
+ * file and a string representation of the exception that was caught.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_CANNOT_READ_EXISTING_USER_SCHEMA =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 221;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to write an updated schema file. This takes two arguments, which
+ * are the path to the schema file and a string representation of the
+ * exception that was caught.
+ */
+ public static final int MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA =
+ CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 222;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -2476,6 +2606,49 @@
"backend. If you wish to alter the contents of the base " +
"schema entry itself, then it may be possible to do so " +
"by modifying the \"%s\" entry in the configuration.");
+ registerMessage(MSGID_SCHEMA_DELETE_MODTYPE_NOT_SUPPORTED,
+ "The schema backend does not currently support removing " +
+ "existing schema elements.");
+ // FIXME -- Change the below message once we support removing schema
+ // elements.
+ registerMessage(MSGID_SCHEMA_INVALID_MODIFICATION_TYPE,
+ "The schema backend does not support the %s modification " +
+ "type. It is currently only possible to add new schema " +
+ "elements.");
+ registerMessage(MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE,
+ "The schema backend does not support the modification of " +
+ "the %s attribute type. Only attribute types, object " +
+ "classes, name forms, DIT content rules, DIT structure " +
+ "rules, and matching rule uses may be modified.");
+ registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE,
+ "An error occurred while attempting to decode the " +
+ "attribute type \"%s\": %s.");
+ registerMessage(MSGID_SCHEMA_MODIFY_ATTRTYPE_ALREADY_EXISTS,
+ "Unable to add attribute type %s to the server schema " +
+ "because there is an existing attribute type with a " +
+ "conflicting name or OID: %s.");
+ registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS,
+ "An error occurred while attempting to decode the object " +
+ "class \"%s\": %s.");
+ registerMessage(MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS,
+ "Unable to add objectclass %s because its superior " +
+ "class of %s is not defined in the server schema.");
+ registerMessage(MSGID_SCHEMA_MODIFY_OC_UNDEFINED_REQUIRED_ATTR,
+ "Unable to add objectclass %s because it requires " +
+ "attribute %s which is not defined in the server schema.");
+ registerMessage(MSGID_SCHEMA_MODIFY_OC_UNDEFINED_OPTIONAL_ATTR,
+ "Unable to add objectclass %s because it allows " +
+ "attribute %s which is not defined in the server schema.");
+ registerMessage(MSGID_SCHEMA_MODIFY_OBJECTCLASS_ALREADY_EXISTS,
+ "Unable to add objectclass %s to the server schema " +
+ "because there is an existing objectclass with a " +
+ "conflicting name or OID: %s");
+ registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_READ_EXISTING_USER_SCHEMA,
+ "An error occurred while attempting to read the contents " +
+ "of schema file %s: %s.");
+ registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA,
+ "An error occurred while attepting to write the updated " +
+ "schema file %s: %s.");
registerMessage(MSGID_SCHEMA_MODIFY_DN_NOT_SUPPORTED,
"Unwilling to rename entry \"%s\" because modify DN " +
"operations are not supported in the schema backend.");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/SchemaMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/SchemaMessages.java
index 47e7233..683722e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/SchemaMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/SchemaMessages.java
@@ -3334,8 +3334,8 @@
"The definition for the objectclass with OID %s declared " +
"a superior objectclass with an OID of %s. No " +
"objectclass with this OID exists in the server schema, " +
- "so the Directory Server will use the top objectclass as " +
- "the superior class for this definition.");
+ "so the Directory Server will assume it to be an empty " +
+ "objectclass with no required or optional attributes.");
registerMessage(MSGID_ATTR_SYNTAX_OBJECTCLASS_EXPECTED_QUOTE_AT_POS,
"The provided value \"%s\" could not be parsed as an " +
"objectclass description because a single quote was " +
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
index 0c28e66..d0ee261 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
@@ -253,6 +253,27 @@
/**
+ * Indicates whether this schema definition includes an attribute
+ * type with the provided name or OID.
+ *
+ * @param lowerName The name or OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains an attribute type
+ * with the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasAttributeType(String lowerName)
+ {
+ assert debugEnter(CLASS_NAME, "hasAttributeType",
+ String.valueOf(lowerName));
+
+ return attributeTypes.containsKey(lowerName);
+ }
+
+
+
+ /**
* Retrieves the attribute type definition with the specified name
* or OID.
*
@@ -425,6 +446,27 @@
/**
+ * Indicates whether this schema definition includes an objectclass
+ * with the provided name or OID.
+ *
+ * @param lowerName The name or OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains an objectclass with
+ * the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasObjectClass(String lowerName)
+ {
+ assert debugEnter(CLASS_NAME, "hasObjectClass",
+ String.valueOf(lowerName));
+
+ return objectClasses.containsKey(lowerName);
+ }
+
+
+
+ /**
* Retrieves the objectclass definition with the specified name or
* OID.
*
@@ -593,6 +635,27 @@
/**
+ * Indicates whether this schema definition includes an attribute
+ * syntax with the provided name or OID.
+ *
+ * @param lowerName The name or OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains an attribute syntax
+ * with the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasSyntax(String lowerName)
+ {
+ assert debugEnter(CLASS_NAME, "hasSyntax",
+ String.valueOf(lowerName));
+
+ return syntaxes.containsKey(lowerName);
+ }
+
+
+
+ /**
* Retrieves the attribute syntax definition with the OID.
*
* @param lowerName The OID of the attribute syntax to retrieve,
@@ -735,6 +798,27 @@
/**
+ * Indicates whether this schema definition includes a matching rule
+ * with the provided name or OID.
+ *
+ * @param lowerName The name or OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains a matching rule
+ * with the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasMatchingRule(String lowerName)
+ {
+ assert debugEnter(CLASS_NAME, "hasMatchingRule",
+ String.valueOf(lowerName));
+
+ return matchingRules.containsKey(lowerName);
+ }
+
+
+
+ /**
* Retrieves the matching rule definition with the specified name or
* OID.
*
@@ -1645,6 +1729,26 @@
/**
+ * Indicates whether this schema definition includes a matching rule
+ * use for the provided matching rule.
+ *
+ * @param matchingRule The matching rule for which to make the
+ * determination.
+ *
+ * @return {@code true} if this schema contains a matching rule use
+ * for the provided matching rule, or {@code false} if not.
+ */
+ public boolean hasMatchingRuleUse(MatchingRule matchingRule)
+ {
+ assert debugEnter(CLASS_NAME, "hasMatchingRuleUse",
+ String.valueOf(matchingRule));
+
+ return matchingRuleUses.containsKey(matchingRule);
+ }
+
+
+
+ /**
* Retrieves the matching rule use definition for the specified
* matching rule.
*
@@ -1794,6 +1898,26 @@
/**
+ * Indicates whether this schema definition includes a DIT content
+ * rule for the provided objectclass.
+ *
+ * @param objectClass The objectclass for which to make the
+ * determination.
+ *
+ * @return {@code true} if this schema contains a DIT content rule
+ * for the provided objectclass, or {@code false} if not.
+ */
+ public boolean hasDITContentRule(ObjectClass objectClass)
+ {
+ assert debugEnter(CLASS_NAME, "hasDITContentRule",
+ String.valueOf(objectClass));
+
+ return ditContentRules.containsKey(objectClass);
+ }
+
+
+
+ /**
* Retrieves the DIT content rule definition for the specified
* objectclass.
*
@@ -1961,6 +2085,46 @@
/**
+ * Indicates whether this schema definition includes a DIT structure
+ * rule with the provided rule ID.
+ *
+ * @param ruleID The rule ID for which to make the determination.
+ *
+ * @return {@code true} if this schema contains a DIT structure
+ * rule with the provided rule ID, or {@code false} if not.
+ */
+ public boolean hasDITStructureRule(int ruleID)
+ {
+ assert debugEnter(CLASS_NAME, "hasDITStructureRule",
+ String.valueOf(ruleID));
+
+ return ditStructureRulesByID.containsKey(ruleID);
+ }
+
+
+
+ /**
+ * Indicates whether this schema definition includes a DIT structure
+ * rule for the provided name form.
+ *
+ * @param nameForm The name form for which to make the
+ * determination.
+ *
+ * @return {@code true} if this schema contains a DIT structure
+ * rule for the provided name form, or {@code false} if
+ * not.
+ */
+ public boolean hasDITStructureRule(NameForm nameForm)
+ {
+ assert debugEnter(CLASS_NAME, "hasDITStructureRule",
+ String.valueOf(nameForm));
+
+ return ditStructureRulesByNameForm.containsKey(nameForm);
+ }
+
+
+
+ /**
* Retrieves the DIT structure rule definition with the provided
* rule ID.
*
@@ -2169,6 +2333,47 @@
/**
+ * Indicates whether this schema definition includes a name form for
+ * the specified objectclass.
+ *
+ * @param objectClass The objectclass for which to make the
+ * determination.
+ *
+ * @return {@code true} if this schema contains a name form for the
+ * provided objectclass, or {@code false} if not.
+ */
+ public boolean hasNameForm(ObjectClass objectClass)
+ {
+ assert debugEnter(CLASS_NAME, "hasNameForm",
+ String.valueOf(objectClass));
+
+ return nameFormsByOC.containsKey(objectClass);
+ }
+
+
+
+ /**
+ * Indicates whether this schema definition includes a name form
+ * with the specified name or OID.
+ *
+ * @param lowerName The name or OID for which to make the
+ * determination, formatted in all lowercase
+ * characters.
+ *
+ * @return {@code true} if this schema contains a name form with
+ * the provided name or OID, or {@code false} if not.
+ */
+ public boolean hasNameForm(String lowerName)
+ {
+ assert debugEnter(CLASS_NAME, "hasNameForm",
+ String.valueOf(lowerName));
+
+ return nameFormsByName.containsKey(lowerName);
+ }
+
+
+
+ /**
* Retrieves the name form definition for the specified objectclass.
*
* @param objectClass The objectclass for the name form to
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
new file mode 100644
index 0000000..67c1e2f
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
@@ -0,0 +1,748 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.backends;
+
+
+
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.tools.LDAPModify;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteStringFactory;
+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.RDN;
+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 test cases for the schema backend.
+ */
+public class SchemaBackendTestCase
+ extends BackendTestCase
+{
+ // A reference to the schema backend.
+ private SchemaBackend schemaBackend;
+
+
+
+ /**
+ * Ensures that the Directory Server is running and gets a reference to the
+ * schema backend.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ schemaBackend = (SchemaBackend) DirectoryServer.getBackend("schema");
+ assertNotNull(schemaBackend);
+ }
+
+
+
+ /**
+ * Tests the {@code isLocal} method to ensure that it is considered local.
+ */
+ @Test()
+ public void testIsLocal()
+ {
+ assertTrue(schemaBackend.isLocal());
+ }
+
+
+
+ /**
+ * Tests the {@code getEntry} method to ensure that it is able to retrieve
+ * the schema entry if it is given a valid entry DN.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetValidEntry()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=schema");
+ Entry schemaEntry = schemaBackend.getEntry(schemaDN);
+ assertNotNull(schemaEntry);
+ assertEquals(schemaEntry.getDN(), schemaDN);
+
+ AttributeType t = DirectoryServer.getAttributeType("attributetypes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("objectclasses");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("ldapsyntaxes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("matchingrules");
+ assertTrue(schemaEntry.hasAttribute(t));
+ }
+
+
+
+ /**
+ * Tests the {@code getEntry} method to ensure that it is not able to retrieve
+ * anything when given an inappropriate DN.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetInvalidEntry()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=notschema");
+ Entry schemaEntry = schemaBackend.getEntry(schemaDN);
+ assertNull(schemaEntry);
+
+ schemaDN = DN.decode("cn=child,cn=schema");
+ schemaEntry = schemaBackend.getEntry(schemaDN);
+ assertNull(schemaEntry);
+ }
+
+
+
+ /**
+ * Tests the {@code getSchemaEntry} method to ensure that it is able to
+ * retrieve the appropriate information with different DNs.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetSchemaEntry()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=schema");
+ Entry schemaEntry = schemaBackend.getSchemaEntry(schemaDN);
+ assertNotNull(schemaEntry);
+ assertEquals(schemaEntry.getDN(), schemaDN);
+
+ AttributeType t = DirectoryServer.getAttributeType("attributetypes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("objectclasses");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("ldapsyntaxes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("matchingrules");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+
+ schemaDN = DN.decode("cn=subschema");
+ schemaEntry = schemaBackend.getSchemaEntry(schemaDN);
+ assertNotNull(schemaEntry);
+ assertEquals(schemaEntry.getDN(), schemaDN);
+
+ t = DirectoryServer.getAttributeType("attributetypes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("objectclasses");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("ldapsyntaxes");
+ assertTrue(schemaEntry.hasAttribute(t));
+
+ t = DirectoryServer.getAttributeType("matchingrules");
+ assertTrue(schemaEntry.hasAttribute(t));
+ }
+
+
+
+ /**
+ * Tests the {@code entryExists} method with a valid schema DN.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testEntryExistsValidDN()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=schema");
+ assertTrue(schemaBackend.entryExists(schemaDN));
+ }
+
+
+
+ /**
+ * Tests the {@code entryExists} method with an invalid schema DN.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testEntryExistsInvalidDN()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=notschema");
+ assertFalse(schemaBackend.entryExists(schemaDN));
+ }
+
+
+
+ /**
+ * Tests to ensure that the {@code addEntry} method always throws an
+ * exception.
+ */
+ @Test(expectedExceptions = { DirectoryException.class })
+ public void testAddEntry()
+ throws Exception
+ {
+ Entry entry = createEntry(DN.decode("cn=schema"));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ AddOperation addOperation =
+ new AddOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ null, entry.getDN(), entry.getObjectClasses(),
+ entry.getUserAttributes(),
+ entry.getOperationalAttributes());
+
+ schemaBackend.addEntry(entry, addOperation);
+ }
+
+
+
+ /**
+ * Tests to ensure that the {@code deleteEntry} method always throws an
+ * exception.
+ */
+ @Test(expectedExceptions = { DirectoryException.class })
+ public void testDeleteEntry()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=schema");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+ null, schemaDN);
+
+ schemaBackend.deleteEntry(schemaDN, deleteOperation);
+ }
+
+
+
+ /**
+ * Tests to ensure that the {@code renameEntry} method always throws an
+ * exception.
+ */
+ @Test(expectedExceptions = { DirectoryException.class })
+ public void testRenameEntry()
+ throws Exception
+ {
+ DN currentSchemaDN = DN.decode("cn=schema");
+ DN newSchemaDN = DN.decode("cn=newschema");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ ModifyDNOperation modifyDNOperation =
+ new ModifyDNOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null,
+ currentSchemaDN, newSchemaDN.getRDN(),
+ true, null);
+
+ schemaBackend.renameEntry(currentSchemaDN,
+ schemaBackend.getSchemaEntry(newSchemaDN),
+ modifyDNOperation);
+ }
+
+
+
+ /**
+ * Performs a simple base-level search to verify that the schema entry is
+ * returned.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSimpleBaseSearch()
+ throws Exception
+ {
+ String filterString = "(|(objectClass=*)(objectClass=ldapSubentry))";
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(DN.decode("cn=schema"), SearchScope.BASE_OBJECT,
+ SearchFilter.createFilterFromString(filterString));
+ assertNotNull(searchOperation);
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ assertFalse(searchOperation.getSearchEntries().isEmpty());
+ }
+
+
+
+ /**
+ * Performs a simple single-level search to verify that nothing is returned.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSimpleOneLevelSearch()
+ throws Exception
+ {
+ String filterString = "(|(objectClass=*)(objectClass=ldapSubentry))";
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(DN.decode("cn=schema"), SearchScope.SINGLE_LEVEL,
+ SearchFilter.createFilterFromString(filterString));
+ assertNotNull(searchOperation);
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(searchOperation.getSearchEntries().isEmpty());
+ }
+
+
+
+ /**
+ * Performs a simple subtree search to verify that the schema entry is
+ * returned.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSimpleSubtreeSearch()
+ throws Exception
+ {
+ String filterString = "(|(objectClass=*)(objectClass=ldapSubentry))";
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(DN.decode("cn=schema"), SearchScope.WHOLE_SUBTREE,
+ SearchFilter.createFilterFromString(filterString));
+ assertNotNull(searchOperation);
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ assertFalse(searchOperation.getSearchEntries().isEmpty());
+ }
+
+
+
+ /**
+ * Performs a simple subordinate subtree search to verify that nothing is
+ * returned.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSimpleSubordinateSubtreeSearch()
+ throws Exception
+ {
+ String filterString = "(|(objectClass=*)(objectClass=ldapSubentry))";
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(DN.decode("cn=schema"),
+ SearchScope.SUBORDINATE_SUBTREE,
+ SearchFilter.createFilterFromString(filterString));
+ assertNotNull(searchOperation);
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ assertTrue(searchOperation.getSearchEntries().isEmpty());
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend with regard to the
+ * ds-cfg-show-all-attributes configuration.
+ *
+ * @throws Exception If a problem occurs.
+ */
+ @Test()
+ public void testTreatAsUserAttrs()
+ throws Exception
+ {
+ DN schemaDN = DN.decode("cn=schema");
+ AttributeType a = DirectoryServer.getAttributeType("attributetypes");
+ AttributeType o = DirectoryServer.getAttributeType("objectclasses");
+ AttributeType m = DirectoryServer.getAttributeType("matchingrules");
+ AttributeType s = DirectoryServer.getAttributeType("ldapsyntaxes");
+
+ assertFalse(schemaBackend.showAllAttributes());
+ Entry schemaEntry = schemaBackend.getSchemaEntry(schemaDN);
+ assertTrue(schemaEntry.hasOperationalAttribute(a));
+ assertTrue(schemaEntry.hasOperationalAttribute(o));
+ assertTrue(schemaEntry.hasOperationalAttribute(m));
+ assertTrue(schemaEntry.hasOperationalAttribute(s));
+
+ schemaBackend.setShowAllAttributes(true);
+ assertTrue(schemaBackend.showAllAttributes());
+ schemaEntry = schemaBackend.getSchemaEntry(schemaDN);
+ assertFalse(schemaEntry.hasOperationalAttribute(a));
+ assertFalse(schemaEntry.hasOperationalAttribute(o));
+ assertFalse(schemaEntry.hasOperationalAttribute(m));
+ assertTrue(schemaEntry.hasOperationalAttribute(s));
+
+ schemaBackend.setShowAllAttributes(false);
+ assertFalse(schemaBackend.showAllAttributes());
+ schemaEntry = schemaBackend.getSchemaEntry(schemaDN);
+ assertTrue(schemaEntry.hasOperationalAttribute(a));
+ assertTrue(schemaEntry.hasOperationalAttribute(o));
+ assertTrue(schemaEntry.hasOperationalAttribute(m));
+ assertTrue(schemaEntry.hasOperationalAttribute(s));
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * attribute type with a valid syntax and that isn't already defined.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddAttributeTypeSuccessful()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: attributeTypes",
+ "attributeTypes: ( 1.3.6.1.4.1.26027.1.999.4 NAME " +
+ "'testAddAttributeTypeSuccessful' SYNTAX " +
+ "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
+ "'SchemaBackendTestCase' )");
+
+ String attrName = "testaddattributetypesuccessful";
+ assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+ assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * attribute type with a valid syntax (but using a textual OID rather than
+ * numeric) and that isn't already defined.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddAttributeTypeSuccessfulNoOID()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: attributeTypes",
+ "attributeTypes: ( testaddattributetypesuccessfulnooid-oid NAME " +
+ "'testAddAttributeTypeSuccessfulNoOID' SYNTAX " +
+ "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
+ "'SchemaBackendTestCase' )");
+
+ String attrName = "testaddattributetypesuccessfulnooid";
+ assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+ assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * attribute type definition that can't be parsed.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddAttributeTypeInvalidSyntax()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: attributeTypes",
+ "attributeTypes: invalidsyntax");
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass that doesn't already exist, that has a valid superior class,
+ * and for which all attributes contained in it are already defined.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassSuccessful()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: ( 1.3.6.1.4.1.26027.1.999.5 NAME " +
+ "'testAddObjectClassSuccessful' SUP top STRUCTURAL MUST cn " +
+ "X-ORIGIN 'SchemaBackendTestCase' )");
+
+ String ocName = "testaddobjectclasssuccessful";
+ assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+ assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass that doesn't already exist, that has a textual OID rather than
+ * numeric, has a valid superior class, and for which all attributes contained
+ * in it are already defined.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassSuccessfulNoOID()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: ( testaddobjectclasssuccessfulnooid-oid NAME " +
+ "'testAddObjectClassSuccessfulNoOID' SUP top STRUCTURAL " +
+ "MUST cn X-ORIGIN 'SchemaBackendTestCase' )");
+
+ String ocName = "testaddobjectclasssuccessfulnooid";
+ assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+ assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass definition that can't be parsed.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassInvalidSyntax()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: invalidsyntax");
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass that references an undefined superior class.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassUndefinedSuperiorClass()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: ( testaddocundefinedsuperior-oid NAME " +
+ "'testAddOCUndefinedSuperior' SUP undefined STRUCTURAL " +
+ "MUST cn X-ORIGIN 'SchemaBackendTestCase' )");
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass that references an undefined required attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassUndefinedRequiredAttribute()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: ( testaddocundefinedrequired-oid NAME " +
+ "'testAddOCUndefinedRequired' SUP top STRUCTURAL " +
+ "MUST undefined X-ORIGIN 'SchemaBackendTestCase' )");
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+ }
+
+
+
+ /**
+ * Tests the behavior of the schema backend when attempting to add a new
+ * objectclass that references an undefined optional attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testAddObjectClassUndefinedOptionalAttribute()
+ throws Exception
+ {
+ String path = TestCaseUtils.createTempFile(
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: objectClasses",
+ "objectClasses: ( testaddocundefinedoptional-oid NAME " +
+ "'testAddOCUndefinedOptional' SUP top STRUCTURAL " +
+ "MAY undefined X-ORIGIN 'SchemaBackendTestCase' )");
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-f", path
+ };
+
+ assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+ }
+}
+
--
Gitblit v1.10.0