| | |
| | | */ |
| | | package org.opends.server.core; |
| | | |
| | | import static org.opends.messages.SchemaMessages.ERR_SCHEMA_HAS_WARNINGS; |
| | | |
| | | import static org.forgerock.util.Utils.*; |
| | | import static org.opends.messages.ConfigMessages.*; |
| | | import static org.opends.server.replication.plugin.HistoricalCsnOrderingMatchingRuleImpl.*; |
| | | import static org.opends.server.schema.AciSyntax.*; |
| | | import static org.opends.server.schema.SubtreeSpecificationSyntax.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.io.File; |
| | |
| | | import java.io.FilenameFilter; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.concurrent.locks.Lock; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | | import org.opends.server.replication.plugin.HistoricalCsnOrderingMatchingRuleImpl; |
| | | import org.opends.server.schema.AciSyntax; |
| | | import org.opends.server.schema.SubtreeSpecificationSyntax; |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.config.ClassPropertyDefinition; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.ldap.schema.SchemaBuilder; |
| | | import org.forgerock.opendj.ldif.EntryReader; |
| | |
| | | import org.forgerock.opendj.server.config.meta.SchemaProviderCfgDefn; |
| | | import org.forgerock.opendj.server.config.server.RootCfg; |
| | | import org.forgerock.opendj.server.config.server.SchemaProviderCfg; |
| | | import org.forgerock.util.Option; |
| | | import org.forgerock.util.Utils; |
| | | import org.opends.server.schema.SchemaProvider; |
| | | import org.opends.server.types.DirectoryException; |
| | |
| | | |
| | | private ServerContext serverContext; |
| | | |
| | | /** |
| | | * The schema. |
| | | * <p> |
| | | * @GuardedBy("exclusiveLock") |
| | | */ |
| | | private volatile Schema schemaNG; |
| | | |
| | | /** Guards updates to the schema. */ |
| | | private final Lock exclusiveLock = new ReentrantLock(); |
| | | |
| | | private long oldestModificationTime = -1L; |
| | | |
| | | private long youngestModificationTime = -1L; |
| | |
| | | { |
| | | this.serverContext = serverContext; |
| | | |
| | | final org.opends.server.types.Schema schema = serverContext.getSchema(); |
| | | |
| | | schema.exclusiveLock(); |
| | | exclusiveLock.lock(); |
| | | try |
| | | { |
| | | // Start from the core schema (TODO: or start with empty schema and add core schema in core schema provider ?) |
| | |
| | | |
| | | try |
| | | { |
| | | schema.updateSchema(new SchemaUpdater() |
| | | { |
| | | @Override |
| | | public Schema update(SchemaBuilder ignored) |
| | | { |
| | | // see RemoteSchemaLoader.readSchema() |
| | | addAciSyntax(schemaBuilder); |
| | | addSubtreeSpecificationSyntax(schemaBuilder); |
| | | addHistoricalCsnOrderingMatchingRule(schemaBuilder); |
| | | // see RemoteSchemaLoader.readSchema() |
| | | AciSyntax.addAciSyntax(schemaBuilder); |
| | | SubtreeSpecificationSyntax.addSubtreeSpecificationSyntax(schemaBuilder); |
| | | HistoricalCsnOrderingMatchingRuleImpl.addHistoricalCsnOrderingMatchingRule(schemaBuilder); |
| | | |
| | | // Uses the builder incrementally updated instead of the default provided by the method. |
| | | // This is why it is necessary to explicitly lock/unlock the schema updater. |
| | | return schemaBuilder.toSchema(); |
| | | } |
| | | }); |
| | | switchSchema(schemaBuilder.toSchema()); |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | |
| | | } |
| | | finally |
| | | { |
| | | schema.exclusiveUnlock(); |
| | | exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Update the schema using the provided schema updater. |
| | | * <p> |
| | | * An implicit lock is performed, so it is in general not necessary |
| | | * to call the {code lock()} and {code unlock() methods. |
| | | * However, these method should be used if/when the SchemaBuilder passed |
| | | * as an argument to the updater is not used to return the schema |
| | | * (see for example usage in {@code CoreSchemaProvider} class). This |
| | | * case should remain exceptional. |
| | | * |
| | | * @param updater |
| | | * the updater that returns a new schema |
| | | * @throws DirectoryException if there is any problem updating the schema |
| | | */ |
| | | public void updateSchema(SchemaUpdater updater) throws DirectoryException |
| | | { |
| | | exclusiveLock.lock(); |
| | | try |
| | | { |
| | | switchSchema(updater.update(new SchemaBuilder(schemaNG))); |
| | | } |
| | | finally |
| | | { |
| | | exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the schema option if the new value differs from the old value. |
| | | * |
| | | * @param <T> the schema option's type |
| | | * @param option the schema option to update |
| | | * @param newValue the new value for the schema option |
| | | * @throws DirectoryException if there is any problem updating the schema |
| | | */ |
| | | public <T> void updateSchemaOption(final Option<T> option, final T newValue) throws DirectoryException |
| | | { |
| | | final T oldValue = schemaNG.getOption(option); |
| | | if (!oldValue.equals(newValue)) |
| | | { |
| | | updateSchema(new SchemaUpdater() |
| | | { |
| | | @Override |
| | | public Schema update(SchemaBuilder builder) |
| | | { |
| | | return builder.setOption(option, newValue).toSchema(); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** Takes an exclusive lock on the schema. */ |
| | | public void exclusiveLock() |
| | | { |
| | | exclusiveLock.lock(); |
| | | } |
| | | |
| | | /** Releases an exclusive lock on the schema. */ |
| | | public void exclusiveUnlock() |
| | | { |
| | | exclusiveLock.unlock(); |
| | | } |
| | | |
| | | /** |
| | | * Load the schema from provided root configuration. |
| | | * |
| | | * @param rootConfiguration |
| | |
| | | } |
| | | } |
| | | |
| | | private void switchSchema(Schema newSchema) throws DirectoryException |
| | | { |
| | | rejectSchemaWithWarnings(newSchema); |
| | | schemaNG = newSchema.asNonStrictSchema(); |
| | | Schema.setDefaultSchema(schemaNG); |
| | | } |
| | | |
| | | private void rejectSchemaWithWarnings(Schema newSchema) throws DirectoryException |
| | | { |
| | | Collection<LocalizableMessage> warnings = newSchema.getWarnings(); |
| | | if (!warnings.isEmpty()) |
| | | { |
| | | throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, |
| | | ERR_SCHEMA_HAS_WARNINGS.get(warnings.size(), Utils.joinAsString("; ", warnings))); |
| | | } |
| | | } |
| | | |
| | | /** A file filter implementation that accepts only LDIF files. */ |
| | | private static class SchemaFileFilter implements FilenameFilter |
| | | { |
| | |
| | | return filename.endsWith(LDIF_SUFFIX); |
| | | } |
| | | } |
| | | |
| | | /** Interface to update a schema provided a schema builder. */ |
| | | public interface SchemaUpdater |
| | | { |
| | | /** |
| | | * Returns an updated schema. |
| | | * |
| | | * @param builder |
| | | * The builder on the current schema |
| | | * @return the new schema |
| | | */ |
| | | Schema update(SchemaBuilder builder); |
| | | } |
| | | } |