From 68978afe2c3d52135c287cd7a381d2e8a6b44315 Mon Sep 17 00:00:00 2001
From: Jean-Noël Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Thu, 31 Mar 2016 08:43:19 +0000
Subject: [PATCH] OPENDJ-2787 Replication: NPE at server start when re-importing a dataset

---
 opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java |  116 ++++++++++++++++++++++++++++++++++++++
 opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java     |   42 +++++--------
 2 files changed, 133 insertions(+), 25 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java b/opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java
index 1954957..688be86 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java
@@ -16,6 +16,9 @@
  */
 package org.opends.server.replication.protocol;
 
+import static org.opends.server.replication.protocol.OperationContext.*;
+import static org.opends.server.replication.protocol.ProtocolVersion.*;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.zip.DataFormatException;
@@ -28,16 +31,11 @@
 import org.opends.server.core.ModifyDNOperationBasis;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.replication.common.CSN;
-import org.opends.server.types.DirectoryException;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Modification;
 import org.opends.server.types.operation.PostOperationModifyDNOperation;
 
-import static org.opends.server.replication.protocol.OperationContext.*;
-
-/**
- * Message used to send Modify DN information.
- */
+/** Message used to send Modify DN information. */
 public class ModifyDNMsg extends ModifyCommonMsg
 {
   private String newRDN;
@@ -143,7 +141,6 @@
     }
   }
 
-  /** {@inheritDoc} */
   @Override
   public ModifyDNOperation createOperation(InternalClientConnection connection,
       DN newDN) throws LDAPException, IOException
@@ -171,7 +168,6 @@
   // Msg Encoding
   // ============
 
-  /** {@inheritDoc} */
   @Override
   public byte[] getBytes_V1()
   {
@@ -183,12 +179,10 @@
     return builder.toByteArray();
   }
 
-  /** {@inheritDoc} */
   @Override
   public byte[] getBytes_V23()
   {
-    final ByteArrayBuilder builder =
-        encodeHeader(MSG_TYPE_MODIFYDN,ProtocolVersion.REPLICATION_PROTOCOL_V3);
+    final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_MODIFYDN, REPLICATION_PROTOCOL_V3);
     builder.appendString(newRDN);
     builder.appendString(newSuperior);
     builder.appendString(newSuperiorEntryUUID);
@@ -197,12 +191,10 @@
     return builder.toByteArray();
   }
 
-  /** {@inheritDoc} */
   @Override
   public byte[] getBytes_V45(short protocolVersion)
   {
-    final ByteArrayBuilder builder =
-        encodeHeader(MSG_TYPE_MODIFYDN, protocolVersion);
+    final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_MODIFYDN, protocolVersion);
     builder.appendString(newRDN);
     builder.appendString(newSuperior);
     builder.appendString(newSuperiorEntryUUID);
@@ -250,11 +242,10 @@
     encodedEclIncludes = scanner.nextByteArray(eclAttrLen);
   }
 
-  /** {@inheritDoc} */
   @Override
   public String toString()
   {
-    if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1)
+    if (protocolVersion >= REPLICATION_PROTOCOL_V1)
     {
       return "ModifyDNMsg content: " +
         " protocolVersion: " + protocolVersion +
@@ -265,7 +256,7 @@
         " newSuperior: " + newSuperior +
         " deleteOldRdn: " + deleteOldRdn +
         " assuredFlag: " + assuredFlag +
-        (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ?
+        (protocolVersion >= REPLICATION_PROTOCOL_V2 ?
           " assuredMode: " + assuredMode +
           " safeDataLevel: " + safeDataLevel
           : "");
@@ -361,13 +352,13 @@
   }
 
   /**
-   * Computes and return the new DN that the entry should
-   * have after this operation.
+   * Computes and return the new DN that the entry should have after this operation.
    *
    * @return the newDN.
-   * @throws DirectoryException in case of decoding problems.
+   * @throws LocalizedIllegalArgumentException
+   *           in case of decoding problems.
    */
-  private DN computeNewDN() throws DirectoryException
+  private DN computeNewDN() throws LocalizedIllegalArgumentException
   {
     if (newSuperior != null)
     {
@@ -391,7 +382,8 @@
     {
       DN newDN = computeNewDN();
       return newDN.isSuperiorOrEqualTo(targetDn);
-    } catch (DirectoryException e)
+    }
+    catch (LocalizedIllegalArgumentException e)
     {
       // The DN was not a correct DN, and therefore does not a parent of the
       // DN given as a parameter.
@@ -414,7 +406,8 @@
     {
       DN newDN = computeNewDN();
       return newDN.equals(targetDN);
-    } catch (DirectoryException e)
+    }
+    catch (LocalizedIllegalArgumentException e)
     {
       // The DN was not a correct DN, and therefore does not match the
       // DN given as a parameter.
@@ -435,7 +428,7 @@
   {
     try
     {
-      DN newSuperiorDN = DN.valueOf(newSuperior);
+      DN newSuperiorDN = newSuperior != null ? DN.valueOf(newSuperior) : DN.rootDN();
       return newSuperiorDN.equals(targetDN);
     }
     catch (LocalizedIllegalArgumentException e)
@@ -452,5 +445,4 @@
     return encodedMods.length + newRDN.length() +
       encodedEclIncludes.length + headerSize();
   }
-
 }
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java
new file mode 100644
index 0000000..047f5ec
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java
@@ -0,0 +1,116 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.replication.protocol;
+
+import static org.testng.Assert.*;
+
+import java.util.UUID;
+
+import org.assertj.core.api.SoftAssertions;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.replication.ReplicationTestCase;
+import org.opends.server.replication.common.CSN;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/** Tests the methods from {@link ModifyDNMsg}. */
+@SuppressWarnings("javadoc")
+public class ModifyDNMsgTest extends ReplicationTestCase
+{
+  private String randomUUID()
+  {
+    return UUID.randomUUID().toString();
+  }
+
+  @DataProvider
+  Object[][] serializeDeserializeMsgDataProvider()
+  {
+    CSN csn = new CSN(System.currentTimeMillis(), 1, 42);
+    // @formatter:off
+    return new Object[][] {
+      // dn, csn, entryUUID, newSuperiorEntryUUID, deleteOldRdn, newSuperior, newRDN
+      { DN.valueOf("dc=com"), csn, null, null, false, null, null },
+      { DN.valueOf("dc=com"), csn, randomUUID(), randomUUID(), true, "dc=org", "ou=People" },
+    };
+    // @formatter:on
+  }
+
+  @Test(dataProvider = "serializeDeserializeMsgDataProvider")
+  public void serializeDeserializeMsg(DN dn, CSN csn, String entryUUID, String newSuperiorEntryUUID,
+      boolean deleteOldRdn, String newSuperior, String newRDN) throws Exception
+  {
+    ModifyDNMsg msg = new ModifyDNMsg(dn, csn, entryUUID, newSuperiorEntryUUID, deleteOldRdn, newSuperior, newRDN);
+    ModifyDNMsg newMsg = new ModifyDNMsg(msg.getBytes());
+
+    SoftAssertions softly = new SoftAssertions();
+    softly.assertThat(msg.getDN()).isEqualTo(newMsg.getDN());
+    softly.assertThat(msg.getCSN()).isEqualTo(newMsg.getCSN());
+    softly.assertThat(msg.getEntryUUID()).isEqualTo(newMsg.getEntryUUID());
+    softly.assertThat(msg.getNewSuperiorEntryUUID()).isEqualTo(newMsg.getNewSuperiorEntryUUID());
+    softly.assertThat(msg.getDeleteOldRdn()).isEqualTo(newMsg.getDeleteOldRdn());
+    softly.assertThat(msg.getNewSuperior()).isEqualTo(newMsg.getNewSuperior());
+    softly.assertThat(msg.getNewRDN()).isEqualTo(newMsg.getNewRDN());
+    softly.assertAll();
+  }
+
+  @Test
+  public void withNewSuperior()
+  {
+    ModifyDNMsg msg = newModifyDNMsg("dc=com", true, "dc=org", "dc=example");
+    assertFalse(msg.newDNIsParent(DN.rootDN()));
+    assertTrue(msg.newDNIsParent(DN.valueOf("dc=example,dc=org")));
+    assertTrue(msg.newDNIsParent(DN.valueOf("ou=people,dc=example,dc=org")));
+
+    assertFalse(msg.newDNIsEqual(DN.rootDN()));
+    assertTrue(msg.newDNIsEqual(DN.valueOf("dc=example,dc=org")));
+    assertFalse(msg.newDNIsEqual(DN.valueOf("ou=people,dc=example,dc=org")));
+
+    assertFalse(msg.newParentIsEqual(DN.rootDN()));
+    assertTrue(msg.newParentIsEqual(DN.valueOf("dc=org")));
+  }
+
+  @Test
+  public void noNewSuperior()
+  {
+    ModifyDNMsg msg = newModifyDNMsg("dc=com", false, null, "dc=example");
+
+    assertFalse(msg.newDNIsParent(DN.rootDN()));
+    assertTrue(msg.newDNIsParent(DN.valueOf("dc=example")));
+    assertTrue(msg.newDNIsParent(DN.valueOf("ou=people,dc=example")));
+
+    assertFalse(msg.newDNIsEqual(DN.rootDN()));
+    assertTrue(msg.newDNIsEqual(DN.valueOf("dc=example")));
+    assertFalse(msg.newDNIsEqual(DN.valueOf("ou=people,dc=example")));
+
+    assertTrue(msg.newParentIsEqual(DN.rootDN()));
+    assertFalse(msg.newParentIsEqual(DN.valueOf("dc=org")));
+  }
+
+  @Test
+  public void bogusNewSuperior()
+  {
+    ModifyDNMsg msg = newModifyDNMsg("dc=com", true, ":::::incorrect:::DN", ":::::incorrect:::RDN");
+    assertFalse(msg.newDNIsParent(DN.rootDN()));
+    assertFalse(msg.newDNIsEqual(DN.rootDN()));
+    assertFalse(msg.newParentIsEqual(DN.rootDN()));
+  }
+
+  private ModifyDNMsg newModifyDNMsg(String dn, boolean deleteOldRdn, String newSuperior, String newRDN)
+  {
+    CSN csn = new CSN(System.currentTimeMillis(), 1, 42);
+    return new ModifyDNMsg(DN.valueOf(dn), csn, randomUUID(), randomUUID(), deleteOldRdn, newSuperior, newRDN);
+  }
+}

--
Gitblit v1.10.0