opends/build.xml
@@ -1148,6 +1148,7 @@ <jvmarg value="-Demma.coverage.out.file=${coverage.data.dir}/unit.emma" /> <jvmarg value="-Demma.coverage.out.merge=false" /> <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}" /> <jvmarg value="-Dorg.opends.server.RunningUnitTests=true" /> <jvmarg value="-Dorg.opends.test.suppressOutput=${org.opends.test.suppressOutput}" /> <jvmarg value="-Dorg.opends.test.debug.target=${org.opends.test.debug.target}" /> <jvmarg value="-Xms${MEM}" /> opends/src/server/org/opends/server/api/SynchronizationProvider.java
@@ -28,6 +28,8 @@ import java.util.List; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.core.AddOperation; @@ -36,6 +38,7 @@ import org.opends.server.core.ModifyDNOperation; import org.opends.server.types.DirectoryException; import org.opends.server.types.InitializationException; import org.opends.server.types.Modification; import org.opends.server.types.SynchronizationProviderResult; @@ -368,5 +371,24 @@ public abstract void doPostOperation( ModifyDNOperation modifyDNOperation) throws DirectoryException; /** * Performs any processing that may be required whenever the server * schema has been updated. This may be invoked for schema * modifications made with the server online, and it may also be * called if the server detects that there were any scheam changes * made with the server offline (e.g., by directly editing the * schema configuration files). * <BR><BR> * At the time this method is called, the schema changes will have * already been applied to the server. As such, this method must * make a best effort attempt to process the associated schema * changes, and is not allowed to throw any exceptions. * * @param modifications The set of modifications that have been * made to the server schema. */ public abstract void processSchemaChange(List<Modification> modifications); } opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -40,6 +40,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -109,7 +110,6 @@ import org.opends.server.types.RestoreConfig; import org.opends.server.types.ResultCode; import org.opends.server.types.Schema; import org.opends.server.types.SchemaFileElement; import org.opends.server.types.SearchFilter; import org.opends.server.types.SearchScope; import org.opends.server.util.DynamicConstants; @@ -239,8 +239,6 @@ { super(); // Perform all initialization in initializeBackend. } @@ -420,6 +418,116 @@ supportedFeatures = new HashSet<String>(0); // 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. try { // First, generate lists of elements from the current schema. LinkedHashSet<String> newATs = new LinkedHashSet<String>(); LinkedHashSet<String> newOCs = new LinkedHashSet<String>(); LinkedHashSet<String> newNFs = new LinkedHashSet<String>(); LinkedHashSet<String> newDCRs = new LinkedHashSet<String>(); LinkedHashSet<String> newDSRs = new LinkedHashSet<String>(); LinkedHashSet<String> newMRUs = new LinkedHashSet<String>(); Schema.genConcatenatedSchema(newATs, newOCs, newNFs, newDCRs, newDSRs, newMRUs); // 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. String concatFilePath; File configFile = new File(DirectoryServer.getConfigFile()); File configDirectory = configFile.getParentFile(); File upgradeDirectory = new File(configDirectory, "upgrade"); File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); if (concatFile.exists()) { concatFilePath = concatFile.getAbsolutePath(); } else { concatFile = new File(upgradeDirectory, SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION + DynamicConstants.REVISION_NUMBER); if (concatFile.exists()) { concatFilePath = concatFile.getAbsolutePath(); } else { String runningUnitTestsStr = System.getProperty(PROPERTY_RUNNING_UNIT_TESTS); if ((runningUnitTestsStr != null) && runningUnitTestsStr.equalsIgnoreCase("true")) { Schema.writeConcatenatedSchema(); concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); concatFilePath = concatFile.getAbsolutePath(); } else { msgID = MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE; String message = getMessage(msgID, upgradeDirectory.getAbsolutePath(), SCHEMA_CONCAT_FILE_NAME, concatFile.getName()); throw new InitializationException(msgID, message); } } } LinkedHashSet<String> oldATs = new LinkedHashSet<String>(); LinkedHashSet<String> oldOCs = new LinkedHashSet<String>(); LinkedHashSet<String> oldNFs = new LinkedHashSet<String>(); LinkedHashSet<String> oldDCRs = new LinkedHashSet<String>(); LinkedHashSet<String> oldDSRs = new LinkedHashSet<String>(); LinkedHashSet<String> oldMRUs = new LinkedHashSet<String>(); Schema.readConcatenatedSchema(concatFilePath, oldATs, oldOCs, oldNFs, oldDCRs, oldDSRs, oldMRUs); // Create a list of modifications and add any differences between the old // and new schema into them. LinkedList<Modification> mods = new LinkedList<Modification>(); 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); if (! mods.isEmpty()) { 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) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } msgID = MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES; String message = getMessage(msgID, stackTraceToSingleLineString(e)); logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message, msgID); } // Register with the Directory Server as a configurable component. DirectoryServer.registerConfigurableComponent(this); } @@ -961,13 +1069,6 @@ } // 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. ArrayList<Modification> mods = new ArrayList<Modification>(modifyOperation.getModifications()); if (mods.isEmpty()) @@ -978,18 +1079,19 @@ Schema newSchema = DirectoryServer.getSchema().duplicate(); TreeSet<String> modifiedSchemaFiles = new TreeSet<String>(); LinkedHashSet<SchemaFileElement> dependentElements = new LinkedHashSet<SchemaFileElement>(); int pos = -1; for (Modification m : mods) Iterator<Modification> iterator = mods.iterator(); while (iterator.hasNext()) { Modification m = iterator.next(); pos++; if (m.isInternal()) { // We don't need to do anything for internal modifications (e.g., like // those that set modifiersName and modifyTimestamp). iterator.remove(); continue; } @@ -1441,6 +1543,13 @@ cleanUpTempSchemaFiles(tempSchemaFiles); } // 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(); DN authzDN = modifyOperation.getAuthorizationDN(); if (authzDN == null) { opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -35,6 +35,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -141,6 +142,7 @@ import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.InitializationException; import org.opends.server.types.MatchingRuleUse; import org.opends.server.types.Modification; import org.opends.server.types.NameForm; import org.opends.server.types.ObjectClass; import org.opends.server.types.ObjectClassType; @@ -435,6 +437,10 @@ // The set of connections that are currently established. private LinkedHashSet<ClientConnection> establishedConnections; // The set of schema changes made by editing the schema configuration files // with the server offline. private List<Modification> offlineSchemaChanges; // The logger configuration manager for the Directory Server. private LoggerConfigManager loggerConfigManager; @@ -670,6 +676,7 @@ directoryServer.saslMechanismHandlers = new ConcurrentHashMap<String,SASLMechanismHandler>(); directoryServer.authenticatedUsers = new AuthenticatedUsers(); directoryServer.offlineSchemaChanges = new LinkedList<Modification>(); } @@ -2585,6 +2592,42 @@ /** * Retrieves a list of modifications detailing any schema changes that may * have been made with the server offline (e.g., by directly editing the * schema configuration files). Note that this information will not be * available until the server backends (and in particular, the schema backend) * have been initialized. * * @return A list of modifications detailing any schema changes that may have * been made with the server offline, or an empty list if no offline * schema changes have been detected. */ public static List<Modification> getOfflineSchemaChanges() { return directoryServer.offlineSchemaChanges; } /** * Specifies a list of modifications detailing any schema changes that may * have been made with the server offline. * * @param offlineSchemaChanges A list of modifications detailing any schema * changes that may have been made with the * server offline. It must not be {@code null}. */ public static void setOfflineSchemaChanges(List<Modification> offlineSchemaChanges) { ensureNotNull(offlineSchemaChanges); directoryServer.offlineSchemaChanges = offlineSchemaChanges; } /** * 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 opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -31,6 +31,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.opends.server.api.AttributeSyntax; @@ -63,6 +64,8 @@ import org.opends.server.types.InitializationException; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.MatchingRuleUse; import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.NameForm; import org.opends.server.types.ObjectClass; import org.opends.server.types.ResultCode; @@ -94,8 +97,6 @@ public class SchemaConfigManager implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener { // The schema that has been parsed from the server configuration. private Schema schema; @@ -771,6 +772,9 @@ * @param schemaFile The name of the schema file to be loaded into the * provided schema. * * @return A list of the modifications that could be performed in order to * obtain the contents of the file. * * @throws ConfigException If a configuration problem causes the schema * element initialization to fail. * @@ -778,10 +782,11 @@ * the schema elements that is not related * to the server configuration. */ public static void loadSchemaFile(Schema schema, String schemaFile) public static List<Modification> loadSchemaFile(Schema schema, String schemaFile) throws ConfigException, InitializationException { loadSchemaFile(schema, schemaFile, true); return loadSchemaFile(schema, schemaFile, true); } @@ -800,6 +805,10 @@ * This should only be {@code false} when called from * {@code initializeSchemaFromFiles}. * * @return A list of the modifications that could be performed in order to * obtain the contents of the file, or {@code null} if a problem * occurred and {@code failOnError} is {@code false}. * * @throws ConfigException If a configuration problem causes the schema * element initialization to fail. * @@ -807,8 +816,9 @@ * the schema elements that is not related * to the server configuration. */ private static void loadSchemaFile(Schema schema, String schemaFile, boolean failOnError) private static List<Modification> loadSchemaFile(Schema schema, String schemaFile, boolean failOnError) throws ConfigException, InitializationException { // Create an LDIF reader to use when reading the files. @@ -838,7 +848,7 @@ { logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message, msgID); return; return null; } } @@ -852,7 +862,7 @@ if (entry == null) { // The file was empty -- skip it. return; return new LinkedList<Modification>(); } } catch (Exception e) @@ -874,7 +884,7 @@ { logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message, msgID); return; return null; } } @@ -892,6 +902,7 @@ // Get the attributeTypes attribute from the entry. LinkedList<Modification> mods = new LinkedList<Modification>(); AttributeTypeSyntax attrTypeSyntax; try { @@ -924,6 +935,13 @@ } List<Attribute> attrList = entry.getAttribute(attributeAttrType); if ((attrList != null) && (! attrList.isEmpty())) { for (Attribute a : attrList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } // Get the objectClasses attribute from the entry. @@ -958,6 +976,13 @@ } List<Attribute> ocList = entry.getAttribute(objectclassAttrType); if ((ocList != null) && (! ocList.isEmpty())) { for (Attribute a : ocList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } // Get the name forms attribute from the entry. @@ -991,6 +1016,13 @@ } List<Attribute> nfList = entry.getAttribute(nameFormAttrType); if ((nfList != null) && (! nfList.isEmpty())) { for (Attribute a : nfList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } // Get the DIT content rules attribute from the entry. @@ -1026,6 +1058,13 @@ } List<Attribute> dcrList = entry.getAttribute(dcrAttrType); if ((dcrList != null) && (! dcrList.isEmpty())) { for (Attribute a : dcrList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } // Get the DIT structure rules attribute from the entry. @@ -1061,6 +1100,13 @@ } List<Attribute> dsrList = entry.getAttribute(dsrAttrType); if ((dsrList != null) && (! dsrList.isEmpty())) { for (Attribute a : dsrList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } // Get the matching rule uses attribute from the entry. @@ -1096,6 +1142,14 @@ } List<Attribute> mruList = entry.getAttribute(mruAttrType); if ((mruList != null) && (! mruList.isEmpty())) { for (Attribute a : mruList) { mods.add(new Modification(ModificationType.ADD, a.duplicate())); } } AttributeType synchronizationStateType = schema.getAttributeType(ATTR_SYNCHRONIZATION_STATE_LC); @@ -1124,6 +1178,8 @@ { attrType = attrTypeSyntax.decodeAttributeType(v.getValue(), schema, false); attrType.setExtraProperty(SCHEMA_PROPERTY_FILENAME, (String) null); attrType.setSchemaFile(schemaFile); } catch (DirectoryException de) { @@ -1221,6 +1277,8 @@ try { oc = ocSyntax.decodeObjectClass(v.getValue(), schema, false); oc.setExtraProperty(SCHEMA_PROPERTY_FILENAME, (String) null); oc.setSchemaFile(schemaFile); } catch (DirectoryException de) { @@ -1702,6 +1760,9 @@ } } } return mods; } opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3181,6 +3181,41 @@ /** * The message ID for the message that will be used if the schema backend is * unable to find a file containing the concatenated schema definitions. This * takes three arguments, which are the path to the directory in which the * file should have been found, the name of the most recent concatenated * schema file, and the name of the base concatenated schema file shipped with * the server. */ public static final int MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE = CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 294; /** * The message ID for the message that will be used if an error occurs while * attempting determine whether there were any changes made to the server * schema while the server was offline. This takes a single argument, which * is a string representation of the exception that was caught. */ public static final int MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES = CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 295; /** * The message ID for the message that will be used if an error occurs while * trying to write a file containing the concatenated server schema. This * takes two arguments, which are the path to the file being written and a * string representation of the exception that was caught. */ public static final int MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE = CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 296; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -3401,6 +3436,22 @@ " attribute of configuration entry %s: %s. The default " + "behavior, which is to treat the attribute types as " + "defined in the server schema, will be used."); registerMessage(MSGID_SCHEMA_CANNOT_FIND_CONCAT_FILE, "Unable to find a file containing concatenated schema " + "element definitions in order to determine if any schema " + "changes were made with the server offline. The " + "file was expected in the %s directory and should have " + "been named either %s or %s."); registerMessage(MSGID_SCHEMA_ERROR_DETERMINING_SCHEMA_CHANGES, "An error occurred while attempting to determine whether " + "any schema changes had been made by directly editing " + "the schema files with the server offline: %s."); registerMessage(MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, "An error occurred while attempting to write file %s " + "containing a concatenated list of all server schema " + "elements: %s. The server may not be able to " + "accurately identify any schema changes made with the " + "server offline."); registerMessage(MSGID_SCHEMA_ADD_NOT_SUPPORTED, "Unwilling to add entry \"%s\" because add operations " + "are not supported in the schema backend."); opends/src/server/org/opends/server/messages/TaskMessages.java
@@ -202,6 +202,18 @@ /** * The message ID for the shutdown message that will be used if an error * occurs while trying to notify a synchronization provider about the new * schema elements added to the server. This takes two arguments, which are * the name of the synchronization provider class and a string representation * of the exception that was caught. */ public static final int MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 17; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -245,6 +257,10 @@ registerMessage(MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE, "An error occurred while attempting to load the contents " + "of schema file %s into the server schema: %s."); registerMessage(MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER, "An error occurred while attempting to notify a " + "synchronization provider of type %s about the schema " + "changes made by the add schema file task: %s."); registerMessage(MSGID_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA, "Unable to add one or more files to the server schema " + "because the server was unable to obtain a write lock on " + opends/src/server/org/opends/server/synchronization/plugin/MultimasterSynchronization.java
@@ -120,6 +120,17 @@ createNewSynchronizationDomain(domainEntry); } } /* * If any schema changes were made with the server offline, then handle them * now. */ List<Modification> offlineSchemaChanges = DirectoryServer.getOfflineSchemaChanges(); if ((offlineSchemaChanges != null) && (! offlineSchemaChanges.isEmpty())) { processSchemaChange(offlineSchemaChanges); } } /** @@ -548,7 +559,7 @@ * applied to the schema. * */ public static void schemaChangeNotification(List<Modification> modifications) public void processSchemaChange(List<Modification> modifications) { SynchronizationDomain domain = findDomain(DirectoryServer.getSchemaDN(), null); opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
@@ -29,11 +29,14 @@ import java.io.File; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import java.util.concurrent.locks.Lock; import org.opends.server.api.ClientConnection; import org.opends.server.api.SynchronizationProvider; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskState; import org.opends.server.config.ConfigException; @@ -43,6 +46,7 @@ import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.DebugLogLevel; import org.opends.server.types.DirectoryException; import org.opends.server.types.DN; import org.opends.server.types.Entry; @@ -50,13 +54,16 @@ import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.InitializationException; import org.opends.server.types.LockManager; import org.opends.server.types.Modification; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.Schema; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; @@ -68,9 +75,6 @@ public class AddSchemaFileTask extends Task { // The list of files to be added to the server schema. TreeSet<String> filesToAdd; @@ -139,6 +143,11 @@ } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } int msgID = MSGID_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE; String message = getMessage(msgID, filename, schemaDirectory, stackTraceToSingleLineString(e)); @@ -163,6 +172,11 @@ } catch (ConfigException ce) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, ce); } int msgID = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE; String message = getMessage(msgID, String.valueOf(schemaFile), ce.getMessage()); @@ -171,6 +185,11 @@ } catch (InitializationException ie) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, ie); } int msgID = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE; String message = getMessage(msgID, String.valueOf(schemaFile), ie.getMessage()); @@ -207,15 +226,53 @@ try { LinkedList<Modification> mods = new LinkedList<Modification>(); Schema schema = DirectoryServer.getSchema().duplicate(); for (String schemaFile : filesToAdd) { try { SchemaConfigManager.loadSchemaFile(schema, schemaFile); List<Modification> modList = SchemaConfigManager.loadSchemaFile(schema, schemaFile); for (Modification m : modList) { Attribute a = m.getAttribute(); LinkedHashSet<AttributeValue> valuesWithFileElement = new LinkedHashSet<AttributeValue>(); for (AttributeValue v : a.getValues()) { String s = v.getStringValue(); if (s.indexOf(SCHEMA_PROPERTY_FILENAME) < 0) { if (s.endsWith(" )")) { s = s.substring(0, s.length()-1) + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; } else if (s.endsWith(")")) { s = s.substring(0, s.length()-1) + " " + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; } } valuesWithFileElement.add(new AttributeValue(a.getAttributeType(), s)); } Attribute attrWithFile = new Attribute(a.getAttributeType(), a.getName(), valuesWithFileElement); mods.add(new Modification(m.getModificationType(), attrWithFile)); } } catch (ConfigException ce) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, ce); } int msgID = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE; String message = getMessage(msgID, String.valueOf(schemaFile), ce.getMessage()); @@ -225,6 +282,11 @@ } catch (InitializationException ie) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, ie); } int msgID = MSGID_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE; String message = getMessage(msgID, String.valueOf(schemaFile), ie.getMessage()); @@ -234,6 +296,33 @@ } } if (! mods.isEmpty()) { for (SynchronizationProvider provider : DirectoryServer.getSynchronizationProviders()) { try { provider.processSchemaChange(mods); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } int msgID = MSGID_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER; String message = getMessage(msgID, provider.getClass().getName(), stackTraceToSingleLineString(e)); logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR, message, msgID); } } Schema.writeConcatenatedSchema(); } schema.setYoungestModificationTime(System.currentTimeMillis()); DirectoryServer.setSchema(schema); return TaskState.COMPLETED_SUCCESSFULLY; opends/src/server/org/opends/server/types/AttributeType.java
@@ -40,10 +40,7 @@ import org.opends.server.core.DirectoryServer; import org.opends.server.schema.AttributeTypeSyntax; import static org.opends.server.loggers.debug.DebugLogger.debugCaught; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.ServerConstants.*; @@ -70,9 +67,6 @@ extends CommonSchemaElements implements SchemaFileElement { // The approximate matching rule for this attribute type. private final ApproximateMatchingRule approximateMatchingRule; @@ -267,7 +261,6 @@ ensureNotNull(definition, oid); this.definition = definition; this.superiorType = superiorType; this.isCollective = isCollective; this.isNoUserModification = isNoUserModification; @@ -275,6 +268,36 @@ mayHaveSubordinateTypes = false; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } if (syntax == null) { if (superiorType != null) opends/src/server/org/opends/server/types/DITContentRule.java
@@ -38,6 +38,7 @@ import org.opends.server.schema.DITContentRuleSyntax; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.ServerConstants.*; @@ -55,9 +56,6 @@ public final class DITContentRule implements SchemaFileElement { // Indicates whether this content rule is declared "obsolete". private final boolean isObsolete; @@ -133,11 +131,40 @@ { ensureNotNull(definition, structuralClass); this.definition = definition; this.structuralClass = structuralClass; this.description = description; this.isObsolete = isObsolete; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } if ((names == null) || names.isEmpty()) { this.names = new LinkedHashMap<String,String>(0); opends/src/server/org/opends/server/types/DITStructureRule.java
@@ -38,6 +38,7 @@ import org.opends.server.schema.DITStructureRuleSyntax; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.ServerConstants.*; @@ -53,9 +54,6 @@ public final class DITStructureRule implements SchemaFileElement { // Indicates whether this DIT structure rule is declared "obsolete". private final boolean isObsolete; @@ -113,12 +111,41 @@ { ensureNotNull(definition); this.definition = definition; this.ruleID = ruleID; this.description = description; this.isObsolete = isObsolete; this.nameForm = nameForm; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } if ((names == null) || names.isEmpty()) { this.names = new LinkedHashMap<String,String>(0); opends/src/server/org/opends/server/types/MatchingRuleUse.java
@@ -39,6 +39,7 @@ import org.opends.server.api.MatchingRule; import org.opends.server.schema.MatchingRuleUseSyntax; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import static org.opends.server.util.Validator.*; @@ -54,9 +55,6 @@ public final class MatchingRuleUse implements SchemaFileElement { // Indicates whether this matching rule use is declared "obsolete". private final boolean isObsolete; @@ -113,11 +111,40 @@ { ensureNotNull(definition, matchingRule); this.definition = definition; this.matchingRule = matchingRule; this.description = description; this.isObsolete = isObsolete; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } if ((names == null) || names.isEmpty()) { this.names = new LinkedHashMap<String,String>(0); opends/src/server/org/opends/server/types/NameForm.java
@@ -38,6 +38,7 @@ import org.opends.server.schema.NameFormSyntax; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import static org.opends.server.util.Validator.*; @@ -53,9 +54,6 @@ public final class NameForm implements SchemaFileElement { // Indicates whether this name form is declared "obsolete". private final boolean isObsolete; @@ -119,12 +117,41 @@ { ensureNotNull(definition, oid, structuralClass); this.definition = definition; this.oid = oid; this.description = description; this.isObsolete = isObsolete; this.structuralClass = structuralClass; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } if ((names == null) || names.isEmpty()) { this.names = new LinkedHashMap<String,String>(0); opends/src/server/org/opends/server/types/ObjectClass.java
@@ -39,6 +39,7 @@ import org.opends.server.schema.ObjectClassSyntax; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.Validator.*; @@ -63,7 +64,6 @@ extends CommonSchemaElements implements SchemaFileElement { // The set of optional attribute types for this objectclass. private final Set<AttributeType> optionalAttributes; @@ -150,9 +150,38 @@ ensureNotNull(definition, oid); this.definition = definition; this.superiorClass = superiorClass; int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); if (schemaFilePos > 0) { String defStr; try { int firstQuotePos = definition.indexOf('\'', schemaFilePos); int secondQuotePos = definition.indexOf('\'', firstQuotePos+1); defStr = definition.substring(0, schemaFilePos).trim() + " " + definition.substring(secondQuotePos+1).trim(); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, e); } defStr = definition; } this.definition = defStr; } else { this.definition = definition; } // Set flag indicating whether or not this object class allows any // attributes. if (hasName(OC_EXTENSIBLE_OBJECT_LC) opends/src/server/org/opends/server/types/Schema.java
@@ -28,10 +28,17 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import org.opends.server.api.ApproximateMatchingRule; @@ -40,10 +47,17 @@ import org.opends.server.api.MatchingRule; import org.opends.server.api.OrderingMatchingRule; import org.opends.server.api.SubstringMatchingRule; import org.opends.server.core.DirectoryServer; import org.opends.server.core.SchemaConfigManager; import org.opends.server.protocols.asn1.ASN1OctetString; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.loggers.Error.*; import static org.opends.server.messages.BackendMessages.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; @@ -2822,5 +2836,434 @@ { synchronizationState = values; } /** * 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 { LinkedHashSet<String> attributeTypes = new LinkedHashSet<String>(); LinkedHashSet<String> objectClasses = new LinkedHashSet<String>(); LinkedHashSet<String> nameForms = new LinkedHashSet<String>(); LinkedHashSet<String> ditContentRules = new LinkedHashSet<String>(); LinkedHashSet<String> ditStructureRules = new LinkedHashSet<String>(); LinkedHashSet<String> matchingRuleUses = new LinkedHashSet<String>(); genConcatenatedSchema(attributeTypes, objectClasses, nameForms, ditContentRules, ditStructureRules, matchingRuleUses); File configFile = new File(DirectoryServer.getConfigFile()); File configDirectory = configFile.getParentFile(); File upgradeDirectory = new File(configDirectory, "upgrade"); File concatFile = new File(upgradeDirectory, SCHEMA_CONCAT_FILE_NAME); concatFilePath = concatFile.getAbsolutePath(); File tempFile = new File(concatFilePath + ".tmp"); BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile, false)); writer.write("dn: " + DirectoryServer.getSchemaDN().toString()); writer.newLine(); writer.write("objectClass: top"); writer.newLine(); writer.write("objectClass: ldapSubentry"); writer.newLine(); writer.write("objectClass: subschema"); writer.newLine(); for (String line : attributeTypes) { writer.write(ATTR_ATTRIBUTE_TYPES); writer.write(": "); writer.write(line); writer.newLine(); } for (String line : objectClasses) { writer.write(ATTR_OBJECTCLASSES); writer.write(": "); writer.write(line); writer.newLine(); } for (String line : nameForms) { writer.write(ATTR_NAME_FORMS); writer.write(": "); writer.write(line); writer.newLine(); } for (String line : ditContentRules) { writer.write(ATTR_DIT_CONTENT_RULES); writer.write(": "); writer.write(line); writer.newLine(); } for (String line : ditStructureRules) { writer.write(ATTR_DIT_STRUCTURE_RULES); writer.write(": "); writer.write(line); writer.newLine(); } for (String line : matchingRuleUses) { writer.write(ATTR_MATCHING_RULE_USE); writer.write(": "); writer.write(line); writer.newLine(); } writer.close(); if (concatFile.exists()) { concatFile.delete(); } tempFile.renameTo(concatFile); } catch (Exception e) { if (debugEnabled()) { debugCaught(DebugLogLevel.ERROR, 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. logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, MSGID_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, String.valueOf(concatFilePath), stackTraceToSingleLineString(e)); } } /** * 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. * * @throws IOException If a problem occurs while reading the * schema file elements. */ public static void genConcatenatedSchema( LinkedHashSet<String> attributeTypes, LinkedHashSet<String> objectClasses, LinkedHashSet<String> nameForms, LinkedHashSet<String> ditContentRules, LinkedHashSet<String> ditStructureRules, LinkedHashSet<String> matchingRuleUses) throws IOException { // Get a sorted list of the files in the schema directory. String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath(); TreeSet<String> schemaFileNames = new TreeSet<String>(); for (File f : new File(schemaDirectory).listFiles()) { if (f.isFile()) { schemaFileNames.add(f.getName()); } } // Open each of the files in order and read the elements that they // contain, appending them to the appropriate lists. for (String name : schemaFileNames) { // Read the contents of the file into a list with one schema // element per list element. LinkedList<StringBuilder> lines = new LinkedList<StringBuilder>(); BufferedReader reader = new BufferedReader(new FileReader( new File(schemaDirectory, name))); while (true) { String line = reader.readLine(); if (line == null) { break; } else if (line.startsWith("#") || (line.length() == 0)) { continue; } else if (line.startsWith(" ")) { lines.getLast().append(line.substring(1)); } else { lines.add(new StringBuilder(line)); } } reader.close(); // Iterate through each line in the list. Find the colon and // get the attribute name at the beginning. If it's someting // 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) { // Get the line and add the X-SCHEMA-FILE extension to the end // of it. All of them should end with " )" but some might // have the parenthesis crammed up against the last character // so deal with that as well. String line = buffer.toString().trim(); if (line.endsWith(" )")) { line = line.substring(0, line.length()-1) + SCHEMA_PROPERTY_FILENAME + " '" + name + "' )"; } else if (line.endsWith(")")) { line = line.substring(0, line.length()-1) + " " + SCHEMA_PROPERTY_FILENAME + " '" + name + "' )"; } else { continue; } String value; String lowerLine = toLowerCase(line); if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) { value = line.substring( ATTR_ATTRIBUTE_TYPES.length()+1).trim(); attributeTypes.add(value); } else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) { value = line.substring( ATTR_OBJECTCLASSES.length()+1).trim(); objectClasses.add(value); } else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) { value = line.substring(ATTR_NAME_FORMS.length()+1).trim(); nameForms.add(value); } else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) { value = line.substring( ATTR_DIT_CONTENT_RULES.length()+1).trim(); ditContentRules.add(value); } else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) { value = line.substring( ATTR_DIT_STRUCTURE_RULES.length()+1).trim(); ditStructureRules.add(value); } else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) { value = line.substring( ATTR_MATCHING_RULE_USE.length()+1).trim(); matchingRuleUses.add(value); } } } } /** * Reads data from the specified concatenated schema file into the * provided sets. * * @param concatSchemaFile The path to 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. * * @throws IOException If a problem occurs while reading the * schema file elements. */ public static void readConcatenatedSchema(String concatSchemaFile, LinkedHashSet<String> attributeTypes, LinkedHashSet<String> objectClasses, LinkedHashSet<String> nameForms, LinkedHashSet<String> ditContentRules, LinkedHashSet<String> ditStructureRules, LinkedHashSet<String> matchingRuleUses) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(concatSchemaFile)); while (true) { String line = reader.readLine(); if (line == null) { break; } String value; String lowerLine = toLowerCase(line); if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) { value = line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim(); attributeTypes.add(value); } else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) { value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim(); objectClasses.add(value); } else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) { value = line.substring(ATTR_NAME_FORMS.length()+1).trim(); nameForms.add(value); } else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) { value = line.substring( ATTR_DIT_CONTENT_RULES.length()+1).trim(); ditContentRules.add(value); } else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) { value = line.substring( ATTR_DIT_STRUCTURE_RULES.length()+1).trim(); ditStructureRules.add(value); } else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) { value = line.substring( ATTR_MATCHING_RULE_USE.length()+1).trim(); matchingRuleUses.add(value); } } reader.close(); } /** * 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( LinkedHashSet<String> oldElements, LinkedHashSet<String> newElements, AttributeType elementType, LinkedList<Modification> mods) { AttributeType attributeTypesType = DirectoryServer.getAttributeType(ATTR_ATTRIBUTE_TYPES_LC, true); LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(); for (String s : oldElements) { if (! newElements.contains(s)) { values.add(new AttributeValue(attributeTypesType, s)); } } if (! values.isEmpty()) { mods.add(new Modification(ModificationType.DELETE, new Attribute(elementType, elementType.getNameOrOID(), values))); } values.clear(); for (String s : newElements) { if (! oldElements.contains(s)) { values.add(new AttributeValue(attributeTypesType, s)); } } if (! values.isEmpty()) { mods.add(new Modification(ModificationType.ADD, new Attribute(elementType, elementType.getNameOrOID(), values))); } } } opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2232,6 +2232,15 @@ /** * The name of the system property that can be used to determine whether the * Directory Server is starting up for the purpose of running the unit tests. */ public static final String PROPERTY_RUNNING_UNIT_TESTS = "org.opends.server.RunningUnitTests"; /** * The name of the system property that can be used to specify the path to the * directory in which the schema configuration files may be found. If this is * not set, then the server wiill use a directory named "schema" below the @@ -2281,5 +2290,25 @@ * The column at which to wrap long lines of output in the command-line tools. */ public static final int MAX_LINE_WIDTH = 79; /** * The name that should be used for the file to which the latest complete * schema data should be concatenated. */ public static final String SCHEMA_CONCAT_FILE_NAME = "schema.ldif.current"; /** * The name that should be used for the concatenated schema file generated at * build time with the base schema for the Subversion revision on which the * current build is based. The value of * {@code DynamicConstants.REVISION_NUMBER} must be appended to this value in * order to get the full name. */ public static final String SCHEMA_BASE_FILE_NAME_WITHOUT_REVISION = "schema.ldif."; } opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SchemaSynchronizationTest.java
@@ -38,6 +38,7 @@ import java.util.List; import org.opends.server.TestCaseUtils; import org.opends.server.api.SynchronizationProvider; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyOperation; import org.opends.server.core.Operation; @@ -276,7 +277,11 @@ Modification mod = new Modification(ModificationType.ADD, attr); mods.add(mod); MultimasterSynchronization.schemaChangeNotification(mods); for (SynchronizationProvider provider : DirectoryServer.getSynchronizationProviders()) { provider.processSchemaChange(mods); } // receive the message on the broker side. SynchronizationMessage msg = broker.receive();