mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
21.25.2014 bd0775818758032e3db84e517e3d9621c3b9cec6
Preparatory work for OPENDJ-1477: Constant re-allocation of DN per-schema caches

CR-4331: Ensure that the same schema instance is always returned across consecutive calls to Schema.asStrictSchema() and Schema.asNonStrictSchema() by delegating ownership of the schema views to the underlying implementations. The changes required that I remove the EmptyImpl schema implementation, which is actually a nice simplification:

* DefaultSchema - renamed to more appropriately named DelayedSchema
* DelayedSchema - renamed from DefaultSchema. Now also initializes the singleton "empty schema"
* Schema - remove EmptyImpl; Impls now responsible for managing strict and non-strict views
* SchemaBuilder - consequence of changes to Schema
* SchemaTestCase - enabled tests.
1 files renamed
3 files modified
372 ■■■■ changed files
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DelayedSchema.java 11 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java 346 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 11 ●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java 4 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DelayedSchema.java
File was renamed from opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DefaultSchema.java
@@ -21,20 +21,21 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2011 ForgeRock AS
 *      Copyright 2011-2014 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
/**
 * This class is used to maintain a reference to the default schema and avoid
 * This class is used to maintain a reference to the global schemas and avoids
 * having to reference the core schema in the {@link Schema} class since this
 * can cause initialization errors because the CoreSchema depends on Schema.
 */
final class DefaultSchema {
    static volatile Schema schema = Schema.getCoreSchema();
final class DelayedSchema {
    static final Schema EMPTY_SCHEMA = new SchemaBuilder("Empty Schema").toSchema().asNonStrictSchema();
    static volatile Schema defaultSchema = Schema.getCoreSchema();
    private DefaultSchema() {
    private DelayedSchema() {
        // Prevent instantiation.
    }
}
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2012 ForgeRock AS
 *      Portions copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2014 Manuel Gaupp
 */
package org.forgerock.opendj.ldap.schema;
@@ -72,252 +72,11 @@
 * </UL>
 */
public final class Schema {
    private static final class EmptyImpl implements Impl {
        private final boolean isStrict;
        private EmptyImpl(final boolean isStrict) {
            this.isStrict = isStrict;
        }
        @Override
        public boolean allowMalformedNamesAndOptions() {
            return true;
        }
        @Override
        public boolean allowNonStandardTelephoneNumbers() {
            return true;
        }
        @Override
        public boolean allowMalformedJPEGPhotos() {
            return true;
        }
        @Override
        public boolean allowMalformedCertificates() {
            return true;
        }
        @Override
        public boolean allowZeroLengthDirectoryStrings() {
            return false;
        }
        @Override
        public Syntax getDefaultSyntax() {
            return Schema.getCoreSchema().getDefaultSyntax();
        }
        @Override
        public MatchingRule getDefaultMatchingRule() {
            return Schema.getCoreSchema().getDefaultMatchingRule();
        }
        @Override
        public AttributeType getAttributeType(final Schema schema, final String name) {
            if (isStrict) {
                throw new UnknownSchemaElementException(WARN_ATTR_TYPE_UNKNOWN.get(name));
            } else {
                // Return a place-holder.
                return new AttributeType(schema, name);
            }
        }
        @Override
        public Collection<AttributeType> getAttributeTypes() {
            return Collections.emptyList();
        }
        @Override
        public List<AttributeType> getAttributeTypesWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public DITContentRule getDITContentRule(final ObjectClass structuralClass) {
            return null;
        }
        @Override
        public DITContentRule getDITContentRule(final String name) {
            throw new UnknownSchemaElementException(WARN_DCR_UNKNOWN.get(name));
        }
        @Override
        public Collection<DITContentRule> getDITContentRules() {
            return Collections.emptyList();
        }
        @Override
        public Collection<DITContentRule> getDITContentRulesWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public DITStructureRule getDITStructureRule(final int ruleID) {
            throw new UnknownSchemaElementException(WARN_DSR_UNKNOWN.get(String.valueOf(ruleID)));
        }
        @Override
        public Collection<DITStructureRule> getDITStructureRules(final NameForm nameForm) {
            return Collections.emptyList();
        }
        @Override
        public Collection<DITStructureRule> getDITStructureRulesWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public Collection<DITStructureRule> getDITStuctureRules() {
            return Collections.emptyList();
        }
        @Override
        public MatchingRule getMatchingRule(final String name) {
            throw new UnknownSchemaElementException(WARN_MR_UNKNOWN.get(name));
        }
        @Override
        public Collection<MatchingRule> getMatchingRules() {
            return Collections.emptyList();
        }
        @Override
        public Collection<MatchingRule> getMatchingRulesWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) {
            return null;
        }
        @Override
        public MatchingRuleUse getMatchingRuleUse(final String name) {
            throw new UnknownSchemaElementException(WARN_MRU_UNKNOWN.get(name));
        }
        @Override
        public Collection<MatchingRuleUse> getMatchingRuleUses() {
            return Collections.emptyList();
        }
        @Override
        public Collection<MatchingRuleUse> getMatchingRuleUsesWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public NameForm getNameForm(final String name) {
            throw new UnknownSchemaElementException(WARN_NAMEFORM_UNKNOWN.get(name));
        }
        @Override
        public Collection<NameForm> getNameForms() {
            return Collections.emptyList();
        }
        @Override
        public Collection<NameForm> getNameForms(final ObjectClass structuralClass) {
            return Collections.emptyList();
        }
        @Override
        public Collection<NameForm> getNameFormsWithName(final String name) {
            return Collections.emptyList();
        }
        @Override
        public ObjectClass getObjectClass(final String name) {
            throw new UnknownSchemaElementException(WARN_OBJECTCLASS_UNKNOWN.get(name));
        }
        @Override
        public Collection<ObjectClass> getObjectClasses() {
            return Collections.emptyList();
        }
        @Override
        public Collection<ObjectClass> getObjectClassesWithName(final String name) {
            return Collections.emptyList();
        }
        /** {@inheritDoc} */
        @Override
        public String getSchemaName() {
            return "Empty Schema";
        }
        @Override
        public Syntax getSyntax(final Schema schema, final String numericOID) {
            // Fake up a syntax substituted by the default syntax.
            return new Syntax(schema, numericOID);
        }
        @Override
        public Collection<Syntax> getSyntaxes() {
            return Collections.emptyList();
        }
        @Override
        public Collection<LocalizableMessage> getWarnings() {
            return Collections.emptyList();
        }
        @Override
        public boolean hasAttributeType(final String name) {
            // In theory a non-strict schema always contains the requested
            // attribute type, so we could always return true. However, we
            // should provide a way for callers to differentiate between a
            // real attribute type and a faked up attribute type.
            return false;
        }
        @Override
        public boolean hasDITContentRule(final String name) {
            return false;
        }
        @Override
        public boolean hasDITStructureRule(final int ruleID) {
            return false;
        }
        @Override
        public boolean hasMatchingRule(final String name) {
            return false;
        }
        @Override
        public boolean hasMatchingRuleUse(final String name) {
            return false;
        }
        @Override
        public boolean hasNameForm(final String name) {
            return false;
        }
        @Override
        public boolean hasObjectClass(final String name) {
            return false;
        }
        @Override
        public boolean hasSyntax(final String numericOID) {
            return false;
        }
        @Override
        public boolean isStrict() {
            return isStrict;
        }
    }
    private static interface Impl {
        Schema asNonStrictSchema();
        Schema asStrictSchema();
        boolean allowMalformedNamesAndOptions();
        boolean allowMalformedJPEGPhotos();
@@ -417,6 +176,16 @@
        }
        @Override
        public Schema asNonStrictSchema() {
            return strictImpl.asNonStrictSchema();
        }
        @Override
        public Schema asStrictSchema() {
            return strictImpl.asStrictSchema();
        }
        @Override
        public boolean allowMalformedNamesAndOptions() {
            return strictImpl.allowMalformedNamesAndOptions();
        }
@@ -650,7 +419,7 @@
        }
    }
    private static final class StrictImpl implements Impl {
    static final class StrictImpl implements Impl {
        private final Map<Integer, DITStructureRule> id2StructureRules;
        private final Map<String, List<AttributeType>> name2AttributeTypes;
        private final Map<String, List<DITContentRule>> name2ContentRules;
@@ -677,6 +446,8 @@
        private final boolean allowMalformedNamesAndOptions;
        private final Syntax defaultSyntax;
        private final MatchingRule defaultMatchingRule;
        private final Schema strictSchema;
        private final Schema nonStrictSchema;
        StrictImpl(final String schemaName, final boolean allowMalformedNamesAndOptions,
                final boolean allowMalformedJPEGPhotos,
@@ -730,6 +501,18 @@
            this.objectClass2NameForms = Collections.unmodifiableMap(objectClass2NameForms);
            this.nameForm2StructureRules = Collections.unmodifiableMap(nameForm2StructureRules);
            this.warnings = Collections.unmodifiableList(warnings);
            this.strictSchema = new Schema(this);
            this.nonStrictSchema = new Schema(new NonStrictImpl(this));
        }
        @Override
        public Schema asNonStrictSchema() {
            return nonStrictSchema;
        }
        @Override
        public Schema asStrictSchema() {
            return strictSchema;
        }
        @Override
@@ -1118,12 +901,6 @@
        }
    }
    /*
     * WARNING: do not reference the core schema in the following declarations.
     */
    private static final Schema EMPTY_STRICT_SCHEMA = new Schema(new EmptyImpl(true));
    private static final Schema EMPTY_NON_STRICT_SCHEMA = new Schema(new EmptyImpl(false));
    static final String ATTR_ATTRIBUTE_TYPES = "attributeTypes";
    static final String ATTR_DIT_CONTENT_RULES = "dITContentRules";
    static final String ATTR_DIT_STRUCTURE_RULES = "dITStructureRules";
@@ -1164,7 +941,7 @@
     * @return The default schema which should be used by this application.
     */
    public static Schema getDefaultSchema() {
        return DefaultSchema.schema;
        return DelayedSchema.defaultSchema;
    }
    /**
@@ -1174,7 +951,7 @@
     * @return The empty schema.
     */
    public static Schema getEmptySchema() {
        return EMPTY_NON_STRICT_SCHEMA;
        return DelayedSchema.EMPTY_SCHEMA;
    }
    /**
@@ -1350,7 +1127,7 @@
     */
    public static void setDefaultSchema(final Schema schema) {
        Reject.ifNull(schema);
        DefaultSchema.schema = schema;
        DelayedSchema.defaultSchema = schema;
    }
    /**
@@ -1368,44 +1145,7 @@
    private final Impl impl;
    Schema(final String schemaName, final boolean allowMalformedNamesAndOptions,
            final boolean allowMalformedJPEGPhotos,
            final boolean allowMalformedCertificates,
            final boolean allowNonStandardTelephoneNumbers,
            final boolean allowZeroLengthDirectoryStrings,
            final Syntax defaultSyntax,
            final MatchingRule defaultMatchingRule,
            final Map<String, Syntax> numericOID2Syntaxes,
            final Map<String, MatchingRule> numericOID2MatchingRules,
            final Map<String, MatchingRuleUse> numericOID2MatchingRuleUses,
            final Map<String, AttributeType> numericOID2AttributeTypes,
            final Map<String, ObjectClass> numericOID2ObjectClasses,
            final Map<String, NameForm> numericOID2NameForms,
            final Map<String, DITContentRule> numericOID2ContentRules,
            final Map<Integer, DITStructureRule> id2StructureRules,
            final Map<String, List<MatchingRule>> name2MatchingRules,
            final Map<String, List<MatchingRuleUse>> name2MatchingRuleUses,
            final Map<String, List<AttributeType>> name2AttributeTypes,
            final Map<String, List<ObjectClass>> name2ObjectClasses,
            final Map<String, List<NameForm>> name2NameForms,
            final Map<String, List<DITContentRule>> name2ContentRules,
            final Map<String, List<DITStructureRule>> name2StructureRules,
            final Map<String, List<NameForm>> objectClass2NameForms,
            final Map<String, List<DITStructureRule>> nameForm2StructureRules,
            final List<LocalizableMessage> warnings) {
        impl =
                new StrictImpl(schemaName, allowMalformedNamesAndOptions, allowMalformedJPEGPhotos,
                        allowMalformedCertificates, allowNonStandardTelephoneNumbers,
                        allowZeroLengthDirectoryStrings, defaultSyntax, defaultMatchingRule,
                        numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses,
                        numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
                        numericOID2ContentRules, id2StructureRules, name2MatchingRules,
                        name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
                        name2NameForms, name2ContentRules, name2StructureRules,
                        objectClass2NameForms, nameForm2StructureRules, warnings);
    }
    private Schema(final Impl impl) {
    Schema(final Impl impl) {
        this.impl = impl;
    }
@@ -1503,14 +1243,7 @@
     * @see Schema#isStrict()
     */
    public Schema asNonStrictSchema() {
        if (!impl.isStrict()) {
            return this;
        } else if (impl instanceof StrictImpl) {
            return new Schema(new NonStrictImpl((StrictImpl) impl));
        } else {
            // EmptyImpl
            return EMPTY_NON_STRICT_SCHEMA;
        }
        return impl.asNonStrictSchema();
    }
    /**
@@ -1522,14 +1255,7 @@
     * @see Schema#isStrict()
     */
    public Schema asStrictSchema() {
        if (impl.isStrict()) {
            return this;
        } else if (impl instanceof NonStrictImpl) {
            return new Schema(((NonStrictImpl) impl).strictImpl);
        } else {
            // EmptyImpl
            return EMPTY_STRICT_SCHEMA;
        }
        return impl.asStrictSchema();
    }
    /**
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -2578,15 +2578,16 @@
        }
        final Schema schema =
                new Schema(localSchemaName, allowMalformedNamesAndOptions,
                        allowMalformedJPEGPhotos, allowMalformedCertificates, allowNonStandardTelephoneNumbers,
                        allowZeroLengthDirectoryStrings, defaultSyntax, defaultMatchingRule,
                        numericOID2Syntaxes, numericOID2MatchingRules, numericOID2MatchingRuleUses,
                new Schema.StrictImpl(localSchemaName, allowMalformedNamesAndOptions,
                        allowMalformedJPEGPhotos, allowMalformedCertificates,
                        allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings,
                        defaultSyntax, defaultMatchingRule, numericOID2Syntaxes,
                        numericOID2MatchingRules, numericOID2MatchingRuleUses,
                        numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
                        numericOID2ContentRules, id2StructureRules, name2MatchingRules,
                        name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
                        name2NameForms, name2ContentRules, name2StructureRules,
                        objectClass2NameForms, nameForm2StructureRules, warnings);
                        objectClass2NameForms, nameForm2StructureRules, warnings).asStrictSchema();
        validate(schema);
        // Re-init this builder so that it can continue to be used afterwards.
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaTestCase.java
@@ -34,7 +34,7 @@
 */
@SuppressWarnings("javadoc")
public class SchemaTestCase extends AbstractSchemaTestCase {
    @Test(description = "Unit test for OPENDJ-1477", enabled = false)
    @Test(description = "Unit test for OPENDJ-1477")
    public void asNonStrictSchemaAlwaysReturnsSameInstance() {
        final Schema schema = Schema.getCoreSchema();
        final Schema nonStrictSchema1 = schema.asNonStrictSchema();
@@ -43,7 +43,7 @@
        assertThat(nonStrictSchema1).isSameAs(nonStrictSchema2);
    }
    @Test(description = "Unit test for OPENDJ-1477", enabled = false)
    @Test(description = "Unit test for OPENDJ-1477")
    public void asStrictSchemaAlwaysReturnsSameInstance() {
        final Schema schema = Schema.getCoreSchema();
        final Schema strictSchema1 = schema.asStrictSchema();