From d2d863001e51eb3f0f678123b11f9ab1b77ebd6c Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 21 Feb 2012 17:15:54 +0000
Subject: [PATCH] Fix OPENDJ-181: DirectoryException provided value has an invalid length for a UUID

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java |    6 ++-
 opends/src/server/org/opends/server/replication/plugin/EntryHistorical.java                        |   94 +++++++++++++++++++++++++++--------------------
 opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                  |    2 -
 3 files changed, 58 insertions(+), 44 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/plugin/EntryHistorical.java b/opends/src/server/org/opends/server/replication/plugin/EntryHistorical.java
index 7080249..5ff0c41 100644
--- a/opends/src/server/org/opends/server/replication/plugin/EntryHistorical.java
+++ b/opends/src/server/org/opends/server/replication/plugin/EntryHistorical.java
@@ -29,18 +29,15 @@
 
 import static org.opends.messages.ReplicationMessages.*;
 import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.util.StaticUtils.getBytes;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
+import java.util.*;
 
 import org.opends.messages.Message;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.replication.common.ChangeNumber;
 import org.opends.server.replication.protocol.OperationContext;
 import org.opends.server.types.*;
@@ -85,6 +82,13 @@
    */
   public static final String ENTRYUUID_ATTRIBUTE_NAME = "entryuuid";
 
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
   /* The delay to purge the historical informations
    * This delay indicates the time the domain keeps the historical
    * information necessary to solve conflicts.When a change stored in the
@@ -838,14 +842,10 @@
             else
             {
               String uuidString = getEntryUUID(entry);
-              if (uuidString != null)
-              {
-                modifyFakeOperation = new FakeModifyOperation(entry.getDN(),
-                    cn, uuidString);
-
-                modifyFakeOperation.addModification(mod);
-                operations.put(histVal.getCn(), modifyFakeOperation);
-              }
+              modifyFakeOperation = new FakeModifyOperation(entry.getDN(), cn,
+                  uuidString);
+              modifyFakeOperation.addModification(mod);
+              operations.put(histVal.getCn(), modifyFakeOperation);
             }
           }
         }
@@ -874,25 +874,15 @@
    *
    * @param entry The entry for which the unique id should be returned.
    *
-   * @return The Unique Id of the entry if it has one. null, otherwise.
+   * @return The Unique Id of the entry, or a fake one if none is found.
    */
   public static String getEntryUUID(Entry entry)
   {
-    String uuidString = null;
     AttributeType entryuuidAttrType =
       DirectoryServer.getSchema().getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
     List<Attribute> uuidAttrs =
              entry.getOperationalAttribute(entryuuidAttrType);
-    if (uuidAttrs != null)
-    {
-      Attribute uuid = uuidAttrs.get(0);
-      if (!uuid.isEmpty())
-      {
-        AttributeValue uuidVal = uuid.iterator().next();
-        uuidString =  uuidVal.getValue().toString();
-      }
-    }
-    return uuidString;
+    return extractEntryUUID(uuidAttrs, entry.getDN());
   }
 
   /**
@@ -905,22 +895,11 @@
    */
   public static String getEntryUUID(PreOperationAddOperation op)
   {
-    String uuidString = null;
     Map<AttributeType, List<Attribute>> attrs = op.getOperationalAttributes();
     AttributeType entryuuidAttrType =
       DirectoryServer.getSchema().getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
     List<Attribute> uuidAttrs = attrs.get(entryuuidAttrType);
-
-    if (uuidAttrs != null)
-    {
-      Attribute uuid = uuidAttrs.get(0);
-      if (!uuid.isEmpty())
-      {
-        AttributeValue uuidVal = uuid.iterator().next();
-        uuidString =  uuidVal.getValue().toString();
-      }
-    }
-    return uuidString;
+    return extractEntryUUID(uuidAttrs, op.getEntryDN());
   }
 
   /**
@@ -969,5 +948,40 @@
   {
     return this.oldestChangeNumber;
   }
+
+
+
+  // Extracts the entryUUID attribute value from the provided list of
+  // attributes. If the attribute is not present one is generated from the DN
+  // using the same algorithm as the entryUUID virtual attribute provider.
+  private static String extractEntryUUID(List<Attribute> entryUUIDAttributes,
+      DN entryDN)
+  {
+    if (entryUUIDAttributes != null)
+    {
+      Attribute uuid = entryUUIDAttributes.get(0);
+      if (!uuid.isEmpty())
+      {
+        AttributeValue uuidVal = uuid.iterator().next();
+        return uuidVal.getValue().toString();
+      }
+    }
+
+    // Generate a fake entryUUID: see OPENDJ-181. In rare pathological cases
+    // an entryUUID attribute may not be present and this causes severe side
+    // effects for replication which requires the attribute to always be
+    // present.
+    if (debugEnabled())
+    {
+      TRACER.debugWarning(
+          "Replication requires an entryUUID attribute in order "
+              + "to perform conflict resolution, but none was "
+              + "found in entry \"%s\": generating virtual entryUUID instead",
+          entryDN);
+    }
+
+    String normDNString = entryDN.toNormalizedString();
+    return UUID.nameUUIDFromBytes(getBytes(normDNString)).toString();
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index 5382759..cb56540 100644
--- a/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -2209,8 +2209,6 @@
 
       ChangeNumber changeNumber = generateChangeNumber(modifyOperation);
       String modifiedEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry);
-      if (modifiedEntryUUID == null)
-        modifiedEntryUUID = modifyOperation.getEntryDN().toString();
       ctx = new ModifyContext(changeNumber, modifiedEntryUUID);
 
       modifyOperation.setAttachment(SYNCHROCONTEXT, ctx);
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
index 99145e1..245c47b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/replication/SchemaReplicationTest.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2012 ForgeRock AS.
  */
 package org.opends.server.replication;
 
@@ -48,6 +49,7 @@
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.service.ReplicationBroker;
 import org.opends.server.replication.common.ChangeNumberGenerator;
+import org.opends.server.replication.plugin.EntryHistorical;
 import org.opends.server.replication.protocol.ModifyMsg;
 import org.opends.server.replication.protocol.ReplicationMsg;
 import org.opends.server.types.Attribute;
@@ -229,8 +231,8 @@
     {
       ChangeNumberGenerator gen = new ChangeNumberGenerator( 2, 0);
 
-      ModifyMsg modMsg = new ModifyMsg(gen.newChangeNumber(),
-        baseDn, rcvdMods, "cn=schema");
+      ModifyMsg modMsg = new ModifyMsg(gen.newChangeNumber(), baseDn, rcvdMods,
+          EntryHistorical.getEntryUUID(DirectoryServer.getEntry(baseDn)));
       broker.publish(modMsg);
 
       boolean found = checkEntryHasAttribute(baseDn, "attributetypes",

--
Gitblit v1.10.0