opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -74,6 +74,7 @@ import org.opends.server.schema.AttributeTypeSyntax; import org.opends.server.schema.DITContentRuleSyntax; import org.opends.server.schema.DITStructureRuleSyntax; import org.opends.server.schema.GeneralizedTimeSyntax; import org.opends.server.schema.MatchingRuleUseSyntax; import org.opends.server.schema.NameFormSyntax; import org.opends.server.schema.ObjectClassSyntax; @@ -148,6 +149,12 @@ // types. private AttributeType attributeTypesType; // The attribute type that will be used to hold the schema creation timestamp. private AttributeType createTimestampType; // The attribute type that will be used to hold the schema creator's name. private AttributeType creatorsNameType; // The attribute type that will be used to include the defined DIT content // rules. private AttributeType ditContentRulesType; @@ -167,25 +174,44 @@ // uses. private AttributeType matchingRuleUsesType; // The attribute that will be used to hold the schema modifier's name. private AttributeType modifiersNameType; // The attribute type that will be used to hold the schema modification // timestamp. private AttributeType modifyTimestampType; // The attribute type that will be used to include the defined object classes. private AttributeType objectClassesType; // The attribute type that will be used to include the defined name forms. private AttributeType nameFormsType; // The value containing DN of the user we'll say created the configuration. private AttributeValue creatorsName; // The value containing the DN of the last user to modify the configuration. private AttributeValue modifiersName; // The timestamp that will be used for the schema creation time. private AttributeValue createTimestamp; // The timestamp that will be used for the latest schema modification time. private AttributeValue modifyTimestamp; // Indicates whether the attributes of the schema entry should always be // treated as user attributes even if they are defined as operational. private boolean showAllAttributes; // The set of objectclasses that will be used in the schema entry. private HashMap<ObjectClass,String> schemaObjectClasses; // The DN of the configuration entry for this backend. private DN configEntryDN; // The set of base DNs for this backend. private DN[] baseDNs; // The set of objectclasses that will be used in the schema entry. private HashMap<ObjectClass,String> schemaObjectClasses; // The set of supported controls for this backend. private HashSet<String> supportedControls; @@ -264,6 +290,29 @@ nameFormsType = DirectoryServer.getAttributeType(ATTR_NAME_FORMS_LC, true); // Initialize the lastmod attributes. creatorsNameType = DirectoryServer.getAttributeType(OP_ATTR_CREATORS_NAME_LC, true); createTimestampType = DirectoryServer.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true); modifiersNameType = DirectoryServer.getAttributeType(OP_ATTR_MODIFIERS_NAME_LC, true); modifyTimestampType = DirectoryServer.getAttributeType(OP_ATTR_MODIFY_TIMESTAMP_LC, true); creatorsName = new AttributeValue(creatorsNameType, baseDNs[0].toString()); modifiersName = new AttributeValue(modifiersNameType, baseDNs[0].toString()); long createTime = DirectoryServer.getSchema().getOldestModificationTime(); createTimestamp = GeneralizedTimeSyntax.createGeneralizedTimeValue(createTime); long modifyTime = DirectoryServer.getSchema().getYoungestModificationTime(); modifyTimestamp = GeneralizedTimeSyntax.createGeneralizedTimeValue(modifyTime); // Get the set of user-defined attributes for the configuration entry. Any // attributes that we don't recognize will be included directly in the // schema entry. @@ -418,7 +467,11 @@ attrType.hasName(ATTR_BACKEND_BASE_DN.toLowerCase()) || attrType.hasName(ATTR_BACKEND_WRITABILITY_MODE.toLowerCase()) || attrType.hasName(ATTR_SCHEMA_SHOW_ALL_ATTRIBUTES.toLowerCase()) || attrType.hasName(ATTR_COMMON_NAME)) attrType.hasName(ATTR_COMMON_NAME) || attrType.hasName(OP_ATTR_CREATORS_NAME_LC) || attrType.hasName(OP_ATTR_CREATE_TIMESTAMP_LC) || attrType.hasName(OP_ATTR_MODIFIERS_NAME_LC) || attrType.hasName(OP_ATTR_MODIFY_TIMESTAMP_LC)) { return true; } @@ -700,6 +753,36 @@ } // Add the lastmod attributes. valueSet = new LinkedHashSet<AttributeValue>(1); valueSet.add(creatorsName); attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(creatorsNameType, OP_ATTR_CREATORS_NAME, valueSet)); operationalAttrs.put(creatorsNameType, attrList); valueSet = new LinkedHashSet<AttributeValue>(1); valueSet.add(createTimestamp); attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(createTimestampType, OP_ATTR_CREATE_TIMESTAMP, valueSet)); operationalAttrs.put(createTimestampType, attrList); valueSet = new LinkedHashSet<AttributeValue>(1); valueSet.add(modifiersName); attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(modifiersNameType, OP_ATTR_MODIFIERS_NAME, valueSet)); operationalAttrs.put(modifiersNameType, attrList); valueSet = new LinkedHashSet<AttributeValue>(1); valueSet.add(modifyTimestamp); attrList = new ArrayList<Attribute>(1); attrList.add(new Attribute(modifyTimestampType, OP_ATTR_MODIFY_TIMESTAMP, valueSet)); operationalAttrs.put(modifyTimestampType, attrList); // Add all the user-defined attributes. for (Attribute a : userDefinedAttributes) { @@ -1279,6 +1362,16 @@ { cleanUpTempSchemaFiles(tempSchemaFiles); } DN authzDN = modifyOperation.getAuthorizationDN(); if (authzDN == null) { authzDN = DN.nullDN(); } modifiersName = new AttributeValue(modifiersNameType, authzDN.toString()); modifyTimestamp = GeneralizedTimeSyntax.createGeneralizedTimeValue( System.currentTimeMillis()); } opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -646,8 +646,10 @@ // Construct the path to the directory that should contain the schema files // and make sure that it exists and is a directory. Get a list of the files // in that directory sorted in alphabetic order. String schemaDirPath = getSchemaDirectoryPath(); File schemaDir = new File(schemaDirPath); String schemaDirPath = getSchemaDirectoryPath(); File schemaDir = new File(schemaDirPath); long oldestModificationTime = -1L; long youngestModificationTime = -1L; String[] fileNames; try @@ -673,6 +675,19 @@ { fileList.add(f.getAbsolutePath()); } long modificationTime = f.lastModified(); if ((oldestModificationTime <= 0L) || (modificationTime < oldestModificationTime)) { oldestModificationTime = modificationTime; } if ((youngestModificationTime <= 0) || (modificationTime > youngestModificationTime)) { youngestModificationTime = modificationTime; } } fileNames = new String[fileList.size()]; @@ -696,6 +711,22 @@ } // If the oldest and youngest modification timestamps didn't get set for // some reason, then set them to the current time. if (oldestModificationTime <= 0) { oldestModificationTime = System.currentTimeMillis(); } if (youngestModificationTime <= 0) { youngestModificationTime = oldestModificationTime; } schema.setOldestModificationTime(oldestModificationTime); schema.setYoungestModificationTime(youngestModificationTime); // Iterate through the schema files and read them as an LDIF file containing // a single entry. Then get the attributeTypes and objectClasses attributes // from that entry and parse them to initialize the server schema. opends/src/server/org/opends/server/types/Schema.java
@@ -172,6 +172,14 @@ // The set of pre-encoded objectclass representations. private LinkedHashSet<AttributeValue> objectClassSet; // The oldest modification timestamp for any schema configuration // file. private long oldestModificationTime; // The youngest modification timestamp for any schema configuration // file. private long youngestModificationTime; /** @@ -214,6 +222,9 @@ matchingRuleUseSet = new LinkedHashSet<AttributeValue>(); nameFormSet = new LinkedHashSet<AttributeValue>(); objectClassSet = new LinkedHashSet<AttributeValue>(); oldestModificationTime = System.currentTimeMillis(); youngestModificationTime = oldestModificationTime; } @@ -2542,6 +2553,77 @@ /** * Retrieves the modification timestamp for the file in the schema * configuration directory with the oldest last modified time. * * @return The modification timestamp for the file in the schema * configuration directory with the oldest last modified * time. */ public long getOldestModificationTime() { assert debugEnter(CLASS_NAME, "getOldestModificationTime"); return oldestModificationTime; } /** * Sets the modification timestamp for the oldest file in the schema * configuration directory. * * @param oldestModificationTime The modification timestamp for * the oldest file in the schema * configuration directory. */ public void setOldestModificationTime(long oldestModificationTime) { assert debugEnter(CLASS_NAME, "setOldestModificationTime", String.valueOf(oldestModificationTime)); this.oldestModificationTime = oldestModificationTime; } /** * Retrieves the modification timestamp for the file in the schema * configuration directory with the youngest last modified time. * * @return The modification timestamp for the file in the schema * configuration directory with the youngest last modified * time. */ public long getYoungestModificationTime() { assert debugEnter(CLASS_NAME, "getYoungestModificationTime"); return youngestModificationTime; } /** * Sets the modification timestamp for the youngest file in the * schema configuration directory. * * @param youngestModificationTime The modification timestamp for * the youngest file in the schema * configuration directory. */ public void setYoungestModificationTime( long youngestModificationTime) { assert debugEnter(CLASS_NAME, "setYoungestModificationTime", String.valueOf(youngestModificationTime)); this.youngestModificationTime = youngestModificationTime; } /** * Recursively rebuilds all schema elements that are dependent upon * the provided element. This must be invoked whenever an existing * schema element is modified in order to ensure that any elements @@ -2817,6 +2899,8 @@ dupSchema.matchingRuleUseSet.addAll(matchingRuleUseSet); dupSchema.nameFormSet.addAll(nameFormSet); dupSchema.objectClassSet.addAll(objectClassSet); dupSchema.oldestModificationTime = oldestModificationTime; dupSchema.youngestModificationTime = youngestModificationTime; return dupSchema; } opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
@@ -3674,6 +3674,71 @@ /** * Tests to ensure that the schema subentry includes the lastmod attributes * and that the modifiersName and modifyTimestamp attributes get updated when * the schema is modified. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testLastModAttributes() throws Exception { Entry schemaEntry = DirectoryServer.getEntry(DN.decode("cn=schema")); assertNotNull(schemaEntry); AttributeType cnType = DirectoryServer.getAttributeType("creatorsname", true); AttributeType ctType = DirectoryServer.getAttributeType("createtimestamp", true); AttributeType mnType = DirectoryServer.getAttributeType("modifiersname", true); AttributeType mtType = DirectoryServer.getAttributeType("modifytimestamp", true); assertTrue(schemaEntry.hasAttribute(cnType)); assertTrue(schemaEntry.hasAttribute(ctType)); assertTrue(schemaEntry.hasAttribute(mnType)); assertTrue(schemaEntry.hasAttribute(mtType)); AttributeValue oldMTValue = schemaEntry.getAttribute(mtType).get(0).getValues().iterator().next(); String path = TestCaseUtils.createTempFile( "dn: cn=schema", "changetype: modify", "add: attributeTypes", "attributeTypes: ( testlastmodattributes-oid " + "NAME 'testLastModAttributes' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " + "X-ORGIN 'SchemaBackendTestCase' )"); 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); schemaEntry = DirectoryServer.getEntry(DN.decode("cn=schema")); assertNotNull(schemaEntry); assertTrue(schemaEntry.hasAttribute(cnType)); assertTrue(schemaEntry.hasAttribute(ctType)); assertTrue(schemaEntry.hasAttribute(mnType)); assertTrue(schemaEntry.hasAttribute(mtType)); AttributeValue newMTValue = schemaEntry.getAttribute(mtType).get(0).getValues().iterator().next(); assertFalse(oldMTValue.equals(newMTValue)); } /** * Tests the {@code exportLDIF} method with a valid configuration. * * @throws Exception If an unexpected problem occurs.