From 6d5fa3e57a02018d5c94e510a1cb8ca1c082c30c Mon Sep 17 00:00:00 2001
From: coulbeck <coulbeck@localhost>
Date: Wed, 18 Oct 2006 20:20:30 +0000
Subject: [PATCH] Add unit test for [Issue 798] break infinite loop when problems with naming resolution conflict. The test will be enabled after the bug is fixed.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java |  120 +++++++++++++++++++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java          |  106 +++++++++++++++-----------
 2 files changed, 181 insertions(+), 45 deletions(-)

diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java
index f0ee8e4..77ac82f 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java
@@ -31,6 +31,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.opends.server.api.plugin.DirectoryServerPlugin;
 import org.opends.server.api.plugin.PluginType;
@@ -45,15 +47,16 @@
 import org.opends.server.protocols.ldap.LDAPControl;
 import org.opends.server.types.Control;
 import org.opends.server.types.ResultCode;
+import org.opends.server.types.OperationType;
 import org.opends.server.types.operation.*;
 
 
-
 /**
  * This class defines a very simple plugin that causes request processing to end
  * immediately and send a specific result code to the client.  It will be
  * triggered by a control contained in the client request, and may be invoked
- * during either pre-parse or pre-operation processing.
+ * during either pre-parse or pre-operation processing.  Short circuits can
+ * also be registered for operations regardless of controls.
  */
 public class ShortCircuitPlugin
        extends DirectoryServerPlugin
@@ -130,8 +133,7 @@
   public PreParsePluginResult
        doPreParse(PreParseAbandonOperation abandonOperation)
   {
-    int resultCode = shortCircuitInternal(abandonOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(abandonOperation, "PreParse");
     if (resultCode >= 0)
     {
       abandonOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -152,8 +154,7 @@
   @Override()
   public PreParsePluginResult doPreParse(PreParseAddOperation addOperation)
   {
-    int resultCode = shortCircuitInternal(addOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(addOperation, "PreParse");
     if (resultCode >= 0)
     {
       addOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -174,8 +175,7 @@
   @Override()
   public PreParsePluginResult doPreParse(PreParseBindOperation bindOperation)
   {
-    int resultCode = shortCircuitInternal(bindOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(bindOperation, "PreParse");
     if (resultCode >= 0)
     {
       bindOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -197,8 +197,7 @@
   public PreParsePluginResult
        doPreParse(PreParseCompareOperation compareOperation)
   {
-    int resultCode = shortCircuitInternal(compareOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(compareOperation, "PreParse");
     if (resultCode >= 0)
     {
       compareOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -220,8 +219,7 @@
   public PreParsePluginResult
        doPreParse(PreParseDeleteOperation deleteOperation)
   {
-    int resultCode = shortCircuitInternal(deleteOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(deleteOperation, "PreParse");
     if (resultCode >= 0)
     {
       deleteOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -243,9 +241,7 @@
   public PreParsePluginResult
        doPreParse(PreParseExtendedOperation extendedOperation)
   {
-    int resultCode =
-             shortCircuitInternal(extendedOperation.getRequestControls(),
-                                  "PreParse");
+    int resultCode = shortCircuitInternal(extendedOperation, "PreParse");
     if (resultCode >= 0)
     {
       extendedOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -267,8 +263,7 @@
   public PreParsePluginResult
        doPreParse(PreParseModifyOperation modifyOperation)
   {
-    int resultCode = shortCircuitInternal(modifyOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(modifyOperation, "PreParse");
     if (resultCode >= 0)
     {
       modifyOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -290,9 +285,7 @@
   public PreParsePluginResult
        doPreParse(PreParseModifyDNOperation modifyDNOperation)
   {
-    int resultCode =
-             shortCircuitInternal(modifyDNOperation.getRequestControls(),
-                                  "PreParse");
+    int resultCode = shortCircuitInternal(modifyDNOperation, "PreParse");
     if (resultCode >= 0)
     {
       modifyDNOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -314,8 +307,7 @@
   public PreParsePluginResult
        doPreParse(PreParseSearchOperation searchOperation)
   {
-    int resultCode = shortCircuitInternal(searchOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(searchOperation, "PreParse");
     if (resultCode >= 0)
     {
       searchOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -337,8 +329,7 @@
   public PreParsePluginResult
        doPreParse(PreParseUnbindOperation unbindOperation)
   {
-    int resultCode = shortCircuitInternal(unbindOperation.getRequestControls(),
-                                          "PreParse");
+    int resultCode = shortCircuitInternal(unbindOperation, "PreParse");
     if (resultCode >= 0)
     {
       unbindOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -360,8 +351,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationAddOperation addOperation)
   {
-    int resultCode = shortCircuitInternal(addOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(addOperation, "PreOperation");
     if (resultCode >= 0)
     {
       addOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -383,8 +373,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationBindOperation bindOperation)
   {
-    int resultCode = shortCircuitInternal(bindOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(bindOperation, "PreOperation");
     if (resultCode >= 0)
     {
       bindOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -406,8 +395,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationCompareOperation compareOperation)
   {
-    int resultCode = shortCircuitInternal(compareOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(compareOperation, "PreOperation");
     if (resultCode >= 0)
     {
       compareOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -429,8 +417,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationDeleteOperation deleteOperation)
   {
-    int resultCode = shortCircuitInternal(deleteOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(deleteOperation, "PreOperation");
     if (resultCode >= 0)
     {
       deleteOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -452,9 +439,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationExtendedOperation extendedOperation)
   {
-    int resultCode =
-             shortCircuitInternal(extendedOperation.getRequestControls(),
-                                  "PreOperation");
+    int resultCode = shortCircuitInternal(extendedOperation, "PreOperation");
     if (resultCode >= 0)
     {
       extendedOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -476,8 +461,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationModifyOperation modifyOperation)
   {
-    int resultCode = shortCircuitInternal(modifyOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(modifyOperation, "PreOperation");
     if (resultCode >= 0)
     {
       modifyOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -499,9 +483,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationModifyDNOperation modifyDNOperation)
   {
-    int resultCode =
-             shortCircuitInternal(modifyDNOperation.getRequestControls(),
-                                  "PreOperation");
+    int resultCode = shortCircuitInternal(modifyDNOperation, "PreOperation");
     if (resultCode >= 0)
     {
       modifyDNOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -523,8 +505,7 @@
   public PreOperationPluginResult
        doPreOperation(PreOperationSearchOperation searchOperation)
   {
-    int resultCode = shortCircuitInternal(searchOperation.getRequestControls(),
-                                          "PreOperation");
+    int resultCode = shortCircuitInternal(searchOperation, "PreOperation");
     if (resultCode >= 0)
     {
       searchOperation.setResultCode(ResultCode.valueOf(resultCode));
@@ -549,9 +530,9 @@
    * @return  The result code that should be immediately sent to the client, or
    *          -1 if operation processing should continue as normal.
    */
-  private int shortCircuitInternal(List<Control> requestControls,
-                                   String section)
+  private int shortCircuitInternal(PluginOperation operation, String section)
   {
+    List<Control> requestControls = operation.getRequestControls();
     if (requestControls != null)
     {
       for (Control c : requestControls)
@@ -582,6 +563,13 @@
       }
     }
 
+    // Check for registered short circuits.
+    Integer resultCode = shortCircuits.get(
+         operation.getOperationType().toString() + "/" + section.toLowerCase());
+    if (resultCode != null)
+    {
+      return resultCode;
+    }
 
     // If we've gotten here, then we shouldn't short-circuit the operation
     // processing.
@@ -667,5 +655,35 @@
     controlList.add(createShortCircuitLDAPControl(resultCode, section));
     return controlList;
   }
+
+  /**
+   * Registered short circuits for operations regardless of controls.
+   */
+  private static Map<String,Integer> shortCircuits =
+       new ConcurrentHashMap<String, Integer>();
+
+  /**
+   * Register a short circuit for the given operation type and plugin point.
+   * @param operation The type of operation the short circuit applies to.
+   * @param section The plugin point the short circuit applies to.
+   * @param resultCode The result code to be returned for the short circuit.
+   */
+  public static void registerShortCircuit(OperationType operation,
+                                          String section, int resultCode)
+  {
+    shortCircuits.put(operation.toString() + "/" + section.toLowerCase(),
+                      resultCode);
+  }
+
+  /**
+   * Deregister a short circuit for the given operation type and plugin point.
+   * @param operation The type of operation the short circuit applies to.
+   * @param section The plugin point the short circuit applies to.
+   */
+  public static void deregisterShortCircuit(OperationType operation,
+                                            String section)
+  {
+    shortCircuits.remove(operation.toString() + "/" + section.toLowerCase());
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
index b9da84a..55047af 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
@@ -35,6 +35,9 @@
 import java.util.List;
 
 import org.opends.server.TestCaseUtils;
+import org.opends.server.plugins.ShortCircuitPlugin;
+import org.opends.server.schema.DirectoryStringSyntax;
+import org.opends.server.schema.IntegerSyntax;
 import org.opends.server.config.ConfigEntry;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
@@ -44,6 +47,8 @@
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.Operation;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.types.*;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -338,7 +343,7 @@
     /*
      * Test that the conflict resolution code is able to find entries
      * that have been renamed by an other master.
-     * To simulate this, create and entry with a given UUID and a given DN
+     * To simulate this, create an entry with a given UUID and a given DN
      * then send a modify operation using another DN but the same UUID.
      * Finally check that the modify operation has been applied.
      */
@@ -1000,4 +1005,117 @@
     op.run();
     assertEquals(op.getResultCode(), ResultCode.NO_SUCH_OBJECT);
   }
+
+  /**
+   * Test case for
+   * [Issue 798]  break infinite loop when problems with naming resolution
+   *              conflict.
+   */
+  @Test(enabled=false)
+  public void infiniteReplayLoop() throws Exception
+  {
+    final DN baseDn = DN.decode("ou=People,dc=example,dc=com");
+
+    ChangelogBroker broker = openChangelogSession(baseDn, (short) 3);
+    try
+    {
+      ChangeNumberGenerator gen = new ChangeNumberGenerator((short) 3, 0);
+
+      // Create a test entry.
+      String personLdif = "dn: uid=user.2,ou=People,dc=example,dc=com\n"
+          + "objectClass: top\n" + "objectClass: person\n"
+          + "objectClass: organizationalPerson\n"
+          + "objectClass: inetOrgPerson\n" + "uid: user.2\n"
+          + "homePhone: 951-245-7634\n"
+          + "description: This is the description for Aaccf Amar.\n"
+          + "st: NC\n"
+          + "mobile: 027-085-0537\n"
+          + "postalAddress: Aaccf Amar$17984 Thirteenth Street"
+          + "$Rockford, NC  85762\n" + "mail: user.1@example.com\n"
+          + "cn: Aaccf Amar\n" + "l: Rockford\n" + "pager: 508-763-4246\n"
+          + "street: 17984 Thirteenth Street\n"
+          + "telephoneNumber: 216-564-6748\n" + "employeeNumber: 1\n"
+          + "sn: Amar\n" + "givenName: Aaccf\n" + "postalCode: 85762\n"
+          + "userPassword: password\n" + "initials: AA\n";
+      Entry tmp = TestCaseUtils.entryFromLdifString(personLdif);
+      AddOperation addOp =
+           new AddOperation(connection,
+                            InternalClientConnection.nextOperationID(),
+                            InternalClientConnection.nextMessageID(),
+                            null, tmp.getDN(), tmp.getObjectClasses(),
+                            tmp.getUserAttributes(),
+                            tmp.getOperationalAttributes());
+      addOp.run();
+      assertEquals(addOp.getResultCode(), ResultCode.SUCCESS);
+      entryList.add(tmp);
+
+      long initialCount = getReplayedUpdatesCount();
+
+      // Get the UUID of the test entry.
+      Entry resultEntry = DirectoryServer.getEntry(tmp.getDN());
+      AttributeType uuidType = DirectoryServer.getAttributeType("entryuuid");
+      String uuid =
+           resultEntry.getAttributeValue(uuidType,
+                                         DirectoryStringSyntax.DECODER);
+
+      // Register a short circuit that will fake a no-such-object result code
+      // on a delete.  This will cause a synchronization replay loop.
+      ShortCircuitPlugin.registerShortCircuit(OperationType.DELETE,
+                                              "PreParse", 32);
+      try
+      {
+        // Publish a delete message for this test entry.
+        DeleteMsg delMsg = new DeleteMsg(tmp.getDN().toString(),
+                                         gen.NewChangeNumber(),
+                                         uuid);
+        broker.publish(delMsg);
+
+        // Wait for the operation to be replayed.
+        long endTime = System.currentTimeMillis() + 5000;
+        while (getReplayedUpdatesCount() == initialCount &&
+             System.currentTimeMillis() < endTime)
+        {
+          Thread.sleep(100);
+        }
+      }
+      finally
+      {
+        ShortCircuitPlugin.deregisterShortCircuit(OperationType.DELETE,
+                                                  "PreParse");
+      }
+
+      // If the synchronization replay loop was detected and broken then the
+      // counter will still be updated even though the replay was unsuccessful.
+      if (getReplayedUpdatesCount() == initialCount)
+      {
+        fail("Synchronization operation was not replayed");
+      }
+    }
+    finally
+    {
+      broker.stop();
+    }
+  }
+
+  /**
+   * Retrieve the number of replayed updates from the monitor entry.
+   * @return The number of replayed updates.
+   * @throws Exception If an error occurs.
+   */
+  private long getReplayedUpdatesCount() throws Exception
+  {
+    String monitorFilter =
+         "(&(cn=synchronization*)(base-dn=ou=People,dc=example,dc=com))";
+
+    InternalSearchOperation op;
+    op = connection.processSearch(
+         ByteStringFactory.create("cn=monitor"),
+         SearchScope.SINGLE_LEVEL,
+         LDAPFilter.decode(monitorFilter));
+    SearchResultEntry entry = op.getSearchEntries().getFirst();
+
+    AttributeType attrType =
+         DirectoryServer.getDefaultAttributeType("replayed-updates");
+    return entry.getAttributeValue(attrType, IntegerSyntax.DECODER).longValue();
+  }
 }

--
Gitblit v1.10.0