From 1c315bfe2fc843dfd77445a7d6b876e79efe3d44 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 09 Dec 2011 18:04:55 +0000
Subject: [PATCH] Fix OPENDJ-381: Implement LDIF diff and patch API support in the SDK

---
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties        |   10 
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java |  233 +++++++++++++++++
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java                   |  418 ++++++++++++++++++++++++++++++++
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java                    |   15 +
 opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java |   59 ++++
 5 files changed, 730 insertions(+), 5 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java
index 20f27f6..3ae1e45 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java
@@ -825,6 +825,21 @@
 
 
   /**
+   * Returns a single valued attribute having the same attribute type and value
+   * as this AVA.
+   *
+   * @return A single valued attribute having the same attribute type and value
+   *         as this AVA.
+   */
+  public Attribute toAttribute()
+  {
+    AttributeDescription ad = AttributeDescription.create(attributeType);
+    return new LinkedAttribute(ad, attributeValue);
+  }
+
+
+
+  /**
    * {@inheritDoc}
    */
   @Override
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
new file mode 100644
index 0000000..d509c43
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -0,0 +1,418 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldif;
+
+
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.forgerock.opendj.ldap.*;
+import org.forgerock.opendj.ldap.requests.*;
+
+
+
+/**
+ * This class contains common utility methods for creating and manipulating
+ * readers and writers.
+ */
+public final class LDIF
+{
+  /**
+   * Compares the content of {@code source} to the content of {@code target} and
+   * writes the differences to {@code output}. This method does not close the
+   * provided readers and writer.
+   * <p>
+   * <b>NOTE:</b> this method reads the content of {@code source} and
+   * {@code target} into memory before calculating the differences, and is
+   * therefore not suited for use in cases where a very large number of entries
+   * are to be compared.
+   *
+   * @param source
+   *          The entry reader containing the source entries to be compared.
+   * @param target
+   *          The entry reader containing the target entries to be compared.
+   * @param output
+   *          The change record writer to which the differences are to be
+   *          written.
+   * @throws IOException
+   *           If an unexpected IO error occurred.
+   */
+  public static void diff(final EntryReader source, final EntryReader target,
+      final ChangeRecordWriter output) throws IOException
+  {
+    final SortedMap<DN, Entry> sourceEntries = readEntries(source);
+    final SortedMap<DN, Entry> targetEntries = readEntries(target);
+    final Iterator<Entry> sourceIterator = sourceEntries.values().iterator();
+    final Iterator<Entry> targetIterator = targetEntries.values().iterator();
+
+    Entry sourceEntry = nextEntry(sourceIterator);
+    Entry targetEntry = nextEntry(targetIterator);
+
+    while (sourceEntry != null && targetEntry != null)
+    {
+      final DN sourceDN = sourceEntry.getName();
+      final DN targetDN = targetEntry.getName();
+      final int cmp = sourceDN.compareTo(targetDN);
+
+      if (cmp == 0)
+      {
+        // Modify record: entry in both source and target.
+        output.writeChangeRecord(Requests.newModifyRequest(sourceEntry,
+            targetEntry));
+        sourceEntry = nextEntry(sourceIterator);
+        targetEntry = nextEntry(targetIterator);
+      }
+      else if (cmp < 0)
+      {
+        // Delete record: entry in source but not in target.
+        output.writeChangeRecord(Requests.newDeleteRequest(sourceEntry
+            .getName()));
+        sourceEntry = nextEntry(sourceIterator);
+      }
+      else
+      {
+        // Add record: entry in target but not in source.
+        output.writeChangeRecord(Requests.newAddRequest(targetEntry));
+        targetEntry = nextEntry(targetIterator);
+      }
+    }
+
+    // Delete remaining source records.
+    while (sourceEntry != null)
+    {
+      output
+          .writeChangeRecord(Requests.newDeleteRequest(sourceEntry.getName()));
+      sourceEntry = nextEntry(sourceIterator);
+    }
+
+    // Add remaining target records.
+    while (targetEntry != null)
+    {
+      output.writeChangeRecord(Requests.newAddRequest(targetEntry));
+      targetEntry = nextEntry(targetIterator);
+    }
+  }
+
+
+
+  /**
+   * Applies the set of changes contained in {@code patch} to the content of
+   * {@code input} and writes the result to {@code output}, while ignoring
+   * missing entries, and overwriting existing entries. This method does not
+   * close the provided readers and writer.
+   * <p>
+   * <b>NOTE:</b> this method reads the content of {@code input} into memory
+   * before applying the changes, and is therefore not suited for use in cases
+   * where a very large number of entries are to be patched.
+   *
+   * @param input
+   *          The entry reader containing the set of entries to be patched.
+   * @param patch
+   *          The change record reader containing the set of changes to be
+   *          applied.
+   * @param output
+   *          The entry writer to which the updated entries are to be written.
+   * @throws IOException
+   *           If an unexpected IO error occurred.
+   */
+  public static void patch(final EntryReader input,
+      final ChangeRecordReader patch, final EntryWriter output)
+      throws IOException
+  {
+    patch(input, patch, output, RejectedChangeListener.OVERWRITE);
+  }
+
+
+
+  /**
+   * Applies the set of changes contained in {@code patch} to the content of
+   * {@code input} and writes the result to {@code output}. This method does not
+   * close the provided readers and writer.
+   * <p>
+   * <b>NOTE:</b> this method reads the content of {@code input} into memory
+   * before applying the changes, and is therefore not suited for use in cases
+   * where a very large number of entries are to be patched.
+   *
+   * @param input
+   *          The entry reader containing the set of entries to be patched.
+   * @param patch
+   *          The change record reader containing the set of changes to be
+   *          applied.
+   * @param output
+   *          The entry writer to which the updated entries are to be written.
+   * @param listener
+   *          The rejected change listener.
+   * @throws IOException
+   *           If an unexpected IO error occurred.
+   */
+  public static void patch(final EntryReader input,
+      final ChangeRecordReader patch, final EntryWriter output,
+      final RejectedChangeListener listener) throws IOException
+  {
+    final SortedMap<DN, Entry> entries = readEntries(input);
+
+    while (patch.hasNext())
+    {
+      final ChangeRecord change = patch.readChangeRecord();
+
+      final DecodeException de = change.accept(
+          new ChangeRecordVisitor<DecodeException, Void>()
+          {
+
+            @Override
+            public DecodeException visitChangeRecord(final Void p,
+                final AddRequest change)
+            {
+              final Entry existingEntry = entries.get(change.getName());
+              if (existingEntry != null)
+              {
+                try
+                {
+                  final Entry entry = listener.handleDuplicateEntry(change,
+                      existingEntry);
+                  entries.put(entry.getName(), entry);
+                }
+                catch (final DecodeException e)
+                {
+                  return e;
+                }
+              }
+              else
+              {
+                entries.put(change.getName(), change);
+              }
+              return null;
+            }
+
+
+
+            @Override
+            public DecodeException visitChangeRecord(final Void p,
+                final DeleteRequest change)
+            {
+              if (!entries.containsKey(change.getName()))
+              {
+                try
+                {
+                  listener.handleMissingEntry(change);
+                }
+                catch (final DecodeException e)
+                {
+                  return e;
+                }
+              }
+              else
+              {
+                entries.remove(change.getName());
+              }
+              return null;
+            }
+
+
+
+            @Override
+            public DecodeException visitChangeRecord(final Void p,
+                final ModifyDNRequest change)
+            {
+              if (!entries.containsKey(change.getName()))
+              {
+                try
+                {
+                  listener.handleMissingEntry(change);
+                }
+                catch (final DecodeException e)
+                {
+                  return e;
+                }
+              }
+              else
+              {
+                // Calculate the old and new DN.
+                final DN oldDN = change.getName();
+
+                DN newSuperior = change.getNewSuperior();
+                if (newSuperior == null)
+                {
+                  newSuperior = change.getName().parent();
+                  if (newSuperior == null)
+                  {
+                    newSuperior = DN.rootDN();
+                  }
+                }
+                final DN newDN = newSuperior.child(change.getNewRDN());
+
+                // Move the renamed entries into a separate map in order to
+                // avoid cases where the renamed subtree overlaps.
+                final SortedMap<DN, Entry> renamedEntries = new TreeMap<DN, Entry>();
+                final Iterator<Map.Entry<DN, Entry>> i = entries
+                    .tailMap(change.getName()).entrySet().iterator();
+                while (i.hasNext())
+                {
+                  final Map.Entry<DN, Entry> e = i.next();
+                  i.remove();
+
+                  final DN renamedDN = e.getKey().rename(oldDN, newDN);
+                  final Entry entry = e.getValue().setName(renamedDN);
+                  renamedEntries.put(renamedDN, entry);
+                }
+
+                // Modify the target entry.
+                final Entry entry = entries.values().iterator().next();
+                if (change.isDeleteOldRDN())
+                {
+                  for (final AVA ava : oldDN.rdn())
+                  {
+                    entry.removeAttribute(ava.toAttribute(), null);
+                  }
+                }
+                for (final AVA ava : newDN.rdn())
+                {
+                  entry.addAttribute(ava.toAttribute());
+                }
+
+                // Add the renamed entries.
+                for (final Entry renamedEntry : renamedEntries.values())
+                {
+                  final Entry existingEntry = entries.get(renamedEntry
+                      .getName());
+                  if (existingEntry != null)
+                  {
+                    try
+                    {
+                      final Entry tmp = listener.handleDuplicateEntry(change,
+                          existingEntry, renamedEntry);
+                      entries.put(tmp.getName(), tmp);
+                    }
+                    catch (final DecodeException e)
+                    {
+                      return e;
+                    }
+                  }
+                  else
+                  {
+                    entries.put(renamedEntry.getName(), renamedEntry);
+                  }
+                }
+              }
+              return null;
+            }
+
+
+
+            @Override
+            public DecodeException visitChangeRecord(final Void p,
+                final ModifyRequest change)
+            {
+              if (!entries.containsKey(change.getName()))
+              {
+                try
+                {
+                  listener.handleMissingEntry(change);
+                }
+                catch (final DecodeException e)
+                {
+                  return e;
+                }
+              }
+              else
+              {
+                final Entry entry = entries.get(change.getName());
+                for (final Modification modification : change
+                    .getModifications())
+                {
+                  final ModificationType modType = modification
+                      .getModificationType();
+                  if (modType.equals(ModificationType.ADD))
+                  {
+                    entry.addAttribute(modification.getAttribute(), null);
+                  }
+                  else if (modType.equals(ModificationType.DELETE))
+                  {
+                    entry.removeAttribute(modification.getAttribute(), null);
+                  }
+                  else if (modType.equals(ModificationType.REPLACE))
+                  {
+                    entry.replaceAttribute(modification.getAttribute());
+                  }
+                  else
+                  {
+                    System.err.println("Unable to apply \"" + modType
+                        + "\" modification to entry \"" + change.getName()
+                        + "\": modification type not supported");
+                  }
+                }
+              }
+              return null;
+            }
+
+          }, null);
+
+      if (de != null)
+      {
+        throw de;
+      }
+    }
+
+    for (final Entry entry : entries.values())
+    {
+      output.writeEntry(entry);
+    }
+  }
+
+
+
+  private static Entry nextEntry(final Iterator<Entry> i)
+  {
+    return i.hasNext() ? i.next() : null;
+  }
+
+
+
+  private static SortedMap<DN, Entry> readEntries(final EntryReader reader)
+      throws IOException
+  {
+    final SortedMap<DN, Entry> entries = new TreeMap<DN, Entry>();
+    while (reader.hasNext())
+    {
+      final Entry entry = reader.readEntry();
+      entries.put(entry.getName(), entry);
+    }
+    return entries;
+  }
+
+
+
+  // Prevent instantiation.
+  private LDIF()
+  {
+    // Do nothing.
+  }
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java
new file mode 100644
index 0000000..856230f
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java
@@ -0,0 +1,233 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *      Copyright 2011 ForgeRock AS
+ */
+
+package org.forgerock.opendj.ldif;
+
+
+
+import static org.forgerock.opendj.ldap.CoreMessages.*;
+
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+
+
+
+/**
+ * A listener interface which is notified whenever a change record cannot be
+ * applied to an entry. This may occur when an attempt is made to update a
+ * non-existent entry, or add an entry which already exists.
+ * <p>
+ * By default the {@link #FAIL_FAST} listener is used.
+ */
+public interface RejectedChangeListener
+{
+  /**
+   * A handler which terminates processing by throwing a {@code DecodeException}
+   * as soon as a change is rejected.
+   */
+  public final static RejectedChangeListener FAIL_FAST = new RejectedChangeListener()
+  {
+
+    @Override
+    public Entry handleDuplicateEntry(final AddRequest change,
+        final Entry existingEntry) throws DecodeException
+    {
+      throw DecodeException.error(REJECTED_CHANGE_FAIL_ADD_DUPE.get(change
+          .getName().toString()));
+    }
+
+
+
+    @Override
+    public Entry handleDuplicateEntry(final ModifyDNRequest change,
+        final Entry existingEntry, final Entry renamedEntry)
+        throws DecodeException
+    {
+      throw DecodeException.error(REJECTED_CHANGE_FAIL_MODIFYDN_DUPE
+          .get(renamedEntry.getName().toString()));
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final DeleteRequest change)
+        throws DecodeException
+    {
+      throw DecodeException.error(REJECTED_CHANGE_FAIL_DELETE.get(change
+          .getName().toString()));
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final ModifyDNRequest change)
+        throws DecodeException
+    {
+      throw DecodeException.error(REJECTED_CHANGE_FAIL_MODIFYDN.get(change
+          .getName().toString()));
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final ModifyRequest change)
+        throws DecodeException
+    {
+      throw DecodeException.error(REJECTED_CHANGE_FAIL_MODIFY.get(change
+          .getName().toString()));
+    }
+  };
+
+  /**
+   * The default handler which ignores changes applied to missing entries and
+   * tolerates duplicate entries by overwriting the existing entry with the new
+   * entry.
+   */
+  public final static RejectedChangeListener OVERWRITE = new RejectedChangeListener()
+  {
+
+    @Override
+    public Entry handleDuplicateEntry(final AddRequest change,
+        final Entry existingEntry) throws DecodeException
+    {
+      // Overwrite existing entries.
+      return change;
+    }
+
+
+
+    @Override
+    public Entry handleDuplicateEntry(final ModifyDNRequest change,
+        final Entry existingEntry, final Entry renamedEntry)
+        throws DecodeException
+    {
+      // Overwrite existing entries.
+      return renamedEntry;
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final DeleteRequest change)
+        throws DecodeException
+    {
+      // Ignore changes applied to missing entries.
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final ModifyDNRequest change)
+        throws DecodeException
+    {
+      // Ignore changes applied to missing entries.
+    }
+
+
+
+    @Override
+    public void handleMissingEntry(final ModifyRequest change)
+        throws DecodeException
+    {
+      // Ignore changes applied to missing entries.
+    }
+  };
+
+
+
+  /**
+   * Invoked when an attempt was made to add an entry which already exists.
+   *
+   * @param change
+   *          The conflicting add request.
+   * @param existingEntry
+   *          The pre-existing entry.
+   * @return The entry which should be kept.
+   * @throws DecodeException
+   *           If processing should terminate.
+   */
+  Entry handleDuplicateEntry(AddRequest change, Entry existingEntry)
+      throws DecodeException;
+
+
+
+  /**
+   * Invoked when an attempt was made to rename an entry which already exists.
+   *
+   * @param change
+   *          The conflicting add request.
+   * @param existingEntry
+   *          The pre-existing entry.
+   * @param renamedEntry
+   *          The renamed entry.
+   * @return The entry which should be kept.
+   * @throws DecodeException
+   *           If processing should terminate.
+   */
+  Entry handleDuplicateEntry(ModifyDNRequest change, Entry existingEntry,
+      Entry renamedEntry) throws DecodeException;
+
+
+
+  /**
+   * Invoked when an attempt was made to delete an entry which does not exist.
+   *
+   * @param change
+   *          The conflicting delete request.
+   * @throws DecodeException
+   *           If processing should terminate.
+   */
+  void handleMissingEntry(DeleteRequest change) throws DecodeException;
+
+
+
+  /**
+   * Invoked when an attempt was made to rename an entry which does not exist.
+   *
+   * @param change
+   *          The conflicting rename request.
+   * @throws DecodeException
+   *           If processing should terminate.
+   */
+  void handleMissingEntry(ModifyDNRequest change) throws DecodeException;
+
+
+
+  /**
+   * Invoked when an attempt was made to modify an entry which does not exist.
+   *
+   * @param change
+   *          The conflicting modify request.
+   * @throws DecodeException
+   *           If processing should terminate.
+   */
+  void handleMissingEntry(ModifyRequest change) throws DecodeException;
+
+}
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java
index 07517b7..157a4d1 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java
@@ -38,6 +38,8 @@
 /**
  * A listener interface which is notified whenever records are skipped,
  * malformed, or fail schema validation.
+ * <p>
+ * By default the {@link #FAIL_FAST} listener is used.
  */
 public interface RejectedRecordListener
 {
@@ -72,21 +74,67 @@
 
 
     @Override
+    public void handleSchemaValidationWarning(final long lineNumber,
+        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
+        throws DecodeException
+    {
+      // Ignore schema validation warnings.
+    }
+
+
+
+    @Override
     public void handleSkippedRecord(final long lineNumber,
         final List<String> ldifRecord, final LocalizableMessage reason)
         throws DecodeException
     {
       // Ignore skipped records.
     }
+  };
+
+  /**
+   * A handler which ignores all rejected record notifications.
+   */
+  public static final RejectedRecordListener IGNORE_ALL = new RejectedRecordListener()
+  {
+
+    @Override
+    public void handleMalformedRecord(final long lineNumber,
+        final List<String> ldifRecord, final LocalizableMessage reason)
+        throws DecodeException
+    {
+      // Ignore malformed records.
+    }
 
 
 
-    public void handleSchemaValidationWarning(long lineNumber,
-        List<String> ldifRecord, List<LocalizableMessage> reasons)
+    @Override
+    public void handleSchemaValidationFailure(final long lineNumber,
+        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
+        throws DecodeException
+    {
+      // Ignore schema validation failures.
+    }
+
+
+
+    @Override
+    public void handleSchemaValidationWarning(final long lineNumber,
+        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
         throws DecodeException
     {
       // Ignore schema validation warnings.
     }
+
+
+
+    @Override
+    public void handleSkippedRecord(final long lineNumber,
+        final List<String> ldifRecord, final LocalizableMessage reason)
+        throws DecodeException
+    {
+      // Ignore skipped records.
+    }
   };
 
 
@@ -131,11 +179,12 @@
 
 
   /**
-   * Invoked when a record was not rejected but contained one or more schema validation warnings.
+   * Invoked when a record was not rejected but contained one or more schema
+   * validation warnings.
    *
    * @param lineNumber
-   *          The line number within the source location in which the
-   *          record is located, if known, otherwise {@code -1}.
+   *          The line number within the source location in which the record is
+   *          located, if known, otherwise {@code -1}.
    * @param ldifRecord
    *          An LDIF representation of the record which contained schema
    *          validation warnings.
diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
index cf21f83..8bdca6e 100755
--- a/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties
@@ -1390,3 +1390,13 @@
  removed from the schema because it is invalid
 ERR_CONNECTION_POOL_CLOSING=No connection could be obtained from connection \
  pool "%s" because it is closing
+REJECTED_CHANGE_FAIL_ADD_DUPE=The entry "%s" could not be added because there \
+ is already an entry with the same name
+REJECTED_CHANGE_FAIL_DELETE=The entry "%s" could not be deleted because the \
+ entry does not exist
+REJECTED_CHANGE_FAIL_MODIFY=The entry "%s" could not be modified because the \
+ entry does not exist
+REJECTED_CHANGE_FAIL_MODIFYDN=The entry "%s" could not be renamed because the \
+ entry does not exist 
+REJECTED_CHANGE_FAIL_MODIFYDN_DUPE=The entry "%s" could not be renamed because \
+ there is already an entry with the same name

--
Gitblit v1.10.0