From b1e3b0ccdaa423b68ef6fa2fee67d3e09990985f Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 24 Sep 2007 23:15:46 +0000
Subject: [PATCH] Perform a significant refactoring of the local backend workflow element classes so that they use smaller method sizes, which allows the VM to better optimize the code.  Also, move the code for processing each type of operation into the class for the associated operation rather than keeping it all in the LocalBackendWorkflowElement class.

---
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java |  807 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 798 insertions(+), 9 deletions(-)

diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
index ebd4182..02e2887 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendDeleteOperation.java
@@ -27,27 +27,97 @@
 package org.opends.server.workflowelement.localbackend;
 
 
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+
+import org.opends.messages.Message;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.SynchronizationProvider;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.controls.LDAPAssertionRequestControl;
+import org.opends.server.controls.LDAPPreReadRequestControl;
+import org.opends.server.controls.LDAPPreReadResponseControl;
+import org.opends.server.controls.ProxiedAuthV1Control;
+import org.opends.server.controls.ProxiedAuthV2Control;
+import org.opends.server.core.AccessControlConfigManager;
 import org.opends.server.core.DeleteOperationWrapper;
 import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.PluginConfigManager;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.CancelledOperationException;
+import org.opends.server.types.CancelResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.LockManager;
+import org.opends.server.types.Privilege;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SynchronizationProviderResult;
 import org.opends.server.types.operation.PostOperationDeleteOperation;
 import org.opends.server.types.operation.PostResponseDeleteOperation;
 import org.opends.server.types.operation.PreOperationDeleteOperation;
 import org.opends.server.types.operation.PostSynchronizationDeleteOperation;
 
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
 /**
  * This class defines an operation used to delete an entry in a local backend
  * of the Directory Server.
  */
-public class LocalBackendDeleteOperation extends DeleteOperationWrapper
-  implements PreOperationDeleteOperation,
-             PostOperationDeleteOperation,
-             PostResponseDeleteOperation,
-             PostSynchronizationDeleteOperation
+public class LocalBackendDeleteOperation
+       extends DeleteOperationWrapper
+       implements PreOperationDeleteOperation, PostOperationDeleteOperation,
+                  PostResponseDeleteOperation,
+                  PostSynchronizationDeleteOperation
 {
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
+  // The backend in which the operation is to be processed.
+  private Backend backend;
+
+  // Indicates whether the LDAP no-op control has been requested.
+  private boolean noOp;
+
+  // Indicates whether to skip post-operation processing.
+  private boolean skipPostOperation;
+
+  // The client connection on which this operation was requested.
+  private ClientConnection clientConnection;
+
+  // The DN of the entry to be deleted.
+  private DN entryDN;
+
   // The entry to be deleted.
   private Entry entry;
 
+  // The pre-read request control included in the request, if applicable.
+  private LDAPPreReadRequestControl preReadRequest;
+
+
+
   /**
    * Creates a new operation that may be used to delete an entry from a
    * local backend of the Directory Server.
@@ -61,6 +131,7 @@
   }
 
 
+
   /**
    * Retrieves the entry to be deleted.
    *
@@ -72,13 +143,731 @@
     return entry;
   }
 
+
+
   /**
-   * Sets the entry to be deleted.
+   * Process this delete operation in a local backend.
    *
-   * @param  entry - The entry to be deleted
+   * @param  backend  The backend in which the delete operation should be
+   *                  processed.
    */
-  public void setEntryToDelete(Entry entry){
-    this.entry = entry;
+  void processLocalDelete(Backend backend)
+  {
+    this.backend = backend;
+
+    clientConnection = getClientConnection();
+
+    // Get the plugin config manager that will be used for invoking plugins.
+    PluginConfigManager pluginConfigManager =
+         DirectoryServer.getPluginConfigManager();
+    skipPostOperation = false;
+
+    // Check for a request to cancel this operation.
+    if (getCancelRequest() != null)
+    {
+      return;
+    }
+
+    // Create a labeled block of code that we can break out of if a problem is
+    // detected.
+deleteProcessing:
+    {
+      // Process the entry DN to convert it from its raw form as provided by the
+      // client to the form required for the rest of the delete processing.
+      entryDN = getEntryDN();
+      if (entryDN == null){
+        break deleteProcessing;
+      }
+
+      // Grab a write lock on the entry.
+      Lock entryLock = null;
+      for (int i=0; i < 3; i++)
+      {
+        entryLock = LockManager.lockWrite(entryDN);
+        if (entryLock != null)
+        {
+          break;
+        }
+      }
+
+      if (entryLock == null)
+      {
+        setResultCode(DirectoryServer.getServerErrorResultCode());
+        appendErrorMessage(ERR_DELETE_CANNOT_LOCK_ENTRY.get(
+                                String.valueOf(entryDN)));
+        break deleteProcessing;
+      }
+
+      try
+      {
+        // Get the entry to delete.  If it doesn't exist, then fail.
+        try
+        {
+          entry = backend.getEntry(entryDN);
+          if (entry == null)
+          {
+            setResultCode(ResultCode.NO_SUCH_OBJECT);
+            appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(
+                                    String.valueOf(entryDN)));
+
+            try
+            {
+              DN parentDN = entryDN.getParentDNInSuffix();
+              while (parentDN != null)
+              {
+                if (DirectoryServer.entryExists(parentDN))
+                {
+                  setMatchedDN(parentDN);
+                  break;
+                }
+
+                parentDN = parentDN.getParentDNInSuffix();
+              }
+            }
+            catch (Exception e)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+              }
+            }
+
+            break deleteProcessing;
+          }
+        }
+        catch (DirectoryException de)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, de);
+          }
+
+          setResponseData(de);
+          break deleteProcessing;
+        }
+
+
+        // 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.continueOperationProcessing())
+            {
+              break deleteProcessing;
+            }
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            logError(ERR_DELETE_SYNCH_CONFLICT_RESOLUTION_FAILED.get(
+                          getConnectionID(), getOperationID(),
+                          getExceptionMessage(de)));
+            setResponseData(de);
+            break deleteProcessing;
+          }
+        }
+
+        // Check to see if the client has permission to perform the
+        // delete.
+
+        // Check to see if there are any controls in the request.  If so, then
+        // see if there is any special processing required.
+        try
+        {
+          handleRequestControls();
+        }
+        catch (DirectoryException de)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, de);
+          }
+
+          setResponseData(de);
+          break deleteProcessing;
+        }
+
+
+        // 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 may
+        // have already exposed sensitive information to the client.
+        if (! AccessControlConfigManager.getInstance().
+                   getAccessControlHandler().isAllowed(this))
+        {
+          setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
+          appendErrorMessage(ERR_DELETE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
+                                  String.valueOf(entryDN)));
+          skipPostOperation = true;
+          break deleteProcessing;
+        }
+
+        // Check for a request to cancel this operation.
+        if (getCancelRequest() != null)
+        {
+          return;
+        }
+
+
+        // If the operation is not a synchronization operation,
+        // invoke the pre-delete plugins.
+        if (! isSynchronizationOperation())
+        {
+          PreOperationPluginResult preOpResult =
+               pluginConfigManager.invokePreOperationDeletePlugins(this);
+          if (preOpResult.connectionTerminated())
+          {
+            // There's no point in continuing with anything.  Log the request
+            // and result and return.
+            setResultCode(ResultCode.CANCELED);
+            appendErrorMessage(ERR_CANCELED_BY_PREOP_DISCONNECT.get());
+            setProcessingStopTime();
+            return;
+          }
+          else if (preOpResult.sendResponseImmediately() ||
+                   preOpResult.skipCoreProcessing())
+          {
+            skipPostOperation = true;
+            break deleteProcessing;
+          }
+        }
+
+
+        // Check for a request to cancel this operation.
+        if (getCancelRequest() != null)
+        {
+          return;
+        }
+
+
+        // Get the backend to use for the delete.  If there is none, then fail.
+        if (backend == null)
+        {
+          setResultCode(ResultCode.NO_SUCH_OBJECT);
+          appendErrorMessage(ERR_DELETE_NO_SUCH_ENTRY.get(
+                                  String.valueOf(entryDN)));
+          break deleteProcessing;
+        }
+
+
+        // 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_DELETE_SERVER_READONLY.get(
+                                      String.valueOf(entryDN)));
+              break deleteProcessing;
+
+            case INTERNAL_ONLY:
+              if (! (isInternalOperation() || isSynchronizationOperation()))
+              {
+                setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+                appendErrorMessage(ERR_DELETE_SERVER_READONLY.get(
+                                        String.valueOf(entryDN)));
+                break deleteProcessing;
+              }
+          }
+
+          switch (backend.getWritabilityMode())
+          {
+            case DISABLED:
+              setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+              appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get(
+                                      String.valueOf(entryDN)));
+              break deleteProcessing;
+
+            case INTERNAL_ONLY:
+              if (! (isInternalOperation() || isSynchronizationOperation()))
+              {
+                setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+                appendErrorMessage(ERR_DELETE_BACKEND_READONLY.get(
+                                        String.valueOf(entryDN)));
+                break deleteProcessing;
+              }
+          }
+        }
+
+
+        // The selected backend will have the responsibility of making sure that
+        // the entry actually exists and does not have any children (or possibly
+        // handling a subtree delete).  But we will need to check if there are
+        // any subordinate backends that should stop us from attempting the
+        // delete.
+        Backend[] subBackends = backend.getSubordinateBackends();
+        for (Backend b : subBackends)
+        {
+          DN[] baseDNs = b.getBaseDNs();
+          for (DN dn : baseDNs)
+          {
+            if (dn.isDescendantOf(entryDN))
+            {
+              setResultCode(ResultCode.NOT_ALLOWED_ON_NONLEAF);
+              appendErrorMessage(ERR_DELETE_HAS_SUB_BACKEND.get(
+                                      String.valueOf(entryDN),
+                                      String.valueOf(dn)));
+              break deleteProcessing;
+            }
+          }
+        }
+
+
+        // Actually perform the delete.
+        try
+        {
+          if (noOp)
+          {
+            setResultCode(ResultCode.NO_OPERATION);
+            appendErrorMessage(INFO_DELETE_NOOP.get());
+          }
+          else
+          {
+            for (SynchronizationProvider provider :
+                 DirectoryServer.getSynchronizationProviders())
+            {
+              try
+              {
+                SynchronizationProviderResult result =
+                     provider.doPreOperation(this);
+                if (! result.continueOperationProcessing())
+                {
+                  break deleteProcessing;
+                }
+              }
+              catch (DirectoryException de)
+              {
+                if (debugEnabled())
+                {
+                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
+                }
+
+                logError(ERR_DELETE_SYNCH_PREOP_FAILED.get(getConnectionID(),
+                              getOperationID(), getExceptionMessage(de)));
+                setResponseData(de);
+                break deleteProcessing;
+              }
+            }
+
+            backend.deleteEntry(entryDN, this);
+          }
+
+
+          processPreReadControl();
+
+
+          if (! noOp)
+          {
+            setResultCode(ResultCode.SUCCESS);
+          }
+        }
+        catch (DirectoryException de)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, de);
+          }
+
+          setResponseData(de);
+          break deleteProcessing;
+        }
+        catch (CancelledOperationException coe)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, coe);
+          }
+
+          CancelResult cancelResult = coe.getCancelResult();
+
+          setCancelResult(cancelResult);
+          setResultCode(cancelResult.getResultCode());
+
+          Message message = coe.getMessageObject();
+          if ((message != null) && (message.length() > 0))
+          {
+            appendErrorMessage(message);
+          }
+          break deleteProcessing;
+        }
+      }
+      finally
+      {
+        LockManager.unlock(entryDN, entryLock);
+
+        for (SynchronizationProvider provider :
+             DirectoryServer.getSynchronizationProviders())
+        {
+          try
+          {
+            provider.doPostOperation(this);
+          }
+          catch (DirectoryException de)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            logError(ERR_DELETE_SYNCH_POSTOP_FAILED.get(getConnectionID(),
+                          getOperationID(), getExceptionMessage(de)));
+            setResponseData(de);
+            break;
+          }
+        }
+      }
+    }
+
+
+    // Indicate that it is now too late to attempt to cancel the operation.
+    setCancelResult(CancelResult.TOO_LATE);
+
+
+    // Invoke the post-operation or post-synchronization delete plugins.
+    if (isSynchronizationOperation())
+    {
+      if (getResultCode() == ResultCode.SUCCESS)
+      {
+        pluginConfigManager.invokePostSynchronizationDeletePlugins(this);
+      }
+    }
+    else if (! skipPostOperation)
+    {
+      PostOperationPluginResult postOperationResult =
+           pluginConfigManager.invokePostOperationDeletePlugins(this);
+      if (postOperationResult.connectionTerminated())
+      {
+        setResultCode(ResultCode.CANCELED);
+        appendErrorMessage(ERR_CANCELED_BY_POSTOP_DISCONNECT.get());
+        setProcessingStopTime();
+        return;
+      }
+    }
+
+
+    // Notify any change notification listeners that might be registered with
+    // the server.
+    if (getResultCode() == ResultCode.SUCCESS)
+    {
+      for (ChangeNotificationListener changeListener :
+           DirectoryServer.getChangeNotificationListeners())
+      {
+        try
+        {
+          changeListener.handleDeleteOperation(this, entry);
+        }
+        catch (Exception e)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+
+          Message message = ERR_DELETE_ERROR_NOTIFYING_CHANGE_LISTENER.get(
+              getExceptionMessage(e));
+          logError(message);
+        }
+      }
+    }
+
+    // Stop the processing timer.
+    setProcessingStopTime();
   }
 
+
+
+  /**
+   * Performs any request control processing needed for this operation.
+   *
+   * @throws  DirectoryException  If a problem occurs that should cause the
+   *                              operation to fail.
+   */
+  private void handleRequestControls()
+          throws DirectoryException
+  {
+    List<Control> requestControls = getRequestControls();
+    if ((requestControls != null) && (! requestControls.isEmpty()))
+    {
+      for (int i=0; i < requestControls.size(); i++)
+      {
+        Control c   = requestControls.get(i);
+        String  oid = c.getOID();
+
+        if (!AccessControlConfigManager.getInstance().
+                 getAccessControlHandler().isAllowed(entryDN, this, c))
+        {
+          skipPostOperation = true;
+          throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
+                         ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
+        }
+
+        if (oid.equals(OID_LDAP_ASSERTION))
+        {
+          LDAPAssertionRequestControl assertControl;
+          if (c instanceof LDAPAssertionRequestControl)
+          {
+            assertControl = (LDAPAssertionRequestControl) c;
+          }
+          else
+          {
+            try
+            {
+              assertControl = LDAPAssertionRequestControl.decodeControl(c);
+              requestControls.set(i, assertControl);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              throw new DirectoryException(
+                             ResultCode.valueOf(le.getResultCode()),
+                             le.getMessageObject());
+            }
+          }
+
+          try
+          {
+            // FIXME -- We need to determine whether the current user has
+            //          permission to make this determination.
+            SearchFilter filter = assertControl.getSearchFilter();
+            if (! filter.matchesEntry(entry))
+            {
+              throw new DirectoryException(ResultCode.ASSERTION_FAILED,
+                                           ERR_DELETE_ASSERTION_FAILED.get(
+                                                String.valueOf(entryDN)));
+            }
+          }
+          catch (DirectoryException de)
+          {
+            if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
+            {
+              throw de;
+            }
+
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, de);
+            }
+
+            throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
+                           ERR_DELETE_CANNOT_PROCESS_ASSERTION_FILTER.get(
+                                String.valueOf(entryDN),
+                                de.getMessageObject()));
+          }
+        }
+        else if (oid.equals(OID_LDAP_NOOP_OPENLDAP_ASSIGNED))
+        {
+          noOp = true;
+        }
+        else if (oid.equals(OID_LDAP_READENTRY_PREREAD))
+        {
+          if (c instanceof LDAPPreReadRequestControl)
+          {
+            preReadRequest = (LDAPPreReadRequestControl) c;
+          }
+          else
+          {
+            try
+            {
+              preReadRequest = LDAPPreReadRequestControl.decodeControl(c);
+              requestControls.set(i, preReadRequest);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              throw new DirectoryException(
+                             ResultCode.valueOf(le.getResultCode()),
+                             le.getMessageObject());
+            }
+          }
+        }
+        else if (oid.equals(OID_PROXIED_AUTH_V1))
+        {
+          // The requester must have the PROXIED_AUTH privilige in order to
+          // be able to use this control.
+          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+          {
+            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+          }
+
+
+          ProxiedAuthV1Control proxyControl;
+          if (c instanceof ProxiedAuthV1Control)
+          {
+            proxyControl = (ProxiedAuthV1Control) c;
+          }
+          else
+          {
+            try
+            {
+              proxyControl = ProxiedAuthV1Control.decodeControl(c);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              throw new DirectoryException(
+                             ResultCode.valueOf(le.getResultCode()),
+                             le.getMessageObject());
+            }
+          }
+
+
+          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+          setAuthorizationEntry(authorizationEntry);
+          if (authorizationEntry == null)
+          {
+            setProxiedAuthorizationDN(DN.nullDN());
+          }
+          else
+          {
+            setProxiedAuthorizationDN(authorizationEntry.getDN());
+          }
+        }
+        else if (oid.equals(OID_PROXIED_AUTH_V2))
+        {
+          // The requester must have the PROXIED_AUTH privilige in order to
+          // be able to use this control.
+          if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
+          {
+            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
+                           ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
+          }
+
+
+          ProxiedAuthV2Control proxyControl;
+          if (c instanceof ProxiedAuthV2Control)
+          {
+            proxyControl = (ProxiedAuthV2Control) c;
+          }
+          else
+          {
+            try
+            {
+              proxyControl = ProxiedAuthV2Control.decodeControl(c);
+            }
+            catch (LDAPException le)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, le);
+              }
+
+              throw new DirectoryException(
+                             ResultCode.valueOf(le.getResultCode()),
+                             le.getMessageObject());
+            }
+          }
+
+
+          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
+          setAuthorizationEntry(authorizationEntry);
+          if (authorizationEntry == null)
+          {
+            setProxiedAuthorizationDN(DN.nullDN());
+          }
+          else
+          {
+            setProxiedAuthorizationDN(authorizationEntry.getDN());
+          }
+        }
+
+        // NYI -- Add support for additional controls.
+
+        else if (c.isCritical())
+        {
+          if ((backend == null) || (! backend.supportsControl(oid)))
+          {
+            throw new DirectoryException(
+                           ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
+                           ERR_DELETE_UNSUPPORTED_CRITICAL_CONTROL.get(
+                                String.valueOf(entryDN), oid));
+          }
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * Performs any processing needed for the LDAP pre-read control.
+   */
+  private void processPreReadControl()
+  {
+    if (preReadRequest != null)
+    {
+      Entry entryCopy = entry.duplicate(true);
+
+      if (! preReadRequest.allowsAttribute(
+                 DirectoryServer.getObjectClassAttributeType()))
+      {
+        entryCopy.removeAttribute(
+             DirectoryServer.getObjectClassAttributeType());
+      }
+
+      if (! preReadRequest.returnAllUserAttributes())
+      {
+        Iterator<AttributeType> iterator =
+             entryCopy.getUserAttributes().keySet().iterator();
+        while (iterator.hasNext())
+        {
+          AttributeType attrType = iterator.next();
+          if (! preReadRequest.allowsAttribute(attrType))
+          {
+            iterator.remove();
+          }
+        }
+      }
+
+      if (! preReadRequest.returnAllOperationalAttributes())
+      {
+        Iterator<AttributeType> iterator =
+             entryCopy.getOperationalAttributes().keySet().iterator();
+        while (iterator.hasNext())
+        {
+          AttributeType attrType = iterator.next();
+          if (! preReadRequest.allowsAttribute(attrType))
+          {
+            iterator.remove();
+          }
+        }
+      }
+
+      // FIXME -- Check access controls on the entry to see if it should
+      //          be returned or if any attributes need to be stripped
+      //          out..
+      SearchResultEntry searchEntry = new SearchResultEntry(entryCopy);
+      LDAPPreReadResponseControl responseControl =
+           new LDAPPreReadResponseControl(preReadRequest.getOID(),
+                                          preReadRequest.isCritical(),
+                                          searchEntry);
+      addResponseControl(responseControl);
+    }
+  }
 }
+

--
Gitblit v1.10.0