mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noël Rouvignac
30.27.2016 68978afe2c3d52135c287cd7a381d2e8a6b44315
OPENDJ-2787 Replication: NPE at server start when re-importing a dataset

This problem is triggered by a change in behaviour of DN.valueOf(String).
In the server DN, if the string value was null, it was returning the root DN,
while the SDK DN throws a NullPointerException.

ModifyDNMasg.java:
In newParentIsEqual(), added a null check.
In newDNIsParent() and newDNIsEqual(), added a fix for the change in behaviour of DN.valueOf()
(now throws LocalizedIllegalArgumentException instead of DirectoryException).
1 files added
1 files modified
158 ■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ModifyDNMsg.java 42 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java 116 ●●●●● patch | view | raw | blame | history
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();
  }
}
opendj-server-legacy/src/test/java/org/opends/server/replication/protocol/ModifyDNMsgTest.java
New file
@@ -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);
  }
}