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