From 94bbd5bb7c20a490558d8ec97d1be7e3dc492a42 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sun, 09 Sep 2007 23:08:09 +0000
Subject: [PATCH] Update the server to provide a basic framework for controlling when plugins will be invoked.  There are two basic changes:

---
 opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java | 1137 +++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 793 insertions(+), 344 deletions(-)

diff --git a/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java b/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
index 9c9a8d8..279164f 100644
--- a/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
+++ b/opends/src/server/org/opends/server/plugins/UniqueAttributePlugin.java
@@ -26,47 +26,104 @@
  */
 package org.opends.server.plugins;
 
-import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
-import org.opends.server.admin.std.meta.PluginCfgDefn;
+
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.PluginCfgDefn;
+import org.opends.server.admin.std.server.PluginCfg;
+import org.opends.server.admin.std.server.UniqueAttributePluginCfg;
+import org.opends.server.api.AlertGenerator;
 import org.opends.server.api.plugin.DirectoryServerPlugin;
 import org.opends.server.api.plugin.PluginType;
 import org.opends.server.api.plugin.PreOperationPluginResult;
 import org.opends.server.config.ConfigException;
-import org.opends.server.types.*;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.RDN;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.operation.PostSynchronizationAddOperation;
+import org.opends.server.types.operation.PostSynchronizationModifyDNOperation;
+import org.opends.server.types.operation.PostSynchronizationModifyOperation;
 import org.opends.server.types.operation.PreOperationAddOperation;
 import org.opends.server.types.operation.PreOperationModifyDNOperation;
 import org.opends.server.types.operation.PreOperationModifyOperation;
-import org.opends.server.types.operation.PreOperationOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.messages.Message;
-import static org.opends.messages.PluginMessages.*;
 
-import java.util.*;
+import static org.opends.messages.PluginMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
 
 /**
- * This class implements a Directory Server plugin that performs attribute
- * uniqueness checking on the add, modify and modifyDN operations. If the
- * operation is eligible for checking based on a set of configuration criteria,
- * then the operation's attribute values will be checked, using that
- * configuration criteria, for uniqueness against the server's values to
- * determine if the operation can proceed.
+ * This class implements a Directory Server plugin that can be used to ensure
+ * that all values for a given attribute or set of attributes are unique within
+ * the server (or optionally, below a specified set of base DNs).  It will
+ * examine all add, modify, and modify DN operations to determine whether any
+ * new conflicts are introduced.  If a conflict is detected then the operation
+ * will be rejected, unless that operation is being applied through
+ * synchronization in which case an alert will be generated to notify
+ * administrators of the problem.
  */
 public class UniqueAttributePlugin
         extends DirectoryServerPlugin<UniqueAttributePluginCfg>
-        implements ConfigurationChangeListener<UniqueAttributePluginCfg> {
+        implements ConfigurationChangeListener<UniqueAttributePluginCfg>,
+                   AlertGenerator
+{
+  /**
+   * The debug log tracer that will be used for this plugin.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
+  /**
+   * The pre-operation plugin result that should be returned if an operation
+   * would have resulted in a unique attribute conflict.
+   */
+  private static final PreOperationPluginResult FAILED_PREOP_RESULT =
+       new PreOperationPluginResult(false, false, false, true);
+
+
+
+  /**
+   * The set of attributes that will be requested when performing internal
+   * search operations.  This indicates that no attributes should be returned.
+   */
+  private static final LinkedHashSet<String> SEARCH_ATTRS =
+       new LinkedHashSet<String>(1);
+  static
+  {
+    SEARCH_ATTRS.add("1.1");
+  }
+
+
 
   //Current plugin configuration.
   private UniqueAttributePluginCfg currentConfiguration;
 
-  //List of attribute types that must be unique.
-  private LinkedHashSet<AttributeType> uniqueAttributeTypes =
-          new LinkedHashSet<AttributeType>();
 
-//List of base DNs that limit the scope of the uniqueness checking.
- private LinkedHashSet<DN> baseDNs = new LinkedHashSet<DN>();
 
   /**
    * {@inheritDoc}
@@ -74,37 +131,710 @@
   @Override()
   public final void initializePlugin(Set<PluginType> pluginTypes,
                                      UniqueAttributePluginCfg configuration)
-          throws ConfigException {
+          throws ConfigException
+  {
     configuration.addUniqueAttributeChangeListener(this);
     currentConfiguration = configuration;
+    DirectoryServer.registerAlertGenerator(this);
+
     for (PluginType t : pluginTypes)
-      switch (t)  {
+    {
+      switch (t)
+      {
         case PRE_OPERATION_ADD:
         case PRE_OPERATION_MODIFY:
         case PRE_OPERATION_MODIFY_DN:
+        case POST_SYNCHRONIZATION_ADD:
+        case POST_SYNCHRONIZATION_MODIFY:
+        case POST_SYNCHRONIZATION_MODIFY_DN:
           // These are acceptable.
           break;
+
         default:
           Message message =
                   ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(t.toString());
           throw new ConfigException(message);
 
       }
-    //Load base DNs if any.
-    for(DN baseDN : configuration.getUniqueAttributeBaseDN())
-      baseDNs.add(baseDN);
-    //Load attribute types if any.
-    for(AttributeType attributeType : configuration.getUniqueAttributeType())
-      uniqueAttributeTypes.add(attributeType);
+    }
   }
 
 
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void finalizePlugin()
+  {
+    currentConfiguration.removeUniqueAttributeChangeListener(this);
+    DirectoryServer.deregisterAlertGenerator(this);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreOperationPluginResult
+               doPreOperation(PreOperationAddOperation addOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+    Entry entry = addOperation.getEntryToAdd();
+
+    Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return PreOperationPluginResult.SUCCESS;
+    }
+
+    for (AttributeType t : config.getUniqueAttributeType())
+    {
+      List<Attribute> attrList = entry.getAttribute(t);
+      if (attrList != null)
+      {
+        for (Attribute a : attrList)
+        {
+          for (AttributeValue v : a.getValues())
+          {
+            try
+            {
+              DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
+                                                    config, v);
+              if (conflictDN != null)
+              {
+                addOperation.appendErrorMessage(
+                     ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+                          v.getStringValue(), conflictDN.toString()));
+                addOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+                return FAILED_PREOP_RESULT;
+              }
+            }
+            catch (DirectoryException de)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, de);
+              }
+
+              Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+                               de.getResultCode().toString(),
+                               de.getMessageObject());
+
+              addOperation.setResultCode(
+                   DirectoryServer.getServerErrorResultCode());
+              addOperation.appendErrorMessage(m);
+              return FAILED_PREOP_RESULT;
+            }
+          }
+        }
+      }
+    }
+
+    return PreOperationPluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreOperationPluginResult
+               doPreOperation(PreOperationModifyOperation modifyOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+    DN entryDN = modifyOperation.getEntryDN();
+
+    Set<DN> baseDNs = getBaseDNs(config, entryDN);
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return PreOperationPluginResult.SUCCESS;
+    }
+
+    for (Modification m : modifyOperation.getModifications())
+    {
+      Attribute a = m.getAttribute();
+      AttributeType t = a.getAttributeType();
+      if (! config.getUniqueAttributeType().contains(t))
+      {
+        // This modification isn't for a unique attribute.
+        continue;
+      }
+
+      switch (m.getModificationType())
+      {
+        case ADD:
+        case REPLACE:
+          for (AttributeValue v : a.getValues())
+          {
+            try
+            {
+              DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
+                                                    v);
+              if (conflictDN != null)
+              {
+                modifyOperation.appendErrorMessage(
+                     ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+                          v.getStringValue(), conflictDN.toString()));
+                modifyOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+                return FAILED_PREOP_RESULT;
+              }
+            }
+            catch (DirectoryException de)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, de);
+              }
+
+              Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+                                     de.getResultCode().toString(),
+                                     de.getMessageObject());
+
+              modifyOperation.setResultCode(
+                   DirectoryServer.getServerErrorResultCode());
+              modifyOperation.appendErrorMessage(message);
+              return FAILED_PREOP_RESULT;
+            }
+          }
+          break;
+
+        case INCREMENT:
+          // We could calculate the new value, but we'll just take it from the
+          // updated entry.
+          List<Attribute> attrList =
+               modifyOperation.getModifiedEntry().getAttribute(t,
+                                                               a.getOptions());
+          if (attrList != null)
+          {
+            for (Attribute updatedAttr : attrList)
+            {
+              if (! updatedAttr.optionsEqual(a.getOptions()))
+              {
+                continue;
+              }
+
+              for (AttributeValue v : updatedAttr.getValues())
+              {
+                try
+                {
+                  DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
+                                                        config, v);
+                  if (conflictDN != null)
+                  {
+                    modifyOperation.appendErrorMessage(
+                         ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(
+                              t.getNameOrOID(), v.getStringValue(),
+                              conflictDN.toString()));
+                    modifyOperation.setResultCode(
+                         ResultCode.CONSTRAINT_VIOLATION);
+                    return FAILED_PREOP_RESULT;
+                  }
+                }
+                catch (DirectoryException de)
+                {
+                  if (debugEnabled())
+                  {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
+                  }
+
+                  Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+                                         de.getResultCode().toString(),
+                                         de.getMessageObject());
+
+                  modifyOperation.setResultCode(
+                       DirectoryServer.getServerErrorResultCode());
+                  modifyOperation.appendErrorMessage(message);
+                  return FAILED_PREOP_RESULT;
+                }
+              }
+            }
+          }
+          break;
+
+        default:
+          // We don't need to look at this modification because it's not a
+          // modification type of interest.
+          continue;
+      }
+    }
+
+    return PreOperationPluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final PreOperationPluginResult doPreOperation(
+                    PreOperationModifyDNOperation modifyDNOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+
+    Set<DN> baseDNs = getBaseDNs(config,
+                                 modifyDNOperation.getUpdatedEntry().getDN());
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return PreOperationPluginResult.SUCCESS;
+    }
+
+    RDN newRDN = modifyDNOperation.getNewRDN();
+    for (int i=0; i < newRDN.getNumValues(); i++)
+    {
+      AttributeType t = newRDN.getAttributeType(i);
+      if (! config.getUniqueAttributeType().contains(t))
+      {
+        // We aren't interested in this attribute type.
+        continue;
+      }
+
+      try
+      {
+        AttributeValue v = newRDN.getAttributeValue(i);
+        DN conflictDN = getConflictingEntryDN(baseDNs,
+                             modifyDNOperation.getEntryDN(), config, v);
+        if (conflictDN != null)
+        {
+          modifyDNOperation.appendErrorMessage(
+               ERR_PLUGIN_UNIQUEATTR_ATTR_NOT_UNIQUE.get(t.getNameOrOID(),
+                    v.getStringValue(), conflictDN.toString()));
+          modifyDNOperation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
+          return FAILED_PREOP_RESULT;
+        }
+      }
+      catch (DirectoryException de)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, de);
+        }
+
+        Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR.get(
+                         de.getResultCode().toString(),
+                         de.getMessageObject());
+
+        modifyDNOperation.setResultCode(
+             DirectoryServer.getServerErrorResultCode());
+        modifyDNOperation.appendErrorMessage(m);
+        return FAILED_PREOP_RESULT;
+      }
+    }
+
+    return PreOperationPluginResult.SUCCESS;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void doPostSynchronization(
+                         PostSynchronizationAddOperation addOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+    Entry entry = addOperation.getEntryToAdd();
+
+    Set<DN> baseDNs = getBaseDNs(config, entry.getDN());
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return;
+    }
+
+    for (AttributeType t : config.getUniqueAttributeType())
+    {
+      List<Attribute> attrList = entry.getAttribute(t);
+      if (attrList != null)
+      {
+        for (Attribute a : attrList)
+        {
+          for (AttributeValue v : a.getValues())
+          {
+            try
+            {
+              DN conflictDN = getConflictingEntryDN(baseDNs, entry.getDN(),
+                                                    config, v);
+              if (conflictDN != null)
+              {
+                Message m = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+                                 t.getNameOrOID(),
+                                 addOperation.getConnectionID(),
+                                 addOperation.getOperationID(),
+                                 v.getStringValue(),
+                                 entry.getDN().toString(),
+                                 conflictDN.toString());
+                DirectoryServer.sendAlertNotification(this,
+                                     ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
+              }
+            }
+            catch (DirectoryException de)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, de);
+              }
+
+              Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+                               addOperation.getConnectionID(),
+                               addOperation.getOperationID(),
+                               entry.getDN().toString(),
+                               de.getResultCode().toString(),
+                               de.getMessageObject());
+              DirectoryServer.sendAlertNotification(this,
+                                   ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
+            }
+          }
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void doPostSynchronization(
+                         PostSynchronizationModifyOperation modifyOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+    DN entryDN = modifyOperation.getEntryDN();
+
+    Set<DN> baseDNs = getBaseDNs(config, entryDN);
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return;
+    }
+
+    for (Modification m : modifyOperation.getModifications())
+    {
+      Attribute a = m.getAttribute();
+      AttributeType t = a.getAttributeType();
+      if (! config.getUniqueAttributeType().contains(t))
+      {
+        // This modification isn't for a unique attribute.
+        continue;
+      }
+
+      switch (m.getModificationType())
+      {
+        case ADD:
+        case REPLACE:
+          for (AttributeValue v : a.getValues())
+          {
+            try
+            {
+              DN conflictDN = getConflictingEntryDN(baseDNs, entryDN, config,
+                                                    v);
+              if (conflictDN != null)
+              {
+                Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+                                       t.getNameOrOID(),
+                                       modifyOperation.getConnectionID(),
+                                       modifyOperation.getOperationID(),
+                                       v.getStringValue(),
+                                       entryDN.toString(),
+                                       conflictDN.toString());
+                DirectoryServer.sendAlertNotification(this,
+                                     ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+                                     message);
+              }
+            }
+            catch (DirectoryException de)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, de);
+              }
+
+              Message message = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+                                    modifyOperation.getConnectionID(),
+                                    modifyOperation.getOperationID(),
+                                    entryDN.toString(),
+                                    de.getResultCode().toString(),
+                                    de.getMessageObject());
+              DirectoryServer.sendAlertNotification(this,
+                                   ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, message);
+            }
+          }
+          break;
+
+        case INCREMENT:
+          // We could calculate the new value, but we'll just take it from the
+          // updated entry.
+          List<Attribute> attrList =
+               modifyOperation.getModifiedEntry().getAttribute(t,
+                                                               a.getOptions());
+          if (attrList != null)
+          {
+            for (Attribute updatedAttr : attrList)
+            {
+              if (! updatedAttr.optionsEqual(a.getOptions()))
+              {
+                continue;
+              }
+
+              for (AttributeValue v : updatedAttr.getValues())
+              {
+                try
+                {
+                  DN conflictDN = getConflictingEntryDN(baseDNs, entryDN,
+                                                        config, v);
+                  if (conflictDN != null)
+                  {
+                    Message message = ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+                                           t.getNameOrOID(),
+                                           modifyOperation.getConnectionID(),
+                                           modifyOperation.getOperationID(),
+                                           v.getStringValue(),
+                                           entryDN.toString(),
+                                           conflictDN.toString());
+                    DirectoryServer.sendAlertNotification(this,
+                                         ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+                                         message);
+                  }
+                }
+                catch (DirectoryException de)
+                {
+                  if (debugEnabled())
+                  {
+                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
+                  }
+
+                  Message message =
+                       ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+                            modifyOperation.getConnectionID(),
+                            modifyOperation.getOperationID(),
+                            entryDN.toString(),
+                            de.getResultCode().toString(),
+                            de.getMessageObject());
+                  DirectoryServer.sendAlertNotification(this,
+                                       ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
+                                       message);
+                }
+              }
+            }
+          }
+          break;
+
+        default:
+          // We don't need to look at this modification because it's not a
+          // modification type of interest.
+          continue;
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public final void doPostSynchronization(
+                         PostSynchronizationModifyDNOperation modifyDNOperation)
+  {
+    UniqueAttributePluginCfg config = currentConfiguration;
+
+    Set<DN> baseDNs = getBaseDNs(config,
+                                 modifyDNOperation.getUpdatedEntry().getDN());
+    if (baseDNs == null)
+    {
+      // The entry is outside the scope of this plugin.
+      return;
+    }
+
+    RDN newRDN = modifyDNOperation.getNewRDN();
+    for (int i=0; i < newRDN.getNumValues(); i++)
+    {
+      AttributeType t = newRDN.getAttributeType(i);
+      if (! config.getUniqueAttributeType().contains(t))
+      {
+        // We aren't interested in this attribute type.
+        continue;
+      }
+
+      try
+      {
+        AttributeValue v = newRDN.getAttributeValue(i);
+        DN conflictDN = getConflictingEntryDN(baseDNs,
+                             modifyDNOperation.getEntryDN(), config, v);
+        if (conflictDN != null)
+        {
+          Message m =
+               ERR_PLUGIN_UNIQUEATTR_SYNC_NOT_UNIQUE.get(
+                    t.getNameOrOID(),
+                    modifyDNOperation.getConnectionID(),
+                    modifyDNOperation.getOperationID(),
+                    v.getStringValue(),
+                    modifyDNOperation.getUpdatedEntry().getDN().toString(),
+                    conflictDN.toString());
+          DirectoryServer.sendAlertNotification(this,
+                               ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT, m);
+        }
+      }
+      catch (DirectoryException de)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, de);
+        }
+
+        Message m = ERR_PLUGIN_UNIQUEATTR_INTERNAL_ERROR_SYNC.get(
+                         modifyDNOperation.getConnectionID(),
+                         modifyDNOperation.getOperationID(),
+                         modifyDNOperation.getUpdatedEntry().getDN().toString(),
+                         de.getResultCode().toString(),
+                         de.getMessageObject());
+        DirectoryServer.sendAlertNotification(this,
+                             ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR, m);
+      }
+    }
+  }
+
+
+
+  /**
+   * Retrieves the set of base DNs below which uniqueness checks should be
+   * performed.  If no uniqueness checks should be performed for the specified
+   * entry, then {@code null} will be returned.
+   *
+   * @param  config   The plugin configuration to use to make the determination.
+   * @param  entryDN  The DN of the entry for which the checks will be
+   *                  performed.
+   */
+  private Set<DN> getBaseDNs(UniqueAttributePluginCfg config, DN entryDN)
+  {
+    Set<DN> baseDNs = config.getUniqueAttributeBaseDN();
+    if ((baseDNs == null) || baseDNs.isEmpty())
+    {
+      baseDNs = DirectoryServer.getPublicNamingContexts().keySet();
+    }
+
+    for (DN baseDN : baseDNs)
+    {
+      if (entryDN.isDescendantOf(baseDN))
+      {
+        return baseDNs;
+      }
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Retrieves the DN of the first entry identified that conflicts with the
+   * provided value.
+   *
+   * @param  baseDNs   The set of base DNs below which the search is to be
+   *                   performed.
+   * @param  targetDN  The DN of the entry at which the change is targeted.  If
+   *                   a conflict is found in that entry, then it will be
+   *                   ignored.
+   * @param  config    The plugin configuration to use when making the
+   *                   determination.
+   * @param  value     The value for which to identify any conflicting entries.
+   *
+   * @return  The DN of the first entry identified that contains a conflicting
+   *          value.
+   *
+   * @throws  DirectoryException  If a problem occurred while attempting to
+   *                              make the determination.
+   */
+  private DN getConflictingEntryDN(Set<DN> baseDNs, DN targetDN,
+                                   UniqueAttributePluginCfg config,
+                                   AttributeValue value)
+          throws DirectoryException
+  {
+    SearchFilter filter;
+    Set<AttributeType> attrTypes = config.getUniqueAttributeType();
+    if (attrTypes.size() == 1)
+    {
+      filter = SearchFilter.createEqualityFilter(attrTypes.iterator().next(),
+                                                 value);
+    }
+    else
+    {
+      ArrayList<SearchFilter> equalityFilters =
+           new ArrayList<SearchFilter>(attrTypes.size());
+      for (AttributeType t : attrTypes)
+      {
+        equalityFilters.add(SearchFilter.createEqualityFilter(t, value));
+      }
+      filter = SearchFilter.createORFilter(equalityFilters);
+    }
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    for (DN baseDN : baseDNs)
+    {
+      InternalSearchOperation searchOperation =
+           conn.processSearch(baseDN, SearchScope.WHOLE_SUBTREE,
+                              DereferencePolicy.NEVER_DEREF_ALIASES, 2, 0,
+                              false, filter, SEARCH_ATTRS);
+      for (SearchResultEntry e : searchOperation.getSearchEntries())
+      {
+        if (! e.getDN().equals(targetDN))
+        {
+          return e.getDN();
+        }
+      }
+
+      switch (searchOperation.getResultCode())
+      {
+        case SUCCESS:
+        case NO_SUCH_OBJECT:
+          // These are fine.  Either the search was successful or the base DN
+          // didn't exist.
+          break;
+
+        default:
+          // An error occurred that prevented the search from completing
+          // successfully.
+          throw new DirectoryException(searchOperation.getResultCode(),
+                         searchOperation.getErrorMessage().toMessage());
+      }
+    }
+
+    // If we've gotten here, then no conflict was found.
+    return null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public boolean isConfigurationAcceptable(PluginCfg configuration,
+                                           List<Message> unacceptableReasons)
+  {
+    UniqueAttributePluginCfg cfg = (UniqueAttributePluginCfg) configuration;
+    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
+  }
+
+
+
   /**
    * {@inheritDoc}
    */
   public boolean isConfigurationChangeAcceptable(
-          UniqueAttributePluginCfg configuration,
-          List<Message> unacceptableReasons) {
+                      UniqueAttributePluginCfg configuration,
+                      List<Message> unacceptableReasons)
+  {
     boolean configAcceptable = true;
     for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType())
     {
@@ -113,12 +843,15 @@
         case PREOPERATIONADD:
         case PREOPERATIONMODIFY:
         case PREOPERATIONMODIFYDN:
+        case POSTSYNCHRONIZATIONADD:
+        case POSTSYNCHRONIZATIONMODIFY:
+        case POSTSYNCHRONIZATIONMODIFYDN:
           // These are acceptable.
           break;
 
         default:
-          Message message =
-           ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(pluginType.toString());
+          Message message = ERR_PLUGIN_UNIQUEATTR_INVALID_PLUGIN_TYPE.get(
+                                 pluginType.toString());
           unacceptableReasons.add(message);
           configAcceptable = false;
       }
@@ -126,337 +859,53 @@
     return configAcceptable;
   }
 
+
+
   /**
    * {@inheritDoc}
    */
   public ConfigChangeResult applyConfigurationChange(
-          UniqueAttributePluginCfg newConfiguration) {
-    ResultCode resultCode          = ResultCode.SUCCESS;
-    boolean           adminActionRequired = false;
-    ArrayList<Message> messages            = new ArrayList<Message>();
-    LinkedHashSet<AttributeType> newUniqueattributeTypes=
-                                             new LinkedHashSet<AttributeType>();
-    LinkedHashSet<DN> newConfiguredBaseDNs = new LinkedHashSet<DN>();
-    //Load base DNs from new configuration.
-    for(DN baseDN : newConfiguration.getUniqueAttributeBaseDN())
-      newConfiguredBaseDNs.add(baseDN);
-    //Load attribute types from new configuration.
-    for(AttributeType attributeType : newConfiguration.getUniqueAttributeType())
-      newUniqueattributeTypes.add(attributeType);
-    //Switch to the new lists and configurations.
-    baseDNs = newConfiguredBaseDNs;
-    uniqueAttributeTypes = newUniqueattributeTypes;
+                                 UniqueAttributePluginCfg newConfiguration)
+  {
     currentConfiguration = newConfiguration;
-    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+    return new ConfigChangeResult(ResultCode.SUCCESS, false);
   }
 
+
+
   /**
    * {@inheritDoc}
    */
-  @Override()
-  public final PreOperationPluginResult
-               doPreOperation(PreOperationAddOperation addOperation) {
-    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
-    DN entryDN=addOperation.getEntryDN();
-    if(isEntryUniquenessCandidate(entryDN)) {
-      List<AttributeValue> valueList =
-                         getEntryAttributeValues(addOperation.getEntryToAdd());
-      if(!searchAllBaseDNs(valueList, entryDN))
-        pluginResult =  getPluginErrorResult(addOperation,
-                ERR_PLUGIN_UNIQUEATTR_ADD_NOT_UNIQUE.get(entryDN.toString()));
-    }
-    return pluginResult;
+  public DN getComponentEntryDN()
+  {
+    return currentConfiguration.dn();
   }
 
+
+
   /**
    * {@inheritDoc}
    */
-  @Override()
-  public final PreOperationPluginResult
-  doPreOperation(PreOperationModifyOperation modifyOperation) {
-    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
-    DN entryDN = modifyOperation.getEntryDN();
-    if(isEntryUniquenessCandidate(entryDN)) {
-      List<AttributeValue> valueList =
-              getModificationAttributeValues(modifyOperation.getModifications(),
-                                        modifyOperation.getModifiedEntry());
-      if(!searchAllBaseDNs(valueList, entryDN))
-        pluginResult =  getPluginErrorResult(modifyOperation,
-                  ERR_PLUGIN_UNIQUEATTR_MOD_NOT_UNIQUE.get(entryDN.toString()));
-    }
-    return pluginResult;
+  public String getClassName()
+  {
+    return UniqueAttributePlugin.class.getName();
   }
 
+
+
   /**
    * {@inheritDoc}
    */
-  @Override()
-  public final PreOperationPluginResult
-               doPreOperation(PreOperationModifyDNOperation modifyDNOperation) {
-    PreOperationPluginResult pluginResult=PreOperationPluginResult.SUCCESS;
-    DN entryDN=modifyDNOperation.getOriginalEntry().getDN();
-    //If the operation has a new superior DN then use that, since any moves
-    //need to make sure there are no conflicts in the new superior base DN.
-    if(modifyDNOperation.getNewSuperior() != null)
-        entryDN = modifyDNOperation.getNewSuperior();
-    if(isEntryUniquenessCandidate(entryDN)) {
-      List<AttributeValue> valueList =
-              getRDNAttributeValues(modifyDNOperation.getNewRDN());
-      if(!searchAllBaseDNs(valueList, entryDN))
-        pluginResult =  getPluginErrorResult(modifyDNOperation,
-                ERR_PLUGIN_UNIQUEATTR_MODDN_NOT_UNIQUE.get(entryDN.toString()));
-    }
-    return pluginResult;
-  }
+  public LinkedHashMap<String,String> getAlerts()
+  {
+    LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>(2);
 
-  /**
-   * Determine if the specified DN is a candidate for attribute uniqueness
-   * checking. Checking is skipped if the the unique attribute type list is
-   * empty or if there are base DNS configured and the specified DN is not a
-   * descendant of any of them. Checking is performed for all other cases.
-   *
-   * @param dn The DN to check.
-   *
-   * @return Returns <code>true</code> if the operation needs uniqueness
-   *         checking performed.
-   */
-  private boolean
-  isEntryUniquenessCandidate(DN dn) {
-    if(uniqueAttributeTypes.isEmpty())
-      return false;
-    else if(baseDNs.isEmpty())
-      return true;
-    else {
-      for(DN baseDN : baseDNs)
-        if(baseDN.isAncestorOf(dn))
-          return true;
-    }
-    return false;
-  }
+    alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_CONFLICT,
+               ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_CONFLICT);
+    alerts.put(ALERT_TYPE_UNIQUE_ATTR_SYNC_ERROR,
+               ALERT_DESCRIPTION_UNIQUE_ATTR_SYNC_ERROR);
 
-  /**
-   * Returns a plugin result instance indicating that the operation should be
-   * terminated; that no further pre-operation processing should be performed
-   * and that the server should send the response immediately. It also adds
-   * a CONSTRAINT_VIOLATION result code and the specified error message to
-   * the specified operation.
-   *
-   * @param operation   The operation to add the result code and message to.
-   *
-   * @param message The message to add to the operation.
-   *
-   * @return Returns a plugin result instance that halts further processing
-   *         on this operation.
-   */
-  private PreOperationPluginResult
-  getPluginErrorResult(PreOperationOperation operation, Message message) {
-        operation.appendErrorMessage(message);
-        operation.setResultCode(ResultCode.CONSTRAINT_VIOLATION);
-        return new PreOperationPluginResult(false, false, true);
-  }
-
-  /**
-   * Searches all of the the attribute types of the specified RDN for matches
-   * in the unique attribute type list. If matches are found, then the
-   * corresponding values are added to a list of values that will be eventually
-   * searched for uniqueness.
-
-   * @param rdn  The RDN to examine.
-   *
-   * @return Returns a list of attribute values from the RDN that matches the
-   *         unique attribute type list.
-   */
-  private List<AttributeValue> getRDNAttributeValues(RDN rdn) {
-    LinkedList<AttributeValue> valueList=
-                                            new LinkedList<AttributeValue>();
-    int numAVAs = rdn.getNumValues();
-    for (int i = 0; i < numAVAs; i++) {
-      if(uniqueAttributeTypes.contains(rdn.getAttributeType(i)))
-        valueList.add(rdn.getAttributeValue(i));
-    }
-    return valueList;
-  }
-
-  /**
-   * Searches all of the attribute types of the specified entry for matches
-   * in the unique attribute type list. Ff matches are found, then the
-   * corresponding values are added to a list of values that will eventually
-   * be searched for uniqueness.
-   *
-   * @param entry The entry to examine.
-   *
-   * @return Returns a list of attribute values from the entry that matches the
-   *         unique attribute type list.
-   */
-  private List<AttributeValue> getEntryAttributeValues(Entry entry) {
-    LinkedList<AttributeValue> valueList=new LinkedList<AttributeValue>();
-    for(AttributeType attributeType : uniqueAttributeTypes) {
-      if(entry.hasAttribute(attributeType))  {
-        List<Attribute> attrList=entry.getAttribute(attributeType);
-        for (Attribute a : attrList)
-          valueList.addAll(a.getValues());
-      }
-    }
-    return valueList;
-  }
-
-  /**
-   * Iterate over the unique attribute type list calling a method that will
-   * search the specified modification list for each attribute type and add
-   * the corresponding values to a list of values.
-   *
-   * @param modificationList  The modification list to search over.
-   *
-   * @param modifedEntry The copy of the entry with modifications applied.
-   *
-   * @return Returns a list of attribute values from the modification list
-   *         that matches the unique attribute type list.
-   */
-  private List<AttributeValue>
-  getModificationAttributeValues(List<Modification>  modificationList,
-                            Entry modifedEntry)  {
-    LinkedList<AttributeValue> valueList =
-                                            new LinkedList<AttributeValue>();
-    for(AttributeType attributeType : uniqueAttributeTypes)
-      getModValuesForAttribute(modificationList, attributeType, valueList,
-                               modifedEntry);
-    return valueList;
-  }
-
-  /**
-   * Searches the specified modification list for the provided attribute type.
-   * If a match is found than the attribute value is added to a list of
-   * attribute values that will be eventually searched for uniqueness.
-   *
-   * @param modificationList The modification list to search over.
-   *
-   * @param attributeType The attribute type to search for.
-   *
-   * @param valueList A list of attribute values to put the values in.
-   *
-   * @param modifiedEntry A copy of the entry with modifications applied.
-   */
-  private void
-  getModValuesForAttribute(List<Modification> modificationList,
-                           AttributeType attributeType,
-                           LinkedList<AttributeValue> valueList,
-                           Entry modifiedEntry) {
-
-    for(Modification modification : modificationList) {
-      ModificationType modType=modification.getModificationType();
-      //Skip delete modifications or modifications on attribute types not
-      //matching the specified type.
-      if(modType == ModificationType.DELETE ||
-         !modification.getAttribute().getAttributeType().equals(attributeType))
-          continue;
-      //Increment uses modified entry to get value for the attribute type.
-      if(modType == ModificationType.INCREMENT) {
-        List<Attribute> modifiedAttrs =
-           modifiedEntry.getAttribute(attributeType,
-                                      modification.getAttribute().getOptions());
-        if (modifiedAttrs != null)  {
-          for (Attribute a : modifiedAttrs)
-            valueList.addAll(a.getValues());
-        }
-      } else {
-        Attribute modifiedAttribute=modification.getAttribute();
-        if(modifiedAttribute.hasValue())
-          valueList.addAll(modifiedAttribute.getValues());
-      }
-    }
-  }
-
-
-  /**
-   * Iterates over the base DNs configured by the plugin entry searching for
-   * value matches. If the base DN list is empty then the public naming
-   * contexts are used instead.
-   *
-   * @param valueList The list of values to search for.
-   *
-   * @param entryDN  The DN of the entry related to the operation.
-   *
-   * @return  Returns <code>true</code> if a value is unique.
-   */
-  private boolean
-  searchAllBaseDNs(List<AttributeValue> valueList, DN entryDN) {
-    if(valueList.isEmpty())
-      return true;
-    if(baseDNs.isEmpty()) {
-      for(DN baseDN : DirectoryServer.getPublicNamingContexts().keySet()) {
-        if(searchBaseDN(valueList, baseDN, entryDN))
-          return false;
-      }
-    } else {
-      for(DN baseDN : baseDNs)  {
-        if(searchBaseDN(valueList, baseDN, entryDN))
-          return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * Search a single base DN for all the values in a specified value list.
-   * A filter is created to search all the attribute at once for each
-   * value in the list.
-   *
-   * @param valueList The list of values to search for.
-   *
-   * @param baseDN  The base DN to base the search at.
-   *
-   * @param entryDN  The DN of the entry related to the operation.
-   *
-   * @return Returns <code>true</code> if the values are not unique under the
-   *         under the base DN.
-   */
-  private boolean
-  searchBaseDN(List<AttributeValue> valueList, DN baseDN,
-                    DN entryDN) {
-    //Filter set to hold component filters.
-    HashSet<SearchFilter> componentFilters=new HashSet<SearchFilter>();
-    for(AttributeValue value : valueList) {
-      //Iterate over the unique attribute list and build a equality filter
-      //using each attribute type in the list and the current value being
-      //matched.
-      for(AttributeType attributeType : uniqueAttributeTypes)
-        componentFilters.add(SearchFilter.createEqualityFilter(attributeType,
-                value));
-      //Perform the search using the OR filter created from the filter
-      //components created above.
-      InternalClientConnection conn =
-              InternalClientConnection.getRootConnection();
-      InternalSearchOperation operation = conn.processSearch(baseDN,
-              SearchScope.WHOLE_SUBTREE,
-              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, true,
-              SearchFilter.createORFilter(componentFilters),
-              null);
-      switch (operation.getResultCode()) {
-        case SUCCESS:
-          break;
-
-        case NO_SUCH_OBJECT:
-          //This base DN doesn't exist, return false.
-          return false;
-
-        case SIZE_LIMIT_EXCEEDED:
-        case TIME_LIMIT_EXCEEDED:
-        case ADMIN_LIMIT_EXCEEDED:
-        default:
-          //Couldn't determine if the attribute is unique because an
-          //administrative limit was reached during the search. Fail the
-          //operation by returning true. Possibly log an error here?
-          return true;
-      }
-      for (SearchResultEntry entry : operation.getSearchEntries()) {
-        //Only allow the entry DN to exist. The user might be modifying
-        //the attribute values and putting the same value back. Any other entry
-        //means the value is not unique.
-        if(!entry.getDN().equals(entryDN))
-          return true;
-      }
-      componentFilters.clear();
-    }
-    return false;
+    return alerts;
   }
 }
+

--
Gitblit v1.10.0