| opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AVA.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedRecordListener.java | ●●●●● patch | view | raw | blame | history | |
| opendj3/opendj-ldap-sdk/src/main/resources/org/forgerock/opendj/ldap/core.properties | ●●●●● patch | view | raw | blame | history |
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 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
New file @@ -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. } } opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/RejectedChangeListener.java
New file @@ -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; } 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. 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