You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2009/08/12 00:49:56 UTC

svn commit: r803319 - /directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java

Author: elecharny
Date: Tue Aug 11 22:49:56 2009
New Revision: 803319

URL: http://svn.apache.org/viewvc?rev=803319&view=rev
Log:
Added the new modify() method, temporarily named modifyNew()

Modified:
    directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java

Modified: directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java?rev=803319&r1=803318&r2=803319&view=diff
==============================================================================
--- directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java (original)
+++ directory/apacheds/trunk/core/src/main/java/org/apache/directory/server/core/schema/SchemaInterceptor.java Tue Aug 11 22:49:56 2009
@@ -161,6 +161,9 @@
     /** A normalized form for the SubschemaSubentry DN */
     private String subschemaSubentryDnNorm;
 
+    /** The SubschemaSubentry DN */
+    private LdapDN subschemaSubentryDn;
+
     /**
      * the normalized name for the schema modification attributes
      */
@@ -215,7 +218,7 @@
 
         // stuff for dealing with subentries (garbage for now)
         Value<?> subschemaSubentry = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
-        LdapDN subschemaSubentryDn = new LdapDN( subschemaSubentry.getString() );
+        subschemaSubentryDn = new LdapDN( subschemaSubentry.getString() );
         subschemaSubentryDn.normalize( atRegistry.getNormalizerMapping() );
         subschemaSubentryDnNorm = subschemaSubentryDn.getNormName();
 
@@ -1549,317 +1552,238 @@
     }
 
     
-    public void modify2( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
+    /**
+     * Create a new attribute using the given values
+     */
+    private EntryAttribute createNewAttribute( ServerAttribute attribute )
     {
-        ServerEntry entry;
-        LdapDN name = opContext.getDn();
-        List<Modification> mods = opContext.getModItems();
-
-        // handle operations against the schema subentry in the schema service
-        // and never try to look it up in the nexus below
-        if ( name.getNormName().equalsIgnoreCase( subschemaSubentryDnNorm ) )
-        {
-            entry = schemaService.getSubschemaEntry( SCHEMA_SUBENTRY_RETURN_ATTRIBUTES );
-            entry.setDn( name );
-        }
-        else
-        {
-            entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
-        }
-
-        // First, we get the entry from the backend. If it does not exist, then we throw an exception
-        ServerEntry targetEntry = (ServerEntry)SchemaUtils.getTargetEntry( mods , entry );
-
-        if ( entry == null )
-        {
-            LOG.error( "No entry with this name :{}", name );
-            throw new LdapNameNotFoundException( "The entry which name is " + name + " is not found." );
-        }
-
-        // We will use this temporary entry to check that the modifications
-        // can be applied as atomic operations
-        ServerEntry tmpEntry = ( ServerEntry ) entry.clone();
-
-        Set<String> modset = new HashSet<String>();
-        Modification objectClassMod = null;
-
-        // Check that we don't have two times the same modification.
-        // This is somehow useless, as modification operations are supposed to
-        // be atomic, so we may have a sucession of Add, DEL, ADD operations
-        // for the same attribute, and this will be legal.
-        // @TODO : check if we can remove this test.
-        for ( Modification mod : mods )
-        {
-            if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
-            {
-                objectClassMod = mod;
-            }
-
-            // Freak out under some weird cases
-            if ( mod.getAttribute().size() == 0 )
-            {
-                // not ok for add but ok for replace and delete
-                if ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE )
-                {
-                    throw new LdapInvalidAttributeValueException( "No value is not a valid value for an attribute.",
-                        ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
-                }
-            }
-
-            StringBuffer keybuf = new StringBuffer();
-            keybuf.append( mod.getOperation() );
-            keybuf.append( mod.getAttribute().getId() );
-
-            for ( Value<?> value : ( ServerAttribute ) mod.getAttribute() )
-            {
-                keybuf.append( value.getString() );
-            }
-
-            if ( !modset.add( keybuf.toString() ) && ( mod.getOperation() == ModificationOperation.ADD_ATTRIBUTE ) )
-            {
-                throw new LdapAttributeInUseException( "found two copies of the following modification item: " + mod );
-            }
-        }
-
-        // Get the objectClass attribute.
-        EntryAttribute objectClass;
-
-        if ( objectClassMod == null )
-        {
-            objectClass = entry.get( SchemaConstants.OBJECT_CLASS_AT );
+        AttributeType attributeType = attribute.getAttributeType();
+        
+        // Create the new Attribute
+        EntryAttribute newAttribute = new DefaultServerAttribute( attribute.getUpId(), attributeType );
 
-            if ( objectClass == null )
-            {
-                objectClass = new DefaultServerAttribute( SchemaConstants.OBJECT_CLASS_AT, OBJECT_CLASS );
-            }
-        }
-        else
+        for ( Value<?> value : attribute )
         {
-            objectClass = getResultantObjectClasses( objectClassMod.getOperation(), objectClassMod.getAttribute(),
-                tmpEntry.get( SchemaConstants.OBJECT_CLASS_AT ) );
+            newAttribute.add( value );
         }
 
-        ObjectClassRegistry ocRegistry = this.registries.getObjectClassRegistry();
-
-        // Now, apply the modifications on the cloned entry before applying it on the
-        // real object.
-        for ( Modification mod : mods )
+        return newAttribute;
+    }
+    
+    
+    /**
+     * Modify an entry, applying the given modifications.
+     */
+    private ServerEntry modifyEntry( LdapDN dn, ServerEntry currentEntry, List<Modification> mods ) throws Exception
+    {
+        // The first step is to check that the modifications are valid :
+        // - the AT are present in the schema
+        // - The value is syntaxically correct
+        //
+        // While doing that, we will apply the modification to a copy of the current entry
+        ServerEntry tempEntry = (ServerEntry)currentEntry.clone();
+        
+        // Now, apply each mod one by one
+        for ( Modification mod:mods )
         {
-            ModificationOperation modOp = mod.getOperation();
-            ServerAttribute change = ( ServerAttribute ) mod.getAttribute();
-
-            // TODO/ handle http://issues.apache.org/jira/browse/DIRSERVER-1198
-            if ( ( change.getAttributeType() == null ) && !atRegistry.hasAttributeType( change.getUpId() )
-                && !objectClass.contains( SchemaConstants.EXTENSIBLE_OBJECT_OC ) )
-            {
-                throw new LdapInvalidAttributeIdentifierException();
-            }
-
-            // We will forbid modification of operational attributes which are not
-            // user modifiable.
-            AttributeType attributeType = change.getAttributeType();
+            ServerAttribute attribute = (ServerAttribute)mod.getAttribute();
+            AttributeType attributeType = attribute.getAttributeType();
             
-            if ( attributeType == null )
+            // We don't allow modification of operational attributes
+            if ( !attributeType.isCanUserModify() )
             {
-                attributeType = atRegistry.lookup( change.getUpId() );
+                String msg = "Cannot modify the attribute : " + attributeType;
+                LOG.error( msg );
+                throw new NoPermissionException( msg );
             }
-
-            if ( !attributeType.isCanUserModify() )
+            
+            // Check the syntax here
+            if ( !attribute.isValid() )
             {
-                throw new NoPermissionException( "Cannot modify the attribute '" + change.getUpId() + "'" );
+                // The value syntax is incorrect : this is an error
+                String msg = "The new Attribute or one of its value is incorrect : " + attributeType;
+                LOG.error( msg );
+                throw new LdapInvalidAttributeValueException( msg, 
+                    ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
             }
 
-            switch ( modOp )
+            switch ( mod.getOperation() )
             {
-                case ADD_ATTRIBUTE:
-                    EntryAttribute attr = tmpEntry.get( attributeType.getName() );
-
-                    if ( attr != null )
-                    {
-                        for ( Value<?> value : change )
-                        {
-                            attr.add( value );
+                case ADD_ATTRIBUTE :
+                    EntryAttribute currentAttribute = tempEntry.get( attributeType );
+                    
+                    // First check if the added Attribute is already present in the entry
+                    // If not, we have to create the entry
+                    if ( currentAttribute != null )
+                    {
+                        for ( Value<?> value : attribute )
+                        {
+                            // At this point, we know that the attribute's syntax is correct
+                            // We just have to check thaat the current attribute does not 
+                            // contains the value already
+                            if ( currentAttribute.contains( value ))
+                            {
+                                // This is an error. 
+                                String msg = "Cannot add a value which is already present : " +
+                                    value;
+                                LOG.error( msg );
+                                throw new LdapAttributeInUseException( msg );
+                            }
+                            
+                            currentAttribute.add( value );
                         }
                     }
                     else
                     {
-                        attr = new DefaultServerAttribute( change.getUpId(), attributeType );
+                        // We don't check if the attribute is not in the MUST or MAY at this
+                        // point, as one of the following modification can change the 
+                        // ObjectClasses.
+                        EntryAttribute newAttribute = createNewAttribute( attribute );
 
-                        for ( Value<?> value : change )
-                        {
-                            attr.add( value );
-                        }
-
-                        tmpEntry.put( attr );
+                        tempEntry.put( newAttribute );
                     }
-
+                    
                     break;
-
-                case REMOVE_ATTRIBUTE:
-                    if ( tmpEntry.get( change.getId() ) == null )
-                    {
-                        LOG.error( "Trying to remove an non-existant attribute: " + change.getUpId() );
-                        throw new LdapNoSuchAttributeException();
+                    
+                case REMOVE_ATTRIBUTE :
+                    // First check that the removed attribute exists
+                    if ( !tempEntry.containsAttribute( attributeType ) )
+                    {
+                        String msg = "Trying to remove an non-existant attribute: " + attributeType;
+                        LOG.error( msg );
+                        throw new LdapNoSuchAttributeException( msg );
                     }
 
                     // We may have to remove the attribute or only some values
-                    if ( change.size() == 0 )
+                    if ( attribute.size() == 0 )
                     {
                         // No value : we have to remove the entire attribute
-                        // Check that we aren't removing a MUST attribute
-                        if ( isRequired( change.getUpId(), objectClass ) )
-                        {
-                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
-                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
-                        }
-                        
-                        attr = tmpEntry.get( change.getUpId() );
-                        if ( attr != null )
-                        {
-                            tmpEntry.removeAttributes( change.getUpId() );
-                        }
+                        tempEntry.removeAttributes( attributeType );
                     }
                     else
                     {
-                        // for required attributes we need to check if all values are removed
-                        // if so then we have a schema violation that must be thrown
-                        if ( isRequired( change.getUpId(), objectClass ) && isCompleteRemoval( change, entry ) )
-                        {
-                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
-                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
-                        }
-
-                        // Now remove the attribute and all its values
-                        EntryAttribute modified = tmpEntry.removeAttributes( change.getId() ).get( 0 );
-
-                        // And inject back the values except the ones to remove
-                        for ( Value<?> value : change )
-                        {
-                            modified.remove( value );
-                        }
-
-                        // ok, done. Last check : if the attribute does not content any more value;
-                        // and if it's a MUST one, we should thow an exception
-                        if ( ( modified.size() == 0 ) && isRequired( change.getId(), objectClass ) )
+                        currentAttribute = tempEntry.get( attributeType );
+                        
+                        // Now remove all the values
+                        for ( Value<?> value:attribute )
                         {
-                            LOG.error( "Trying to remove a required attribute: " + change.getUpId() );
-                            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION );
+                            // We can only remove existing values.
+                            if ( currentAttribute.contains( value ) )
+                            {
+                                currentAttribute.remove( value );
+                            }
+                            else
+                            {
+                                String msg = "Cannot remove an absent value from attribute : " + attributeType;
+                                LOG.error( msg );
+                                throw new LdapNoSuchAttributeException( msg );
+                            }
                         }
-
-                        // Put back the attribute in the entry only if it has values left in it
-                        if ( modified.size() > 0 )
+                        
+                        // If the current attribute is empty, we have to remove
+                        // it from the entry
+                        if ( currentAttribute.size() == 0 )
                         {
-                            tmpEntry.put( modified );
+                            tempEntry.removeAttributes( attributeType );
                         }
                     }
 
-                    SchemaChecker
-                        .preventRdnChangeOnModifyRemove( name, modOp, change, this.registries.getOidRegistry() );
-                    SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, change,
-                        objectClass );
                     break;
-
-                case REPLACE_ATTRIBUTE:
-                    SchemaChecker.preventRdnChangeOnModifyReplace( name, modOp, change, registries.getOidRegistry() );
-                    SchemaChecker.preventStructuralClassRemovalOnModifyReplace( ocRegistry, name, modOp, change );
-
-                    attr = tmpEntry.get( change.getUpId() );
-
-                    if ( attr != null )
-                    {
-                        tmpEntry.removeAttributes( change.getUpId() );
-                    }
-
-                    attr = new DefaultServerAttribute( change.getUpId(), attributeType );
-
-                    if ( change.size() != 0 )
+                
+                case REPLACE_ATTRIBUTE :
+                    // The replaced attribute might not exist, it will then be a Add
+                    // If there is no value, then the attribute will be removed
+                    if ( !tempEntry.containsAttribute( attributeType ) )
                     {
-                        for ( Value<?> value : change )
+                        if ( attribute.size() == 0 )
                         {
-                            attr.add( value );
+                            // Ignore the modification, as the attributeType does not
+                            // exists in the entry
+                            break;
                         }
-
-                        tmpEntry.put( attr );
-                    }
-
-                    break;
-            }
-        }
-
-        check( name, tmpEntry );
-
-        // let's figure out if we need to add or take away from mods to maintain 
-        // the objectClass attribute with it's hierarchy of ancestors 
-        if ( objectClassMod != null )
-        {
-            ServerAttribute alteredObjectClass = ( ServerAttribute ) objectClass.clone();
-            alterObjectClasses( alteredObjectClass );
-
-            if ( !alteredObjectClass.equals( objectClass ) )
-            {
-                ServerAttribute ocMods = ( ServerAttribute ) objectClassMod.getAttribute();
-
-                switch ( objectClassMod.getOperation() )
-                {
-                    case ADD_ATTRIBUTE:
-                        if ( ocMods.contains( SchemaConstants.TOP_OC ) )
+                        else
                         {
-                            ocMods.remove( SchemaConstants.TOP_OC );
-                        }
+                            // Create the new Attribute
+                            EntryAttribute newAttribute = createNewAttribute( attribute );
 
-                        for ( Value<?> value : alteredObjectClass )
-                        {
-                            if ( !objectClass.contains( value ) )
-                            {
-                                ocMods.add( value );
-                            }
+                            tempEntry.put( newAttribute );
                         }
-
-                        break;
-
-                    case REMOVE_ATTRIBUTE:
-                        for ( Value<?> value : alteredObjectClass )
+                    }
+                    else
+                    {
+                        if ( attribute.size() == 0 )
                         {
-                            if ( !objectClass.contains( value ) )
-                            {
-                                ocMods.remove( value );
-                            }
+                            // Remove the attribute from the entry
+                            tempEntry.removeAttributes( attributeType );
                         }
-
-                        break;
-
-                    case REPLACE_ATTRIBUTE:
-                        for ( Value<?> value : alteredObjectClass )
+                        else
                         {
-                            if ( !objectClass.contains( value ) )
-                            {
-                                ocMods.add( value );
-                            }
-                        }
+                            // Replace the existing values with the new values
+                            // This is done by removing the Attribute
+                            tempEntry.removeAttributes( attributeType );
 
-                        break;
+                            // Create the new Attribute
+                            EntryAttribute newAttribute = createNewAttribute( attribute );
 
-                    default:
-                }
+                            tempEntry.put( newAttribute );
+                        }
+                    }
+                    
+                    break;
             }
         }
+        
+        // Ok, we have created the modified entry. We now have to check that it's a valid 
+        // entry wrt the schema.
+        // We have to check that :
+        // - the rdn values are present in the entry
+        // - the objectClasses inheritence is correct
+        // - all the MUST are present
+        // - all the attribute are in MUST and MAY, except fo the extensibleObeject OC
+        // is present
+        check( dn, tempEntry );
+        
+        return tempEntry;
+    }
 
-        if ( name.startsWith( schemaBaseDN ) )
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void modifyNew( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
+    {
+        // First, check that the entry is either a subschemaSubentry or a schema element.
+        // This is the case if it's a child of cn=schema or ou=schema
+        LdapDN dn = opContext.getDn();
+        ServerEntry targetEntry = null;
+        
+        if ( dn.equals( subschemaSubentryDn ) )
         {
-            LOG.debug( "Modification attempt on schema partition {}: \n{}", name, opContext );
+            ServerEntry currentEntry = schemaService.getSubschemaEntry( SCHEMA_SUBENTRY_RETURN_ATTRIBUTES );
+            targetEntry = modifyEntry( dn, currentEntry, opContext.getModItems() );
+        }
+        else
+        {
+            targetEntry = modifyEntry( dn, opContext.getEntry(), opContext.getModItems() );
+        }
+        
+        if ( dn.startsWith( schemaBaseDN ) )
+        {
+            LOG.debug( "Modification attempt on schema partition {}: \n{}", dn, opContext );
 
-            schemaManager.modify( opContext, entry, targetEntry, opContext
+            schemaManager.modify( opContext, opContext.getEntry(), targetEntry, opContext
                 .hasRequestControl( CascadeControl.CONTROL_OID ) );
         }
-        else if ( subschemaSubentryDnNorm.equals( name.getNormName() ) )
+        else if ( dn.equals( subschemaSubentryDn ) )
         {
-            LOG.debug( "Modification attempt on schema subentry {}: \n{}", name, opContext );
+            LOG.debug( "Modification attempt on schema subentry {}: \n{}", dn, opContext );
 
-            schemaManager.modifySchemaSubentry( opContext, entry, targetEntry, opContext
+            schemaManager.modifySchemaSubentry( opContext, opContext.getEntry(), targetEntry, opContext
                 .hasRequestControl( CascadeControl.CONTROL_OID ) );
             return;
         }
 
+        // We are golden ! Let's apply the change then.
         next.modify( opContext );
     }