From 972106eb4ed0d3d84fca6a5b25b74d0842aaea45 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 09 Dec 2011 23:54:16 +0000
Subject: [PATCH] Fix OPENDJ-381: Implement LDIF diff and patch API support in the SDK
---
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java | 307 ++++++++++++++++++++++++++++++++++++++------------
1 files changed, 231 insertions(+), 76 deletions(-)
diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
index d509c43..d3061ec 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -29,10 +29,7 @@
import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.*;
import org.forgerock.opendj.ldap.*;
import org.forgerock.opendj.ldap.requests.*;
@@ -45,10 +42,70 @@
*/
public final class LDIF
{
+ // @formatter:off
+ private static final class EntryIteratorReader implements EntryReader
+ {
+ private final Iterator<Entry> iterator;
+ private EntryIteratorReader(final Iterator<Entry> iterator)
+ { this.iterator = iterator; }
+ public void close() { }
+ public boolean hasNext() { return iterator.hasNext(); }
+ public Entry readEntry() { return iterator.next(); }
+ }
+ // @formatter:on
+
+ /**
+ * Copies the content of {@code input} to {@code output}. This method does not
+ * close {@code input} or {@code output}.
+ *
+ * @param input
+ * The input change record reader.
+ * @param output
+ * The output change record reader.
+ * @return The output change record reader.
+ * @throws IOException
+ * If an unexpected IO error occurred.
+ */
+ public static final ChangeRecordWriter copyTo(final ChangeRecordReader input,
+ final ChangeRecordWriter output) throws IOException
+ {
+ while (input.hasNext())
+ {
+ output.writeChangeRecord(input.readChangeRecord());
+ }
+ return output;
+ }
+
+
+
+ /**
+ * Copies the content of {@code input} to {@code output}. This method does not
+ * close {@code input} or {@code output}.
+ *
+ * @param input
+ * The input entry reader.
+ * @param output
+ * The output entry reader.
+ * @return The output entry reader.
+ * @throws IOException
+ * If an unexpected IO error occurred.
+ */
+ public static final EntryWriter copyTo(final EntryReader input,
+ final EntryWriter output) throws IOException
+ {
+ while (input.hasNext())
+ {
+ output.writeEntry(input.readEntry());
+ }
+ return output;
+ }
+
+
+
/**
* 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.
+ * returns the differences in a change record reader. Closing the returned
+ * reader will cause {@code source} and {@code target} to be closed as well.
* <p>
* <b>NOTE:</b> this method reads the content of {@code source} and
* {@code target} into memory before calculating the differences, and is
@@ -59,75 +116,151 @@
* 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.
+ * @return A change record reader containing the differences.
* @throws IOException
* If an unexpected IO error occurred.
*/
- public static void diff(final EntryReader source, final EntryReader target,
- final ChangeRecordWriter output) throws IOException
+ public static ChangeRecordReader diff(final EntryReader source,
+ final EntryReader target) 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)
+ return new ChangeRecordReader()
{
- final DN sourceDN = sourceEntry.getName();
- final DN targetDN = targetEntry.getName();
- final int cmp = sourceDN.compareTo(targetDN);
+ private Entry sourceEntry = nextEntry(sourceIterator);
+ private Entry targetEntry = nextEntry(targetIterator);
- 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);
- }
+ @Override
+ public void close() throws IOException
+ {
+ try
+ {
+ source.close();
+ }
+ finally
+ {
+ target.close();
+ }
+ }
+
+
+
+ @Override
+ public boolean hasNext()
+ {
+ return (sourceEntry != null || targetEntry != null);
+ }
+
+
+
+ @Override
+ public ChangeRecord readChangeRecord() throws IOException,
+ NoSuchElementException
+ {
+ if (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.
+ final ModifyRequest request = Requests.newModifyRequest(
+ sourceEntry, targetEntry);
+ sourceEntry = nextEntry(sourceIterator);
+ targetEntry = nextEntry(targetIterator);
+ return request;
+ }
+ else if (cmp < 0)
+ {
+ // Delete record: entry in source but not in target.
+ final DeleteRequest request = Requests.newDeleteRequest(sourceEntry
+ .getName());
+ sourceEntry = nextEntry(sourceIterator);
+ return request;
+ }
+ else
+ {
+ // Add record: entry in target but not in source.
+ final AddRequest request = Requests.newAddRequest(targetEntry);
+ targetEntry = nextEntry(targetIterator);
+ return request;
+ }
+ }
+ else if (sourceEntry != null)
+ {
+ // Delete remaining source records.
+ final DeleteRequest request = Requests.newDeleteRequest(sourceEntry
+ .getName());
+ sourceEntry = nextEntry(sourceIterator);
+ return request;
+ }
+ else if (targetEntry != null)
+ {
+ // Add remaining target records.
+ final AddRequest request = Requests.newAddRequest(targetEntry);
+ targetEntry = nextEntry(targetIterator);
+ return request;
+ }
+ else
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+
+
+ private Entry nextEntry(final Iterator<Entry> i)
+ {
+ return i.hasNext() ? i.next() : null;
+ }
+ };
+
+ }
+
+
+
+ /**
+ * Returns an entry reader over the provided entry collection.
+ *
+ * @param entries
+ * The entry collection.
+ * @return An entry reader over the provided entry collection.
+ */
+ public static EntryReader newEntryCollectionReader(
+ final Collection<Entry> entries)
+ {
+ return new EntryIteratorReader(entries.iterator());
+ }
+
+
+
+ /**
+ * Returns an entry reader over the provided entry iterator.
+ *
+ * @param entries
+ * The entry iterator.
+ * @return An entry reader over the provided entry iterator.
+ */
+ public static EntryReader newEntryIteratorReader(final Iterator<Entry> entries)
+ {
+ return new EntryIteratorReader(entries);
}
/**
* 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.
+ * {@code input} and returns the result in an entry reader. This method
+ * ignores missing entries, and overwrites existing entries. Closing the
+ * returned reader will cause {@code input} and {@code patch} to be closed as
+ * well.
* <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
@@ -138,24 +271,23 @@
* @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.
+ * @return An entry reader containing the patched entries.
* @throws IOException
* If an unexpected IO error occurred.
*/
- public static void patch(final EntryReader input,
- final ChangeRecordReader patch, final EntryWriter output)
- throws IOException
+ public static EntryReader patch(final EntryReader input,
+ final ChangeRecordReader patch) throws IOException
{
- patch(input, patch, output, RejectedChangeListener.OVERWRITE);
+ return patch(input, patch, 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.
+ * {@code input} and returns the result in an entry reader. Closing the
+ * returned reader will cause {@code input} and {@code patch} to be closed as
+ * well.
* <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
@@ -166,16 +298,15 @@
* @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.
+ * @return An entry reader containing the patched entries.
* @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
+ public static EntryReader patch(final EntryReader input,
+ final ChangeRecordReader patch, final RejectedChangeListener listener)
+ throws IOException
{
final SortedMap<DN, Entry> entries = readEntries(input);
@@ -381,17 +512,41 @@
}
}
- for (final Entry entry : entries.values())
+ return new EntryReader()
{
- output.writeEntry(entry);
- }
- }
+ private final Iterator<Entry> iterator = entries.values().iterator();
- private static Entry nextEntry(final Iterator<Entry> i)
- {
- return i.hasNext() ? i.next() : null;
+ @Override
+ public void close() throws IOException
+ {
+ try
+ {
+ input.close();
+ }
+ finally
+ {
+ patch.close();
+ }
+ }
+
+
+
+ @Override
+ public boolean hasNext() throws IOException
+ {
+ return iterator.hasNext();
+ }
+
+
+
+ @Override
+ public Entry readEntry() throws IOException, NoSuchElementException
+ {
+ return iterator.next();
+ }
+ };
}
--
Gitblit v1.10.0