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)); // 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; } } 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. */ 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 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."); 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 " + 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 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
New file @@ -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); } }