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 2010/12/21 01:09:04 UTC

svn commit: r1051326 - in /directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree: Subentry.java SubentryCache.java SubentryInterceptor.java

Author: elecharny
Date: Tue Dec 21 00:09:04 2010
New Revision: 1051326

URL: http://svn.apache.org/viewvc?rev=1051326&view=rev
Log:
o Moved the Subentry class to core-api
o Implemented the Add operation for Subtrees (fo AP and Subentry, still have to check that it works for entries)

Removed:
    directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/Subentry.java
Modified:
    directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java
    directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java

Modified: directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java?rev=1051326&r1=1051325&r2=1051326&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java (original)
+++ directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryCache.java Tue Dec 21 00:09:04 2010
@@ -25,6 +25,7 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.directory.server.core.administrative.Subentry;
 import org.apache.directory.shared.ldap.name.DN;
 
 

Modified: directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java?rev=1051326&r1=1051325&r2=1051326&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java (original)
+++ directory/apacheds/branches/apacheds-AP/core/src/main/java/org/apache/directory/server/core/subtree/SubentryInterceptor.java Tue Dec 21 00:09:04 2010
@@ -21,9 +21,12 @@ package org.apache.directory.server.core
 
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import javax.naming.directory.SearchControls;
 
@@ -33,10 +36,28 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.core.DefaultCoreSession;
 import org.apache.directory.server.core.DirectoryService;
 import org.apache.directory.server.core.LdapPrincipal;
+import org.apache.directory.server.core.administrative.AccessControlAAP;
+import org.apache.directory.server.core.administrative.AccessControlAdministrativePoint;
+import org.apache.directory.server.core.administrative.AccessControlIAP;
+import org.apache.directory.server.core.administrative.AccessControlSAP;
+import org.apache.directory.server.core.administrative.AdministrativePoint;
+import org.apache.directory.server.core.administrative.CollectiveAttributeAAP;
+import org.apache.directory.server.core.administrative.CollectiveAttributeAdministrativePoint;
+import org.apache.directory.server.core.administrative.CollectiveAttributeIAP;
+import org.apache.directory.server.core.administrative.CollectiveAttributeSAP;
+import org.apache.directory.server.core.administrative.Subentry;
+import org.apache.directory.server.core.administrative.SubschemaAAP;
+import org.apache.directory.server.core.administrative.SubschemaAdministrativePoint;
+import org.apache.directory.server.core.administrative.SubschemaSAP;
+import org.apache.directory.server.core.administrative.TriggerExecutionAAP;
+import org.apache.directory.server.core.administrative.TriggerExecutionAdministrativePoint;
+import org.apache.directory.server.core.administrative.TriggerExecutionIAP;
+import org.apache.directory.server.core.administrative.TriggerExecutionSAP;
 import org.apache.directory.server.core.entry.ClonedServerEntry;
 import org.apache.directory.server.core.filtering.EntryFilter;
 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
 import org.apache.directory.server.core.interceptor.BaseInterceptor;
+import org.apache.directory.server.core.interceptor.Interceptor;
 import org.apache.directory.server.core.interceptor.NextInterceptor;
 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
@@ -48,7 +69,6 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
-import org.apache.directory.server.core.partition.ByPassConstants;
 import org.apache.directory.server.core.partition.PartitionNexus;
 import org.apache.directory.server.i18n.I18n;
 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
@@ -65,11 +85,11 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.entry.Value;
 import org.apache.directory.shared.ldap.exception.LdapException;
 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
-import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
 import org.apache.directory.shared.ldap.exception.LdapOperationErrorException;
 import org.apache.directory.shared.ldap.exception.LdapOperationException;
 import org.apache.directory.shared.ldap.exception.LdapOtherException;
 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
+import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
 import org.apache.directory.shared.ldap.filter.ExprNode;
 import org.apache.directory.shared.ldap.filter.PresenceNode;
@@ -82,6 +102,8 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.subtree.AdministrativeRole;
 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
 import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.apache.directory.shared.ldap.util.tree.DnNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -115,6 +137,9 @@ public class SubentryInterceptor extends
     /** A reference to the nexus for direct backend operations */
     private PartitionNexus nexus;
 
+    /** A reference to the DirectoryService instance */
+    private DirectoryService directoryService;
+
     /** The SchemManager instance */
     private SchemaManager schemaManager;
 
@@ -136,6 +161,9 @@ public class SubentryInterceptor extends
     /** A reference to the CollectiveAttributeSubentries AT */
     private static AttributeType COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT;
 
+    /** A reference to the EntryUUID AT */
+    private static AttributeType ENTRY_UUID_AT;
+    
     /** A reference to the TriggerExecutionSubentries AT */
     private static AttributeType TRIGGER_EXECUTION_SUBENTRIES_AT;
 
@@ -146,7 +174,84 @@ public class SubentryInterceptor extends
         REMOVE,
         REPLACE
     }
+    
+    /** The possible roles */
+    private static final Set<String> ROLES = new HashSet<String>();
+
+    // Initialize the ROLES field
+    static
+    {
+        ROLES.add( SchemaConstants.AUTONOMOUS_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.AUTONOMOUS_AREA_OID );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase() );
+        ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** A Map to associate a role with it's OID */
+    private static final Map<String, String> ROLES_OID = new HashMap<String, String>();
+
+    // Initialize the roles/oid map
+    static
+    {
+        ROLES_OID.put( SchemaConstants.AUTONOMOUS_AREA.toLowerCase(), SchemaConstants.AUTONOMOUS_AREA_OID );
+        ROLES_OID.put( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase(),
+            SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        ROLES_OID.put( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase(),
+            SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        ROLES_OID.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase(),
+            SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        ROLES_OID.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase(),
+            SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        ROLES_OID.put( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase(),
+            SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        ROLES_OID.put( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase(),
+            SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+        ROLES_OID.put( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase(),
+            SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** The possible inner area roles */
+    private static final Set<String> INNER_AREA_ROLES = new HashSet<String>();
+
+    static
+    {
+        INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA.toLowerCase() );
+        INNER_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+        INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA.toLowerCase() );
+        INNER_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+        INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA.toLowerCase() );
+        INNER_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+    /** The possible specific area roles */
+    private static final Set<String> SPECIFIC_AREA_ROLES = new HashSet<String>();
+
+    static
+    {
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA.toLowerCase() );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA.toLowerCase() );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA.toLowerCase() );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA.toLowerCase() );
+        SPECIFIC_AREA_ROLES.add( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
 
+    /** A lock to guarantee the AP cache consistency */
+    private ReentrantReadWriteLock mutex = new ReentrantReadWriteLock();
 
     //-------------------------------------------------------------------------------------------
     // Search filter methods
@@ -202,6 +307,7 @@ public class SubentryInterceptor extends
     {
         super.init( directoryService );
 
+        this.directoryService = directoryService;
         nexus = directoryService.getPartitionNexus();
         schemaManager = directoryService.getSchemaManager();
 
@@ -213,6 +319,7 @@ public class SubentryInterceptor extends
         SUBSCHEMA_SUBENTRY_AT = schemaManager.getAttributeType( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
         COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
         TRIGGER_EXECUTION_SUBENTRIES_AT = schemaManager.getAttributeType( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
+        ENTRY_UUID_AT = schemaManager.getAttributeType( SchemaConstants.ENTRY_UUID_AT );
 
         SUBENTRY_OPATTRS = new AttributeType[]
             {
@@ -300,29 +407,22 @@ public class SubentryInterceptor extends
     {
         Set<AdministrativeRole> adminRoles = new HashSet<AdministrativeRole>();
 
-        EntryAttribute oc = subentry.get( OBJECT_CLASS_AT );
-
-        if ( oc == null )
-        {
-            throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION, I18n.err( I18n.ERR_305 ) );
-        }
-
-        if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) )
+        if ( subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) )
         {
             adminRoles.add( AdministrativeRole.AccessControlInnerArea );
         }
 
-        if ( oc.contains( SchemaConstants.SUBSCHEMA_OC ) )
+        if ( subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC ) )
         {
             adminRoles.add( AdministrativeRole.SubSchemaSpecificArea );
         }
 
-        if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
+        if ( subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
         {
             adminRoles.add( AdministrativeRole.CollectiveAttributeSpecificArea );
         }
 
-        if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) )
+        if ( subentry.hasObjectClass( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) )
         {
             adminRoles.add( AdministrativeRole.TriggerExecutionInnerArea );
         }
@@ -426,12 +526,10 @@ public class SubentryInterceptor extends
 
 
     /**
-     * Get the administrativePoint role
-     */
-    private void checkAdministrativeRole( OperationContext opContext, DN apDn ) throws LdapException
+     * Check that a subentry has the same role than it's parent AP
+     *
+    private void checkAdministrativeRole( Entry entry, DN apDn ) throws LdapException
     {
-        Entry administrationPoint = opContext.lookup( apDn, ByPassConstants.LOOKUP_BYPASS );
-
         // The administrativeRole AT must exist and not be null
         EntryAttribute administrativeRole = administrationPoint.get( ADMINISTRATIVE_ROLE_AT );
 
@@ -572,8 +670,361 @@ public class SubentryInterceptor extends
 
         return modifications;
     }
+    
+    
+    /**
+     * Check that the AT contains the AccessControl SAP role
+     */
+    private boolean hasAccessControlSpecificRole( EntryAttribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Check that the AT contains the CollectiveAttribute SAP role
+     */
+    private boolean hasCollectiveAttributeSpecificRole( EntryAttribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Check that the AT contains the TriggerExecution SAP role
+     */
+    private boolean hasTriggerExecutionSpecificRole( EntryAttribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Check that the AT contains the SubSchema SAP role
+     */
+    private boolean hasSubSchemaSpecificRole( EntryAttribute adminPoint )
+    {
+        return adminPoint.contains( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) ||
+               adminPoint.contains( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is an AC IAP
+     */
+    private boolean isAccessControlInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_INNER_AREA ) ||
+               role.equals( SchemaConstants.ACCESS_CONTROL_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is an AC SAP
+     */
+    private boolean isAccessControlSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.ACCESS_CONTROL_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a CA IAP
+     */
+    private boolean isCollectiveAttributeInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA ) ||
+               role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a CA SAP
+     */
+    private boolean isCollectiveAttributeSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.COLLECTIVE_ATTRIBUTE_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a TE IAP
+     */
+    private boolean isTriggerExecutionInnerRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA ) ||
+               role.equals( SchemaConstants.TRIGGER_EXECUTION_INNER_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a TE SAP
+     */
+    private boolean isTriggerExecutionSpecificRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.TRIGGER_EXECUTION_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is a SS SAP
+     */
+    private boolean isSubschemaSpecficRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA ) ||
+               role.equals( SchemaConstants.SUB_SCHEMA_ADMIN_SPECIFIC_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the role is an AAP
+     */
+    private boolean isAutonomousAreaRole( String role )
+    {
+        return role.equalsIgnoreCase( SchemaConstants.AUTONOMOUS_AREA ) ||
+               role.equals( SchemaConstants.AUTONOMOUS_AREA_OID );
+    }
+
+
+    /**
+     * Tells if the Administrative Point role is an AAP
+     */
+    private boolean isAAP( EntryAttribute adminPoint )
+    {
+        return ( adminPoint.contains( SchemaConstants.AUTONOMOUS_AREA ) || adminPoint
+            .contains( SchemaConstants.AUTONOMOUS_AREA_OID ) );
+    }
 
 
+    /**
+     * Check that we don't have an IAP and a SAP with the same family
+     */
+    private void checkInnerSpecificMix( String role, EntryAttribute adminPoint ) throws LdapUnwillingToPerformException
+    {
+        if ( isAccessControlInnerRole( role ) )
+        {
+            if ( hasAccessControlSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+
+        if ( isCollectiveAttributeInnerRole( role ) )
+        {
+            if ( hasCollectiveAttributeSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+
+        if ( isTriggerExecutionInnerRole( role ) )
+        {
+            if ( hasTriggerExecutionSpecificRole( adminPoint ) )
+            {
+                // This is inconsistent
+                String message = "Cannot add a specific Administrative Point and the same"
+                    + " inner Administrative point at the same time : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+            else
+            {
+                return;
+            }
+        }
+    }
+
+
+    private boolean isIAP( String role )
+    {
+        return INNER_AREA_ROLES.contains( role );
+    }
+
+
+    /**
+     * Check that the IAPs (if any) have a parent. We will check for each kind or role :
+     * AC, CA and TE.
+     */
+    private void checkIAPHasParent( String role, EntryAttribute adminPoint, DN dn )
+        throws LdapUnwillingToPerformException
+    {
+        // Check for the AC role
+        if ( isAccessControlInnerRole( role ) )
+        {
+            DnNode<AdministrativePoint> acCache = directoryService.getAccessControlAPCache();
+            
+            DnNode<AdministrativePoint> parent =  acCache.getNode( dn );
+            
+            if ( parent == null )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else if ( isCollectiveAttributeInnerRole( role ) )
+        {
+            DnNode<AdministrativePoint> caCache = directoryService.getCollectiveAttributeAPCache();
+            
+            boolean hasAP = caCache.hasParentElement( dn );
+            
+            if ( !hasAP )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else if ( isTriggerExecutionInnerRole( role ) )
+        {
+            DnNode<AdministrativePoint> caCache = directoryService.getTriggerExecutionAPCache();
+            
+            DnNode<AdministrativePoint> parent =  caCache.getNode( dn );
+            
+            if ( parent == null )
+            {
+                // We don't have any AC administrativePoint in the tree, this is an error
+                String message = "Cannot add an IAP with no parent : " + adminPoint;
+                LOG.error( message );
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        else
+        {
+            // Wtf ? We *must* have an IAP here...
+            String message = "This is not an IAP : " + role;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+    }
+    
+    
+    /**
+     * Check if we can safely add a role. If it's an AAP, we have to be sure that
+     * all the 4 SAPs are present.
+     */
+    private void checkAddRole( Value<?> role, EntryAttribute adminPoint, DN dn ) throws LdapException
+    {
+        String roleStr = StringTools.toLowerCase( StringTools.trim( role.getString() ) );
+
+        // Check that the added AdministrativeRole is valid
+        if ( !ROLES.contains( roleStr ) )
+        {
+            String message = "Cannot add the given role, it's not a valid one :" + role;
+            LOG.error( message );
+            throw new LdapUnwillingToPerformException( message );
+        }
+
+        // If we are trying to add an AAP, we have to check that 
+        // all the SAP roles are present 
+        if ( isAutonomousAreaRole( roleStr ) )
+        {
+            if ( hasAccessControlSpecificRole( adminPoint ) && hasCollectiveAttributeSpecificRole( adminPoint )
+                && hasTriggerExecutionSpecificRole( adminPoint ) && hasSubSchemaSpecificRole( adminPoint ) )
+            {
+                // Check thet we don't have any other role : we should have only 5 roles
+                if ( adminPoint.size() != 5 )
+                {
+                    String message = "Cannot add an Autonomous Administrative Point if we have some IAP roles : "
+                        + adminPoint;
+                    LOG.error( message );
+                    
+                    throw new LdapUnwillingToPerformException( message );
+                }
+                
+                // Fine, we have an AAP and the 4 SAPs
+                return;
+            }
+            else
+            {
+                // We have some missing SAP : this is not allowed
+                String message = "Cannot add an Autonomous Administrative Point if the other SAP are not present"
+                    + adminPoint;
+                LOG.error( message );
+                
+                throw new LdapUnwillingToPerformException( message );
+            }
+        }
+        
+        // check that we can't mix Inner and Specific areas
+        checkInnerSpecificMix( roleStr, adminPoint );
+
+        // Check that we don't add an IAP with no parent. The IAP must be added under
+        // either a AAP, or a SAP/IAP within the same family
+        if ( isIAP( roleStr ) )
+        {
+            checkIAPHasParent( roleStr, adminPoint, dn );
+        }
+    }
+
+
+    /**
+     * Check if an AP being added is valid or not :
+     * - it's not under a subentry
+     * - it cannot be a subentry
+     * - the roles must be consistent (ie, AAP is coming with the 4 SAPs, we can't have a SAP and a IAP for the same role)
+     * - it can't be the rootDSE or a NamingContext
+     */
+    private boolean checkIsValidAP( Entry entry ) throws LdapException
+    {
+        DN dn = entry.getDn();
+        
+        // Not rootDSE nor a namingContext
+        if ( dn.isRootDSE() || isNamingContext( dn ) )
+        {
+            return false;
+        }
+        
+        // Not a subentry
+        if ( entry.hasObjectClass( SchemaConstants.SUBENTRY_OC ) )
+        {
+            return false;
+        }
+        
+        DN parentDN = dn.getParent();
+        
+        // The parent is not a subentry
+        if ( subentryCache.getSubentry( parentDN ) != null )
+        {
+            return false;
+        }
+        
+        // Check the roles
+        EntryAttribute adminPoint = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        for ( Value<?> role : adminPoint )
+        {
+            checkAddRole( role, adminPoint, dn );
+        }
+
+        return true;
+    }
+
+    
     // -----------------------------------------------------------------------
     // Methods dealing with subentry modification
     // -----------------------------------------------------------------------
@@ -846,84 +1297,376 @@ public class SubentryInterceptor extends
     }
 
 
+    /**
+     * Get a read-lock on the AP cache.
+     * No read operation can be done on the AP cache if this
+     * method is not called before.
+     */
+    public void lockRead()
+    {
+        mutex.readLock().lock();
+    }
+
+
+    /**
+     * Get a write-lock on the AP cache.
+     * No write operation can be done on the apCache if this
+     * method is not called before.
+     */
+    public void lockWrite()
+    {
+        mutex.writeLock().lock();
+    }
+
+
+    /**
+     * Release the read-write lock on the AP cache.
+     * This method must be called after having read or modified the
+     * AP cache
+     */
+    public void unlock()
+    {
+        if ( mutex.isWriteLockedByCurrentThread() )
+        {
+            mutex.writeLock().unlock();
+        }
+        else
+        {
+            mutex.readLock().unlock();
+        }
+    }
+
+    
+    /**
+     * Create the list of AP for a given entry.
+     */
+    private void createAdministrativePoints( EntryAttribute adminPoint, DN dn, String uuid, long seqNumber ) throws LdapException
+    {
+        if ( isAAP( adminPoint ) )
+        {
+            // The AC AAP
+            AccessControlAdministrativePoint acAap = new AccessControlAAP( dn, uuid, seqNumber );
+            directoryService.getAccessControlAPCache().add( dn, acAap );
+
+            // The CA AAP
+            CollectiveAttributeAdministrativePoint caAap = new CollectiveAttributeAAP( dn, uuid, seqNumber );
+            directoryService.getCollectiveAttributeAPCache().add( dn, caAap );
+
+            // The TE AAP
+            TriggerExecutionAdministrativePoint teAap = new TriggerExecutionAAP( dn, uuid, seqNumber );
+            directoryService.getTriggerExecutionAPCache().add( dn, teAap );
+
+            // The SS AAP
+            SubschemaAdministrativePoint ssAap = new SubschemaAAP( dn, uuid, seqNumber );
+            directoryService.getSubschemaAPCache().add( dn, ssAap );
+
+            // If it's an AAP, we can get out immediately
+            return;
+        }
+
+        // Not an AAP
+        for ( Value<?> value : adminPoint )
+        {
+            String role = value.getString();
+
+            // Deal with AccessControl AP
+            if ( isAccessControlSpecificRole( role ) )
+            {
+                AccessControlAdministrativePoint sap = new AccessControlSAP( dn, uuid, seqNumber );
+                directoryService.getAccessControlAPCache().add( dn, sap );
+
+                continue;
+            }
+
+            if ( isAccessControlInnerRole( role ) )
+            {
+                AccessControlAdministrativePoint iap = new AccessControlIAP( dn, uuid, seqNumber );
+                directoryService.getAccessControlAPCache().add( dn, iap );
+
+                continue;
+            }
+
+            // Deal with CollectiveAttribute AP
+            if ( isCollectiveAttributeSpecificRole( role ) )
+            {
+                CollectiveAttributeAdministrativePoint sap = new CollectiveAttributeSAP( dn, uuid, seqNumber );
+                directoryService.getCollectiveAttributeAPCache().add( dn, sap );
+
+                continue;
+            }
+
+            if ( isCollectiveAttributeInnerRole( role ) )
+            {
+                CollectiveAttributeAdministrativePoint iap = new CollectiveAttributeIAP( dn, uuid, seqNumber );
+                directoryService.getCollectiveAttributeAPCache().add( dn, iap );
+
+                continue;
+            }
+
+            // Deal with SubSchema AP
+            if ( isSubschemaSpecficRole( role ) )
+            {
+                SubschemaAdministrativePoint sap = new SubschemaSAP( dn, uuid, seqNumber );
+                directoryService.getSubschemaAPCache().add( dn, sap );
+
+                continue;
+            }
+
+            // Deal with TriggerExecution AP
+            if ( isTriggerExecutionSpecificRole( role ) )
+            {
+                TriggerExecutionAdministrativePoint sap = new TriggerExecutionSAP( dn, uuid, seqNumber );
+                directoryService.getTriggerExecutionAPCache().add( dn, sap );
+
+                continue;
+            }
+
+            if ( isTriggerExecutionInnerRole( role ) )
+            {
+                TriggerExecutionAdministrativePoint iap = new TriggerExecutionIAP( dn, uuid, seqNumber );
+                directoryService.getTriggerExecutionAPCache().add( dn, iap );
+
+                continue;
+            }
+        }
+
+        return;
+    }
+    
+    
+    /**
+     * Get the AdministrativePoint associated with a subentry
+     * @param apDn
+     * @return
+     */
+    private AdministrativePoint getAdministrativePoint( DN apDn )
+    {
+        AdministrativePoint administrativePoint = directoryService.getAccessControlAPCache().getElement( apDn );
+        
+        if ( administrativePoint != null )
+        {
+            return administrativePoint;
+        }
+        
+        administrativePoint = directoryService.getCollectiveAttributeAPCache().getElement( apDn );
+
+        if ( administrativePoint != null )
+        {
+            return administrativePoint;
+        }
+        
+        administrativePoint = directoryService.getTriggerExecutionAPCache().getElement( apDn );
+
+        if ( administrativePoint != null )
+        {
+            return administrativePoint;
+        }
+        
+        administrativePoint = directoryService.getSubschemaAPCache().getElement( apDn );
+
+        if ( administrativePoint != null )
+        {
+            return administrativePoint;
+        }
+        
+        return null;
+    }
+    
+    
+    /**
+     * Tells if the subentry's roles point to an AP
+     */
+    private boolean isValidSubentry( Entry subentry, DN apDn )
+    {
+        boolean isValid = true;
+        
+        // Check that the ACAP exists if the AC subentry OC is present in the subentry
+        if ( subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ||
+             subentry.hasObjectClass( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC_OID ) )
+        {
+            isValid &= directoryService.getAccessControlAPCache().hasElement( apDn );
+        }
+
+        // Check that the CAAP exists if the CA subentry OC is present in the subentry
+        if ( subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) ||
+             subentry.hasObjectClass( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC_OID ) )
+        {
+            isValid &= directoryService.getCollectiveAttributeAPCache().hasElement( apDn );
+        }
+
+        // Check that the TEAP exists if the TE subentry OC is present in the subentry
+        if ( subentry.hasObjectClass( SchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) ||
+             subentry.hasObjectClass( SchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC_OID ) )
+        {
+            isValid &= directoryService.getTriggerExecutionAPCache().hasElement( apDn );
+        }
+        
+
+        // Check that the SSAP exists if the SS subentry OC is present in the subentry
+        if ( subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC ) ||
+             subentry.hasObjectClass( SchemaConstants.SUBSCHEMA_OC_OID ) )
+        {
+            isValid &= directoryService.getSubschemaAPCache().hasElement( apDn );
+        }
+        
+        return isValid;
+    }
+    
+    
+    /**
+     * Inject a new seqNumber in an AP
+     */
+    private void updateSeqNumber( DN apDn ) throws LdapException
+    {
+        long seqNumber = directoryService.getNewApSeqNumber();
+        
+        EntryAttribute newSeqNumber = new DefaultEntryAttribute( ApacheSchemaConstants.AP_SEQ_NUMBER_AT, Long.toString( seqNumber ) );
+        
+        Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, newSeqNumber );
+        List<Modification> modifications = new ArrayList<Modification>();
+        modifications.add( modification );
+        directoryService.getAdminSession().modify( apDn, modifications );
+    }
+
+
     //-------------------------------------------------------------------------------------------
     // Interceptor API methods
     //-------------------------------------------------------------------------------------------
     /**
-     * {@inheritDoc}
+     * Add a new entry into the DIT. We deal with the Administrative aspects.
+     * 
+     * @param next The next {@link Interceptor} in the chain
+     * @param addContext The {@link AddOperationContext} instance
+     * @throws LdapException If we had some error while processing the Add operation
      */
     public void add( NextInterceptor next, AddOperationContext addContext ) throws LdapException
     {
+        LOG.debug( "Entering into the Subtree Interceptor, addRequest : {}", addContext );
+        
         DN dn = addContext.getDn();
-        ClonedServerEntry entry = addContext.getEntry();
+        Entry entry = addContext.getEntry();
 
-        // Check if the added entry is a subentry
-        if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
+        boolean isAdmin = addContext.getSession().getAuthenticatedPrincipal().getName().equals(
+            ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
+
+        // Check if we are adding an Administrative Point
+        EntryAttribute adminPointAT = entry.get( ADMINISTRATIVE_ROLE_AT );
+
+        // First, deal with an AP addition
+        if ( adminPointAT != null )
         {
-            // get the name of the administrative point and its administrativeRole attributes
-            // The AP must be the parent DN, but we also have to check that the given DN
-            // is not the rootDSE or a NamingContext
-            if ( dn.isRootDSE() || isNamingContext( dn ) )
+            // Only admin can add an AP
+            if ( !isAdmin )
             {
-                // Not allowed : we can't get a parent in those cases
-                throw new LdapOtherException( "Cannot find an AdministrativePoint for " + dn );
+                String message = "Cannot add the given AdministrativePoint, user is not an Admin";
+                LOG.error( message );
+                
+                throw new LdapUnwillingToPerformException( message );
             }
 
+            LOG.debug( "Addition of an administrative point at {} for the role {}", dn, adminPointAT );
+
+            try
+            {
+                // Protect the AP caches against concurrent access
+                lockWrite();
+
+                // This is an AP, do the initial check
+                checkIsValidAP( entry );
+                
+                // Ok, we are golden.
+                next.add( addContext );
+    
+                String apUuid = entry.get( ENTRY_UUID_AT ).getString();
+    
+                // Now, update the AdminPoint cache
+                createAdministrativePoints( adminPointAT, dn, apUuid, -1 );
+            }
+            finally
+            {
+                // Release the APCaches lock
+                unlock();
+            }
+            
+            LOG.debug( "Added an Administrative Point at {}", dn );
+        }
+        else if ( entry.contains( OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
+        {
+            // It's a subentry
+            if ( !isAdmin )
+            {
+                String message = "Cannot add the given Subentry, user is not an Admin";
+                LOG.error( message );
+                
+                throw new LdapUnwillingToPerformException( message );
+            }
+            
             // Get the administrativePoint role : we must have one immediately
             // upper
             DN apDn = dn.getParent();
-            checkAdministrativeRole( addContext, apDn );
-
-            /* ----------------------------------------------------------------
-             * Build the set of operational attributes to be injected into
-             * entries that are contained within the subtree represented by this
-             * new subentry.  In the process we make sure the proper roles are
-             * supported by the administrative point to allow the addition of
-             * this new subentry.
-             * ----------------------------------------------------------------
-             */
-            Subentry subentry = new Subentry();
-            subentry.setAdministrativeRoles( getSubentryAdminRoles( entry ) );
-            List<EntryAttribute> operationalAttributes = getSubentryOperationalAttributes( dn, subentry );
-
-            /* ----------------------------------------------------------------
-             * Parse the subtreeSpecification of the subentry and add it to the
-             * SubtreeSpecification cache.  If the parse succeeds we continue
-             * to add the entry to the DIT.  Thereafter we search out entries
-             * to modify the subentry operational attributes of.
-             * ----------------------------------------------------------------
-             */
-            setSubtreeSpecification( subentry, entry );
-            subentryCache.addSubentry( dn, subentry );
-
-            // Now inject the subentry into the backend
-            next.add( addContext );
-
-            /* ----------------------------------------------------------------
-             * Find the baseDn for the subentry and use that to search the tree
-             * while testing each entry returned for inclusion within the
-             * subtree of the subentry's subtreeSpecification.  All included
-             * entries will have their operational attributes merged with the
-             * operational attributes calculated above.
-             * ----------------------------------------------------------------
-             */
-            DN baseDn = apDn;
-            baseDn = baseDn.addAll( subentry.getSubtreeSpecification().getBase() );
-
-            updateEntries( OperationEnum.ADD, addContext.getSession(), dn, apDn, subentry.getSubtreeSpecification(), baseDn, operationalAttributes );
-
-            // Store the newly modified entry into the context for later use in interceptor
-            // just in case
-            addContext.setEntry( entry );
+            
+            try
+            {
+                // Protect the AP caches while checking the subentry
+                lockRead();
+    
+                if ( !isValidSubentry( entry,  apDn ) )
+                {
+                    String message = "Cannot add the given Subentry, it does not have a parent AP";
+                    LOG.error( message );
+                    
+                    throw new LdapUnwillingToPerformException( message );
+                }
+                
+                // Now, get the AdministrativePoint
+                AdministrativePoint adminPoint = getAdministrativePoint( apDn );
+                
+                /* ----------------------------------------------------------------
+                 * Build the set of operational attributes to be injected into
+                 * entries that are contained within the subtree represented by this
+                 * new subentry.  In the process we make sure the proper roles are
+                 * supported by the administrative point to allow the addition of
+                 * this new subentry.
+                 * ----------------------------------------------------------------
+                 */
+                Subentry subentry = new Subentry();
+                subentry.setAdministrativeRoles( getSubentryAdminRoles( entry ) );
+                List<EntryAttribute> operationalAttributes = getSubentryOperationalAttributes( dn, subentry );
+    
+                /* ----------------------------------------------------------------
+                 * Parse the subtreeSpecification of the subentry and add it to the
+                 * SubtreeSpecification cache.  If the parse succeeds we continue
+                 * to add the entry to the DIT.  Thereafter we search out entries
+                 * to modify the subentry operational attributes of.
+                 * ----------------------------------------------------------------
+                 */
+                setSubtreeSpecification( subentry, entry );
+                subentryCache.addSubentry( dn, subentry );
+
+                // Inject the subentry into its parent AP
+                adminPoint.addSubentry( subentry );
+
+                // Update the seqNumber and update the parent AP
+                updateSeqNumber( apDn );
+    
+                // Now inject the subentry into the backend
+                next.add( addContext );
+            }
+            finally
+            {
+                // Release the APCaches lock
+                unlock();
+            }
+            
+            LOG.debug( "Added a subentry at {}", dn );
         }
         else
         {
-            // The added entry is not a Subentry.
+            // The added entry is a normal entry
             // Nevertheless, we have to check if the entry is added into an AdministrativePoint
             // and is associated with some SubtreeSpecification
             // We brutally check *all* the subentries, as we don't hold a hierarchy
             // of AP
-            // TODO : add a hierarchy of subentries
             for ( DN subentryDn : subentryCache )
             {
                 DN apDn = subentryDn.getParent();
@@ -960,14 +1703,10 @@ public class SubentryInterceptor extends
                         }
                     }
                 }
-            }
 
-            // Now that the entry has been updated with the operational attributes,
-            // we can update it into the add context
-            addContext.setEntry( entry );
-
-            // Propagate the addition down to the backend.
-            next.add( addContext );
+                // Propagate the addition down to the backend.
+                next.add( addContext );
+            }
         }
     }
 
@@ -1218,7 +1957,7 @@ public class SubentryInterceptor extends
 
             // If we move it, we have to check that
             // the new parent is an AP
-            checkAdministrativeRole( moveContext, newSuperiorDn );
+            //checkAdministrativeRole( moveContext, newSuperiorDn );
 
             Subentry subentry = subentryCache.removeSubentry( oldDn );
             SubtreeSpecification ss = subentry.getSubtreeSpecification();