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

Matthew Swift
12.33.2011 702b6351ed3207102d0bf4a152f45cf74452eeb3
Additional fixes for OPENDJ-381: Implement LDIF diff, patch, and search API support in the SDK

* add RDN.maxValue() which can be used to specify sub ranges in DN keyed sorted collections
* fix bug in patch modify DN processing
* add support for sub-tree delete in patch
* rename LDIF listener APIs in order to differentiate their purposes more clearly
* add support for search LDIF using LDIF.search().
1 files deleted
1 files added
1 files renamed
8 files modified
949 ■■■■■ changed files
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DN.java 6 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RDN.java 53 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java 247 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java 10 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java 8 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java 233 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeRecordListener.java 275 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedLDIFListener.java 51 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/RDNTestCase.java 44 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java 20 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/DN.java
@@ -374,12 +374,18 @@
  /**
   * Returns a DN which is an immediate child of this DN and having the
   * specified RDN.
   * <p>
   * <b>Note:</b> the child DN whose RDN is {@link RDN#maxValue()} compares
   * greater than all other possible child DNs, and may be used to construct
   * range queries against DN keyed sorted collections such as {@code SortedSet}
   * and {@code SortedMap}.
   *
   * @param rdn
   *          The RDN for the child DN.
   * @return The child DN.
   * @throws NullPointerException
   *           If {@code rdn} was {@code null}.
   * @see RDN#maxValue()
   */
  public DN child(final RDN rdn) throws NullPointerException
  {
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/RDN.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
@@ -75,6 +76,38 @@
 */
public final class RDN implements Iterable<AVA>, Comparable<RDN>
{
  // A constant holding a special RDN having zero AVAs and which always compares
  // greater than any other RDN other than itself.
  private static final RDN MAX_VALUE = new RDN(new AVA[0], "");
  /**
   * Returns a constant containing a special RDN which is greater than any other
   * RDN other than itself. This RDN may be used in order to perform range
   * queries on DN keyed collections such as {@code SortedSet}s and
   * {@code SortedMap}s. For example, the following code can be used to
   * construct a range whose contents is a sub-tree of entries:
   *
   * <pre>
   * SortedMap<DN, Entry> entries = ...;
   * DN baseDN = ...;
   *
   * // Returns a map containing the baseDN and all of its subordinates.
   * SortedMap<DN,Entry> subtree = entries.subMap(baseDN, baseDN.child(RDN.maxValue));
   * </pre>
   *
   * @return A constant containing a special RDN which is greater than any other
   *         RDN other than itself.
   */
  public static RDN maxValue()
  {
    return MAX_VALUE;
  }
  /**
   * Parses the provided LDAP string representation of an RDN using the default
   * schema.
@@ -235,14 +268,32 @@
   */
  public int compareTo(final RDN rdn)
  {
    // Identity.
    if (this == rdn)
    {
      return 0;
    }
    // MAX_VALUE is always greater than any other RDN other than itself.
    if (this == MAX_VALUE)
    {
      return 1;
    }
    if (rdn == MAX_VALUE)
    {
      return -1;
    }
    // Compare number of AVAs first as this is quick and easy.
    final int sz1 = avas.length;
    final int sz2 = rdn.avas.length;
    if (sz1 != sz2)
    {
      return sz1 - sz2 > 0 ? 1 : -1;
    }
    // Fast path for common case.
    if (sz1 == 1)
    {
      return avas[0].compareTo(rdn.avas[0]);
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/AbstractLDIFReader.java
@@ -230,7 +230,7 @@
  RejectedRecordListener rejectedRecordListener = RejectedRecordListener.FAIL_FAST;
  RejectedLDIFListener rejectedRecordListener = RejectedLDIFListener.FAIL_FAST;
  Schema schema = Schema.getDefaultSchema().asNonStrictSchema();
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -28,11 +28,18 @@
import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_DELETE;
import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_MODIFY;
import static org.forgerock.opendj.ldap.CoreMessages.REJECTED_CHANGE_FAIL_MODIFYDN;
import java.io.IOException;
import java.util.*;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
import org.forgerock.opendj.ldap.requests.*;
import org.forgerock.opendj.ldap.schema.AttributeUsage;
import org.forgerock.opendj.ldap.schema.Schema;
@@ -66,7 +73,7 @@
   * @throws IOException
   *           If an unexpected IO error occurred.
   */
  public static final ChangeRecordWriter copyTo(final ChangeRecordReader input,
  public static ChangeRecordWriter copyTo(final ChangeRecordReader input,
      final ChangeRecordWriter output) throws IOException
  {
    while (input.hasNext())
@@ -90,7 +97,7 @@
   * @throws IOException
   *           If an unexpected IO error occurred.
   */
  public static final EntryWriter copyTo(final EntryReader input,
  public static EntryWriter copyTo(final EntryReader input,
      final EntryWriter output) throws IOException
  {
    while (input.hasNext())
@@ -278,7 +285,7 @@
  public static EntryReader patch(final EntryReader input,
      final ChangeRecordReader patch) throws IOException
  {
    return patch(input, patch, RejectedChangeListener.OVERWRITE);
    return patch(input, patch, RejectedChangeRecordListener.OVERWRITE);
  }
@@ -305,8 +312,8 @@
   *           If an unexpected IO error occurred.
   */
  public static EntryReader patch(final EntryReader input,
      final ChangeRecordReader patch, final RejectedChangeListener listener)
      throws IOException
      final ChangeRecordReader patch,
      final RejectedChangeRecordListener listener) throws IOException
  {
    final SortedMap<DN, Entry> entries = readEntries(input);
@@ -353,7 +360,9 @@
              {
                try
                {
                  listener.handleMissingEntry(change);
                  listener.handleRejectedChangeRecord(change,
                      REJECTED_CHANGE_FAIL_DELETE.get(change.getName()
                          .toString()));
                }
                catch (final DecodeException e)
                {
@@ -362,7 +371,24 @@
              }
              else
              {
                entries.remove(change.getName());
                try
                {
                  if (change.getControl(SubtreeDeleteRequestControl.DECODER,
                      new DecodeOptions()) != null)
                  {
                    entries.subMap(change.getName(),
                        change.getName().child(RDN.maxValue())).clear();
                  }
                  else
                  {
                    entries.remove(change.getName());
                  }
                }
                catch (final DecodeException e)
                {
                  return e;
                }
              }
              return null;
            }
@@ -377,7 +403,9 @@
              {
                try
                {
                  listener.handleMissingEntry(change);
                  listener.handleRejectedChangeRecord(change,
                      REJECTED_CHANGE_FAIL_MODIFYDN.get(change.getName()
                          .toString()));
                }
                catch (final DecodeException e)
                {
@@ -404,7 +432,9 @@
                // 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();
                    .subMap(change.getName(),
                        change.getName().child(RDN.maxValue())).entrySet()
                    .iterator();
                while (i.hasNext())
                {
                  final Map.Entry<DN, Entry> e = i.next();
@@ -466,7 +496,9 @@
              {
                try
                {
                  listener.handleMissingEntry(change);
                  listener.handleRejectedChangeRecord(change,
                      REJECTED_CHANGE_FAIL_MODIFY.get(change.getName()
                          .toString()));
                }
                catch (final DecodeException e)
                {
@@ -551,6 +583,201 @@
  /**
   * Returns a filtered view of {@code input} containing only those entries
   * which match the search base DN, scope, and filtered defined in
   * {@code search}. In addition, returned entries will be filtered according to
   * any attribute filtering criteria defined in the search request.
   * <p>
   * The filter and attribute descriptions will be decoded using the default
   * schema.
   *
   * @param input
   *          The entry reader containing the set of entries to be filtered.
   * @param search
   *          The search request defining the filtering criteria.
   * @return A filtered view of {@code input} containing only those entries
   *         which match the provided search request.
   */
  public static EntryReader search(final EntryReader input,
      final SearchRequest search)
  {
    return search(input, search, Schema.getDefaultSchema());
  }
  /**
   * Returns a filtered view of {@code input} containing only those entries
   * which match the search base DN, scope, and filtered defined in
   * {@code search}. In addition, returned entries will be filtered according to
   * any attribute filtering criteria defined in the search request.
   * <p>
   * The filter and attribute descriptions will be decoded using the provided
   * schema.
   *
   * @param input
   *          The entry reader containing the set of entries to be filtered.
   * @param search
   *          The search request defining the filtering criteria.
   * @param schema
   *          The schema which should be used to decode the search filter and
   *          attribute descriptions.
   * @return A filtered view of {@code input} containing only those entries
   *         which match the provided search request.
   */
  public static EntryReader search(final EntryReader input,
      final SearchRequest search, final Schema schema)
  {
    final Matcher matcher = search.getFilter().matcher(schema);
    return new EntryReader()
    {
      private Entry nextEntry = null;
      private int entryCount = 0;
      public void close() throws IOException
      {
        input.close();
      }
      public boolean hasNext() throws IOException
      {
        if (nextEntry == null)
        {
          final int sizeLimit = search.getSizeLimit();
          if (sizeLimit == 0 || entryCount < sizeLimit)
          {
            final DN baseDN = search.getName();
            final SearchScope scope = search.getScope();
            while (input.hasNext())
            {
              final Entry entry = input.readEntry();
              if (entry.getName().isInScopeOf(baseDN, scope)
                  && matcher.matches(entry).toBoolean())
              {
                nextEntry = filterEntry(entry);
                break;
              }
            }
          }
        }
        return nextEntry != null;
      }
      public Entry readEntry() throws IOException, NoSuchElementException
      {
        if (hasNext())
        {
          final Entry entry = nextEntry;
          nextEntry = null;
          entryCount++;
          return entry;
        }
        else
        {
          throw new NoSuchElementException();
        }
      }
      private Entry filterEntry(final Entry entry)
      {
        // TODO: rename attributes; move functionality to Entries.
        if (search.getAttributes().isEmpty())
        {
          if (search.isTypesOnly())
          {
            final Entry filteredEntry = new LinkedHashMapEntry(entry.getName());
            for (final Attribute attribute : entry.getAllAttributes())
            {
              filteredEntry.addAttribute(Attributes.emptyAttribute(attribute
                  .getAttributeDescription()));
            }
            return filteredEntry;
          }
          else
          {
            return entry;
          }
        }
        else
        {
          final Entry filteredEntry = new LinkedHashMapEntry(entry.getName());
          for (final String atd : search.getAttributes())
          {
            if (atd.equals("*"))
            {
              for (final Attribute attribute : entry.getAllAttributes())
              {
                if (attribute.getAttributeDescription().getAttributeType()
                    .getUsage() == AttributeUsage.USER_APPLICATIONS)
                {
                  if (search.isTypesOnly())
                  {
                    filteredEntry.addAttribute(Attributes
                        .emptyAttribute(attribute.getAttributeDescription()));
                  }
                  else
                  {
                    filteredEntry.addAttribute(attribute);
                  }
                }
              }
            }
            else if (atd.equals("+"))
            {
              for (final Attribute attribute : entry.getAllAttributes())
              {
                if (attribute.getAttributeDescription().getAttributeType()
                    .getUsage() != AttributeUsage.USER_APPLICATIONS)
                {
                  if (search.isTypesOnly())
                  {
                    filteredEntry.addAttribute(Attributes
                        .emptyAttribute(attribute.getAttributeDescription()));
                  }
                  else
                  {
                    filteredEntry.addAttribute(attribute);
                  }
                }
              }
            }
            else
            {
              final AttributeDescription ad = AttributeDescription.valueOf(atd,
                  schema);
              for (final Attribute attribute : entry.getAllAttributes(ad))
              {
                if (search.isTypesOnly())
                {
                  filteredEntry.addAttribute(Attributes
                      .emptyAttribute(attribute.getAttributeDescription()));
                }
                else
                {
                  filteredEntry.addAttribute(attribute);
                }
              }
            }
          }
          return filteredEntry;
        }
      }
    };
  }
  private static SortedMap<DN, Entry> readEntries(final EntryReader reader)
      throws IOException
  {
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFChangeRecordReader.java
@@ -336,17 +336,17 @@
  /**
   * Sets the rejected record listener which should be notified whenever a
   * Sets the rejected record listener which should be notified whenever an LDIF
   * record is skipped, malformed, or fails schema validation.
   * <p>
   * By default the {@link RejectedRecordListener#FAIL_FAST} listener is used.
   * By default the {@link RejectedLDIFListener#FAIL_FAST} listener is used.
   *
   * @param listener
   *          The rejected record listener.
   * @return A reference to this {@code LDIFChangeRecordReader}.
   */
  public LDIFChangeRecordReader setRejectedRecordListener(
      final RejectedRecordListener listener)
  public LDIFChangeRecordReader setRejectedLDIFListener(
      final RejectedLDIFListener listener)
  {
    this.rejectedRecordListener = listener;
    return this;
@@ -623,7 +623,7 @@
        case WARN:
          schemaErrors.add(message);
          continue;
        default: //Ignore
        default: // Ignore
          // This should not happen: we should be using a non-strict schema for
          // this policy.
          throw new IllegalStateException(
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIFEntryReader.java
@@ -364,17 +364,17 @@
  /**
   * Sets the rejected record listener which should be notified whenever a
   * Sets the rejected record listener which should be notified whenever an LDIF
   * record is skipped, malformed, or fails schema validation.
   * <p>
   * By default the {@link RejectedRecordListener#FAIL_FAST} listener is used.
   * By default the {@link RejectedLDIFListener#FAIL_FAST} listener is used.
   *
   * @param listener
   *          The rejected record listener.
   * @return A reference to this {@code LDIFEntryReader}.
   */
  public LDIFEntryReader setRejectedRecordListener(
      final RejectedRecordListener listener)
  public LDIFEntryReader setRejectedLDIFListener(
      final RejectedLDIFListener listener)
  {
    this.rejectedRecordListener = listener;
    return this;
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java
File was deleted
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeRecordListener.java
New file
@@ -0,0 +1,275 @@
/*
 * 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.i18n.LocalizableMessage;
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 RejectedChangeRecordListener
{
  /**
   * A handler which terminates processing by throwing a {@code DecodeException}
   * as soon as a change is rejected.
   */
  public final static RejectedChangeRecordListener FAIL_FAST = new RejectedChangeRecordListener()
  {
    public Entry handleDuplicateEntry(final AddRequest change,
        final Entry existingEntry) throws DecodeException
    {
      throw DecodeException.error(REJECTED_CHANGE_FAIL_ADD_DUPE.get(change
          .getName().toString()));
    }
    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()));
    }
    public void handleRejectedChangeRecord(final AddRequest change,
        final LocalizableMessage reason) throws DecodeException
    {
      throw DecodeException.error(reason);
    }
    public void handleRejectedChangeRecord(final DeleteRequest change,
        final LocalizableMessage reason) throws DecodeException
    {
      throw DecodeException.error(reason);
    }
    public void handleRejectedChangeRecord(final ModifyRequest change,
        final LocalizableMessage reason) throws DecodeException
    {
      throw DecodeException.error(reason);
    }
    public void handleRejectedChangeRecord(final ModifyDNRequest change,
        final LocalizableMessage reason) throws DecodeException
    {
      throw DecodeException.error(reason);
    }
  };
  /**
   * 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 RejectedChangeRecordListener OVERWRITE = new RejectedChangeRecordListener()
  {
    public Entry handleDuplicateEntry(final AddRequest change,
        final Entry existingEntry) throws DecodeException
    {
      // Overwrite existing entries.
      return change;
    }
    public Entry handleDuplicateEntry(final ModifyDNRequest change,
        final Entry existingEntry, final Entry renamedEntry)
        throws DecodeException
    {
      // Overwrite existing entries.
      return renamedEntry;
    }
    public void handleRejectedChangeRecord(AddRequest change,
        LocalizableMessage reason) throws DecodeException
    {
      // Ignore.
    }
    public void handleRejectedChangeRecord(DeleteRequest change,
        LocalizableMessage reason) throws DecodeException
    {
      // Ignore.
    }
    public void handleRejectedChangeRecord(ModifyRequest change,
        LocalizableMessage reason) throws DecodeException
    {
      // Ignore.
    }
    public void handleRejectedChangeRecord(ModifyDNRequest change,
        LocalizableMessage reason) throws DecodeException
    {
      // Ignore.
    }
  };
  /**
   * 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 to add an entry was rejected. This may be because
   * the target parent entry was not found, or controls provided with the
   * request are not supported. This method will not be called when the entry to
   * be added already exists, since this is handled by
   * {@link #handleDuplicateEntry(AddRequest, Entry)}.
   *
   * @param change
   *          The rejected add request.
   * @param reason
   *          The reason why the record was rejected.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleRejectedChangeRecord(AddRequest change, LocalizableMessage reason)
      throws DecodeException;
  /**
   * Invoked when an attempt to delete an entry was rejected. This may be
   * because the target entry was not found, or controls provided with the
   * request are not supported.
   *
   * @param change
   *          The rejected delete request.
   * @param reason
   *          The reason why the record was rejected.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleRejectedChangeRecord(DeleteRequest change,
      LocalizableMessage reason) throws DecodeException;
  /**
   * Invoked when an attempt to modify an entry was rejected. This may be
   * because the target entry was not found, or controls provided with the
   * request are not supported.
   *
   * @param change
   *          The rejected modify request.
   * @param reason
   *          The reason why the record was rejected.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleRejectedChangeRecord(ModifyRequest change,
      LocalizableMessage reason) throws DecodeException;
  /**
   * Invoked when an attempt to rename an entry was rejected. This may be
   * because the target entry was not found, or controls provided with the
   * request are not supported. This method will not be called when a renamed
   * entry already exists, since this is handled by
   * {@link #handleDuplicateEntry(ModifyDNRequest, Entry, Entry)}.
   *
   * @param change
   *          The rejected modify DN request.
   * @param reason
   *          The reason why the record was rejected.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleRejectedChangeRecord(ModifyDNRequest change,
      LocalizableMessage reason) throws DecodeException;
}
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedLDIFListener.java
File was renamed from opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java
@@ -36,24 +36,24 @@
/**
 * A listener interface which is notified whenever records are skipped,
 * A listener interface which is notified whenever LDIF records are skipped,
 * malformed, or fail schema validation.
 * <p>
 * By default the {@link #FAIL_FAST} listener is used.
 */
public interface RejectedRecordListener
public interface RejectedLDIFListener
{
  /**
   * The default handler which ignores skipped records but which terminates
   * processing by throwing a {@code DecodeException} as soon as a record is
   * found to be malformed or rejected due to a schema validation failure.
   */
  public static final RejectedRecordListener FAIL_FAST = new RejectedRecordListener()
  public static final RejectedLDIFListener FAIL_FAST = new RejectedLDIFListener()
  {
    @Override
    public void handleMalformedRecord(final long lineNumber,
        final List<String> ldifRecord, final LocalizableMessage reason)
        final List<String> lines, final LocalizableMessage reason)
        throws DecodeException
    {
      // Fail fast.
@@ -64,7 +64,7 @@
    @Override
    public void handleSchemaValidationFailure(final long lineNumber,
        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
        final List<String> lines, final List<LocalizableMessage> reasons)
        throws DecodeException
    {
      // Fail fast - just use first message.
@@ -75,7 +75,7 @@
    @Override
    public void handleSchemaValidationWarning(final long lineNumber,
        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
        final List<String> lines, final List<LocalizableMessage> reasons)
        throws DecodeException
    {
      // Ignore schema validation warnings.
@@ -85,7 +85,7 @@
    @Override
    public void handleSkippedRecord(final long lineNumber,
        final List<String> ldifRecord, final LocalizableMessage reason)
        final List<String> lines, final LocalizableMessage reason)
        throws DecodeException
    {
      // Ignore skipped records.
@@ -95,12 +95,12 @@
  /**
   * A handler which ignores all rejected record notifications.
   */
  public static final RejectedRecordListener IGNORE_ALL = new RejectedRecordListener()
  public static final RejectedLDIFListener IGNORE_ALL = new RejectedLDIFListener()
  {
    @Override
    public void handleMalformedRecord(final long lineNumber,
        final List<String> ldifRecord, final LocalizableMessage reason)
        final List<String> lines, final LocalizableMessage reason)
        throws DecodeException
    {
      // Ignore malformed records.
@@ -110,7 +110,7 @@
    @Override
    public void handleSchemaValidationFailure(final long lineNumber,
        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
        final List<String> lines, final List<LocalizableMessage> reasons)
        throws DecodeException
    {
      // Ignore schema validation failures.
@@ -120,7 +120,7 @@
    @Override
    public void handleSchemaValidationWarning(final long lineNumber,
        final List<String> ldifRecord, final List<LocalizableMessage> reasons)
        final List<String> lines, final List<LocalizableMessage> reasons)
        throws DecodeException
    {
      // Ignore schema validation warnings.
@@ -130,7 +130,7 @@
    @Override
    public void handleSkippedRecord(final long lineNumber,
        final List<String> ldifRecord, final LocalizableMessage reason)
        final List<String> lines, final LocalizableMessage reason)
        throws DecodeException
    {
      // Ignore skipped records.
@@ -146,14 +146,14 @@
   * @param lineNumber
   *          The line number within the source location in which the malformed
   *          record is located, if known, otherwise {@code -1}.
   * @param ldifRecord
   *          An LDIF representation of the malformed record.
   * @param lines
   *          The content of the malformed record.
   * @param reason
   *          The reason why the record is malformed.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleMalformedRecord(long lineNumber, List<String> ldifRecord,
  void handleMalformedRecord(long lineNumber, List<String> lines,
      LocalizableMessage reason) throws DecodeException;
@@ -165,15 +165,14 @@
   * @param lineNumber
   *          The line number within the source location in which the rejected
   *          record is located, if known, otherwise {@code -1}.
   * @param ldifRecord
   *          An LDIF representation of the record which failed schema
   *          validation.
   * @param lines
   *          The content of the record which failed schema validation.
   * @param reasons
   *          The reasons why the record failed schema validation.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleSchemaValidationFailure(long lineNumber, List<String> ldifRecord,
  void handleSchemaValidationFailure(long lineNumber, List<String> lines,
      List<LocalizableMessage> reasons) throws DecodeException;
@@ -185,15 +184,15 @@
   * @param lineNumber
   *          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.
   * @param lines
   *          The content of the record which contained schema validation
   *          warnings.
   * @param reasons
   *          The schema validation warnings.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleSchemaValidationWarning(long lineNumber, List<String> ldifRecord,
  void handleSchemaValidationWarning(long lineNumber, List<String> lines,
      List<LocalizableMessage> reasons) throws DecodeException;
@@ -205,14 +204,14 @@
   * @param lineNumber
   *          The line number within the source location in which the skipped
   *          record is located, if known, otherwise {@code -1}.
   * @param ldifRecord
   *          An LDIF representation of the skipped record.
   * @param lines
   *          The content of the record which was skipped.
   * @param reason
   *          The reason why the record was skipped.
   * @throws DecodeException
   *           If processing should terminate.
   */
  void handleSkippedRecord(long lineNumber, List<String> ldifRecord,
  void handleSkippedRecord(long lineNumber, List<String> lines,
      LocalizableMessage reason) throws DecodeException;
}
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/RDNTestCase.java
@@ -23,6 +23,7 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
@@ -149,6 +150,7 @@
  @DataProvider(name = "createRDNEqualityData")
  public Object[][] createRDNEqualityData()
  {
    // @formatter:off
    return new Object[][] {
        { "cn=hello world", "cn=hello world", 0 },
        { "cn=hello world", "CN=hello world", 0 },
@@ -168,10 +170,19 @@
        // { "x-test-integer-type=999", "x-test-integer-type=1000", -1 },
        // { "x-test-integer-type=-1", "x-test-integer-type=0", -1 },
        // { "x-test-integer-type=0", "x-test-integer-type=-1", 1 },
        { "cn=aaa", "cn=aaaa", -1 }, { "cn=AAA", "cn=aaaa", -1 },
        { "cn=aaa", "cn=AAAA", -1 }, { "cn=aaaa", "cn=aaa", 1 },
        { "cn=AAAA", "cn=aaa", 1 }, { "cn=aaaa", "cn=AAA", 1 },
        { "cn=aaab", "cn=aaaa", 1 }, { "cn=aaaa", "cn=aaab", -1 } };
        { "cn=aaa", "cn=aaaa", -1 },
        { "cn=AAA", "cn=aaaa", -1 },
        { "cn=aaa", "cn=AAAA", -1 },
        { "cn=aaaa", "cn=aaa", 1 },
        { "cn=AAAA", "cn=aaa", 1 },
        { "cn=aaaa", "cn=AAA", 1 },
        { "cn=aaab", "cn=aaaa", 1 },
        { "cn=aaaa", "cn=aaab", -1 },
        { RDN.maxValue(), RDN.maxValue(), 0 },
        { RDN.maxValue(), "cn=aaa", 1 },
        { "cn=aaa", RDN.maxValue(), -1 },
    };
    // @formatter:on
  }
@@ -206,11 +217,11 @@
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testCompareTo(final String first, final String second,
  public void testCompareTo(final Object first, final Object second,
      final int result) throws Exception
  {
    final RDN rdn1 = RDN.valueOf(first);
    final RDN rdn2 = RDN.valueOf(second);
    final RDN rdn1 = parseRDN(first);
    final RDN rdn2 = parseRDN(second);
    int rc = rdn1.compareTo(rdn2);
@@ -230,6 +241,13 @@
  private RDN parseRDN(final Object value)
  {
    return (value instanceof RDN) ? ((RDN) value) : RDN.valueOf(value.toString());
  }
  /**
   * Test RDN construction with single AVA.
   *
@@ -362,11 +380,11 @@
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testEquality(final String first, final String second,
  public void testEquality(final Object first, final Object second,
      final int result) throws Exception
  {
    final RDN rdn1 = RDN.valueOf(first);
    final RDN rdn2 = RDN.valueOf(second);
    final RDN rdn1 = parseRDN(first);
    final RDN rdn2 = parseRDN(second);
    if (result == 0)
    {
@@ -448,11 +466,11 @@
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testHashCode(final String first, final String second,
  public void testHashCode(final Object first, final Object second,
      final int result) throws Exception
  {
    final RDN rdn1 = RDN.valueOf(first);
    final RDN rdn2 = RDN.valueOf(second);
    final RDN rdn1 = parseRDN(first);
    final RDN rdn2 = parseRDN(second);
    final int h1 = rdn1.hashCode();
    final int h2 = rdn2.hashCode();
opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldif/LDIFChangeRecordReaderTestCase.java
@@ -342,7 +342,7 @@
  @Test
  public void testRejectedRecordListenerMalformedFirstRecord() throws Exception
  {
    RejectedRecordListener listener = mock(RejectedRecordListener.class);
    RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
    // @formatter:off
    LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -351,7 +351,7 @@
        "objectClass: top",
        "objectClass: domainComponent",
        "dc: example"
        ).setRejectedRecordListener(listener);
        ).setRejectedLDIFListener(listener);
    // @formatter:on
    assertThat(reader.hasNext()).isFalse();
@@ -375,7 +375,7 @@
  public void testRejectedRecordListenerMalformedSecondRecord()
      throws Exception
  {
    RejectedRecordListener listener = mock(RejectedRecordListener.class);
    RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
    // @formatter:off
    LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -390,7 +390,7 @@
        "objectClass: top",
        "objectClass: domainComponent",
        "dc: example"
        ).setRejectedRecordListener(listener);
        ).setRejectedLDIFListener(listener);
    // @formatter:on
    reader.readChangeRecord(); // Skip good record.
@@ -414,7 +414,7 @@
  @Test
  public void testRejectedRecordListenerSkipsRecord() throws Exception
  {
    RejectedRecordListener listener = mock(RejectedRecordListener.class);
    RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
    // @formatter:off
    LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -423,7 +423,7 @@
        "objectClass: top",
        "objectClass: domainComponent",
        "dc: example"
        ).setRejectedRecordListener(listener).setExcludeBranch(DN.valueOf("dc=com"));
        ).setRejectedLDIFListener(listener).setExcludeBranch(DN.valueOf("dc=com"));
    // @formatter:on
    assertThat(reader.hasNext()).isFalse();
@@ -449,7 +449,7 @@
  public void testRejectedRecordListenerRejectsBadSchemaRecord()
      throws Exception
  {
    RejectedRecordListener listener = mock(RejectedRecordListener.class);
    RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
    // @formatter:off
    LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -459,7 +459,7 @@
        "objectClass: domainComponent",
        "dc: example",
        "xxx: unknown attribute"
        ).setRejectedRecordListener(listener)
        ).setRejectedLDIFListener(listener)
         .setSchemaValidationPolicy(
             SchemaValidationPolicy.ignoreAll()
             .checkAttributesAndObjectClasses(Policy.REJECT));
@@ -487,7 +487,7 @@
  public void testRejectedRecordListenerWarnsBadSchemaRecord()
      throws Exception
  {
    RejectedRecordListener listener = mock(RejectedRecordListener.class);
    RejectedLDIFListener listener = mock(RejectedLDIFListener.class);
    // @formatter:off
    LDIFChangeRecordReader reader = new LDIFChangeRecordReader(
@@ -497,7 +497,7 @@
        "objectClass: domainComponent",
        "dc: example",
        "xxx: unknown attribute"
        ).setRejectedRecordListener(listener)
        ).setRejectedLDIFListener(listener)
         .setSchemaValidationPolicy(
             SchemaValidationPolicy.ignoreAll()
             .checkAttributesAndObjectClasses(Policy.WARN));