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
| | |
| | | /** |
| | | * 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 |
| | | { |
| | |
| | | * |
| | | * |
| | | * Copyright 2009-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2011 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | |
| | | */ |
| | | 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. |
| | |
| | | */ |
| | | 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]); |
| | |
| | | |
| | | |
| | | |
| | | RejectedRecordListener rejectedRecordListener = RejectedRecordListener.FAIL_FAST; |
| | | RejectedLDIFListener rejectedRecordListener = RejectedLDIFListener.FAIL_FAST; |
| | | |
| | | Schema schema = Schema.getDefaultSchema().asNonStrictSchema(); |
| | | |
| | |
| | | |
| | | |
| | | |
| | | 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; |
| | | |
| | | |
| | | |
| | |
| | | * @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()) |
| | |
| | | * @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()) |
| | |
| | | public static EntryReader patch(final EntryReader input, |
| | | final ChangeRecordReader patch) throws IOException |
| | | { |
| | | return patch(input, patch, RejectedChangeListener.OVERWRITE); |
| | | return patch(input, patch, RejectedChangeRecordListener.OVERWRITE); |
| | | } |
| | | |
| | | |
| | |
| | | * 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); |
| | | |
| | |
| | | { |
| | | try |
| | | { |
| | | listener.handleMissingEntry(change); |
| | | listener.handleRejectedChangeRecord(change, |
| | | REJECTED_CHANGE_FAIL_DELETE.get(change.getName() |
| | | .toString())); |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | |
| | | } |
| | | 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; |
| | | } |
| | |
| | | { |
| | | try |
| | | { |
| | | listener.handleMissingEntry(change); |
| | | listener.handleRejectedChangeRecord(change, |
| | | REJECTED_CHANGE_FAIL_MODIFYDN.get(change.getName() |
| | | .toString())); |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | |
| | | // 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(); |
| | |
| | | { |
| | | try |
| | | { |
| | | listener.handleMissingEntry(change); |
| | | listener.handleRejectedChangeRecord(change, |
| | | REJECTED_CHANGE_FAIL_MODIFY.get(change.getName() |
| | | .toString())); |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | |
| | | 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( |
| | |
| | | |
| | | |
| | | /** |
| | | * 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; |
| New file |
| | |
| | | /* |
| | | * 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; |
| | | |
| | | } |
| File was renamed from opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java |
| | |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | |
| | | |
| | | @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. |
| | |
| | | |
| | | @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. |
| | |
| | | |
| | | @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. |
| | |
| | | /** |
| | | * 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. |
| | |
| | | |
| | | @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. |
| | |
| | | |
| | | @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. |
| | |
| | | |
| | | @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. |
| | |
| | | * @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; |
| | | |
| | | |
| | |
| | | * @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; |
| | | |
| | | |
| | |
| | | * @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; |
| | | |
| | | |
| | |
| | | * @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; |
| | | |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2011 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | |
| | | @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 }, |
| | |
| | | // { "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 |
| | | } |
| | | |
| | | |
| | |
| | | * 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); |
| | | |
| | |
| | | |
| | | |
| | | |
| | | private RDN parseRDN(final Object value) |
| | | { |
| | | return (value instanceof RDN) ? ((RDN) value) : RDN.valueOf(value.toString()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Test RDN construction with single AVA. |
| | | * |
| | |
| | | * 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) |
| | | { |
| | |
| | | * 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(); |
| | |
| | | @Test |
| | | public void testRejectedRecordListenerMalformedFirstRecord() throws Exception |
| | | { |
| | | RejectedRecordListener listener = mock(RejectedRecordListener.class); |
| | | RejectedLDIFListener listener = mock(RejectedLDIFListener.class); |
| | | |
| | | // @formatter:off |
| | | LDIFChangeRecordReader reader = new LDIFChangeRecordReader( |
| | |
| | | "objectClass: top", |
| | | "objectClass: domainComponent", |
| | | "dc: example" |
| | | ).setRejectedRecordListener(listener); |
| | | ).setRejectedLDIFListener(listener); |
| | | // @formatter:on |
| | | |
| | | assertThat(reader.hasNext()).isFalse(); |
| | |
| | | public void testRejectedRecordListenerMalformedSecondRecord() |
| | | throws Exception |
| | | { |
| | | RejectedRecordListener listener = mock(RejectedRecordListener.class); |
| | | RejectedLDIFListener listener = mock(RejectedLDIFListener.class); |
| | | |
| | | // @formatter:off |
| | | LDIFChangeRecordReader reader = new LDIFChangeRecordReader( |
| | |
| | | "objectClass: top", |
| | | "objectClass: domainComponent", |
| | | "dc: example" |
| | | ).setRejectedRecordListener(listener); |
| | | ).setRejectedLDIFListener(listener); |
| | | // @formatter:on |
| | | |
| | | reader.readChangeRecord(); // Skip good record. |
| | |
| | | @Test |
| | | public void testRejectedRecordListenerSkipsRecord() throws Exception |
| | | { |
| | | RejectedRecordListener listener = mock(RejectedRecordListener.class); |
| | | RejectedLDIFListener listener = mock(RejectedLDIFListener.class); |
| | | |
| | | // @formatter:off |
| | | LDIFChangeRecordReader reader = new LDIFChangeRecordReader( |
| | |
| | | "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(); |
| | |
| | | public void testRejectedRecordListenerRejectsBadSchemaRecord() |
| | | throws Exception |
| | | { |
| | | RejectedRecordListener listener = mock(RejectedRecordListener.class); |
| | | RejectedLDIFListener listener = mock(RejectedLDIFListener.class); |
| | | |
| | | // @formatter:off |
| | | LDIFChangeRecordReader reader = new LDIFChangeRecordReader( |
| | |
| | | "objectClass: domainComponent", |
| | | "dc: example", |
| | | "xxx: unknown attribute" |
| | | ).setRejectedRecordListener(listener) |
| | | ).setRejectedLDIFListener(listener) |
| | | .setSchemaValidationPolicy( |
| | | SchemaValidationPolicy.ignoreAll() |
| | | .checkAttributesAndObjectClasses(Policy.REJECT)); |
| | |
| | | public void testRejectedRecordListenerWarnsBadSchemaRecord() |
| | | throws Exception |
| | | { |
| | | RejectedRecordListener listener = mock(RejectedRecordListener.class); |
| | | RejectedLDIFListener listener = mock(RejectedLDIFListener.class); |
| | | |
| | | // @formatter:off |
| | | LDIFChangeRecordReader reader = new LDIFChangeRecordReader( |
| | |
| | | "objectClass: domainComponent", |
| | | "dc: example", |
| | | "xxx: unknown attribute" |
| | | ).setRejectedRecordListener(listener) |
| | | ).setRejectedLDIFListener(listener) |
| | | .setSchemaValidationPolicy( |
| | | SchemaValidationPolicy.ignoreAll() |
| | | .checkAttributesAndObjectClasses(Policy.WARN)); |