OPENDJ-3089 OPENDJ-1237 Move code from Schema and SchemaBackend to SchemaUtils and new class SchemaWriter
- create a new class SchemaWriter to handle writing of schema files (move
code from SchemaBackend and Schema classes)
- move static code from SchemaHandler to SchemaUtils
1 files added
6 files modified
| | |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.ListIterator; |
| | | import java.util.Map; |
| | |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.Schema; |
| | | import org.opends.server.types.SchemaWriter; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.util.BackupManager; |
| | | import org.opends.server.util.BuildVersion; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.LDIFReader; |
| | | import org.opends.server.util.LDIFWriter; |
| | | import org.opends.server.util.SchemaUtils; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | updateConcatenatedSchema(); |
| | | SchemaWriter.updateConcatenatedSchema(); |
| | | |
| | | // Register with the Directory Server as a configurable component. |
| | | currentConfig.addSchemaChangeListener(this); |
| | | } |
| | | |
| | | /** |
| | | * Updates the concatenated schema if changes are detected in the current schema files. |
| | | * <p> |
| | | * Identify any differences that may exist between the concatenated schema file from the last |
| | | * online modification and the current schema files. If there are any differences, then they |
| | | * should be from making changes to the schema files with the server offline. |
| | | */ |
| | | private void updateConcatenatedSchema() throws InitializationException |
| | | { |
| | | try |
| | | { |
| | | // First, generate lists of elements from the current schema. |
| | | Set<String> newATs = new LinkedHashSet<>(); |
| | | Set<String> newOCs = new LinkedHashSet<>(); |
| | | Set<String> newNFs = new LinkedHashSet<>(); |
| | | Set<String> newDCRs = new LinkedHashSet<>(); |
| | | Set<String> newDSRs = new LinkedHashSet<>(); |
| | | Set<String> newMRUs = new LinkedHashSet<>(); |
| | | Set<String> newLSs = new LinkedHashSet<>(); |
| | | Schema.genConcatenatedSchema(newATs, newOCs, newNFs, newDCRs, newDSRs, newMRUs, newLSs); |
| | | |
| | | // Next, generate lists of elements from the previous concatenated schema. |
| | | // If there isn't a previous concatenated schema, then use the base |
| | | // schema for the current revision. |
| | | File concatFile = getConcatFile(); |
| | | |
| | | Set<String> oldATs = new LinkedHashSet<>(); |
| | | Set<String> oldOCs = new LinkedHashSet<>(); |
| | | Set<String> oldNFs = new LinkedHashSet<>(); |
| | | Set<String> oldDCRs = new LinkedHashSet<>(); |
| | | Set<String> oldDSRs = new LinkedHashSet<>(); |
| | | Set<String> oldMRUs = new LinkedHashSet<>(); |
| | | Set<String> oldLSs = new LinkedHashSet<>(); |
| | | Schema.readConcatenatedSchema(concatFile, oldATs, oldOCs, oldNFs, |
| | | oldDCRs, oldDSRs, oldMRUs,oldLSs); |
| | | |
| | | // Create a list of modifications and add any differences between the old |
| | | // and new schema into them. |
| | | List<Modification> mods = new LinkedList<>(); |
| | | Schema.compareConcatenatedSchema(oldATs, newATs, attributeTypesType, mods); |
| | | Schema.compareConcatenatedSchema(oldOCs, newOCs, objectClassesType, mods); |
| | | Schema.compareConcatenatedSchema(oldNFs, newNFs, nameFormsType, mods); |
| | | Schema.compareConcatenatedSchema(oldDCRs, newDCRs, ditContentRulesType, mods); |
| | | Schema.compareConcatenatedSchema(oldDSRs, newDSRs, ditStructureRulesType, mods); |
| | | Schema.compareConcatenatedSchema(oldMRUs, newMRUs, matchingRuleUsesType, mods); |
| | | Schema.compareConcatenatedSchema(oldLSs, newLSs, ldapSyntaxesType, mods); |
| | | if (! mods.isEmpty()) |
| | | { |
| | | // TODO : Raise an alert notification. |
| | | |
| | | DirectoryServer.setOfflineSchemaChanges(mods); |
| | | |
| | | // Write a new concatenated schema file with the most recent information |
| | | // so we don't re-find these same changes on the next startup. |
| | | Schema.writeConcatenatedSchema(); |
| | | } |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | throw ie; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | logger.error(ERR_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES, getExceptionMessage(e)); |
| | | } |
| | | } |
| | | |
| | | private File getConcatFile() throws InitializationException |
| | | { |
| | | File configDirectory = new File(DirectoryServer.getConfigFile()).getParentFile(); |
| | | File upgradeDirectory = new File(configDirectory, "upgrade"); |
| | | File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | if (concatFile.exists()) |
| | | { |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | |
| | | String fileName = SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION + BuildVersion.instanceVersion().getRevision(); |
| | | concatFile = new File(upgradeDirectory, fileName); |
| | | if (concatFile.exists()) |
| | | { |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | |
| | | String runningUnitTestsStr = System.getProperty(PROPERTY_RUNNING_UNIT_TESTS); |
| | | if ("true".equalsIgnoreCase(runningUnitTestsStr)) |
| | | { |
| | | Schema.writeConcatenatedSchema(); |
| | | concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | throw new InitializationException(ERR_SCHEMA_CANNOT_FIND_CONCAT_FILE.get( |
| | | upgradeDirectory.getAbsolutePath(), SCHEMA_CONCAT_FILE_NAME, concatFile.getName())); |
| | | } |
| | | |
| | | @Override |
| | | public void closeBackend() |
| | | { |
| | |
| | | // Create a single file with all of the concatenated schema information |
| | | // that we can use on startup to detect whether the schema files have been |
| | | // edited with the server offline. |
| | | Schema.writeConcatenatedSchema(); |
| | | SchemaWriter.writeConcatenatedSchema(); |
| | | } |
| | | |
| | | /** |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseAttributeTypeOID(definition); |
| | | String oid = SchemaUtils.parseAttributeTypeOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasAttributeType(oid)) |
| | | { |
| | |
| | | private String completeDefinitionWhenAddingSchemaElement(String definition, Set<String> modifiedSchemaFiles) |
| | | throws DirectoryException |
| | | { |
| | | String givenSchemaFile = SchemaHandler.parseSchemaFileFromElementDefinition(definition); |
| | | String givenSchemaFile = SchemaUtils.parseSchemaFileFromElementDefinition(definition); |
| | | String finalSchemaFile = givenSchemaFile == null ? FILE_USER_SCHEMA_ELEMENTS : givenSchemaFile; |
| | | modifiedSchemaFiles.add(finalSchemaFile); |
| | | return SchemaHandler.addSchemaFileToElementDefinitionIfAbsent(definition, finalSchemaFile); |
| | | return SchemaUtils.addSchemaFileToElementDefinitionIfAbsent(definition, finalSchemaFile); |
| | | } |
| | | |
| | | /** |
| | |
| | | private String completeDefinitionWhenReplacingSchemaElement(String definition, SchemaElement existingElement, |
| | | Set<String> modifiedSchemaFiles) throws DirectoryException |
| | | { |
| | | String givenSchemaFile = SchemaHandler.parseSchemaFileFromElementDefinition(definition); |
| | | String givenSchemaFile = SchemaUtils.parseSchemaFileFromElementDefinition(definition); |
| | | String oldSchemaFile = getElementSchemaFile(existingElement); |
| | | |
| | | if (givenSchemaFile == null) |
| | |
| | | oldSchemaFile = FILE_USER_SCHEMA_ELEMENTS; |
| | | } |
| | | modifiedSchemaFiles.add(oldSchemaFile); |
| | | return SchemaHandler.addSchemaFileToElementDefinitionIfAbsent(definition, oldSchemaFile); |
| | | return SchemaUtils.addSchemaFileToElementDefinitionIfAbsent(definition, oldSchemaFile); |
| | | } |
| | | else if (oldSchemaFile == null || oldSchemaFile.equals(givenSchemaFile)) |
| | | { |
| | |
| | | int currentPosition, Set<String> modifiedSchemaFiles) throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String atOID = SchemaHandler.parseAttributeTypeOID(definition); |
| | | String atOID = SchemaUtils.parseAttributeTypeOID(definition); |
| | | |
| | | if (!currentSchema.hasAttributeType(atOID)) |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | String oid = SchemaHandler.parseAttributeTypeOID(v.toString()); |
| | | String oid = SchemaUtils.parseAttributeTypeOID(v.toString()); |
| | | if (atOID.equals(oid)) |
| | | { |
| | | // We found a match where the attribute type is added back later, |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseObjectClassOID(definition); |
| | | String oid = SchemaUtils.parseObjectClassOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasObjectClass(oid)) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String ocOID = SchemaHandler.parseObjectClassOID(definition); |
| | | String ocOID = SchemaUtils.parseObjectClassOID(definition); |
| | | |
| | | if (!currentSchema.hasObjectClass(ocOID)) |
| | | { |
| | |
| | | String oid; |
| | | try |
| | | { |
| | | oid = SchemaHandler.parseObjectClassOID(v.toString()); |
| | | oid = SchemaUtils.parseObjectClassOID(v.toString()); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseNameFormOID(definition); |
| | | String oid = SchemaUtils.parseNameFormOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasNameForm(oid)) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String nfOID = SchemaHandler.parseNameFormOID(definition); |
| | | String nfOID = SchemaUtils.parseNameFormOID(definition); |
| | | |
| | | if (!currentSchema.hasNameForm(nfOID)) |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | String oid = SchemaHandler.parseNameFormOID(v.toString()); |
| | | String oid = SchemaUtils.parseNameFormOID(v.toString()); |
| | | if (nfOID.equals(oid)) |
| | | { |
| | | // We found a match where the name form is added back later, so we |
| | |
| | | Set<String> modifiedSchemaFiles) throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseDITContentRuleOID(definition); |
| | | String oid = SchemaUtils.parseDITContentRuleOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasDITContentRule(oid)) |
| | | { |
| | |
| | | SchemaBuilder newSchemaBuilder, Set<String> modifiedSchemaFiles) throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String ruleOid = SchemaHandler.parseDITContentRuleOID(definition); |
| | | String ruleOid = SchemaUtils.parseDITContentRuleOID(definition); |
| | | |
| | | if (! currentSchema.hasDITContentRule(ruleOid)) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | int ruleId = SchemaHandler.parseRuleID(definition); |
| | | int ruleId = SchemaUtils.parseRuleID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasDITStructureRule(ruleId)) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | int ruleID = SchemaHandler.parseRuleID(definition); |
| | | int ruleID = SchemaUtils.parseRuleID(definition); |
| | | |
| | | if (!currentSchema.hasDITStructureRule(ruleID)) |
| | | { |
| | |
| | | |
| | | for (ByteString v : a) |
| | | { |
| | | int id = SchemaHandler.parseRuleID(v.toString()); |
| | | int id = SchemaUtils.parseRuleID(v.toString()); |
| | | if (ruleID == id) |
| | | { |
| | | // We found a match where the DIT structure rule is added back later, |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseMatchingRuleUseOID(definition); |
| | | String oid = SchemaUtils.parseMatchingRuleUseOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasMatchingRuleUse(oid)) |
| | | { |
| | |
| | | throws DirectoryException |
| | | { |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String mruOid = SchemaHandler.parseMatchingRuleUseOID(definition); |
| | | String mruOid = SchemaUtils.parseMatchingRuleUseOID(definition); |
| | | |
| | | if (!currentSchema.hasMatchingRuleUse(mruOid)) |
| | | { |
| | |
| | | // reject a change if a syntax with oid already exists, but I don't understand why. |
| | | // I kept an implementation that behave like other schema elements. |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = schemaHandler.getSchema(); |
| | | String oid = SchemaHandler.parseSyntaxOID(definition); |
| | | String oid = SchemaUtils.parseSyntaxOID(definition); |
| | | final String finalDefinition; |
| | | if (!currentSchema.hasSyntax(oid)) |
| | | { |
| | |
| | | * hence never deleted. |
| | | */ |
| | | org.forgerock.opendj.ldap.schema.Schema currentSchema = newSchemaBuilder.toSchema(); |
| | | String oid = SchemaHandler.parseSyntaxOID(definition); |
| | | String oid = SchemaUtils.parseSyntaxOID(definition); |
| | | |
| | | if (!currentSchema.hasSyntax(oid)) |
| | | { |
| | |
| | | for (ByteString v : a) |
| | | { |
| | | String definition = v.toString(); |
| | | String schemaFile = SchemaHandler.parseSchemaFileFromElementDefinition(definition); |
| | | String schemaFile = SchemaUtils.parseSchemaFileFromElementDefinition(definition); |
| | | if (is02ConfigLdif(schemaFile)) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | String oid = SchemaHandler.parseAttributeTypeOID(definition); |
| | | String oid = SchemaUtils.parseAttributeTypeOID(definition); |
| | | oidList.add(oid); |
| | | try |
| | | { |
| | |
| | | // It IS important here to allow the unknown elements that could |
| | | // appear in the new config schema. |
| | | String definition = v.toString(); |
| | | String schemaFile = SchemaHandler.parseSchemaFileFromElementDefinition(definition); |
| | | String schemaFile = SchemaUtils.parseSchemaFileFromElementDefinition(definition); |
| | | if (is02ConfigLdif(schemaFile)) |
| | | { |
| | | continue; |
| | | } |
| | | String oid = SchemaHandler.parseObjectClassOID(definition); |
| | | String oid = SchemaUtils.parseObjectClassOID(definition); |
| | | oidList.add(oid); |
| | | try |
| | | { |
| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_LDAP_SYNTAX_OID; |
| | | |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_MATCHING_RULE_USE_OID; |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_DIT_STRUCTURE_RULE_RULEID; |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_DIT_CONTENT_RULE_OID; |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_NAME_FORM_OID; |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_OBJECTCLASS_OID; |
| | | import static org.opends.messages.SchemaMessages.ERR_PARSING_ATTRIBUTE_TYPE_OID; |
| | | import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_TRUNCATED_VALUE1; |
| | | import static org.opends.messages.ConfigMessages.WARN_CONFIG_SCHEMA_CANNOT_OPEN_FILE; |
| | | import static org.opends.server.util.ServerConstants.SCHEMA_PROPERTY_FILENAME; |
| | |
| | | import org.opends.server.schema.AciSyntax; |
| | | import org.opends.server.schema.SubtreeSpecificationSyntax; |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.config.ClassPropertyDefinition; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | |
| | | import org.opends.server.util.ActivateOnceSDKSchemaIsUsed; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | import com.forgerock.opendj.util.SubstringReader; |
| | | |
| | | /** |
| | | * Responsible for loading the server schema. |
| | | * <p> |
| | |
| | | return extraAttributes.values(); |
| | | } |
| | | |
| | | /** |
| | | * Adds the provided schema file to the provided schema element definition. |
| | | * |
| | | * @param definition |
| | | * The schema element definition |
| | | * @param schemaFile |
| | | * The name of the schema file to include in the definition |
| | | * @return The definition string of the element |
| | | * including the X-SCHEMA-FILE extension. |
| | | */ |
| | | public static String addSchemaFileToElementDefinitionIfAbsent(String definition, String schemaFile) |
| | | { |
| | | if (schemaFile != null && !definition.contains(SCHEMA_PROPERTY_FILENAME)) |
| | | { |
| | | int pos = definition.lastIndexOf(')'); |
| | | return definition.substring(0, pos).trim() + " " + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; |
| | | } |
| | | return definition; |
| | | } |
| | | |
| | | /** |
| | | * Parses the schema file (value of X-SCHEMA-FILE extension) from the provided schema element |
| | | * definition. |
| | | * <p> |
| | | * It expects a single value for the X-SCHEMA-FILE extension, e.g.: |
| | | * "X-SCHEMA-FILE '99-user.ldif'", as there is no sensible meaning for multiple values. |
| | | * |
| | | * @param definition |
| | | * The definition of a schema element |
| | | * @return the value of the schema file or {@code null} if the X-SCHEMA-FILE extension is not |
| | | * present in the definition |
| | | * @throws DirectoryException |
| | | * If an error occurs while parsing the schema element definition |
| | | */ |
| | | public static String parseSchemaFileFromElementDefinition(String definition) throws DirectoryException |
| | | { |
| | | int pos = definition.lastIndexOf(SCHEMA_PROPERTY_FILENAME); |
| | | if (pos == -1) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | SubstringReader reader = new SubstringReader(definition); |
| | | reader.read(pos + SCHEMA_PROPERTY_FILENAME.length()); |
| | | |
| | | int length = 0; |
| | | reader.skipWhitespaces(); |
| | | reader.mark(); |
| | | try |
| | | { |
| | | // Accept both a quoted value or an unquoted value |
| | | char c = reader.read(); |
| | | if (c == '\'') |
| | | { |
| | | reader.mark(); |
| | | // Parse until the closing quote. |
| | | while (reader.read() != '\'') |
| | | { |
| | | length++; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Parse until the next space. |
| | | do |
| | | { |
| | | length++; |
| | | } |
| | | while (reader.read() != ' '); |
| | | } |
| | | reader.reset(); |
| | | return reader.read(length); |
| | | } |
| | | catch (final StringIndexOutOfBoundsException e) |
| | | { |
| | | // TODO : write the correct message = Error when trying to parse the schema file from a schema |
| | | // element definition |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, LocalizableMessage.raw("")); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** Takes an exclusive lock on the schema. */ |
| | | public void exclusiveLock() |
| | | { |
| | |
| | | */ |
| | | Schema update(SchemaBuilder builder) throws DirectoryException; |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided attribute type definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of an attribute type, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseAttributeTypeOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_ATTRIBUTE_TYPE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided object class definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a object class, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseObjectClassOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_OBJECTCLASS_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided name form definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a name form, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseNameFormOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_NAME_FORM_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided DIT content rule definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a DIT content rule, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseDITContentRuleOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_DIT_CONTENT_RULE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the ruleID from the provided DIT structure rule definition, assuming the definition is |
| | | * valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a DIT structure rule, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static int parseRuleID(String definition) throws DirectoryException |
| | | { |
| | | // Reuse code of parseOID, even though this is not an OID |
| | | return Integer.parseInt(parseOID(definition, ERR_PARSING_DIT_STRUCTURE_RULE_RULEID)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided matching rule use definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a matching rule use, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseMatchingRuleUseOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_MATCHING_RULE_USE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided syntax definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a syntax, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseSyntaxOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_LDAP_SYNTAX_OID); |
| | | } |
| | | |
| | | private static String parseOID(String definition, Arg1<Object> parsingErrorMsg) throws DirectoryException |
| | | { |
| | | try |
| | | { |
| | | int pos = 0; |
| | | int length = definition.length(); |
| | | // Skip over any leading whitespace. |
| | | while (pos < length && (definition.charAt(pos) == ' ')) |
| | | { |
| | | pos++; |
| | | } |
| | | // Skip the open parenthesis. |
| | | pos++; |
| | | // Skip over any spaces immediately following the opening parenthesis. |
| | | while (pos < length && definition.charAt(pos) == ' ') |
| | | { |
| | | pos++; |
| | | } |
| | | // The next set of characters must be the OID. |
| | | int oidStartPos = pos; |
| | | while (pos < length && definition.charAt(pos) != ' ' && definition.charAt(pos) != ')') |
| | | { |
| | | pos++; |
| | | } |
| | | return definition.substring(oidStartPos, pos); |
| | | } |
| | | catch (IndexOutOfBoundsException e) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, parsingErrorMsg.get(definition), e); |
| | | } |
| | | } |
| | | } |
| | |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.Schema; |
| | | import org.opends.server.types.SchemaWriter; |
| | | import org.opends.server.util.SchemaUtils; |
| | | |
| | | import static org.opends.messages.TaskMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | |
| | | AttributeBuilder builder = new AttributeBuilder(a.getAttributeDescription()); |
| | | for (ByteString v : a) |
| | | { |
| | | builder.add(Schema.addSchemaFileToElementDefinitionIfAbsent(v.toString(), schemaFile)); |
| | | builder.add(SchemaUtils.addSchemaFileToElementDefinitionIfAbsent(v.toString(), schemaFile)); |
| | | } |
| | | |
| | | mods.add(new Modification(m.getModificationType(), builder.toAttribute())); |
| | |
| | | } |
| | | } |
| | | |
| | | Schema.writeConcatenatedSchema(); |
| | | SchemaWriter.writeConcatenatedSchema(); |
| | | } |
| | | |
| | | schema.setYoungestModificationTime(System.currentTimeMillis()); |
| | |
| | | */ |
| | | package org.opends.server.tools.upgrade; |
| | | |
| | | import static org.opends.server.util.SchemaUtils.addSchemaFileToElementDefinitionIfAbsent; |
| | | |
| | | import static java.nio.charset.StandardCharsets.*; |
| | | import static java.nio.file.StandardOpenOption.*; |
| | | import static javax.security.auth.callback.ConfirmationCallback.NO; |
| | | import static javax.security.auth.callback.ConfirmationCallback.YES; |
| | | import static javax.security.auth.callback.TextOutputCallback.*; |
| | | |
| | | import static org.forgerock.util.Utils.joinAsString; |
| | | import static org.opends.messages.ToolMessages.*; |
| | | import static org.opends.server.tools.upgrade.FileManager.copyRecursively; |
| | | import static org.opends.server.tools.upgrade.UpgradeUtils.*; |
| | | import static org.opends.server.types.Schema.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.io.BufferedWriter; |
| | |
| | | import org.opends.server.tools.RebuildIndex; |
| | | import org.opends.server.util.BuildVersion; |
| | | import org.opends.server.util.ChangeOperationType; |
| | | import org.opends.server.util.SchemaUtils; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | import com.forgerock.opendj.cli.ClientException; |
| | |
| | | */ |
| | | package org.opends.server.types; |
| | | |
| | | import static org.forgerock.opendj.ldap.ModificationType.*; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaOptions.*; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | import static org.opends.messages.SchemaMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.BufferedWriter; |
| | | import java.io.File; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.FileReader; |
| | | import java.io.FileWriter; |
| | | import java.io.FilenameFilter; |
| | | import java.io.IOException; |
| | | import java.text.ParseException; |
| | | import java.util.Collection; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.TreeSet; |
| | | import java.util.concurrent.locks.Lock; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | |
| | | import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; |
| | | import org.forgerock.i18n.LocalizedIllegalArgumentException; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.ConflictingSchemaElementException; |
| | |
| | | import org.forgerock.util.Option; |
| | | import org.forgerock.util.Utils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.SchemaConfigManager; |
| | | import org.opends.server.util.Base64; |
| | | |
| | | /** |
| | | * This class defines a data structure that holds information about the components of the Directory |
| | |
| | | extraAttributes.put(name, attr); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Writes a single file containing all schema element definitions, |
| | | * which can be used on startup to determine whether the schema |
| | | * files were edited with the server offline. |
| | | */ |
| | | public static void writeConcatenatedSchema() |
| | | { |
| | | String concatFilePath = null; |
| | | try |
| | | { |
| | | Set<String> attributeTypes = new LinkedHashSet<>(); |
| | | Set<String> objectClasses = new LinkedHashSet<>(); |
| | | Set<String> nameForms = new LinkedHashSet<>(); |
| | | Set<String> ditContentRules = new LinkedHashSet<>(); |
| | | Set<String> ditStructureRules = new LinkedHashSet<>(); |
| | | Set<String> matchingRuleUses = new LinkedHashSet<>(); |
| | | Set<String> ldapSyntaxes = new LinkedHashSet<>(); |
| | | genConcatenatedSchema(attributeTypes, objectClasses, nameForms, |
| | | ditContentRules, ditStructureRules, |
| | | matchingRuleUses,ldapSyntaxes); |
| | | |
| | | |
| | | File configFile = new File(DirectoryServer.getConfigFile()); |
| | | File configDirectory = configFile.getParentFile(); |
| | | File upgradeDirectory = new File(configDirectory, "upgrade"); |
| | | upgradeDirectory.mkdir(); |
| | | File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | concatFilePath = concatFile.getAbsolutePath(); |
| | | |
| | | File tempFile = new File(concatFilePath + ".tmp"); |
| | | try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile, false))) |
| | | { |
| | | writeLines(writer, |
| | | "dn: " + DirectoryServer.getSchemaDN(), |
| | | "objectClass: top", |
| | | "objectClass: ldapSubentry", |
| | | "objectClass: subschema"); |
| | | |
| | | writeLines(writer, ATTR_ATTRIBUTE_TYPES, attributeTypes); |
| | | writeLines(writer, ATTR_OBJECTCLASSES, objectClasses); |
| | | writeLines(writer, ATTR_NAME_FORMS, nameForms); |
| | | writeLines(writer, ATTR_DIT_CONTENT_RULES, ditContentRules); |
| | | writeLines(writer, ATTR_DIT_STRUCTURE_RULES, ditStructureRules); |
| | | writeLines(writer, ATTR_MATCHING_RULE_USE, matchingRuleUses); |
| | | writeLines(writer, ATTR_LDAP_SYNTAXES, ldapSyntaxes); |
| | | } |
| | | |
| | | if (concatFile.exists()) |
| | | { |
| | | concatFile.delete(); |
| | | } |
| | | tempFile.renameTo(concatFile); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | // This is definitely not ideal, but it's not the end of the |
| | | // world. The worst that should happen is that the schema |
| | | // changes could potentially be sent to the other servers again |
| | | // when this server is restarted, which shouldn't hurt anything. |
| | | // Still, we should log a warning message. |
| | | logger.error(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, concatFilePath, getExceptionMessage(e)); |
| | | } |
| | | } |
| | | |
| | | private static void writeLines(BufferedWriter writer, String... lines) throws IOException |
| | | { |
| | | for (String line : lines) |
| | | { |
| | | writer.write(line); |
| | | writer.newLine(); |
| | | } |
| | | } |
| | | |
| | | private static void writeLines(BufferedWriter writer, String beforeColumn, Set<String> lines) throws IOException |
| | | { |
| | | for (String line : lines) |
| | | { |
| | | writer.write(beforeColumn); |
| | | writer.write(": "); |
| | | writer.write(line); |
| | | writer.newLine(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Reads the files contained in the schema directory and generates a |
| | | * concatenated view of their contents in the provided sets. |
| | | * |
| | | * @param attributeTypes The set into which to place the |
| | | * attribute types read from the schema |
| | | * files. |
| | | * @param objectClasses The set into which to place the object |
| | | * classes read from the schema files. |
| | | * @param nameForms The set into which to place the name |
| | | * forms read from the schema files. |
| | | * @param ditContentRules The set into which to place the DIT |
| | | * content rules read from the schema |
| | | * files. |
| | | * @param ditStructureRules The set into which to place the DIT |
| | | * structure rules read from the schema |
| | | * files. |
| | | * @param matchingRuleUses The set into which to place the |
| | | * matching rule uses read from the |
| | | * schema files. |
| | | * @param ldapSyntaxes The set into which to place the |
| | | * ldap syntaxes read from the |
| | | * schema files. |
| | | * |
| | | * @throws IOException If a problem occurs while reading the |
| | | * schema file elements. |
| | | */ |
| | | public static void genConcatenatedSchema( |
| | | Set<String> attributeTypes, |
| | | Set<String> objectClasses, |
| | | Set<String> nameForms, |
| | | Set<String> ditContentRules, |
| | | Set<String> ditStructureRules, |
| | | Set<String> matchingRuleUses, |
| | | Set<String> ldapSyntaxes) |
| | | throws IOException |
| | | { |
| | | // Get a sorted list of the files in the schema directory. |
| | | TreeSet<File> schemaFiles = new TreeSet<>(); |
| | | String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath(); |
| | | |
| | | final FilenameFilter filter = new SchemaConfigManager.SchemaFileFilter(); |
| | | for (File f : new File(schemaDirectory).listFiles(filter)) |
| | | { |
| | | if (f.isFile()) |
| | | { |
| | | schemaFiles.add(f); |
| | | } |
| | | } |
| | | |
| | | |
| | | // Open each of the files in order and read the elements that they |
| | | // contain, appending them to the appropriate lists. |
| | | for (File f : schemaFiles) |
| | | { |
| | | List<StringBuilder> lines = readSchemaElementsFromLdif(f); |
| | | |
| | | // Iterate through each line in the list. Find the colon and |
| | | // get the attribute name at the beginning. If it's something |
| | | // that we don't recognize, then skip it. Otherwise, add the |
| | | // X-SCHEMA-FILE extension and add it to the appropriate schema |
| | | // element list. |
| | | for (StringBuilder buffer : lines) |
| | | { |
| | | String line = buffer.toString().trim(); |
| | | parseSchemaLine(line, f.getName(), attributeTypes, objectClasses, |
| | | nameForms, ditContentRules, ditStructureRules, matchingRuleUses, |
| | | ldapSyntaxes); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private static List<StringBuilder> readSchemaElementsFromLdif(File f) throws IOException, FileNotFoundException |
| | | { |
| | | final LinkedList<StringBuilder> lines = new LinkedList<>(); |
| | | |
| | | try (BufferedReader reader = new BufferedReader(new FileReader(f))) |
| | | { |
| | | String line; |
| | | while ((line = reader.readLine()) != null) |
| | | { |
| | | if (line.startsWith("#") || line.length() == 0) |
| | | { |
| | | continue; |
| | | } |
| | | else if (line.startsWith(" ")) |
| | | { |
| | | lines.getLast().append(line.substring(1)); |
| | | } |
| | | else |
| | | { |
| | | lines.add(new StringBuilder(line)); |
| | | } |
| | | } |
| | | } |
| | | return lines; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Reads data from the specified concatenated schema file into the |
| | | * provided sets. |
| | | * |
| | | * @param concatSchemaFile The concatenated schema file to be read. |
| | | * @param attributeTypes The set into which to place the attribute types |
| | | * read from the concatenated schema file. |
| | | * @param objectClasses The set into which to place the object classes |
| | | * read from the concatenated schema file. |
| | | * @param nameForms The set into which to place the name forms |
| | | * read from the concatenated schema file. |
| | | * @param ditContentRules The set into which to place the DIT content rules |
| | | * read from the concatenated schema file. |
| | | * @param ditStructureRules The set into which to place the DIT structure rules |
| | | * read from the concatenated schema file. |
| | | * @param matchingRuleUses The set into which to place the matching rule |
| | | * uses read from the concatenated schema file. |
| | | * @param ldapSyntaxes The set into which to place the ldap syntaxes |
| | | * read from the concatenated schema file. |
| | | * |
| | | * @throws IOException If a problem occurs while reading the |
| | | * schema file elements. |
| | | */ |
| | | public static void readConcatenatedSchema(File concatSchemaFile, |
| | | Set<String> attributeTypes, |
| | | Set<String> objectClasses, |
| | | Set<String> nameForms, |
| | | Set<String> ditContentRules, |
| | | Set<String> ditStructureRules, |
| | | Set<String> matchingRuleUses, |
| | | Set<String> ldapSyntaxes) |
| | | throws IOException |
| | | { |
| | | try (BufferedReader reader = new BufferedReader(new FileReader(concatSchemaFile))) |
| | | { |
| | | String line; |
| | | while ((line = reader.readLine()) != null) |
| | | { |
| | | parseSchemaLine(line, null, attributeTypes, objectClasses, |
| | | nameForms, ditContentRules, ditStructureRules, matchingRuleUses, |
| | | ldapSyntaxes); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private static void parseSchemaLine(String definition, String fileName, |
| | | Set<String> attributeTypes, |
| | | Set<String> objectClasses, |
| | | Set<String> nameForms, |
| | | Set<String> ditContentRules, |
| | | Set<String> ditStructureRules, |
| | | Set<String> matchingRuleUses, |
| | | Set<String> ldapSyntaxes) |
| | | { |
| | | String lowerLine = toLowerCase(definition); |
| | | |
| | | try |
| | | { |
| | | if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) |
| | | { |
| | | addSchemaDefinition(attributeTypes, definition, ATTR_ATTRIBUTE_TYPES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) |
| | | { |
| | | addSchemaDefinition(objectClasses, definition, ATTR_OBJECTCLASSES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) |
| | | { |
| | | addSchemaDefinition(nameForms, definition, ATTR_NAME_FORMS_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) |
| | | { |
| | | addSchemaDefinition(ditContentRules, definition, ATTR_DIT_CONTENT_RULES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) |
| | | { |
| | | addSchemaDefinition(ditStructureRules, definition, ATTR_DIT_STRUCTURE_RULES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) |
| | | { |
| | | addSchemaDefinition(matchingRuleUses, definition, ATTR_MATCHING_RULE_USE_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC)) |
| | | { |
| | | addSchemaDefinition(ldapSyntaxes, definition, ATTR_LDAP_SYNTAXES_LC, fileName); |
| | | } |
| | | } catch (ParseException pe) |
| | | { |
| | | logger.error(ERR_SCHEMA_PARSE_LINE.get(definition, pe.getLocalizedMessage())); |
| | | } |
| | | } |
| | | |
| | | private static void addSchemaDefinition(Set<String> definitions, String line, String attrName, String fileName) |
| | | throws ParseException |
| | | { |
| | | definitions.add(getSchemaDefinition(line.substring(attrName.length()), fileName)); |
| | | } |
| | | |
| | | private static String getSchemaDefinition(String definition, String schemaFile) throws ParseException |
| | | { |
| | | if (definition.startsWith("::")) |
| | | { |
| | | // See OPENDJ-2792: the definition of the ds-cfg-csv-delimiter-char attribute type |
| | | // had a space accidentally added after the closing parenthesis. |
| | | // This was unfortunately interpreted as base64 |
| | | definition = ByteString.wrap(Base64.decode(definition.substring(2).trim())).toString(); |
| | | } |
| | | else if (definition.startsWith(":")) |
| | | { |
| | | definition = definition.substring(1).trim(); |
| | | } |
| | | else |
| | | { |
| | | throw new ParseException(ERR_SCHEMA_COULD_NOT_PARSE_DEFINITION.get().toString(), 0); |
| | | } |
| | | |
| | | return addSchemaFileToElementDefinitionIfAbsent(definition, schemaFile); |
| | | } |
| | | |
| | | /** |
| | | * Compares the provided sets of schema element definitions and |
| | | * writes any differences found into the given list of |
| | | * modifications. |
| | | * |
| | | * @param oldElements The set of elements of the specified type |
| | | * read from the previous concatenated schema |
| | | * files. |
| | | * @param newElements The set of elements of the specified type |
| | | * read from the server's current schema. |
| | | * @param elementType The attribute type associated with the |
| | | * schema element being compared. |
| | | * @param mods The list of modifications into which any |
| | | * identified differences should be written. |
| | | */ |
| | | public static void compareConcatenatedSchema( |
| | | Set<String> oldElements, |
| | | Set<String> newElements, |
| | | AttributeType elementType, |
| | | List<Modification> mods) |
| | | { |
| | | AttributeBuilder builder = new AttributeBuilder(elementType); |
| | | addModification(mods, DELETE, oldElements, newElements, builder); |
| | | |
| | | builder.setAttributeDescription(AttributeDescription.create(elementType)); |
| | | addModification(mods, ADD, newElements, oldElements, builder); |
| | | } |
| | | |
| | | private static void addModification(List<Modification> mods, ModificationType modType, Set<String> included, |
| | | Set<String> excluded, AttributeBuilder builder) |
| | | { |
| | | for (String val : included) |
| | | { |
| | | if (!excluded.contains(val)) |
| | | { |
| | | builder.add(val); |
| | | } |
| | | } |
| | | |
| | | if (!builder.isEmpty()) |
| | | { |
| | | mods.add(new Modification(modType, builder.toAttribute())); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Destroys the structures maintained by the schema so that they are |
| | | * no longer usable. This should only be called at the end of the |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.types; |
| | | |
| | | import static org.opends.messages.BackendMessages.ERR_SCHEMA_CANNOT_FIND_CONCAT_FILE; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | |
| | | import static org.opends.messages.BackendMessages.ERR_SCHEMA_COULD_NOT_PARSE_DEFINITION; |
| | | import static org.opends.messages.BackendMessages.ERR_SCHEMA_PARSE_LINE; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.messages.BackendMessages.ERR_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES; |
| | | import static org.forgerock.opendj.ldap.schema.CoreSchema.*; |
| | | import static org.forgerock.opendj.ldap.ModificationType.ADD; |
| | | import static org.forgerock.opendj.ldap.ModificationType.DELETE; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.getExceptionMessage; |
| | | import static org.opends.server.util.StaticUtils.toLowerCase; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.BufferedWriter; |
| | | import java.io.File; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.FileReader; |
| | | import java.io.FileWriter; |
| | | import java.io.FilenameFilter; |
| | | import java.io.IOException; |
| | | import java.text.ParseException; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.SchemaConfigManager; |
| | | import org.opends.server.util.Base64; |
| | | import org.opends.server.util.BuildVersion; |
| | | import org.opends.server.util.SchemaUtils; |
| | | |
| | | /** |
| | | * Provides support to write schema files. |
| | | */ |
| | | public class SchemaWriter |
| | | { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | private static final AttributeType attributeTypesType = getAttributeTypesAttributeType(); |
| | | private static final AttributeType ditStructureRulesType = getDITStructureRulesAttributeType(); |
| | | private static final AttributeType ditContentRulesType = getDITContentRulesAttributeType(); |
| | | private static final AttributeType ldapSyntaxesType = getLDAPSyntaxesAttributeType(); |
| | | private static final AttributeType matchingRuleUsesType = getMatchingRuleUseAttributeType(); |
| | | private static final AttributeType nameFormsType = getNameFormsAttributeType(); |
| | | private static final AttributeType objectClassesType = getObjectClassesAttributeType(); |
| | | |
| | | /** |
| | | * Compares the provided sets of schema element definitions and writes any differences found into |
| | | * the given list of modifications. |
| | | * |
| | | * @param oldElements |
| | | * The set of elements of the specified type read from the previous concatenated schema |
| | | * files. |
| | | * @param newElements |
| | | * The set of elements of the specified type read from the server's current schema. |
| | | * @param elementType |
| | | * The attribute type associated with the schema element being compared. |
| | | * @param mods |
| | | * The list of modifications into which any identified differences should be written. |
| | | */ |
| | | public static void compareConcatenatedSchema(Set<String> oldElements, Set<String> newElements, |
| | | AttributeType elementType, List<Modification> mods) |
| | | { |
| | | AttributeBuilder builder = new AttributeBuilder(elementType); |
| | | addModification(mods, DELETE, oldElements, newElements, builder); |
| | | |
| | | builder.setAttributeDescription(AttributeDescription.create(elementType)); |
| | | addModification(mods, ADD, newElements, oldElements, builder); |
| | | } |
| | | |
| | | /** |
| | | * Reads the files contained in the schema directory and generates a concatenated view of their |
| | | * contents in the provided sets. |
| | | * |
| | | * @param attributeTypes |
| | | * The set into which to place the attribute types read from the schema files. |
| | | * @param objectClasses |
| | | * The set into which to place the object classes read from the schema files. |
| | | * @param nameForms |
| | | * The set into which to place the name forms read from the schema files. |
| | | * @param ditContentRules |
| | | * The set into which to place the DIT content rules read from the schema files. |
| | | * @param ditStructureRules |
| | | * The set into which to place the DIT structure rules read from the schema files. |
| | | * @param matchingRuleUses |
| | | * The set into which to place the matching rule uses read from the schema files. |
| | | * @param ldapSyntaxes |
| | | * The set into which to place the ldap syntaxes read from the schema files. |
| | | * @throws IOException |
| | | * If a problem occurs while reading the schema file elements. |
| | | */ |
| | | public static void generateConcatenatedSchema(Set<String> attributeTypes, Set<String> objectClasses, |
| | | Set<String> nameForms, Set<String> ditContentRules, Set<String> ditStructureRules, Set<String> matchingRuleUses, |
| | | Set<String> ldapSyntaxes) throws IOException |
| | | { |
| | | // Get a sorted list of the files in the schema directory. |
| | | TreeSet<File> schemaFiles = new TreeSet<>(); |
| | | String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath(); |
| | | |
| | | final FilenameFilter filter = new SchemaConfigManager.SchemaFileFilter(); |
| | | for (File f : new File(schemaDirectory).listFiles(filter)) |
| | | { |
| | | if (f.isFile()) |
| | | { |
| | | schemaFiles.add(f); |
| | | } |
| | | } |
| | | |
| | | // Open each of the files in order and read the elements that they |
| | | // contain, appending them to the appropriate lists. |
| | | for (File f : schemaFiles) |
| | | { |
| | | List<StringBuilder> lines = readSchemaElementsFromLdif(f); |
| | | |
| | | // Iterate through each line in the list. Find the colon and |
| | | // get the attribute name at the beginning. If it's something |
| | | // that we don't recognize, then skip it. Otherwise, add the |
| | | // X-SCHEMA-FILE extension and add it to the appropriate schema |
| | | // element list. |
| | | for (StringBuilder buffer : lines) |
| | | { |
| | | String line = buffer.toString().trim(); |
| | | parseSchemaLine(line, f.getName(), attributeTypes, objectClasses, nameForms, ditContentRules, |
| | | ditStructureRules, matchingRuleUses, ldapSyntaxes); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Reads data from the specified concatenated schema file into the provided sets. |
| | | * |
| | | * @param concatSchemaFile |
| | | * The concatenated schema file to be read. |
| | | * @param attributeTypes |
| | | * The set into which to place the attribute types read from the concatenated schema |
| | | * file. |
| | | * @param objectClasses |
| | | * The set into which to place the object classes read from the concatenated schema file. |
| | | * @param nameForms |
| | | * The set into which to place the name forms read from the concatenated schema file. |
| | | * @param ditContentRules |
| | | * The set into which to place the DIT content rules read from the concatenated schema |
| | | * file. |
| | | * @param ditStructureRules |
| | | * The set into which to place the DIT structure rules read from the concatenated schema |
| | | * file. |
| | | * @param matchingRuleUses |
| | | * The set into which to place the matching rule uses read from the concatenated schema |
| | | * file. |
| | | * @param ldapSyntaxes |
| | | * The set into which to place the ldap syntaxes read from the concatenated schema file. |
| | | * @throws IOException |
| | | * If a problem occurs while reading the schema file elements. |
| | | */ |
| | | public static void readConcatenatedSchema(File concatSchemaFile, Set<String> attributeTypes, |
| | | Set<String> objectClasses, Set<String> nameForms, Set<String> ditContentRules, Set<String> ditStructureRules, |
| | | Set<String> matchingRuleUses, Set<String> ldapSyntaxes) throws IOException |
| | | { |
| | | try (BufferedReader reader = new BufferedReader(new FileReader(concatSchemaFile))) |
| | | { |
| | | String line; |
| | | while ((line = reader.readLine()) != null) |
| | | { |
| | | parseSchemaLine(line, null, attributeTypes, objectClasses, nameForms, ditContentRules, ditStructureRules, |
| | | matchingRuleUses, ldapSyntaxes); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the concatenated schema if changes are detected in the current schema files. |
| | | * <p> |
| | | * Identify any differences that may exist between the concatenated schema file from the last |
| | | * online modification and the current schema files. If there are any differences, then they |
| | | * should be from making changes to the schema files with the server offline. |
| | | */ |
| | | public static void updateConcatenatedSchema() throws InitializationException |
| | | { |
| | | try |
| | | { |
| | | // First, generate lists of elements from the current schema. |
| | | Set<String> newATs = new LinkedHashSet<>(); |
| | | Set<String> newOCs = new LinkedHashSet<>(); |
| | | Set<String> newNFs = new LinkedHashSet<>(); |
| | | Set<String> newDCRs = new LinkedHashSet<>(); |
| | | Set<String> newDSRs = new LinkedHashSet<>(); |
| | | Set<String> newMRUs = new LinkedHashSet<>(); |
| | | Set<String> newLSs = new LinkedHashSet<>(); |
| | | generateConcatenatedSchema(newATs, newOCs, newNFs, newDCRs, newDSRs, newMRUs, newLSs); |
| | | |
| | | // Next, generate lists of elements from the previous concatenated schema. |
| | | // If there isn't a previous concatenated schema, then use the base |
| | | // schema for the current revision. |
| | | File concatFile = getConcatenatedSchemaFile(); |
| | | |
| | | Set<String> oldATs = new LinkedHashSet<>(); |
| | | Set<String> oldOCs = new LinkedHashSet<>(); |
| | | Set<String> oldNFs = new LinkedHashSet<>(); |
| | | Set<String> oldDCRs = new LinkedHashSet<>(); |
| | | Set<String> oldDSRs = new LinkedHashSet<>(); |
| | | Set<String> oldMRUs = new LinkedHashSet<>(); |
| | | Set<String> oldLSs = new LinkedHashSet<>(); |
| | | readConcatenatedSchema(concatFile, oldATs, oldOCs, oldNFs, oldDCRs, oldDSRs, oldMRUs, oldLSs); |
| | | |
| | | // Create a list of modifications and add any differences between the old |
| | | // and new schema into them. |
| | | List<Modification> mods = new LinkedList<>(); |
| | | compareConcatenatedSchema(oldATs, newATs, attributeTypesType, mods); |
| | | compareConcatenatedSchema(oldOCs, newOCs, objectClassesType, mods); |
| | | compareConcatenatedSchema(oldNFs, newNFs, nameFormsType, mods); |
| | | compareConcatenatedSchema(oldDCRs, newDCRs, ditContentRulesType, mods); |
| | | compareConcatenatedSchema(oldDSRs, newDSRs, ditStructureRulesType, mods); |
| | | compareConcatenatedSchema(oldMRUs, newMRUs, matchingRuleUsesType, mods); |
| | | compareConcatenatedSchema(oldLSs, newLSs, ldapSyntaxesType, mods); |
| | | if (!mods.isEmpty()) |
| | | { |
| | | // TODO : Raise an alert notification. |
| | | |
| | | DirectoryServer.setOfflineSchemaChanges(mods); |
| | | |
| | | // Write a new concatenated schema file with the most recent information |
| | | // so we don't re-find these same changes on the next startup. |
| | | writeConcatenatedSchema(); |
| | | } |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | throw ie; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | logger.error(ERR_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES, getExceptionMessage(e)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Writes a single file containing all schema element definitions, which can be used on startup to |
| | | * determine whether the schema files were edited with the server offline. |
| | | */ |
| | | public static void writeConcatenatedSchema() |
| | | { |
| | | String concatFilePath = null; |
| | | try |
| | | { |
| | | Set<String> attributeTypes = new LinkedHashSet<>(); |
| | | Set<String> objectClasses = new LinkedHashSet<>(); |
| | | Set<String> nameForms = new LinkedHashSet<>(); |
| | | Set<String> ditContentRules = new LinkedHashSet<>(); |
| | | Set<String> ditStructureRules = new LinkedHashSet<>(); |
| | | Set<String> matchingRuleUses = new LinkedHashSet<>(); |
| | | Set<String> ldapSyntaxes = new LinkedHashSet<>(); |
| | | generateConcatenatedSchema(attributeTypes, objectClasses, nameForms, ditContentRules, ditStructureRules, |
| | | matchingRuleUses, ldapSyntaxes); |
| | | |
| | | File configFile = new File(DirectoryServer.getConfigFile()); |
| | | File configDirectory = configFile.getParentFile(); |
| | | File upgradeDirectory = new File(configDirectory, "upgrade"); |
| | | upgradeDirectory.mkdir(); |
| | | File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | concatFilePath = concatFile.getAbsolutePath(); |
| | | |
| | | File tempFile = new File(concatFilePath + ".tmp"); |
| | | try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile, false))) |
| | | { |
| | | writeLines(writer, "dn: " + DirectoryServer.getSchemaDN(), "objectClass: top", "objectClass: ldapSubentry", |
| | | "objectClass: subschema"); |
| | | |
| | | writeLines(writer, ATTR_ATTRIBUTE_TYPES, attributeTypes); |
| | | writeLines(writer, ATTR_OBJECTCLASSES, objectClasses); |
| | | writeLines(writer, ATTR_NAME_FORMS, nameForms); |
| | | writeLines(writer, ATTR_DIT_CONTENT_RULES, ditContentRules); |
| | | writeLines(writer, ATTR_DIT_STRUCTURE_RULES, ditStructureRules); |
| | | writeLines(writer, ATTR_MATCHING_RULE_USE, matchingRuleUses); |
| | | writeLines(writer, ATTR_LDAP_SYNTAXES, ldapSyntaxes); |
| | | } |
| | | |
| | | if (concatFile.exists()) |
| | | { |
| | | concatFile.delete(); |
| | | } |
| | | tempFile.renameTo(concatFile); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | // This is definitely not ideal, but it's not the end of the |
| | | // world. The worst that should happen is that the schema |
| | | // changes could potentially be sent to the other servers again |
| | | // when this server is restarted, which shouldn't hurt anything. |
| | | // Still, we should log a warning message. |
| | | logger.error(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, concatFilePath, getExceptionMessage(e)); |
| | | } |
| | | } |
| | | |
| | | private static void addModification(List<Modification> mods, ModificationType modType, Set<String> included, |
| | | Set<String> excluded, AttributeBuilder builder) |
| | | { |
| | | for (String val : included) |
| | | { |
| | | if (!excluded.contains(val)) |
| | | { |
| | | builder.add(val); |
| | | } |
| | | } |
| | | |
| | | if (!builder.isEmpty()) |
| | | { |
| | | mods.add(new Modification(modType, builder.toAttribute())); |
| | | } |
| | | } |
| | | |
| | | private static void addSchemaDefinition(Set<String> definitions, String line, String attrName, String fileName) |
| | | throws ParseException |
| | | { |
| | | definitions.add(getSchemaDefinition(line.substring(attrName.length()), fileName)); |
| | | } |
| | | |
| | | private static File getConcatenatedSchemaFile() throws InitializationException |
| | | { |
| | | File configDirectory = new File(DirectoryServer.getConfigFile()).getParentFile(); |
| | | File upgradeDirectory = new File(configDirectory, "upgrade"); |
| | | File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | if (concatFile.exists()) |
| | | { |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | |
| | | String fileName = SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION + BuildVersion.instanceVersion().getRevision(); |
| | | concatFile = new File(upgradeDirectory, fileName); |
| | | if (concatFile.exists()) |
| | | { |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | |
| | | String runningUnitTestsStr = System.getProperty(PROPERTY_RUNNING_UNIT_TESTS); |
| | | if ("true".equalsIgnoreCase(runningUnitTestsStr)) |
| | | { |
| | | writeConcatenatedSchema(); |
| | | concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); |
| | | return concatFile.getAbsoluteFile(); |
| | | } |
| | | throw new InitializationException(ERR_SCHEMA_CANNOT_FIND_CONCAT_FILE.get(upgradeDirectory.getAbsolutePath(), |
| | | SCHEMA_CONCAT_FILE_NAME, concatFile.getName())); |
| | | } |
| | | |
| | | private static String getSchemaDefinition(String definition, String schemaFile) throws ParseException |
| | | { |
| | | if (definition.startsWith("::")) |
| | | { |
| | | // See OPENDJ-2792: the definition of the ds-cfg-csv-delimiter-char attribute type |
| | | // had a space accidentally added after the closing parenthesis. |
| | | // This was unfortunately interpreted as base64 |
| | | definition = ByteString.wrap(Base64.decode(definition.substring(2).trim())).toString(); |
| | | } |
| | | else if (definition.startsWith(":")) |
| | | { |
| | | definition = definition.substring(1).trim(); |
| | | } |
| | | else |
| | | { |
| | | throw new ParseException(ERR_SCHEMA_COULD_NOT_PARSE_DEFINITION.get().toString(), 0); |
| | | } |
| | | |
| | | return SchemaUtils.addSchemaFileToElementDefinitionIfAbsent(definition, schemaFile); |
| | | } |
| | | |
| | | private static void parseSchemaLine(String definition, String fileName, Set<String> attributeTypes, |
| | | Set<String> objectClasses, Set<String> nameForms, Set<String> ditContentRules, Set<String> ditStructureRules, |
| | | Set<String> matchingRuleUses, Set<String> ldapSyntaxes) |
| | | { |
| | | String lowerLine = toLowerCase(definition); |
| | | |
| | | try |
| | | { |
| | | if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) |
| | | { |
| | | addSchemaDefinition(attributeTypes, definition, ATTR_ATTRIBUTE_TYPES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) |
| | | { |
| | | addSchemaDefinition(objectClasses, definition, ATTR_OBJECTCLASSES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) |
| | | { |
| | | addSchemaDefinition(nameForms, definition, ATTR_NAME_FORMS_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) |
| | | { |
| | | addSchemaDefinition(ditContentRules, definition, ATTR_DIT_CONTENT_RULES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) |
| | | { |
| | | addSchemaDefinition(ditStructureRules, definition, ATTR_DIT_STRUCTURE_RULES_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) |
| | | { |
| | | addSchemaDefinition(matchingRuleUses, definition, ATTR_MATCHING_RULE_USE_LC, fileName); |
| | | } |
| | | else if (lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC)) |
| | | { |
| | | addSchemaDefinition(ldapSyntaxes, definition, ATTR_LDAP_SYNTAXES_LC, fileName); |
| | | } |
| | | } |
| | | catch (ParseException pe) |
| | | { |
| | | logger.error(ERR_SCHEMA_PARSE_LINE.get(definition, pe.getLocalizedMessage())); |
| | | } |
| | | } |
| | | |
| | | private static List<StringBuilder> readSchemaElementsFromLdif(File f) throws IOException, FileNotFoundException |
| | | { |
| | | final LinkedList<StringBuilder> lines = new LinkedList<>(); |
| | | |
| | | try (BufferedReader reader = new BufferedReader(new FileReader(f))) |
| | | { |
| | | String line; |
| | | while ((line = reader.readLine()) != null) |
| | | { |
| | | if (line.startsWith("#") || line.length() == 0) |
| | | { |
| | | continue; |
| | | } |
| | | else if (line.startsWith(" ")) |
| | | { |
| | | lines.getLast().append(line.substring(1)); |
| | | } |
| | | else |
| | | { |
| | | lines.add(new StringBuilder(line)); |
| | | } |
| | | } |
| | | } |
| | | return lines; |
| | | } |
| | | |
| | | private static void writeLines(BufferedWriter writer, String... lines) throws IOException |
| | | { |
| | | for (String line : lines) |
| | | { |
| | | writer.write(line); |
| | | writer.newLine(); |
| | | } |
| | | } |
| | | |
| | | private static void writeLines(BufferedWriter writer, String beforeColumn, Set<String> lines) throws IOException |
| | | { |
| | | for (String line : lines) |
| | | { |
| | | writer.write(beforeColumn); |
| | | writer.write(": "); |
| | | writer.write(line); |
| | | writer.newLine(); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.util; |
| | | |
| | | import static org.opends.server.types.Schema.addSchemaFileToElementDefinitionIfAbsent; |
| | | import static org.opends.messages.SchemaMessages.*; |
| | | |
| | | import static org.opends.server.util.ServerConstants.SCHEMA_PROPERTY_FILENAME; |
| | | import static org.opends.server.schema.SchemaConstants.SYNTAX_AUTH_PASSWORD_OID; |
| | | import static org.opends.server.schema.SchemaConstants.SYNTAX_USER_PASSWORD_OID; |
| | | |
| | |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.ObjectClass; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.ldap.schema.SchemaBuilder; |
| | | import org.forgerock.opendj.ldap.schema.SchemaElement; |
| | | import org.opends.server.core.ServerContext; |
| | | import org.opends.server.types.DirectoryException; |
| | | |
| | | import com.forgerock.opendj.util.SubstringReader; |
| | | |
| | | /** Utility methods related to schema. */ |
| | | public class SchemaUtils |
| | |
| | | } |
| | | return attributeType; |
| | | } |
| | | |
| | | /** |
| | | * Adds the provided schema file to the provided schema element definition. |
| | | * |
| | | * @param definition |
| | | * The schema element definition |
| | | * @param schemaFile |
| | | * The name of the schema file to include in the definition |
| | | * @return The definition string of the element |
| | | * including the X-SCHEMA-FILE extension. |
| | | */ |
| | | public static String addSchemaFileToElementDefinitionIfAbsent(String definition, String schemaFile) |
| | | { |
| | | if (schemaFile != null && !definition.contains(SCHEMA_PROPERTY_FILENAME)) |
| | | { |
| | | int pos = definition.lastIndexOf(')'); |
| | | return definition.substring(0, pos).trim() + " " + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; |
| | | } |
| | | return definition; |
| | | } |
| | | |
| | | /** |
| | | * Parses the schema file (value of X-SCHEMA-FILE extension) from the provided schema element |
| | | * definition. |
| | | * <p> |
| | | * It expects a single value for the X-SCHEMA-FILE extension, e.g.: |
| | | * "X-SCHEMA-FILE '99-user.ldif'", as there is no sensible meaning for multiple values. |
| | | * |
| | | * @param definition |
| | | * The definition of a schema element |
| | | * @return the value of the schema file or {@code null} if the X-SCHEMA-FILE extension is not |
| | | * present in the definition |
| | | * @throws DirectoryException |
| | | * If an error occurs while parsing the schema element definition |
| | | */ |
| | | public static String parseSchemaFileFromElementDefinition(String definition) throws DirectoryException |
| | | { |
| | | int pos = definition.lastIndexOf(SCHEMA_PROPERTY_FILENAME); |
| | | if (pos == -1) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | SubstringReader reader = new SubstringReader(definition); |
| | | reader.read(pos + SCHEMA_PROPERTY_FILENAME.length()); |
| | | |
| | | int length = 0; |
| | | reader.skipWhitespaces(); |
| | | reader.mark(); |
| | | try |
| | | { |
| | | // Accept both a quoted value or an unquoted value |
| | | char c = reader.read(); |
| | | if (c == '\'') |
| | | { |
| | | reader.mark(); |
| | | // Parse until the closing quote. |
| | | while (reader.read() != '\'') |
| | | { |
| | | length++; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Parse until the next space. |
| | | do |
| | | { |
| | | length++; |
| | | } |
| | | while (reader.read() != ' '); |
| | | } |
| | | reader.reset(); |
| | | return reader.read(length); |
| | | } |
| | | catch (final StringIndexOutOfBoundsException e) |
| | | { |
| | | // TODO : write the correct message = Error when trying to parse the schema file from a schema |
| | | // element definition |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, LocalizableMessage.raw("")); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided attribute type definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of an attribute type, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseAttributeTypeOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_ATTRIBUTE_TYPE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided object class definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a object class, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseObjectClassOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_OBJECTCLASS_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided name form definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a name form, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseNameFormOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_NAME_FORM_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided DIT content rule definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a DIT content rule, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseDITContentRuleOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_DIT_CONTENT_RULE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the ruleID from the provided DIT structure rule definition, assuming the definition is |
| | | * valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a DIT structure rule, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static int parseRuleID(String definition) throws DirectoryException |
| | | { |
| | | // Reuse code of parseOID, even though this is not an OID |
| | | return Integer.parseInt(parseOID(definition, ERR_PARSING_DIT_STRUCTURE_RULE_RULEID)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided matching rule use definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a matching rule use, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseMatchingRuleUseOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_MATCHING_RULE_USE_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided syntax definition, assuming the definition is valid. |
| | | * <p> |
| | | * This method does not perform any check. |
| | | * |
| | | * @param definition |
| | | * The definition of a syntax, assumed to be valid |
| | | * @return the OID, which is never {@code null} |
| | | * @throws DirectoryException |
| | | * If a problem occurs while parsing the definition |
| | | */ |
| | | public static String parseSyntaxOID(String definition) throws DirectoryException |
| | | { |
| | | return parseOID(definition, ERR_PARSING_LDAP_SYNTAX_OID); |
| | | } |
| | | |
| | | /** |
| | | * Returns the OID from the provided definition, using the provided message if an error occurs. |
| | | * |
| | | * @param definition |
| | | * The definition of a schema element |
| | | * @param parsingErrorMsg |
| | | * Error message to use in case of failure (should be related to |
| | | * the specific schema element parsed) |
| | | * @return the OID corresponding to the definition |
| | | * @throws DirectoryException |
| | | * If the parsing of the definition fails |
| | | * |
| | | * */ |
| | | public static String parseOID(String definition, Arg1<Object> parsingErrorMsg) throws DirectoryException |
| | | { |
| | | try |
| | | { |
| | | int pos = 0; |
| | | int length = definition.length(); |
| | | // Skip over any leading whitespace. |
| | | while (pos < length && (definition.charAt(pos) == ' ')) |
| | | { |
| | | pos++; |
| | | } |
| | | // Skip the open parenthesis. |
| | | pos++; |
| | | // Skip over any spaces immediately following the opening parenthesis. |
| | | while (pos < length && definition.charAt(pos) == ' ') |
| | | { |
| | | pos++; |
| | | } |
| | | // The next set of characters must be the OID. |
| | | int oidStartPos = pos; |
| | | while (pos < length && definition.charAt(pos) != ' ' && definition.charAt(pos) != ')') |
| | | { |
| | | pos++; |
| | | } |
| | | return definition.substring(oidStartPos, pos); |
| | | } |
| | | catch (IndexOutOfBoundsException e) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, parsingErrorMsg.get(definition), e); |
| | | } |
| | | } |
| | | } |