All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.directory.shared.ldap.model.schema.registries.Registries Maven / Gradle / Ivy

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 * 
 */
package org.apache.directory.shared.ldap.model.schema.registries;


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.directory.shared.i18n.I18n;
import org.apache.directory.shared.ldap.model.constants.MetaSchemaConstants;
import org.apache.directory.shared.ldap.model.exception.LdapException;
import org.apache.directory.shared.ldap.model.exception.LdapProtocolErrorException;
import org.apache.directory.shared.ldap.model.exception.LdapSchemaException;
import org.apache.directory.shared.ldap.model.exception.LdapSchemaExceptionCodes;
import org.apache.directory.shared.ldap.model.exception.LdapSchemaViolationException;
import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException;
import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.model.schema.AttributeType;
import org.apache.directory.shared.ldap.model.schema.DitContentRule;
import org.apache.directory.shared.ldap.model.schema.DitStructureRule;
import org.apache.directory.shared.ldap.model.schema.LdapComparator;
import org.apache.directory.shared.ldap.model.schema.LdapSyntax;
import org.apache.directory.shared.ldap.model.schema.LoadableSchemaObject;
import org.apache.directory.shared.ldap.model.schema.MatchingRule;
import org.apache.directory.shared.ldap.model.schema.MatchingRuleUse;
import org.apache.directory.shared.ldap.model.schema.MutableAttributeType;
import org.apache.directory.shared.ldap.model.schema.MutableMatchingRule;
import org.apache.directory.shared.ldap.model.schema.NameForm;
import org.apache.directory.shared.ldap.model.schema.Normalizer;
import org.apache.directory.shared.ldap.model.schema.ObjectClass;
import org.apache.directory.shared.ldap.model.schema.SchemaManager;
import org.apache.directory.shared.ldap.model.schema.SchemaObject;
import org.apache.directory.shared.ldap.model.schema.SchemaObjectWrapper;
import org.apache.directory.shared.ldap.model.schema.SyntaxChecker;
import org.apache.directory.shared.ldap.model.schema.registries.helper.AttributeTypeHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.DitContentRuleHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.LdapSyntaxHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.MatchingRuleHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.MatchingRuleUseHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.NameFormHelper;
import org.apache.directory.shared.ldap.model.schema.registries.helper.ObjectClassHelper;
import org.apache.directory.shared.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Document this class.
 *
 * @author Apache Directory Project
 */
public class Registries implements SchemaLoaderListener, Cloneable
{
    /** A logger for this class */
    private static final Logger LOG = LoggerFactory.getLogger( Registries.class );

    /**
     * A String name to Schema object map for the schemas loaded into this
     * registry. The loaded schemas may be disabled.
     */
    protected Map loadedSchemas = new HashMap();

    /** The AttributeType registry */
    protected AttributeTypeRegistry attributeTypeRegistry;

    /** The ObjectClass registry */
    protected ObjectClassRegistry objectClassRegistry;

    /** The LdapSyntax registry */
    protected ComparatorRegistry comparatorRegistry;

    /** The DitContentRule registry */
    protected DitContentRuleRegistry ditContentRuleRegistry;

    /** The DitStructureRule registry */
    protected DitStructureRuleRegistry ditStructureRuleRegistry;

    /** The MatchingRule registry */
    protected MatchingRuleRegistry matchingRuleRegistry;

    /** The MatchingRuleUse registry */
    protected MatchingRuleUseRegistry matchingRuleUseRegistry;

    /** The NameForm registry */
    protected NameFormRegistry nameFormRegistry;

    /** The Normalizer registry */
    protected NormalizerRegistry normalizerRegistry;

    /** The global OID registry */
    protected OidRegistry globalOidRegistry;

    /** The SyntaxChecker registry */
    protected SyntaxCheckerRegistry syntaxCheckerRegistry;

    /** The LdapSyntax registry */
    protected LdapSyntaxRegistry ldapSyntaxRegistry;

    /** A map storing all the schema objects associated with a schema */
    private Map> schemaObjects;

    /** A flag indicating that the Registries is relaxed or not */
    private boolean isRelaxed;

    /** A flag indicating that disabled SchemaObject are accepted */
    private boolean disabledAccepted;

    /** Two flags for RELAXED and STRUCT */
    public static final boolean STRICT = false;
    public static final boolean RELAXED = true;

    /**
     *  A map storing a relation between a SchemaObject and all the
     *  referencing SchemaObjects.
     */
    protected Map> usedBy;

    /**
     *  A map storing a relation between a SchemaObject and all the
     *  SchemaObjects it uses.
     */
    protected Map> using;

    /** A reference on the schema Manager */
    @SuppressWarnings(
        { "PMD.UnusedPrivateField", "unused" })
    // False positive
    private SchemaManager schemaManager;


    /**
     * Creates a new instance of Registries.
     *
     * @param schemaManager the schema manager
     */
    public Registries( SchemaManager schemaManager )
    {
        globalOidRegistry = new OidRegistry();
        attributeTypeRegistry = new DefaultAttributeTypeRegistry();
        comparatorRegistry = new DefaultComparatorRegistry();
        ditContentRuleRegistry = new DefaultDitContentRuleRegistry();
        ditStructureRuleRegistry = new DefaultDitStructureRuleRegistry();
        ldapSyntaxRegistry = new DefaultLdapSyntaxRegistry();
        matchingRuleRegistry = new DefaultMatchingRuleRegistry();
        matchingRuleUseRegistry = new DefaultMatchingRuleUseRegistry();
        nameFormRegistry = new DefaultNameFormRegistry();
        normalizerRegistry = new DefaultNormalizerRegistry();
        objectClassRegistry = new DefaultObjectClassRegistry();
        syntaxCheckerRegistry = new DefaultSyntaxCheckerRegistry();
        schemaObjects = new HashMap>();
        usedBy = new HashMap>();
        using = new HashMap>();

        isRelaxed = STRICT;
        disabledAccepted = false;
        this.schemaManager = schemaManager;
    }


    /**
     * @return The AttributeType registry
     */
    public AttributeTypeRegistry getAttributeTypeRegistry()
    {
        return attributeTypeRegistry;
    }


    /**
     * @return The Comparator registry
     */
    public ComparatorRegistry getComparatorRegistry()
    {
        return comparatorRegistry;
    }


    /**
     * @return The DitContentRule registry
     */
    public DitContentRuleRegistry getDitContentRuleRegistry()
    {
        return ditContentRuleRegistry;
    }


    /**
     * @return The DitStructureRule registry
     */
    public DitStructureRuleRegistry getDitStructureRuleRegistry()
    {
        return ditStructureRuleRegistry;
    }


    /**
     * @return The MatchingRule registry
     */
    public MatchingRuleRegistry getMatchingRuleRegistry()
    {
        return matchingRuleRegistry;
    }


    /**
     * @return The MatchingRuleUse registry
     */
    public MatchingRuleUseRegistry getMatchingRuleUseRegistry()
    {
        return matchingRuleUseRegistry;
    }


    /**
     * @return The NameForm registry
     */
    public NameFormRegistry getNameFormRegistry()
    {
        return nameFormRegistry;
    }


    /**
     * @return The Normalizer registry
     */
    public NormalizerRegistry getNormalizerRegistry()
    {
        return normalizerRegistry;
    }


    /**
     * @return The ObjectClass registry
     */
    public ObjectClassRegistry getObjectClassRegistry()
    {
        return objectClassRegistry;
    }


    /**
     * @return The global Oid registry
     */
    public OidRegistry getGlobalOidRegistry()
    {
        return globalOidRegistry;
    }


    /**
     * @return The SyntaxChecker registry
     */
    public SyntaxCheckerRegistry getSyntaxCheckerRegistry()
    {
        return syntaxCheckerRegistry;
    }


    /**
     * @return The LdapSyntax registry
     */
    public LdapSyntaxRegistry getLdapSyntaxRegistry()
    {
        return ldapSyntaxRegistry;
    }


    /**
     * Get an OID from a name. As we have many possible registries, we
     * have to look in all of them to get the one containing the OID.
     *
     * @param name The name we are looking at
     * @return The associated OID
     */
    // This will suppress PMD.EmptyCatchBlock warnings in this method
    @SuppressWarnings("PMD.EmptyCatchBlock")
    public String getOid( String name )
    {
        // we have many possible Registries to look at.
        // AttributeType
        try
        {
            AttributeType attributeType = attributeTypeRegistry.lookup( name );

            if ( attributeType != null )
            {
                return attributeType.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // ObjectClass
        try
        {
            ObjectClass objectClass = objectClassRegistry.lookup( name );

            if ( objectClass != null )
            {
                return objectClass.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // LdapSyntax
        try
        {
            LdapSyntax ldapSyntax = ldapSyntaxRegistry.lookup( name );

            if ( ldapSyntax != null )
            {
                return ldapSyntax.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // MatchingRule
        try
        {
            MatchingRule matchingRule = matchingRuleRegistry.lookup( name );

            if ( matchingRule != null )
            {
                return matchingRule.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // MatchingRuleUse
        try
        {
            MatchingRuleUse matchingRuleUse = matchingRuleUseRegistry.lookup( name );

            if ( matchingRuleUse != null )
            {
                return matchingRuleUse.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // NameForm
        try
        {
            NameForm nameForm = nameFormRegistry.lookup( name );

            if ( nameForm != null )
            {
                return nameForm.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // DitContentRule
        try
        {
            DitContentRule ditContentRule = ditContentRuleRegistry.lookup( name );

            if ( ditContentRule != null )
            {
                return ditContentRule.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // Fall down to the next registry
        }

        // DitStructureRule
        try
        {
            DitStructureRule ditStructureRule = ditStructureRuleRegistry.lookup( name );

            if ( ditStructureRule != null )
            {
                return ditStructureRule.getOid();
            }
        }
        catch ( LdapException ne )
        {
            // No more registries to look at...
        }

        return null;
    }


    /**
     * Gets a schema that has been loaded into these Registries.
     * 
     * @param schemaName the name of the schema to lookup
     * @return the loaded Schema if one corresponding to the name exists
     */
    public Schema getLoadedSchema( String schemaName )
    {
        return loadedSchemas.get( Strings.toLowerCase( schemaName ) );
    }


    /**
     * Checks to see if a particular Schema is loaded.
     *
     * @param schemaName the name of the Schema to check
     * @return true if the Schema is loaded, false otherwise
     */
    public boolean isSchemaLoaded( String schemaName )
    {
        return loadedSchemas.containsKey( Strings.toLowerCase( schemaName ) );
    }


    // ------------------------------------------------------------------------
    // Code used to sanity check the resolution of entities in registries
    // ------------------------------------------------------------------------
    /**
     * Attempts to resolve the dependent schema objects of all entities that
     * refer to other objects within the registries.  Null references will be
     * handed appropriately.
     * The order in which the SchemaObjects must be :
     * 
  • 1) Normalizers, Comparators and SyntaxCheckers (as they depend on nothing) *
  • 2) Syntaxes (depend on SyntaxCheckers) *
  • 3) MatchingRules (depend on Syntaxes, Normalizers and Comparators *
  • 4) AttributeTypes (depend on MatchingRules, Syntaxes and AttributeTypes : in this case, we first handle the superior) *
  • 5) ObjectClasses (depend on AttributeTypes and ObjectClasses) *

    * Later, when we will support them : *
  • 6) MatchingRuleUses (depend on matchingRules and AttributeTypes) *
  • 7) DitContentRules (depend on ObjectClasses and AttributeTypes) *
  • 8) NameForms (depends on ObjectClasses and AttributeTypes) *
  • 9) DitStructureRules (depends onNameForms and DitStructureRules) * * * @return a list of exceptions encountered while resolving entities */ public List checkRefInteg() { ArrayList errors = new ArrayList(); // Step 1 : // We start with Normalizers, Comparators and SyntaxCheckers // as they depend on nothing // Check the Normalizers for ( Normalizer normalizer : normalizerRegistry ) { resolve( normalizer, errors ); } // Check the Comparators for ( LdapComparator comparator : comparatorRegistry ) { resolve( comparator, errors ); } // Check the SyntaxCheckers for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry ) { resolve( syntaxChecker, errors ); } // Step 2 : // Check the LdapSyntaxes for ( LdapSyntax ldapSyntax : ldapSyntaxRegistry ) { resolve( ldapSyntax, errors ); } // Step 3 : // Check the matchingRules for ( MatchingRule matchingRule : matchingRuleRegistry ) { resolve( matchingRule, errors ); } // Step 4 : // Check the AttributeTypes for ( AttributeType attributeType : attributeTypeRegistry ) { resolve( attributeType, errors ); } // Step 5 : // Check the ObjectClasses for ( ObjectClass objectClass : objectClassRegistry ) { resolve( objectClass, errors ); } // Step 6-9 aren't yet defined return errors; } /** * Add the SchemaObjectReferences. This method does nothing, it's just * a catch all. The other methods will be called for each specific * schemaObject * public void addCrossReferences( SchemaObject schemaObject ) { // Do nothing : it's a catch all method. } /** * Delete the AT references (using and usedBy) : * AT -> MR (for EQUALITY, ORDERING and SUBSTR) * AT -> S * AT -> AT */ public void delCrossReferences( AttributeType attributeType ) { if ( attributeType.getEquality() != null ) { delReference( attributeType, attributeType.getEquality() ); } if ( attributeType.getOrdering() != null ) { delReference( attributeType, attributeType.getOrdering() ); } if ( attributeType.getSubstring() != null ) { delReference( attributeType, attributeType.getSubstring() ); } if ( attributeType.getSyntax() != null ) { delReference( attributeType, attributeType.getSyntax() ); } if ( attributeType.getSuperior() != null ) { delReference( attributeType, attributeType.getSuperior() ); } } /** * Build the AttributeType references. This has to be done recursively, as * an AttributeType may inherit its parent's MatchingRules. The references * to update are : * - EQUALITY MR * - ORDERING MR * - SUBSTRING MR * - SUP AT * - SYNTAX */ private void buildAttributeTypeReferences( List errors ) { for ( AttributeType attributeType : attributeTypeRegistry ) { if ( ( getUsing( attributeType ) == null ) || getUsing( attributeType ).isEmpty() ) { buildReference( errors, attributeType ); } } } /** * Build the Comparator references */ private void buildComparatorReferences( List errors ) { for ( LdapComparator comparator : comparatorRegistry ) { buildReference( errors, comparator ); } } /** * Build the DitContentRule references */ // Remove me when TODO is implemented @SuppressWarnings("PMD.UnusedFormalParameter") private void buildDitContentRuleReferences( List errors ) { for ( @SuppressWarnings("unused") DitContentRule ditContentRule : ditContentRuleRegistry ) { // TODO } } /** * Build the DitStructureRule references */ // Remove me when TODO is implemented @SuppressWarnings("PMD.UnusedFormalParameter") private void buildDitStructureRuleReferences( List errors ) { for ( @SuppressWarnings("unused") DitStructureRule ditStructureRule : ditStructureRuleRegistry ) { // TODO } } /** * Delete the MR references (using and usedBy) : * MR -> C * MR -> N * MR -> S */ public void delCrossReferences( MatchingRule matchingRule ) { if ( matchingRule.getLdapComparator() != null ) { delReference( matchingRule, matchingRule.getLdapComparator() ); } if ( matchingRule.getNormalizer() != null ) { delReference( matchingRule, matchingRule.getNormalizer() ); } if ( matchingRule.getSyntax() != null ) { delReference( matchingRule, matchingRule.getSyntax() ); } } /** * Build the SchemaObject references */ public void buildReference( List errors, SchemaObject schemaObject ) { try { switch ( schemaObject.getObjectType() ) { case ATTRIBUTE_TYPE : AttributeTypeHelper.addToRegistries( (MutableAttributeType)schemaObject, errors, this ); break; case DIT_CONTENT_RULE : DitContentRuleHelper.addToRegistries( (DitContentRule)schemaObject, errors, this ); break; case LDAP_SYNTAX : LdapSyntaxHelper.addToRegistries( (LdapSyntax)schemaObject, errors, this ); break; case MATCHING_RULE : MatchingRuleHelper.addToRegistries( (MutableMatchingRule)schemaObject, errors, this ); break; case MATCHING_RULE_USE : MatchingRuleUseHelper.addToRegistries( (MatchingRuleUse)schemaObject, errors, this ); break; case NAME_FORM : NameFormHelper.addToRegistries( (NameForm)schemaObject, errors, this ); break; case OBJECT_CLASS : ObjectClassHelper.addToRegistries( (ObjectClass)schemaObject, errors, this ); break; } } catch ( LdapException ne ) { // Not allowed. String msg = I18n.err( I18n.ERR_04292, schemaObject.getName(), ne.getLocalizedMessage() ); Throwable error = new LdapProtocolErrorException( msg, ne ); errors.add( error ); LOG.info( msg ); } } /** * Unlink the SchemaObject references */ public void removeReference( List errors, SchemaObject schemaObject ) { try { switch ( schemaObject.getObjectType() ) { case ATTRIBUTE_TYPE : AttributeTypeHelper.removeFromRegistries( (AttributeType)schemaObject, errors, this ); break; case LDAP_SYNTAX : LdapSyntaxHelper.removeFromRegistries( (LdapSyntax)schemaObject, errors, this ); break; case MATCHING_RULE : MatchingRuleHelper.removeFromRegistries( (MatchingRule)schemaObject, errors, this ); break; case OBJECT_CLASS : ObjectClassHelper.removeFromRegistries( (ObjectClass)schemaObject, errors, this ); break; } } catch ( LdapException ne ) { // Not allowed. String msg = I18n.err( I18n.ERR_04293, schemaObject.getName(), ne.getLocalizedMessage() ); Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg, ne ); errors.add( error ); LOG.info( msg ); } } /** * Build the MatchingRule references */ private void buildMatchingRuleReferences( List errors ) { for ( MatchingRule matchingRule : matchingRuleRegistry ) { buildReference( errors, matchingRule ); } } /** * Build the MatchingRuleUse references */ private void buildMatchingRuleUseReferences( List errors ) { for ( MatchingRuleUse matchingRuleUse : matchingRuleUseRegistry ) { buildReference( errors, matchingRuleUse ); } } /** * Build the NameForm references */ // Remove me when TODO is implemented @SuppressWarnings("PMD.UnusedFormalParameter") private void buildNameFormReferences( List errors ) { for ( @SuppressWarnings("unused") NameForm nameFormRule : nameFormRegistry ) { // TODO } } /** * Build the Normalizer references */ private void buildNormalizerReferences( List errors ) { for ( Normalizer normalizer : normalizerRegistry ) { buildReference( errors, normalizer ); } } /** * Build the ObjectClasses references */ private void buildObjectClassReferences( List errors ) { // Remember the OC we have already processed Set done = new HashSet(); // The ObjectClass for ( ObjectClass objectClass : objectClassRegistry ) { if ( done.contains( objectClass.getOid() ) ) { continue; } else { done.add( objectClass.getOid() ); } buildReference( errors, objectClass ); } } /** * Build the Syntax references */ private void buildLdapSyntaxReferences( List errors ) { for ( LdapSyntax syntax : ldapSyntaxRegistry ) { buildReference( errors, syntax ); } } /** * Build the SyntaxChecker references */ private void buildSyntaxCheckerReferences( List errors ) { for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry ) { buildReference( errors, syntaxChecker ); } } /** * Build the usedBy and using references from the stored elements. * * @return A list of all the errors we met during the cross reference update */ public List buildReferences() { List errors = new ArrayList(); // The Comparator references buildComparatorReferences( errors ); // The Normalizer references buildNormalizerReferences( errors ); // The SyntaxChecker references buildSyntaxCheckerReferences( errors ); // The Syntax references buildLdapSyntaxReferences( errors ); // The MatchingRules references buildMatchingRuleReferences( errors ); // The AttributeType references buildAttributeTypeReferences( errors ); // The MatchingRuleUse references buildMatchingRuleUseReferences( errors ); // The ObjectClasses references buildObjectClassReferences( errors ); // The DitContentRules references buildDitContentRuleReferences( errors ); // The NameForms references buildNameFormReferences( errors ); // The DitStructureRules references buildDitStructureRuleReferences( errors ); return errors; } /** * Attempts to resolve the SyntaxChecker associated with a Syntax. * * @param syntax the LdapSyntax to resolve the SyntaxChecker of * @param errors the list of errors to add exceptions to */ private void resolve( LdapSyntax syntax, List errors ) { // A LdapSyntax must point to a valid SyntaxChecker // or to the OctetString SyntaxChecker try { LdapSyntaxHelper.addToRegistries( syntax, errors, this ); } catch ( LdapException e ) { errors.add( e ); } } /** * Attempts to resolve the Normalizer * * @param normalizer the Normalizer * @param errors the list of errors to add exceptions to */ private void resolve( Normalizer normalizer, List errors ) { // This is currently doing nothing. } /** * Attempts to resolve the LdapComparator * * @param comparator the LdapComparator * @param errors the list of errors to add exceptions to */ private void resolve( LdapComparator comparator, List errors ) { // This is currently doing nothing. } /** * Attempts to resolve the SyntaxChecker * * @param normalizer the SyntaxChecker * @param errors the list of errors to add exceptions to */ private void resolve( SyntaxChecker syntaxChecker, List errors ) { // This is currently doing nothing. } /** * Check if the Comparator, Normalizer and the syntax are * existing for a matchingRule. */ private void resolve( MatchingRule matchingRule, List errors ) { // Process the Syntax. It can't be null String syntaxOid = matchingRule.getSyntaxOid(); if ( syntaxOid != null ) { // Check if the Syntax is present in the registries try { ldapSyntaxRegistry.lookup( syntaxOid ); } catch ( LdapException ne ) { // This MR's syntax has not been loaded into the Registries. LdapSchemaException ldapSchemaException = new LdapSchemaException( LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ), ne ); ldapSchemaException.setSourceObject( matchingRule ); errors.add( ldapSchemaException ); } } else { // This is an error. LdapSchemaException ldapSchemaException = new LdapSchemaException( LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ) ); ldapSchemaException.setSourceObject( matchingRule ); errors.add( ldapSchemaException ); } // Process the Normalizer Normalizer normalizer = matchingRule.getNormalizer(); if ( normalizer == null ) { // Ok, no normalizer, this is an error Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( I18n.ERR_04295, matchingRule.getOid() ) ); errors.add( error ); } // Process the Comparator LdapComparator comparator = matchingRule.getLdapComparator(); if ( comparator == null ) { // Ok, no comparator, this is an error Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( I18n.ERR_04296, matchingRule.getOid() ) ); errors.add( error ); } } /** * Check AttributeType referential integrity */ private void resolveRecursive( AttributeType attributeType, Set processed, List errors ) { // Process the Superior, if any String superiorOid = attributeType.getSuperiorOid(); AttributeType superior = null; if ( superiorOid != null ) { // Check if the Superior is present in the registries try { superior = attributeTypeRegistry.lookup( superiorOid ); } catch ( LdapException ne ) { // This AT's superior has not been loaded into the Registries. if ( !processed.contains( superiorOid ) ) { errors.add( ne ); } } // We now have to process the superior, if it hasn't been // processed yet. if ( superior != null ) { if ( !processed.contains( superiorOid ) ) { resolveRecursive( superior, processed, errors ); processed.add( attributeType.getOid() ); } else { // Not allowed : we have a cyle Throwable error = new LdapSchemaViolationException( ResultCodeEnum.OTHER, I18n.err( I18n.ERR_04297, attributeType.getOid() ) ); errors.add( error ); return; } } } // Process the Syntax. If it's null, the attributeType must have // a Superior. String syntaxOid = attributeType.getSyntaxOid(); if ( syntaxOid != null ) { // Check if the Syntax is present in the registries try { ldapSyntaxRegistry.lookup( syntaxOid ); } catch ( LdapException ne ) { // This AT's syntax has not been loaded into the Registries. errors.add( ne ); } } else { // No Syntax : get it from the AttributeType's superior if ( superior == null ) { // This is an error. if the AT does not have a Syntax, // then it must have a superior, which syntax is get from. Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( I18n.ERR_04298, attributeType.getOid() ) ); errors.add( error ); } } // Process the EQUALITY MatchingRule. It may be null, but if it's not // it must have been processed before String equalityOid = attributeType.getEqualityOid(); if ( equalityOid != null ) { // Check if the MatchingRule is present in the registries try { matchingRuleRegistry.lookup( equalityOid ); } catch ( LdapException ne ) { // This AT's EQUALITY matchingRule has not been loaded into the Registries. errors.add( ne ); } } // Process the ORDERING MatchingRule. It may be null, but if it's not // it must have been processed before String orderingOid = attributeType.getOrderingOid(); if ( orderingOid != null ) { // Check if the MatchingRule is present in the registries try { matchingRuleRegistry.lookup( orderingOid ); } catch ( LdapException ne ) { // This AT's ORDERING matchingRule has not been loaded into the Registries. errors.add( ne ); } } // Process the SUBSTR MatchingRule. It may be null, but if it's not // it must have been processed before String substringOid = attributeType.getSubstringOid(); if ( substringOid != null ) { // Check if the MatchingRule is present in the registries try { matchingRuleRegistry.lookup( substringOid ); } catch ( LdapException ne ) { // This AT's SUBSTR matchingRule has not been loaded into the Registries. errors.add( ne ); } } } /** * Check the inheritance, and the existence of MatchingRules and LdapSyntax * for an attribute */ private void resolve( AttributeType attributeType, List errors ) { // This set is used to avoid having more than one error // for an AttributeType. It's mandatory when processing // a Superior, as it may be broken and referenced more than once. Set processed = new HashSet(); // Store the AttributeType itself in the processed, to avoid cycle processed.add( attributeType.getOid() ); // Call the recursive method, as we may have superiors to deal with resolveRecursive( attributeType, processed, errors ); } private List getMustRecursive( List musts, Set processed, ObjectClass objectClass ) { if ( objectClass != null ) { if ( processed.contains( objectClass ) ) { // We have found a cycle. It has already been reported, // don't add a new error, just exit. return null; } processed.add( objectClass ); for ( AttributeType must : objectClass.getMustAttributeTypes() ) { musts.add( must ); } for ( ObjectClass superior : objectClass.getSuperiors() ) { getMustRecursive( musts, processed, superior ); } } return musts; } private void resolve( ObjectClass objectClass, List errors ) { // This set is used to avoid having more than one error // for an ObjectClass. It's mandatory when processing // the Superiors, as they may be broken and referenced more than once. Set processed = new HashSet(); // Store the ObjectClass itself in the processed, to avoid cycle processed.add( objectClass.getOid() ); // Call the recursive method, as we may have superiors to deal with resolveRecursive( objectClass, processed, errors ); // Check that the MAY and MUST AT are consistent (no AT in MAY and in MUST // in one of its superior List musts = getMustRecursive( new ArrayList(), new HashSet(), objectClass ); if ( musts != null ) { for ( AttributeType may : objectClass.getMayAttributeTypes() ) { if ( musts.contains( may ) ) { // This is not allowed. LdapSchemaException ldapSchemaException = new LdapSchemaException( LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST ); ldapSchemaException.setSourceObject( objectClass ); ldapSchemaException.setOtherObject( may ); errors.add( ldapSchemaException ); } } } } // This will suppress PMD.EmptyCatchBlock warnings in this method @SuppressWarnings("PMD.EmptyCatchBlock") private void resolveRecursive( ObjectClass objectClass, Set processed, List errors ) { // Process the Superiors, if any List superiorOids = objectClass.getSuperiorOids(); ObjectClass superior = null; for ( String superiorOid : superiorOids ) { // Check if the Superior is present in the registries try { superior = objectClassRegistry.lookup( superiorOid ); } catch ( LdapException ne ) { // This OC's superior has not been loaded into the Registries. if ( !processed.contains( superiorOid ) ) { LdapSchemaException ldapSchemaException = new LdapSchemaException( LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, ne ); ldapSchemaException.setSourceObject( objectClass ); ldapSchemaException.setRelatedId( superiorOid ); errors.add( ldapSchemaException ); } } // We now have to process the superior, if it hasn't been // processed yet. if ( superior != null ) { if ( !processed.contains( superior.getOid() ) ) { resolveRecursive( superior, processed, errors ); processed.add( objectClass.getOid() ); } else { // Not allowed : we have a cyle LdapSchemaException ldapSchemaException = new LdapSchemaException( LdapSchemaExceptionCodes.OC_CYCLE_CLASS_HIERARCHY ); ldapSchemaException.setSourceObject( objectClass ); ldapSchemaException.setOtherObject( superior ); errors.add( ldapSchemaException ); return; } } } // Process the MAY attributeTypes. for ( String mayOid : objectClass.getMayAttributeTypeOids() ) { // Check if the MAY AttributeType is present in the registries try { attributeTypeRegistry.lookup( mayOid ); } catch ( LdapException ne ) { // This AT has not been loaded into the Registries. errors.add( ne ); } } // Process the MUST attributeTypes. for ( String mustOid : objectClass.getMustAttributeTypeOids() ) { // Check if the MUST AttributeType is present in the registries try { attributeTypeRegistry.lookup( mustOid ); } catch ( LdapException ne ) { // This AT has not been loaded into the Registries. errors.add( ne ); } } // All is done for this ObjectClass, let's apply the registries try { ObjectClassHelper.addToRegistries( objectClass, errors, this ); } catch ( LdapException ne ) { // Do nothing. We may have a broken OC, // but at this point, it doesn't matter. } } /** * Applies the added SchemaObject to the given register */ public List add( List errors, SchemaObject schemaObject, boolean check ) throws LdapException { // Relax the registries boolean wasRelaxed = isRelaxed; setRelaxed(); // Register the SchemaObject in the registries register( errors, schemaObject ); // Associate the SchemaObject with its schema associateWithSchema( errors, schemaObject ); // Build the SchemaObject references if ( check ) { buildReference( errors, schemaObject ); } // Lock the SchemaObject schemaObject.lock(); if ( check ) { if ( errors.isEmpty() ) { // Check the registries now List checkErrors = checkRefInteg(); errors.addAll( checkErrors ); } } // Get back to Strict mode if ( !wasRelaxed ) { setStrict(); } // return the errors return errors; } /** * Remove the given SchemaObject from the registries */ public List delete( List errors, SchemaObject schemaObject ) throws LdapException { // Relax the registries boolean wasRelaxed = isRelaxed; setRelaxed(); // Remove the SchemaObject from the registries SchemaObject removed = unregister( errors, schemaObject ); // Remove the SchemaObject from its schema dissociateFromSchema( errors, removed ); // Unlink the SchemaObject references removeReference( errors, removed ); if ( errors.isEmpty() ) { // Check the registries now List checkErrors = checkRefInteg(); errors.addAll( checkErrors ); } // Restore the previous registries state if ( !wasRelaxed ) { setStrict(); } // return the errors return errors; } /** * Merely adds the schema to the set of loaded schemas. Does not * actually do any work to add schema objects to registries. * * {@inheritDoc} */ public void schemaLoaded( Schema schema ) { this.loadedSchemas.put( Strings.toLowerCase( schema.getSchemaName() ), schema ); } /** * Merely removes the schema from the set of loaded schemas. Does not * actually do any work to remove schema objects from registries. * * {@inheritDoc} */ public void schemaUnloaded( Schema schema ) { this.loadedSchemas.remove( Strings.toLowerCase( schema.getSchemaName() ) ); } /** * Gets an unmodifiable Map of schema names to loaded Schema objects. * * @return the map of loaded Schema objects */ public Map getLoadedSchemas() { return Collections.unmodifiableMap( loadedSchemas ); } /** * @return Gets a reference to the Map associating a schemaName to * its contained SchemaObjects */ public Map> getObjectBySchemaName() { return schemaObjects; } /** * Retrieve the schema name for a specific SchemaObject, or return "other" if none is found. */ private String getSchemaName( SchemaObject schemaObject ) { String schemaName = Strings.toLowerCase( schemaObject.getSchemaName() ); if ( loadedSchemas.containsKey( schemaName ) ) { return schemaName; } else { return MetaSchemaConstants.SCHEMA_OTHER; } } /** * Tells if the given SchemaObject is present in one schema. The schema * may be disabled. * * @param schemaObject The schemaObject we are looking for * @return true if the schemaObject is present in a schema */ public boolean contains( SchemaObject schemaObject ) { String schemaName = schemaObject.getSchemaName(); Set setSchemaObjects = schemaObjects.get( schemaName ); if ( ( setSchemaObjects == null ) || setSchemaObjects.isEmpty() ) { return false; } SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); return setSchemaObjects.contains( wrapper ); } /** * Create a new schema association with its content * * @param schemaName The schema name */ public Set addSchema( String schemaName ) { Set content = new HashSet(); schemaObjects.put( schemaName, content ); return content; } /** * Register the given SchemaObject into the associated Registry */ // Remove SuppressWarnings when TODO is fixed @SuppressWarnings("PMD.EmptyIfStmt") private void register( List errors, SchemaObject schemaObject ) throws LdapException { LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); // Check that the SchemaObject is not already registered if ( schemaObject instanceof LoadableSchemaObject ) { // TODO : Check for existing Loadable SchemaObject } else { if ( globalOidRegistry.contains( schemaObject.getOid() ) ) { // TODO : throw an exception here String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() ); LOG.error( msg ); Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); errors.add( error ); return; } } try { // First call the specific registry's register method switch ( schemaObject.getObjectType() ) { case ATTRIBUTE_TYPE: attributeTypeRegistry.register( ( AttributeType ) schemaObject ); break; case COMPARATOR: comparatorRegistry.register( ( LdapComparator ) schemaObject ); break; case DIT_CONTENT_RULE: ditContentRuleRegistry.register( ( DitContentRule ) schemaObject ); break; case DIT_STRUCTURE_RULE: ditStructureRuleRegistry.register( ( DitStructureRule ) schemaObject ); break; case LDAP_SYNTAX: ldapSyntaxRegistry.register( ( LdapSyntax ) schemaObject ); break; case MATCHING_RULE: matchingRuleRegistry.register( ( MatchingRule ) schemaObject ); break; case MATCHING_RULE_USE: matchingRuleUseRegistry.register( ( MatchingRuleUse ) schemaObject ); break; case NAME_FORM: nameFormRegistry.register( ( NameForm ) schemaObject ); break; case NORMALIZER: normalizerRegistry.register( ( Normalizer ) schemaObject ); break; case OBJECT_CLASS: objectClassRegistry.register( ( ObjectClass ) schemaObject ); break; case SYNTAX_CHECKER: syntaxCheckerRegistry.register( ( SyntaxChecker ) schemaObject ); break; } } catch ( Exception e ) { errors.add( e ); } } /** * Store the given SchemaObject in the Map associating SchemaObjetcs to their * related Schema. * * @param schemaObject The schemaObject to register * @throws LdapException If there is a problem */ public void associateWithSchema( List errors, SchemaObject schemaObject ) { LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); // Check that the SchemaObject is not already registered if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) ) { // TODO : throw an exception here String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() ); LOG.error( msg ); Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); errors.add( error ); return; } // Get a normalized form of schema name String schemaName = getSchemaName( schemaObject ); // And register the schemaObject within its schema Set content = schemaObjects.get( schemaName ); if ( content == null ) { content = new HashSet(); schemaObjects.put( Strings.toLowerCase( schemaName ), content ); } SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); if ( content.contains( schemaObjectWrapper ) ) { // Already present ! // What should we do ? LOG.info( "Registering of {}:{} failed, is already present in the Registries", schemaObject.getObjectType(), schemaObject.getOid() ); } else { // Create the association content.add( schemaObjectWrapper ); // Update the global OidRegistry if the SchemaObject is not // an instance of LoadableSchemaObject if ( !( schemaObject instanceof LoadableSchemaObject ) ) { try { globalOidRegistry.register( schemaObject ); } catch ( LdapException ne ) { errors.add( ne ); return; } } LOG.debug( "registered {} for OID {}", schemaObject.getName(), schemaObject.getOid() ); } } /** * Store the given SchemaObject in the Map associating SchemaObjetcs to their * related Schema. * * @param schemaObject The schemaObject to register * @throws LdapException If there is a problem */ public void dissociateFromSchema( List errors, SchemaObject schemaObject ) throws LdapException { LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); // Check that the SchemaObject is already registered if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) ) { // TODO : throw an exception here String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() ); LOG.error( msg ); Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); errors.add( error ); return; } // Get a normalized form of schema name String schemaName = getSchemaName( schemaObject ); String oid = schemaObject.getOid(); // And unregister the schemaObject from its schema Set content = schemaObjects.get( schemaName ); SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); if ( !content.contains( schemaObjectWrapper ) ) { // Not present ! // What should we do ? LOG.info( "Unregistering of {}:{} failed, is not present in the Registries", schemaObject.getObjectType(), schemaObject.getOid() ); } else { // Remove the association content.remove( schemaObjectWrapper ); // Update the global OidRegistry if the SchemaObject is not // an instance of LoadableSchemaObject if ( !( schemaObject instanceof LoadableSchemaObject ) ) { try { globalOidRegistry.unregister( oid ); } catch ( LdapException ne ) { errors.add( ne ); return; } } LOG.debug( "Unregistered {} for OID {}", schemaObject.getName(), schemaObject.getOid() ); } } /** * Unregister a SchemaObject from the registries * * @param schemaObject The SchemaObject we want to deregister * @throws LdapException If the removal failed */ // Remove me when TODO is implemented @SuppressWarnings( { "PMD.UnusedFormalParameter", "PMD.EmptyIfStmt" }) private SchemaObject unregister( List errors, SchemaObject schemaObject ) throws LdapException { LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); // Check that the SchemaObject is present in the registries if ( schemaObject instanceof LoadableSchemaObject ) { // TODO : check for an existing Loadable SchemaObject } else { if ( !globalOidRegistry.contains( schemaObject.getOid() ) ) { // TODO : throw an exception here String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() ); LOG.error( msg ); throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); } } SchemaObject unregistered = null; // First call the specific registry's register method switch ( schemaObject.getObjectType() ) { case ATTRIBUTE_TYPE: unregistered = attributeTypeRegistry.unregister( ( AttributeType ) schemaObject ); break; case COMPARATOR: unregistered = comparatorRegistry.unregister( ( LdapComparator ) schemaObject ); break; case DIT_CONTENT_RULE: unregistered = ditContentRuleRegistry.unregister( ( DitContentRule ) schemaObject ); break; case DIT_STRUCTURE_RULE: unregistered = ditStructureRuleRegistry.unregister( ( DitStructureRule ) schemaObject ); break; case LDAP_SYNTAX: unregistered = ldapSyntaxRegistry.unregister( ( LdapSyntax ) schemaObject ); break; case MATCHING_RULE: unregistered = matchingRuleRegistry.unregister( ( MatchingRule ) schemaObject ); break; case MATCHING_RULE_USE: unregistered = matchingRuleUseRegistry.unregister( ( MatchingRuleUse ) schemaObject ); break; case NAME_FORM: unregistered = nameFormRegistry.unregister( ( NameForm ) schemaObject ); break; case NORMALIZER: unregistered = normalizerRegistry.unregister( ( Normalizer ) schemaObject ); break; case OBJECT_CLASS: unregistered = objectClassRegistry.unregister( ( ObjectClass ) schemaObject ); break; case SYNTAX_CHECKER: unregistered = syntaxCheckerRegistry.unregister( ( SyntaxChecker ) schemaObject ); break; } return unregistered; } /** * Remove the given SchemaObject from the Map associating SchemaObjetcs to their * related Schema. * * @param schemaObject The schemaObject to remove * @throws LdapException If there is a problem */ public void dissociateFromSchema( SchemaObject schemaObject ) throws LdapException { // And unregister the schemaObject within its schema Set content = schemaObjects.get( Strings.toLowerCase( schemaObject.getSchemaName() ) ); if ( content != null ) { SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); if ( content.contains( schemaObjectWrapper ) ) { // remove the schemaObject content.remove( schemaObjectWrapper ); // Update the global OidRegistry if the SchemaObject is not // an instance of LoadableSchemaObject if ( !( schemaObject instanceof LoadableSchemaObject ) ) { globalOidRegistry.unregister( schemaObject.getOid() ); } LOG.debug( "Unregistered {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); } else { // Not present !! // What should we do ? LOG.debug( "Unregistering of {}:{} failed, not found in Registries", schemaObject.getObjectType(), schemaObject.getOid() ); } } } /** * Checks if a specific SchemaObject is referenced by any other SchemaObject. * * @param schemaObject The SchemaObject we are looking for * @return true if there is at least one SchemaObjetc referencing the given one */ public boolean isReferenced( SchemaObject schemaObject ) { SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); Set set = usedBy.get( wrapper ); boolean referenced = ( set != null ) && ( set.size() != 0 ); if ( LOG.isDebugEnabled() ) { if ( referenced ) { LOG.debug( "The {}:{} is referenced", schemaObject.getObjectType(), schemaObject.getOid() ); } else { LOG.debug( "The {}:{} is not referenced", schemaObject.getObjectType(), schemaObject.getOid() ); } } return referenced; } /** * Gets the Set of SchemaObjects referencing the given SchemaObject * * @param schemaObject The SchemaObject we are looking for * @return The Set of referencing SchemaObject, or null */ public Set getUsedBy( SchemaObject schemaObject ) { SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); return usedBy.get( wrapper ); } /** * Dump the UsedBy data structure as a String */ public String dumpUsedBy() { StringBuilder sb = new StringBuilder(); sb.append( "USED BY :\n" ); for ( SchemaObjectWrapper wrapper : usedBy.keySet() ) { sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "] : {" ); boolean isFirst = true; for ( SchemaObjectWrapper uses : usedBy.get( wrapper ) ) { if ( isFirst ) { isFirst = false; } else { sb.append( ", " ); } sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" ); } sb.append( "}\n" ); } return sb.toString(); } /** * Dump the Using data structure as a String */ public String dumpUsing() { StringBuilder sb = new StringBuilder(); sb.append( "USING :\n" ); for ( SchemaObjectWrapper wrapper : using.keySet() ) { sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "] : {" ); boolean isFirst = true; for ( SchemaObjectWrapper uses : using.get( wrapper ) ) { if ( isFirst ) { isFirst = false; } else { sb.append( ", " ); } sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" ); } sb.append( "}\n" ); } return sb.toString(); } /** * Gets the Set of SchemaObjects referenced by the given SchemaObject * * @param schemaObject The SchemaObject we are looking for * @return The Set of referenced SchemaObject, or null */ public Set getUsing( SchemaObject schemaObject ) { SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); return using.get( wrapper ); } /** * Add an association between a SchemaObject an the SchemaObject it refers * * @param reference The base SchemaObject * @param referee The SchemaObject pointing on the reference */ private void addUsing( SchemaObject reference, SchemaObject referee ) { if ( ( reference == null ) || ( referee == null ) ) { return; } SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference ); Set uses = getUsing( reference ); if ( uses == null ) { uses = new HashSet(); } uses.add( new SchemaObjectWrapper( referee ) ); // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly using.put( wrapper, uses ); } /** * Add an association between a SchemaObject an the SchemaObject it refers * * @param base The base SchemaObject * @param referenced The referenced SchemaObject */ public void addReference( SchemaObject base, SchemaObject referenced ) { if ( LOG.isDebugEnabled() ) { LOG.debug( dump( "add", base, referenced ) ); } addUsing( base, referenced ); addUsedBy( referenced, base ); if ( LOG.isDebugEnabled() ) { LOG.debug( dumpUsedBy() ); LOG.debug( dumpUsing() ); } } /** * Add an association between a SchemaObject an the SchemaObject that refers it * * @param reference The base SchemaObject * @param referee The SchemaObject pointing on the reference */ private void addUsedBy( SchemaObject referee, SchemaObject reference ) { if ( ( reference == null ) || ( referee == null ) ) { return; } SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee ); Set uses = getUsedBy( referee ); if ( uses == null ) { uses = new HashSet(); } uses.add( new SchemaObjectWrapper( reference ) ); // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly usedBy.put( wrapper, uses ); } /** * Del an association between a SchemaObject an the SchemaObject it refers * * @param reference The base SchemaObject * @param referee The SchemaObject pointing on the reference */ private void delUsing( SchemaObject reference, SchemaObject referee ) { if ( ( reference == null ) || ( referee == null ) ) { return; } Set uses = getUsing( reference ); if ( uses == null ) { return; } uses.remove( new SchemaObjectWrapper( referee ) ); SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference ); if ( uses.size() == 0 ) { using.remove( wrapper ); } else { using.put( wrapper, uses ); } } /** * Del an association between a SchemaObject an the SchemaObject that refers it * * @param reference The base SchemaObject * @param referee The SchemaObject pointing on the reference */ private void delUsedBy( SchemaObject referee, SchemaObject reference ) { if ( ( reference == null ) || ( referee == null ) ) { return; } Set uses = getUsedBy( referee ); if ( uses == null ) { return; } uses.remove( new SchemaObjectWrapper( reference ) ); SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee ); if ( uses.size() == 0 ) { usedBy.remove( wrapper ); } else { usedBy.put( wrapper, uses ); } } /** * Delete an association between a SchemaObject an the SchemaObject it refers * * @param base The base SchemaObject * @param referenced The referenced SchemaObject */ public void delReference( SchemaObject base, SchemaObject referenced ) { if ( LOG.isDebugEnabled() ) { LOG.debug( dump( "del", base, referenced ) ); } delUsing( base, referenced ); delUsedBy( referenced, base ); if ( LOG.isDebugEnabled() ) { LOG.debug( dumpUsedBy() ); LOG.debug( dumpUsing() ); } } /** * Dump the reference operation as a String */ private String dump( String op, SchemaObject reference, SchemaObject referee ) { return op + " : " + reference.getObjectType() + "[" + reference.getOid() + "]/[" + referee.getObjectType() + "[" + referee.getOid() + "]"; } private boolean checkReferences( SchemaObject reference, SchemaObject referee, String message ) { SchemaObjectWrapper referenceWrapper = new SchemaObjectWrapper( reference ); SchemaObjectWrapper refereeWrapper = new SchemaObjectWrapper( referee ); // Check the references : Syntax -> SyntaxChecker if ( !using.containsKey( referenceWrapper ) ) { LOG.debug( "The Syntax {}:{} does not reference any " + message, reference.getObjectType(), reference .getOid() ); return false; } Set usings = using.get( referenceWrapper ); if ( !usings.contains( refereeWrapper ) ) { LOG.debug( "The {}:{} does not reference any " + message, reference.getObjectType(), reference.getOid() ); return false; } // Check the referees : SyntaxChecker -> Syntax if ( !usedBy.containsKey( refereeWrapper ) ) { LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() ); return false; } Set used = usedBy.get( refereeWrapper ); if ( !used.contains( referenceWrapper ) ) { LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() ); return false; } return true; } /** * Check the registries for invalid relations. This check stops at the first error. * * @return true if the Registries is consistent, false otherwise */ public boolean check() { // Check the Syntaxes : check for a SyntaxChecker LOG.debug( "Checking Syntaxes" ); for ( LdapSyntax syntax : ldapSyntaxRegistry ) { // Check that each Syntax has a SyntaxChecker if ( syntax.getSyntaxChecker() == null ) { LOG.debug( "The Syntax {} has no SyntaxChecker", syntax ); return false; } if ( !syntaxCheckerRegistry.contains( syntax.getSyntaxChecker().getOid() ) ) { LOG.debug( "Cannot find the SyntaxChecker {} for the Syntax {}", syntax.getSyntaxChecker().getOid(), syntax ); return false; } // Check the references : Syntax -> SyntaxChecker and SyntaxChecker -> Syntax if ( !checkReferences( syntax, syntax.getSyntaxChecker(), "SyntaxChecker" ) ) { return false; } } // Check the MatchingRules : check for a Normalizer, a Comparator and a Syntax LOG.debug( "Checking MatchingRules..." ); for ( MatchingRule matchingRule : matchingRuleRegistry ) { // Check that each MatchingRule has a Normalizer if ( matchingRule.getNormalizer() == null ) { LOG.debug( "The MatchingRule {} has no Normalizer", matchingRule ); return false; } // Check that each MatchingRule has a Normalizer if ( !normalizerRegistry.contains( matchingRule.getNormalizer().getOid() ) ) { LOG.debug( "Cannot find the Normalizer {} for the MatchingRule {}", matchingRule.getNormalizer() .getOid(), matchingRule ); return false; } // Check that each MatchingRule has a Comparator if ( matchingRule.getLdapComparator() == null ) { LOG.debug( "The MatchingRule {} has no Comparator", matchingRule ); return false; } if ( !comparatorRegistry.contains( matchingRule.getLdapComparator().getOid() ) ) { LOG.debug( "Cannot find the Comparator {} for the MatchingRule {}", matchingRule.getLdapComparator() .getOid(), matchingRule ); return false; } // Check that each MatchingRule has a Syntax if ( matchingRule.getSyntax() == null ) { LOG.debug( "The MatchingRule {} has no Syntax", matchingRule ); return false; } if ( !ldapSyntaxRegistry.contains( matchingRule.getSyntax().getOid() ) ) { LOG.debug( "Cannot find the Syntax {} for the MatchingRule {}", matchingRule.getSyntax().getOid(), matchingRule ); return false; } // Check the references : MR -> S and S -> MR if ( !checkReferences( matchingRule, matchingRule.getSyntax(), "Syntax" ) ) { return false; } // Check the references : MR -> N if ( !checkReferences( matchingRule, matchingRule.getNormalizer(), "Normalizer" ) ) { return false; } // Check the references : MR -> C and C -> MR if ( !checkReferences( matchingRule, matchingRule.getLdapComparator(), "Comparator" ) ) { return false; } } // Check the ObjectClasses : check for MAY, MUST, SUPERIORS LOG.debug( "Checking ObjectClasses..." ); for ( ObjectClass objectClass : objectClassRegistry ) { // Check that each ObjectClass has all the MAY AttributeTypes if ( objectClass.getMayAttributeTypes() != null ) { for ( AttributeType may : objectClass.getMayAttributeTypes() ) { if ( !attributeTypeRegistry.contains( may.getOid() ) ) { LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MAY", may, objectClass ); return false; } // Check the references : OC -> AT and AT -> OC (MAY) if ( !checkReferences( objectClass, may, "AttributeType" ) ) { return false; } } } // Check that each ObjectClass has all the MUST AttributeTypes if ( objectClass.getMustAttributeTypes() != null ) { for ( AttributeType must : objectClass.getMustAttributeTypes() ) { if ( !attributeTypeRegistry.contains( must.getOid() ) ) { LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MUST", must, objectClass ); return false; } // Check the references : OC -> AT and AT -> OC (MUST) if ( !checkReferences( objectClass, must, "AttributeType" ) ) { return false; } } } // Check that each ObjectClass has all the SUPERIORS ObjectClasses if ( objectClass.getSuperiors() != null ) { for ( ObjectClass superior : objectClass.getSuperiors() ) { if ( !objectClassRegistry.contains( objectClass.getOid() ) ) { LOG.debug( "Cannot find the ObjectClass {} for the ObjectClass {} SUPERIORS", superior, objectClass ); return false; } // Check the references : OC -> OC and OC -> OC (SUPERIORS) if ( !checkReferences( objectClass, superior, "ObjectClass" ) ) { return false; } } } } // Check the AttributeTypes : check for MatchingRules, Syntaxes LOG.debug( "Checking AttributeTypes..." ); for ( AttributeType attributeType : attributeTypeRegistry ) { // Check that each AttributeType has a SYNTAX if ( attributeType.getSyntax() == null ) { LOG.debug( "The AttributeType {} has no Syntax", attributeType ); return false; } if ( !ldapSyntaxRegistry.contains( attributeType.getSyntax().getOid() ) ) { LOG.debug( "Cannot find the Syntax {} for the AttributeType {}", attributeType.getSyntax().getOid(), attributeType ); return false; } // Check the references for AT -> S and S -> AT if ( !checkReferences( attributeType, attributeType.getSyntax(), "AttributeType" ) ) { return false; } // Check the EQUALITY MatchingRule if ( attributeType.getEquality() != null ) { if ( !matchingRuleRegistry.contains( attributeType.getEquality().getOid() ) ) { LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getEquality() .getOid(), attributeType ); return false; } // Check the references for AT -> MR and MR -> AT if ( !checkReferences( attributeType, attributeType.getEquality(), "AttributeType" ) ) { return false; } } // Check the ORDERING MatchingRule if ( attributeType.getOrdering() != null ) { if ( !matchingRuleRegistry.contains( attributeType.getOrdering().getOid() ) ) { LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getOrdering() .getOid(), attributeType ); return false; } // Check the references for AT -> MR and MR -> AT if ( !checkReferences( attributeType, attributeType.getOrdering(), "AttributeType" ) ) { return false; } } // Check the SUBSTR MatchingRule if ( attributeType.getSubstring() != null ) { if ( !matchingRuleRegistry.contains( attributeType.getSubstring().getOid() ) ) { LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getSubstring() .getOid(), attributeType ); return false; } // Check the references for AT -> MR and MR -> AT if ( !checkReferences( attributeType, attributeType.getSubstring(), "AttributeType" ) ) { return false; } } // Check the SUP if ( attributeType.getSuperior() != null ) { AttributeType superior = attributeType.getSuperior(); if ( !attributeTypeRegistry.contains( superior.getOid() ) ) { LOG.debug( "Cannot find the AttributeType {} for the AttributeType {} SUPERIOR", superior, attributeType ); return false; } // Check the references : AT -> AT and AT -> AT (SUPERIOR) if ( !checkReferences( attributeType, superior, "AttributeType" ) ) { return false; } } } return true; } /** * Clone the Registries. This is done in two steps : * - first clone the SchemaObjetc registries * - second restore the relation between them */ // False positive @SuppressWarnings("PMD.EmptyCatchBlock") public Registries clone() throws CloneNotSupportedException { // First clone the structure Registries clone = ( Registries ) super.clone(); // Now, clone the oidRegistry clone.globalOidRegistry = globalOidRegistry.copy(); // We have to clone every SchemaObject registries now clone.attributeTypeRegistry = attributeTypeRegistry.copy(); clone.comparatorRegistry = comparatorRegistry.copy(); clone.ditContentRuleRegistry = ditContentRuleRegistry.copy(); clone.ditStructureRuleRegistry = ditStructureRuleRegistry.copy(); clone.ldapSyntaxRegistry = ldapSyntaxRegistry.copy(); clone.matchingRuleRegistry = matchingRuleRegistry.copy(); clone.matchingRuleUseRegistry = matchingRuleUseRegistry.copy(); clone.nameFormRegistry = nameFormRegistry.copy(); clone.normalizerRegistry = normalizerRegistry.copy(); clone.objectClassRegistry = objectClassRegistry.copy(); clone.syntaxCheckerRegistry = syntaxCheckerRegistry.copy(); // Store all the SchemaObjects into the globalOid registry for ( AttributeType attributeType : clone.attributeTypeRegistry ) { clone.globalOidRegistry.put( attributeType ); } for ( DitContentRule ditContentRule : clone.ditContentRuleRegistry ) { clone.globalOidRegistry.put( ditContentRule ); } for ( DitStructureRule ditStructureRule : clone.ditStructureRuleRegistry ) { clone.globalOidRegistry.put( ditStructureRule ); } for ( MatchingRule matchingRule : clone.matchingRuleRegistry ) { clone.globalOidRegistry.put( matchingRule ); } for ( MatchingRuleUse matchingRuleUse : clone.matchingRuleUseRegistry ) { clone.globalOidRegistry.put( matchingRuleUse ); } for ( NameForm nameForm : clone.nameFormRegistry ) { clone.globalOidRegistry.put( nameForm ); } for ( ObjectClass objectClass : clone.objectClassRegistry ) { clone.globalOidRegistry.put( objectClass ); } for ( LdapSyntax syntax : clone.ldapSyntaxRegistry ) { clone.globalOidRegistry.put( syntax ); } // Clone the schema list clone.loadedSchemas = new HashMap(); for ( String schemaName : loadedSchemas.keySet() ) { // We don't clone the schemas clone.loadedSchemas.put( schemaName, loadedSchemas.get( schemaName ) ); } // Clone the Using and usedBy structures // They will be empty clone.using = new HashMap>(); clone.usedBy = new HashMap>(); // Last, rebuild the using and usedBy references clone.buildReferences(); // Now, check the registries. We don't care about errors clone.checkRefInteg(); clone.schemaObjects = new HashMap>(); // Last, not least, clone the SchemaObjects Map, and reference all the copied // SchemaObjects for ( String schemaName : schemaObjects.keySet() ) { Set objects = new HashSet(); for ( SchemaObjectWrapper schemaObjectWrapper : schemaObjects.get( schemaName ) ) { SchemaObject original = schemaObjectWrapper.get(); try { if ( !( original instanceof LoadableSchemaObject ) ) { SchemaObject copy = clone.globalOidRegistry.getSchemaObject( original.getOid() ); SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( copy ); objects.add( newWrapper ); } else { SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( original ); objects.add( newWrapper ); } } catch ( LdapException ne ) { // Nothing to do } } clone.schemaObjects.put( schemaName, objects ); } return clone; } /** * Tells if the Registries is permissive or if it must be checked * against inconsistencies. * * @return True if SchemaObjects can be added even if they break the consistency */ public boolean isRelaxed() { return isRelaxed; } /** * Tells if the Registries is strict. * * @return True if SchemaObjects cannot be added if they break the consistency */ public boolean isStrict() { return !isRelaxed; } /** * Change the Registries to a relaxed mode, where invalid SchemaObjects * can be registered. */ public void setRelaxed() { isRelaxed = RELAXED; } /** * Change the Registries to a strict mode, where invalid SchemaObjects * cannot be registered. */ public void setStrict() { isRelaxed = STRICT; } /** * Tells if the Registries accept disabled elements. * * @return True if disabled SchemaObjects can be added */ public boolean isDisabledAccepted() { return disabledAccepted; } /** * Check that we can remove a given SchemaObject without breaking some of its references. * We will return the list of refereing objects. * * @param schemaObject The SchemaObject to remove * @return The list of SchemaObjects referencing the SchemaObjetc we want to remove */ public Set getReferencing( SchemaObject schemaObject ) { SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); return usedBy.get( schemaObjectWrapper ); } /** * Change the Registries behavior regarding disabled SchemaObject element. * * @param disabledAccepted If false, then the Registries won't accept * disabled SchemaObject or enabled SchemaObject from disabled schema */ public void setDisabledAccepted( boolean disabledAccepted ) { this.disabledAccepted = disabledAccepted; } /** * Clear the registries from all its elements * * @throws LdapException If something goes wrong */ public void clear() throws LdapException { // The AttributeTypeRegistry if ( attributeTypeRegistry != null ) { attributeTypeRegistry.clear(); } // The ComparatorRegistry if ( comparatorRegistry != null ) { comparatorRegistry.clear(); } // The DitContentRuleRegistry if ( ditContentRuleRegistry != null ) { ditContentRuleRegistry.clear(); } // The DitStructureRuleRegistry if ( ditStructureRuleRegistry != null ) { ditStructureRuleRegistry.clear(); } // The MatchingRuleRegistry if ( matchingRuleRegistry != null ) { matchingRuleRegistry.clear(); } // The MatchingRuleUseRegistry if ( matchingRuleUseRegistry != null ) { matchingRuleUseRegistry.clear(); } // The NameFormRegistry if ( nameFormRegistry != null ) { nameFormRegistry.clear(); } // The NormalizerRegistry if ( normalizerRegistry != null ) { normalizerRegistry.clear(); } // The ObjectClassRegistry if ( objectClassRegistry != null ) { objectClassRegistry.clear(); } // The SyntaxRegistry if ( ldapSyntaxRegistry != null ) { ldapSyntaxRegistry.clear(); } // The SyntaxCheckerRegistry if ( syntaxCheckerRegistry != null ) { syntaxCheckerRegistry.clear(); } // Clear the schemaObjects map for ( String schemaName : schemaObjects.keySet() ) { Set wrapperSet = schemaObjects.get( schemaName ); wrapperSet.clear(); } schemaObjects.clear(); // Clear the usedBy map for ( SchemaObjectWrapper wrapper : usedBy.keySet() ) { Set wrapperSet = usedBy.get( wrapper ); wrapperSet.clear(); } usedBy.clear(); // Clear the using map for ( SchemaObjectWrapper wrapper : using.keySet() ) { Set wrapperSet = using.get( wrapper ); wrapperSet.clear(); } using.clear(); // Clear the global OID registry globalOidRegistry.clear(); // Clear the loadedSchema Map loadedSchemas.clear(); } /** * @see Object#toString() */ public String toString() { StringBuilder sb = new StringBuilder(); sb.append( "Registries [" ); if ( isRelaxed ) { sb.append( "RELAXED," ); } else { sb.append( "STRICT," ); } if ( disabledAccepted ) { sb.append( " Disabled accepted] :\n" ); } else { sb.append( " Disabled forbidden] :\n" ); } sb.append( "loaded schemas [" ); boolean isFirst = true; for ( String schema : loadedSchemas.keySet() ) { if ( isFirst ) { isFirst = false; } else { sb.append( ", " ); } sb.append( schema ); } sb.append( "]\n" ); sb.append( "AttributeTypes : " ).append( attributeTypeRegistry.size() ).append( "\n" ); sb.append( "Comparators : " ).append( comparatorRegistry.size() ).append( "\n" ); sb.append( "DitContentRules : " ).append( ditContentRuleRegistry.size() ).append( "\n" ); sb.append( "DitStructureRules : " ).append( ditStructureRuleRegistry.size() ).append( "\n" ); sb.append( "MatchingRules : " ).append( matchingRuleRegistry.size() ).append( "\n" ); sb.append( "MatchingRuleUses : " ).append( matchingRuleUseRegistry.size() ).append( "\n" ); sb.append( "NameForms : " ).append( nameFormRegistry.size() ).append( "\n" ); sb.append( "Normalizers : " ).append( normalizerRegistry.size() ).append( "\n" ); sb.append( "ObjectClasses : " ).append( objectClassRegistry.size() ).append( "\n" ); sb.append( "Syntaxes : " ).append( ldapSyntaxRegistry.size() ).append( "\n" ); sb.append( "SyntaxCheckers : " ).append( syntaxCheckerRegistry.size() ).append( "\n" ); sb.append( "GlobalOidRegistry : " ).append( globalOidRegistry.size() ).append( '\n' ); return sb.toString(); } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy