From d941ff93b228d6475249087c520c1e961c2c992a Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 12 Jul 2013 14:37:47 +0000
Subject: [PATCH] Big refactoring of LocalBackendAddOperation, removed nearly 300 lines of code.

---
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java | 1354 +++++++++++++++++++++++-----------------------------------
 1 files changed, 537 insertions(+), 817 deletions(-)

diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
index d873722..88cb11e 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendAddOperation.java
@@ -75,46 +75,50 @@
   private static final DebugTracer TRACER = getTracer();
 
 
+  private static final class BooleanHolder
+  {
+    boolean value;
+  }
 
   /**
    * The backend in which the entry is to be added.
    */
-  protected Backend backend;
+  private Backend backend;
 
   /**
    * Indicates whether the request includes the LDAP no-op control.
    */
-  protected boolean noOp;
+  private boolean noOp;
 
   /**
    * The DN of the entry to be added.
    */
-  protected DN entryDN;
+  private DN entryDN;
 
   /**
    * The entry being added to the server.
    */
-  protected Entry entry;
+  private Entry entry;
 
   /**
    * The post-read request control included in the request, if applicable.
    */
-  protected LDAPPostReadRequestControl postReadRequest;
+  private LDAPPostReadRequestControl postReadRequest;
 
   /**
    * The set of object classes for the entry to add.
    */
-  protected Map<ObjectClass, String> objectClasses;
+  private Map<ObjectClass, String> objectClasses;
 
   /**
    * The set of operational attributes for the entry to add.
    */
-  protected Map<AttributeType,List<Attribute>> operationalAttributes;
+  private Map<AttributeType, List<Attribute>> operationalAttributes;
 
   /**
    * The set of user attributes for the entry to add.
    */
-  protected Map<AttributeType,List<Attribute>> userAttributes;
+  private Map<AttributeType, List<Attribute>> userAttributes;
 
 
 
@@ -160,8 +164,6 @@
   public void processLocalAdd(final LocalBackendWorkflowElement wfe)
       throws CanceledOperationException
   {
-    boolean executePostOpPlugins = false;
-
     this.backend = wfe.getBackend();
     ClientConnection clientConnection = getClientConnection();
 
@@ -172,536 +174,10 @@
     // Check for a request to cancel this operation.
     checkIfCanceled(false);
 
-    // Create a labeled block of code that we can break out of if a problem is
-    // detected.
-addProcessing:
-    {
-      // Process the entry DN and set of attributes to convert them from their
-      // raw forms as provided by the client to the forms required for the rest
-      // of the add processing.
-      entryDN = getEntryDN();
-      if (entryDN == null)
-      {
-        break addProcessing;
-      }
 
-
-      // Check for a request to cancel this operation.
-      checkIfCanceled(false);
-
-
-      // Grab a read lock on the parent entry, if there is one.  We need to do
-      // this to ensure that the parent is not deleted or renamed while this add
-      // is in progress, and we could also need it to check the entry against
-      // a DIT structure rule.
-      Lock parentLock = null;
-      Lock entryLock  = null;
-
-      DN parentDN = entryDN.getParentDNInSuffix();
-      try
-      {
-        parentLock = lockParent(parentDN);
-      }
-      catch (DirectoryException de)
-      {
-        if (debugEnabled())
-        {
-          TRACER.debugCaught(DebugLogLevel.ERROR, de);
-        }
-
-        setResponseData(de);
-        break addProcessing;
-      }
-
-      try
-      {
-        // Check for a request to cancel this operation.
-        checkIfCanceled(false);
-
-
-        // Grab a write lock on the target entry.  We'll need to do this
-        // eventually anyway, and we want to make sure that the two locks are
-        // always released when exiting this method, no matter what.  Since
-        // the entry shouldn't exist yet, locking earlier than necessary
-        // shouldn't cause a problem.
-        entryLock = LockManager.lockWrite(entryDN);
-        if (entryLock == null)
-        {
-          setResultCode(ResultCode.BUSY);
-          appendErrorMessage(ERR_ADD_CANNOT_LOCK_ENTRY.get(
-                                  String.valueOf(entryDN)));
-          break addProcessing;
-        }
-
-
-        // Invoke any conflict resolution processing that might be needed by the
-        // synchronization provider.
-        for (SynchronizationProvider<?> provider :
-             DirectoryServer.getSynchronizationProviders())
-        {
-          try
-          {
-            SynchronizationProviderResult result =
-                provider.handleConflictResolution(this);
-            if (! result.continueProcessing())
-            {
-              setResultCode(result.getResultCode());
-              appendErrorMessage(result.getErrorMessage());
-              setMatchedDN(result.getMatchedDN());
-              setReferralURLs(result.getReferralURLs());
-              break addProcessing;
-            }
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            logError(ERR_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
-                          getConnectionID(), getOperationID(),
-                          getExceptionMessage(de)));
-
-            setResponseData(de);
-            break addProcessing;
-          }
-        }
-
-        objectClasses = getObjectClasses();
-        userAttributes = getUserAttributes();
-        operationalAttributes = getOperationalAttributes();
-
-        if ((objectClasses == null ) || (userAttributes == null) ||
-            (operationalAttributes == null))
-        {
-          break addProcessing;
-        }
-
-        for (AttributeType at : userAttributes.keySet())
-        {
-          // If the attribute type is marked "NO-USER-MODIFICATION" then fail
-          // unless this is an internal operation or is related to
-          // synchronization in some way.
-          // This must be done before running the password policy code
-          // and any other code that may add attributes marked as
-          // "NO-USER-MODIFICATION"
-          //
-          // Note that doing this checks at this time
-          // of the processing does not make it possible for pre-parse plugins
-          // to add NO-USER-MODIFICATION attributes to the entry.
-          if (at.isNoUserModification())
-          {
-            if (! (isInternalOperation() || isSynchronizationOperation()))
-            {
-              setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-              appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
-                                      String.valueOf(entryDN),
-                                      at.getNameOrOID()));
-
-              break addProcessing;
-            }
-          }
-        }
-
-        for (AttributeType at : operationalAttributes.keySet())
-        {
-          if (at.isNoUserModification())
-          {
-            if (! (isInternalOperation() || isSynchronizationOperation()))
-            {
-              setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-              appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(
-                                      String.valueOf(entryDN),
-                                      at.getNameOrOID()));
-
-              break addProcessing;
-            }
-          }
-        }
-
-        // Check to see if the entry already exists.  We do this before
-        // checking whether the parent exists to ensure a referral entry
-        // above the parent results in a correct referral.
-        try
-        {
-          if (DirectoryServer.entryExists(entryDN))
-          {
-            setResultCode(ResultCode.ENTRY_ALREADY_EXISTS);
-            appendErrorMessage(ERR_ADD_ENTRY_ALREADY_EXISTS.get(
-                                    String.valueOf(entryDN)));
-            break addProcessing;
-          }
-        }
-        catch (DirectoryException de)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, de);
-          }
-
-          setResponseData(de);
-          break addProcessing;
-        }
-
-
-        // Get the parent entry, if it exists.
-        Entry parentEntry = null;
-        if (parentDN != null)
-        {
-          try
-          {
-            parentEntry = DirectoryServer.getEntry(parentDN);
-
-            if (parentEntry == null)
-            {
-              DN matchedDN = parentDN.getParentDNInSuffix();
-              while (matchedDN != null)
-              {
-                try
-                {
-                  if (DirectoryServer.entryExists(matchedDN))
-                  {
-                    setMatchedDN(matchedDN);
-                    break;
-                  }
-                }
-                catch (Exception e)
-                {
-                  if (debugEnabled())
-                  {
-                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
-                  }
-                  break;
-                }
-
-                matchedDN = matchedDN.getParentDNInSuffix();
-              }
-
-
-              // The parent doesn't exist, so this add can't be successful.
-              setResultCode(ResultCode.NO_SUCH_OBJECT);
-              appendErrorMessage(ERR_ADD_NO_PARENT.get(String.valueOf(entryDN),
-                                      String.valueOf(parentDN)));
-              break addProcessing;
-            }
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            setResponseData(de);
-            break addProcessing;
-          }
-        }
-
-
-        // Check to make sure that all of the RDN attributes are included as
-        // attribute values.  If not, then either add them or report an error.
-        try
-        {
-          addRDNAttributesIfNecessary();
-        }
-        catch (DirectoryException de)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, de);
-          }
-
-          setResponseData(de);
-          break addProcessing;
-        }
-
-        //Add any superior objectclass(s) missing in an entries
-        //objectclass map.
-        addSuperiorObjectClasses(objectClasses);
-
-        // Create an entry object to encapsulate the set of attributes and
-        // objectclasses.
-        entry = new Entry(entryDN, objectClasses, userAttributes,
-                          operationalAttributes);
-
-        // Check to see if the entry includes a privilege specification.  If so,
-        // then the requester must have the PRIVILEGE_CHANGE privilege.
-        AttributeType privType =
-             DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true);
-        if (entry.hasAttribute(privType) &&
-            (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this)))
-        {
-
-          appendErrorMessage(
-               ERR_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES.get());
-          setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-          break addProcessing;
-        }
-
-
-        // If it's not a synchronization operation, then check
-        // to see if the entry contains one or more passwords and if they
-        // are valid in accordance with the password policies associated with
-        // the user.  Also perform any encoding that might be required by
-        // password storage schemes.
-        if (! isSynchronizationOperation())
-        {
-          try
-          {
-            handlePasswordPolicy();
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            setResponseData(de);
-            break addProcessing;
-          }
-        }
-
-
-        // If the server is configured to check schema and the
-        // operation is not a synchronization operation,
-        // check to see if the entry is valid according to the server schema,
-        // and also whether its attributes are valid according to their syntax.
-        if ((DirectoryServer.checkSchema()) && (! isSynchronizationOperation()))
-        {
-          try
-          {
-            checkSchema(parentEntry);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            setResponseData(de);
-            break addProcessing;
-          }
-        }
-
-
-        // Get the backend in which the add is to be performed.
-        if (backend == null)
-        {
-          setResultCode(ResultCode.NO_SUCH_OBJECT);
-          appendErrorMessage(Message.raw("No backend for entry " +
-                                         entryDN.toString())); // TODO: i18n
-          break addProcessing;
-        }
-
-
-        // Check to see if there are any controls in the request. If so, then
-        // see if there is any special processing required.
-        try
-        {
-          processControls(parentDN);
-        }
-        catch (DirectoryException de)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, de);
-          }
-
-          setResponseData(de);
-          break addProcessing;
-        }
-
-
-        // Check to see if the client has permission to perform the add.
-
-        // FIXME: for now assume that this will check all permission
-        // pertinent to the operation. This includes proxy authorization
-        // and any other controls specified.
-
-        // FIXME: earlier checks to see if the entry already exists or
-        // if the parent entry does not exist may have already exposed
-        // sensitive information to the client.
-        try
-        {
-          if (AccessControlConfigManager.getInstance()
-              .getAccessControlHandler().isAllowed(this) == false)
-          {
-            setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
-            appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS
-                .get(String.valueOf(entryDN)));
-            break addProcessing;
-          }
-        }
-        catch (DirectoryException e)
-        {
-          setResultCode(e.getResultCode());
-          appendErrorMessage(e.getMessageObject());
-          break addProcessing;
-        }
-
-        // Check for a request to cancel this operation.
-        checkIfCanceled(false);
-
-        // If the operation is not a synchronization operation,
-        // Invoke the pre-operation add plugins.
-        if (! isSynchronizationOperation())
-        {
-          executePostOpPlugins = true;
-          PluginResult.PreOperation preOpResult =
-            pluginConfigManager.invokePreOperationAddPlugins(this);
-          if (!preOpResult.continueProcessing())
-          {
-            setResultCode(preOpResult.getResultCode());
-            appendErrorMessage(preOpResult.getErrorMessage());
-            setMatchedDN(preOpResult.getMatchedDN());
-            setReferralURLs(preOpResult.getReferralURLs());
-            break addProcessing;
-          }
-        }
-
-
-        // If it is not a private backend, then check to see if the server or
-        // backend is operating in read-only mode.
-        if (! backend.isPrivateBackend())
-        {
-          switch (DirectoryServer.getWritabilityMode())
-          {
-            case DISABLED:
-              setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-              appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
-                                      String.valueOf(entryDN)));
-              break addProcessing;
-
-            case INTERNAL_ONLY:
-              if (! (isInternalOperation() || isSynchronizationOperation()))
-              {
-                setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-                appendErrorMessage(ERR_ADD_SERVER_READONLY.get(
-                                        String.valueOf(entryDN)));
-                break addProcessing;
-              }
-              break;
-          }
-
-          switch (backend.getWritabilityMode())
-          {
-            case DISABLED:
-              setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-              appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
-                                      String.valueOf(entryDN)));
-              break addProcessing;
-
-            case INTERNAL_ONLY:
-              if (! (isInternalOperation() || isSynchronizationOperation()))
-              {
-                setResultCode(ResultCode.UNWILLING_TO_PERFORM);
-                appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(
-                                        String.valueOf(entryDN)));
-                break addProcessing;
-              }
-              break;
-          }
-        }
-
-
-        try
-        {
-          if (noOp)
-          {
-            appendErrorMessage(INFO_ADD_NOOP.get());
-            setResultCode(ResultCode.NO_OPERATION);
-          }
-          else
-          {
-            for (SynchronizationProvider<?> provider :
-                 DirectoryServer.getSynchronizationProviders())
-            {
-              try
-              {
-                SynchronizationProviderResult result =
-                    provider.doPreOperation(this);
-                if (! result.continueProcessing())
-                {
-                  setResultCode(result.getResultCode());
-                  appendErrorMessage(result.getErrorMessage());
-                  setMatchedDN(result.getMatchedDN());
-                  setReferralURLs(result.getReferralURLs());
-                  break addProcessing;
-                }
-              }
-              catch (DirectoryException de)
-              {
-                if (debugEnabled())
-                {
-                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
-                }
-
-                logError(ERR_ADD_SYNCH_PREOP_FAILED.get(getConnectionID(),
-                              getOperationID(), getExceptionMessage(de)));
-                setResponseData(de);
-                break addProcessing;
-              }
-            }
-
-            backend.addEntry(entry, this);
-          }
-
-          LocalBackendWorkflowElement.addPostReadResponse(this,
-              postReadRequest, entry);
-
-          if (! noOp)
-          {
-            setResultCode(ResultCode.SUCCESS);
-          }
-        }
-        catch (DirectoryException de)
-        {
-          if (debugEnabled())
-          {
-            TRACER.debugCaught(DebugLogLevel.ERROR, de);
-          }
-
-          setResponseData(de);
-          break addProcessing;
-        }
-      }
-      finally
-      {
-        for (SynchronizationProvider<?> provider :
-          DirectoryServer.getSynchronizationProviders())
-        {
-          try
-          {
-            provider.doPostOperation(this);
-          }
-          catch (DirectoryException de)
-          {
-            if (debugEnabled())
-            {
-              TRACER.debugCaught(DebugLogLevel.ERROR, de);
-            }
-
-            logError(ERR_ADD_SYNCH_POSTOP_FAILED.get(getConnectionID(),
-                getOperationID(), getExceptionMessage(de)));
-            setResponseData(de);
-            break;
-          }
-        }
-
-        if (entryLock != null)
-        {
-          LockManager.unlock(entryDN, entryLock);
-        }
+    BooleanHolder executePostOpPlugins = new BooleanHolder();
+    processAdd(clientConnection, executePostOpPlugins, pluginConfigManager);
 
-        if (parentLock != null)
-        {
-          LockManager.unlock(parentDN, parentLock);
-        }
-      }
-    }
 
     // Invoke the post-operation or post-synchronization add plugins.
     if (isSynchronizationOperation())
@@ -711,12 +187,12 @@
         pluginConfigManager.invokePostSynchronizationAddPlugins(this);
       }
     }
-    else if (executePostOpPlugins)
+    else if (executePostOpPlugins.value)
     {
       // FIXME -- Should this also be done while holding the locks?
       PluginResult.PostOperation postOpResult =
           pluginConfigManager.invokePostOperationAddPlugins(this);
-      if(!postOpResult.continueProcessing())
+      if (!postOpResult.continueProcessing())
       {
         setResultCode(postOpResult.getResultCode());
         appendErrorMessage(postOpResult.getErrorMessage());
@@ -736,7 +212,8 @@
         public void run()
         {
           // Notify persistent searches.
-          for (PersistentSearch psearch : wfe.getPersistentSearches()) {
+          for (PersistentSearch psearch : wfe.getPersistentSearches())
+          {
             psearch.processAdd(entry, getChangeNumber());
           }
 
@@ -765,7 +242,412 @@
     }
   }
 
+  private void processAdd(ClientConnection clientConnection,
+      BooleanHolder executePostOpPlugins,
+      PluginConfigManager pluginConfigManager)
+      throws CanceledOperationException
+  {
+    // Process the entry DN and set of attributes to convert them from their
+    // raw forms as provided by the client to the forms required for the rest
+    // of the add processing.
+    entryDN = getEntryDN();
+    if (entryDN == null)
+    {
+      return;
+    }
 
+    // Check for a request to cancel this operation.
+    checkIfCanceled(false);
+
+    // Grab a read lock on the parent entry, if there is one. We need to do
+    // this to ensure that the parent is not deleted or renamed while this add
+    // is in progress, and we could also need it to check the entry against
+    // a DIT structure rule.
+    Lock entryLock = null;
+    Lock parentLock = null;
+    DN parentDN = null;
+
+    try
+    {
+      parentDN = entryDN.getParentDNInSuffix();
+      parentLock = lockParent(parentDN);
+
+      // Check for a request to cancel this operation.
+      checkIfCanceled(false);
+
+      // Grab a write lock on the target entry. We'll need to do this
+      // eventually anyway, and we want to make sure that the two locks are
+      // always released when exiting this method, no matter what. Since
+      // the entry shouldn't exist yet, locking earlier than necessary
+      // shouldn't cause a problem.
+      entryLock = LockManager.lockWrite(entryDN);
+      if (entryLock == null)
+      {
+        setResultCode(ResultCode.BUSY);
+        appendErrorMessage(ERR_ADD_CANNOT_LOCK_ENTRY.get(String
+            .valueOf(entryDN)));
+        return;
+      }
+
+      // Invoke any conflict resolution processing that might be needed by the
+      // synchronization provider.
+      for (SynchronizationProvider<?> provider : DirectoryServer
+          .getSynchronizationProviders())
+      {
+        try
+        {
+          SynchronizationProviderResult result =
+              provider.handleConflictResolution(this);
+          if (!result.continueProcessing())
+          {
+            setResultCode(result.getResultCode());
+            appendErrorMessage(result.getErrorMessage());
+            setMatchedDN(result.getMatchedDN());
+            setReferralURLs(result.getReferralURLs());
+            return;
+          }
+        }
+        catch (DirectoryException de)
+        {
+          logError(ERR_ADD_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+              getConnectionID(), getOperationID(), getExceptionMessage(de)));
+          throw de;
+        }
+      }
+
+      objectClasses = getObjectClasses();
+      userAttributes = getUserAttributes();
+      operationalAttributes = getOperationalAttributes();
+
+      if ((objectClasses == null) || (userAttributes == null)
+          || (operationalAttributes == null))
+      {
+        return;
+      }
+
+      // If the attribute type is marked "NO-USER-MODIFICATION" then fail
+      // unless this is an internal operation or is related to
+      // synchronization in some way.
+      // This must be done before running the password policy code
+      // and any other code that may add attributes marked as
+      // "NO-USER-MODIFICATION"
+      //
+      // Note that doing this checks at this time
+      // of the processing does not make it possible for pre-parse plugins
+      // to add NO-USER-MODIFICATION attributes to the entry.
+      if (checkHasReadOnlyAttributes(userAttributes)
+          || checkHasReadOnlyAttributes(operationalAttributes))
+      {
+        return;
+      }
+
+
+      // Check to see if the entry already exists. We do this before
+      // checking whether the parent exists to ensure a referral entry
+      // above the parent results in a correct referral.
+      if (DirectoryServer.entryExists(entryDN))
+      {
+        setResultCode(ResultCode.ENTRY_ALREADY_EXISTS);
+        appendErrorMessage(ERR_ADD_ENTRY_ALREADY_EXISTS.get(String
+            .valueOf(entryDN)));
+        return;
+      }
+
+      // Get the parent entry, if it exists.
+      Entry parentEntry = null;
+      if (parentDN != null)
+      {
+        parentEntry = DirectoryServer.getEntry(parentDN);
+
+        if (parentEntry == null)
+        {
+          DN matchedDN = parentDN.getParentDNInSuffix();
+          while (matchedDN != null)
+          {
+            try
+            {
+              if (DirectoryServer.entryExists(matchedDN))
+              {
+                setMatchedDN(matchedDN);
+                break;
+              }
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+              break;
+            }
+
+            matchedDN = matchedDN.getParentDNInSuffix();
+          }
+
+          // The parent doesn't exist, so this add can't be successful.
+          setResultCode(ResultCode.NO_SUCH_OBJECT);
+          appendErrorMessage(ERR_ADD_NO_PARENT.get(String.valueOf(entryDN),
+              String.valueOf(parentDN)));
+          return;
+        }
+      }
+
+      // Check to make sure that all of the RDN attributes are included as
+      // attribute values. If not, then either add them or report an error.
+      addRDNAttributesIfNecessary();
+
+      // Add any superior objectclass(s) missing in an entries
+      // objectclass map.
+      addSuperiorObjectClasses(objectClasses);
+
+      // Create an entry object to encapsulate the set of attributes and
+      // objectclasses.
+      entry = new Entry(entryDN, objectClasses, userAttributes,
+              operationalAttributes);
+
+      // Check to see if the entry includes a privilege specification. If so,
+      // then the requester must have the PRIVILEGE_CHANGE privilege.
+      AttributeType privType =
+          DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true);
+      if (entry.hasAttribute(privType)
+          && !clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this))
+      {
+        appendErrorMessage(ERR_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES
+            .get());
+        setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+        return;
+      }
+
+      // If it's not a synchronization operation, then check
+      // to see if the entry contains one or more passwords and if they
+      // are valid in accordance with the password policies associated with
+      // the user. Also perform any encoding that might be required by
+      // password storage schemes.
+      if (!isSynchronizationOperation())
+      {
+        handlePasswordPolicy();
+      }
+
+      // If the server is configured to check schema and the
+      // operation is not a synchronization operation,
+      // check to see if the entry is valid according to the server schema,
+      // and also whether its attributes are valid according to their syntax.
+      if (DirectoryServer.checkSchema() && !isSynchronizationOperation())
+      {
+        checkSchema(parentEntry);
+      }
+
+      // Get the backend in which the add is to be performed.
+      if (backend == null)
+      {
+        setResultCode(ResultCode.NO_SUCH_OBJECT);
+        appendErrorMessage(Message.raw("No backend for entry "
+            + entryDN.toString())); // TODO: i18n
+        return;
+      }
+
+      // Check to see if there are any controls in the request. If so, then
+      // see if there is any special processing required.
+      processControls(parentDN);
+
+      // Check to see if the client has permission to perform the add.
+
+      // FIXME: for now assume that this will check all permission
+      // pertinent to the operation. This includes proxy authorization
+      // and any other controls specified.
+
+      // FIXME: earlier checks to see if the entry already exists or
+      // if the parent entry does not exist may have already exposed
+      // sensitive information to the client.
+      try
+      {
+        if (!AccessControlConfigManager.getInstance().getAccessControlHandler()
+            .isAllowed(this))
+        {
+          setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+          appendErrorMessage(ERR_ADD_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS
+              .get(String.valueOf(entryDN)));
+          return;
+        }
+      }
+      catch (DirectoryException e)
+      {
+        setResultCode(e.getResultCode());
+        appendErrorMessage(e.getMessageObject());
+        return;
+      }
+
+      // Check for a request to cancel this operation.
+      checkIfCanceled(false);
+
+      // If the operation is not a synchronization operation,
+      // Invoke the pre-operation add plugins.
+      if (!isSynchronizationOperation())
+      {
+        executePostOpPlugins.value = true;
+        PluginResult.PreOperation preOpResult =
+            pluginConfigManager.invokePreOperationAddPlugins(this);
+        if (!preOpResult.continueProcessing())
+        {
+          setResultCode(preOpResult.getResultCode());
+          appendErrorMessage(preOpResult.getErrorMessage());
+          setMatchedDN(preOpResult.getMatchedDN());
+          setReferralURLs(preOpResult.getReferralURLs());
+          return;
+        }
+      }
+
+      // If it is not a private backend, then check to see if the server or
+      // backend is operating in read-only mode.
+      if (!backend.isPrivateBackend())
+      {
+        switch (DirectoryServer.getWritabilityMode())
+        {
+        case DISABLED:
+          setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+          appendErrorMessage(ERR_ADD_SERVER_READONLY.get(String
+              .valueOf(entryDN)));
+          return;
+
+        case INTERNAL_ONLY:
+          if (!(isInternalOperation() || isSynchronizationOperation()))
+          {
+            setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+            appendErrorMessage(ERR_ADD_SERVER_READONLY.get(String
+                .valueOf(entryDN)));
+            return;
+          }
+          break;
+        }
+
+        switch (backend.getWritabilityMode())
+        {
+        case DISABLED:
+          setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+          appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(String
+              .valueOf(entryDN)));
+          return;
+
+        case INTERNAL_ONLY:
+          if (!(isInternalOperation() || isSynchronizationOperation()))
+          {
+            setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+            appendErrorMessage(ERR_ADD_BACKEND_READONLY.get(String
+                .valueOf(entryDN)));
+            return;
+          }
+          break;
+        }
+      }
+
+      if (noOp)
+      {
+        appendErrorMessage(INFO_ADD_NOOP.get());
+        setResultCode(ResultCode.NO_OPERATION);
+      }
+      else
+      {
+        for (SynchronizationProvider<?> provider : DirectoryServer
+            .getSynchronizationProviders())
+        {
+          try
+          {
+            SynchronizationProviderResult result =
+                provider.doPreOperation(this);
+            if (!result.continueProcessing())
+            {
+              setResultCode(result.getResultCode());
+              appendErrorMessage(result.getErrorMessage());
+              setMatchedDN(result.getMatchedDN());
+              setReferralURLs(result.getReferralURLs());
+              return;
+            }
+          }
+          catch (DirectoryException de)
+          {
+            logError(ERR_ADD_SYNCH_PREOP_FAILED.get(getConnectionID(),
+                getOperationID(), getExceptionMessage(de)));
+            throw de;
+          }
+        }
+
+        backend.addEntry(entry, this);
+      }
+
+      LocalBackendWorkflowElement.addPostReadResponse(this, postReadRequest,
+          entry);
+
+      if (!noOp)
+      {
+        setResultCode(ResultCode.SUCCESS);
+      }
+    }
+    catch (DirectoryException de)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, de);
+      }
+
+      setResponseData(de);
+      return;
+    }
+    finally
+    {
+      for (SynchronizationProvider<?> provider : DirectoryServer
+          .getSynchronizationProviders())
+      {
+        try
+        {
+          provider.doPostOperation(this);
+        }
+        catch (DirectoryException de)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, de);
+          }
+
+          logError(ERR_ADD_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+              getOperationID(), getExceptionMessage(de)));
+          setResponseData(de);
+          break;
+        }
+      }
+
+      if (entryLock != null)
+      {
+        LockManager.unlock(entryDN, entryLock);
+      }
+
+      if (parentLock != null)
+      {
+        LockManager.unlock(parentDN, parentLock);
+      }
+    }
+  }
+
+
+
+  private boolean checkHasReadOnlyAttributes(
+      Map<AttributeType, List<Attribute>> attributes)
+  {
+    for (AttributeType at : attributes.keySet())
+    {
+      if (at.isNoUserModification())
+      {
+        if (!(isInternalOperation() || isSynchronizationOperation()))
+        {
+          setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+          appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get(String
+              .valueOf(entryDN), at.getNameOrOID()));
+
+          return true;
+        }
+      }
+    }
+    return false;
+  }
 
   /**
    * Acquire a read lock on the parent of the entry to add.
@@ -827,8 +709,7 @@
    *                              attributes and the server is configured to
    *                              reject such entries.
    */
-  protected void addRDNAttributesIfNecessary()
-          throws DirectoryException
+  private void addRDNAttributesIfNecessary() throws DirectoryException
   {
     RDN rdn = entryDN.getRDN();
     int numAVAs = rdn.getNumValues();
@@ -839,116 +720,72 @@
       String         n = rdn.getAttributeName(i);
       if (t.isOperational())
       {
-        List<Attribute> attrList = operationalAttributes.get(t);
-        if (attrList == null)
-        {
-          if (isSynchronizationOperation() ||
-              DirectoryServer.addMissingRDNAttributes())
-          {
-            attrList = new ArrayList<Attribute>();
-            attrList.add(Attributes.create(t, n, v));
-            operationalAttributes.put(t, attrList);
-          }
-          else
-          {
-            throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
-                                         ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
-                                              String.valueOf(entryDN), n));
-          }
-        }
-        else
-        {
-          boolean found = false;
-          for (int j = 0; j < attrList.size(); j++) {
-            Attribute a = attrList.get(j);
-
-            if (a.hasOptions())
-            {
-              continue;
-            }
-
-            if (!a.contains(v))
-            {
-              AttributeBuilder builder = new AttributeBuilder(a);
-              builder.add(v);
-              attrList.set(j, builder.toAttribute());
-            }
-
-            found = true;
-            break;
-          }
-
-          if (!found)
-          {
-            if (isSynchronizationOperation() ||
-                DirectoryServer.addMissingRDNAttributes())
-            {
-              attrList.add(Attributes.create(t, n, v));
-            }
-            else
-            {
-              throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
-                                           ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
-                                                String.valueOf(entryDN), n));
-            }
-          }
-        }
+        addRDNAttributesIfNecessary(operationalAttributes, t, v, n);
       }
       else
       {
-        List<Attribute> attrList = userAttributes.get(t);
-        if (attrList == null)
+        addRDNAttributesIfNecessary(userAttributes, t, v, n);
+      }
+    }
+  }
+
+
+
+  private void addRDNAttributesIfNecessary(
+      Map<AttributeType, List<Attribute>> attributes, AttributeType t,
+      AttributeValue v, String n) throws DirectoryException
+  {
+    List<Attribute> attrList = attributes.get(t);
+    if (attrList == null)
+    {
+      if (isSynchronizationOperation() ||
+          DirectoryServer.addMissingRDNAttributes())
+      {
+        attrList = new ArrayList<Attribute>();
+        attrList.add(Attributes.create(t, n, v));
+        attributes.put(t, attrList);
+      }
+      else
+      {
+        throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+                                     ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+                                          String.valueOf(entryDN), n));
+      }
+    }
+    else
+    {
+      boolean found = false;
+      for (int j = 0; j < attrList.size(); j++) {
+        Attribute a = attrList.get(j);
+
+        if (a.hasOptions())
         {
-          if (isSynchronizationOperation() ||
-              DirectoryServer.addMissingRDNAttributes())
-          {
-            attrList = new ArrayList<Attribute>();
-            attrList.add(Attributes.create(t, n, v));
-            userAttributes.put(t, attrList);
-          }
-          else
-          {
-            throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
-                                         ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
-                                              String.valueOf(entryDN),n));
-          }
+          continue;
+        }
+
+        if (!a.contains(v))
+        {
+          AttributeBuilder builder = new AttributeBuilder(a);
+          builder.add(v);
+          attrList.set(j, builder.toAttribute());
+        }
+
+        found = true;
+        break;
+      }
+
+      if (!found)
+      {
+        if (isSynchronizationOperation() ||
+            DirectoryServer.addMissingRDNAttributes())
+        {
+          attrList.add(Attributes.create(t, n, v));
         }
         else
         {
-          boolean found = false;
-          for (int j = 0; j < attrList.size(); j++) {
-            Attribute a = attrList.get(j);
-
-            if (a.hasOptions())
-            {
-              continue;
-            }
-
-            if (!a.contains(v))
-            {
-              AttributeBuilder builder = new AttributeBuilder(a);
-              builder.add(v);
-              attrList.set(j, builder.toAttribute());
-            }
-
-            found = true;
-            break;
-          }
-
-          if (!found)
-          {
-            if (isSynchronizationOperation() ||
-                DirectoryServer.addMissingRDNAttributes())
-            {
-              attrList.add(Attributes.create(t, n, v));
-            }
-            else
-            {
-              throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
-                                           ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
-                                                String.valueOf(entryDN),n));
-            }
-          }
+          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+                                       ERR_ADD_MISSING_RDN_ATTRIBUTE.get(
+                                            String.valueOf(entryDN), n));
         }
       }
     }
@@ -1210,8 +1047,7 @@
    * @throws  DirectoryException  If the entry violates the server schema
    *                              configuration.
    */
-  protected void checkSchema(Entry parentEntry)
-          throws DirectoryException
+  private void checkSchema(Entry parentEntry) throws DirectoryException
   {
     MessageBuilder invalidReason = new MessageBuilder();
     if (! entry.conformsToSchema(parentEntry, true, true, true, invalidReason))
@@ -1219,172 +1055,10 @@
       throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
                                    invalidReason.toMessage());
     }
-    else
-    {
-      switch (DirectoryServer.getSyntaxEnforcementPolicy())
-      {
-        case REJECT:
-          invalidReason = new MessageBuilder();
-          for (List<Attribute> attrList : userAttributes.values())
-          {
-            for (Attribute a : attrList)
-            {
-              AttributeSyntax<?> syntax = a.getAttributeType().getSyntax();
-              if (syntax != null)
-              {
-                for (AttributeValue v : a)
-                {
-                  if (! syntax.valueIsAcceptable(v.getValue(), invalidReason))
-                  {
-                    if (!syntax.isHumanReadable() || syntax.isBinary())
-                    {
-                      // Value is not human-readable
-                      Message message = WARN_ADD_OP_INVALID_SYNTAX_NO_VALUE.get(
-                                        String.valueOf(entryDN),
-                                        String.valueOf(a.getName()),
-                                        String.valueOf(invalidReason));
 
-                      throw new DirectoryException(
-                                   ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-                                   message);
-                    }
-                    else
-                    {
-                      Message message = WARN_ADD_OP_INVALID_SYNTAX.get(
-                                        String.valueOf(entryDN),
-                                        String.valueOf(v.getValue().toString()),
-                                        String.valueOf(a.getName()),
-                                        String.valueOf(invalidReason));
-
-                      throw new DirectoryException(
-                                   ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-                                   message);
-                    }
-                  }
-                }
-              }
-            }
-          }
-
-          for (List<Attribute> attrList :
-               operationalAttributes.values())
-          {
-            for (Attribute a : attrList)
-            {
-              AttributeSyntax<?> syntax = a.getAttributeType().getSyntax();
-              if (syntax != null)
-              {
-                for (AttributeValue v : a)
-                {
-                  if (! syntax.valueIsAcceptable(v.getValue(),
-                                                 invalidReason))
-                  {
-                    if (!syntax.isHumanReadable() || syntax.isBinary())
-                    {
-                      // Value is not human-readable
-                      Message message = WARN_ADD_OP_INVALID_SYNTAX_NO_VALUE.
-                        get(String.valueOf(entryDN),
-                            String.valueOf(a.getName()),
-                            String.valueOf(invalidReason));
-
-                      throw new DirectoryException(
-                                   ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-                                   message);
-                    }
-                    else
-                    {
-                      Message message = WARN_ADD_OP_INVALID_SYNTAX.
-                        get(String.valueOf(entryDN),
-                            String.valueOf(v.getValue().toString()),
-                            String.valueOf(a.getName()),
-                            String.valueOf(invalidReason));
-
-                      throw new DirectoryException(
-                                   ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-                                   message);
-                    }
-                  }
-                }
-              }
-            }
-          }
-
-          break;
-
-
-        case WARN:
-          invalidReason = new MessageBuilder();
-          for (List<Attribute> attrList : userAttributes.values())
-          {
-            for (Attribute a : attrList)
-            {
-              AttributeSyntax<?> syntax = a.getAttributeType().getSyntax();
-              if (syntax != null)
-              {
-                for (AttributeValue v : a)
-                {
-                  if (! syntax.valueIsAcceptable(v.getValue(),
-                                                 invalidReason))
-                  {
-                    if (!syntax.isHumanReadable() || syntax.isBinary())
-                    {
-                      // Value is not human-readable
-                      logError(WARN_ADD_OP_INVALID_SYNTAX_NO_VALUE.get(
-                                  String.valueOf(entryDN),
-                                  String.valueOf(a.getName()),
-                                  String.valueOf(invalidReason)));
-                    }
-                    else
-                    {
-                      logError(WARN_ADD_OP_INVALID_SYNTAX.get(
-                                  String.valueOf(entryDN),
-                                  String.valueOf(v.getValue().toString()),
-                                  String.valueOf(a.getName()),
-                                  String.valueOf(invalidReason)));
-                    }
-                  }
-                }
-              }
-            }
-          }
-
-          for (List<Attribute> attrList : operationalAttributes.values())
-          {
-            for (Attribute a : attrList)
-            {
-              AttributeSyntax<?> syntax = a.getAttributeType().getSyntax();
-              if (syntax != null)
-              {
-                for (AttributeValue v : a)
-                {
-                  if (! syntax.valueIsAcceptable(v.getValue(),
-                                                 invalidReason))
-                  {
-                    if (!syntax.isHumanReadable() || syntax.isBinary())
-                    {
-                      // Value is not human-readable
-                      logError(WARN_ADD_OP_INVALID_SYNTAX_NO_VALUE.get(
-                                  String.valueOf(entryDN),
-                                  String.valueOf(a.getName()),
-                                  String.valueOf(invalidReason)));
-                    }
-                    else
-                    {
-                      logError(WARN_ADD_OP_INVALID_SYNTAX.get(
-                                  String.valueOf(entryDN),
-                                  String.valueOf(v.getValue().toString()),
-                                  String.valueOf(a.getName()),
-                                  String.valueOf(invalidReason)));
-                    }
-                  }
-                }
-              }
-            }
-          }
-
-          break;
-      }
-    }
+    invalidReason = new MessageBuilder();
+    checkAttributes(invalidReason, userAttributes);
+    checkAttributes(invalidReason, operationalAttributes);
 
 
     // See if the entry contains any attributes or object classes marked
@@ -1424,6 +1098,53 @@
   }
 
 
+  private void checkAttributes(MessageBuilder invalidReason,
+      Map<AttributeType, List<Attribute>> attributes) throws DirectoryException
+  {
+    for (List<Attribute> attrList : attributes.values())
+    {
+      for (Attribute a : attrList)
+      {
+        AttributeSyntax<?> syntax = a.getAttributeType().getSyntax();
+        if (syntax != null)
+        {
+          for (AttributeValue v : a)
+          {
+            if (!syntax.valueIsAcceptable(v.getValue(), invalidReason))
+            {
+              Message message;
+              if (!syntax.isHumanReadable() || syntax.isBinary())
+              {
+                // Value is not human-readable
+                message = WARN_ADD_OP_INVALID_SYNTAX_NO_VALUE.
+                    get(String.valueOf(entryDN),
+                        String.valueOf(a.getName()),
+                        String.valueOf(invalidReason));
+              }
+              else
+              {
+                message = WARN_ADD_OP_INVALID_SYNTAX.
+                    get(String.valueOf(entryDN),
+                        String.valueOf(v.getValue().toString()),
+                        String.valueOf(a.getName()),
+                        String.valueOf(invalidReason));
+
+              }
+
+              switch (DirectoryServer.getSyntaxEnforcementPolicy())
+              {
+              case REJECT:
+                throw new DirectoryException(
+                    ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+              case WARN:
+                logError(message);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 
   /**
    * Processes the set of controls contained in the add request.
@@ -1433,8 +1154,7 @@
    * @throws  DirectoryException  If there is a problem with any of the
    *                              request controls.
    */
-  protected void processControls(DN parentDN)
-          throws DirectoryException
+  private void processControls(DN parentDN) throws DirectoryException
   {
     List<Control> requestControls = getRequestControls();
     if ((requestControls != null) && (! requestControls.isEmpty()))
@@ -1528,7 +1248,7 @@
           addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(),
               "obsoleteProxiedAuthzV1Control"));
 
-          // The requester must have the PROXIED_AUTH privilige in order to
+          // The requester must have the PROXIED_AUTH privilege in order to
           // be able to use this control.
           if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
                                                    this))
@@ -1553,7 +1273,7 @@
         }
         else if (oid.equals(OID_PROXIED_AUTH_V2))
         {
-          // The requester must have the PROXIED_AUTH privilige in order to
+          // The requester must have the PROXIED_AUTH privilege in order to
           // be able to use this control.
           if (! getClientConnection().hasPrivilege(Privilege.PROXIED_AUTH,
                                                    this))

--
Gitblit v1.10.0