From d9722bcadc7bf619808426fc82cbb0c74b1646b0 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 09 Sep 2010 17:31:59 +0000
Subject: [PATCH] Make EntryReader and ChangeRecordReader APIs easier to use:
---
sdk/src/com/sun/opends/sdk/tools/LDAPModify.java | 9
sdk/src/org/opends/sdk/ldif/ChangeRecordReader.java | 33 +
sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java | 224 +++++++++++---
sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java | 29 +
sdk/src/org/opends/sdk/ldif/EntryReader.java | 31 +
sdk/src/org/opends/sdk/ldif/AbstractLDIFReader.java | 9
sdk/src/org/opends/sdk/ldif/LDIFChangeRecordReader.java | 265 ++++++++++-------
sdk/tests/unit-tests-testng/src/org/opends/sdk/ldif/LDIFEntryReaderTestCase.java | 76 ++++
sdk/src/org/opends/sdk/ldif/LDIFEntryReader.java | 199 ++++++++-----
9 files changed, 589 insertions(+), 286 deletions(-)
diff --git a/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java b/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
index 885a450..c62ad71 100644
--- a/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
+++ b/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package com.sun.opends.sdk.tools;
@@ -762,13 +762,12 @@
reader = new LDIFChangeRecordReader(getInputStream());
}
- ChangeRecord cr;
try
{
- int result;
- while ((cr = reader.readChangeRecord()) != null)
+ while (reader.hasNext())
{
- result = cr.accept(visitor, null);
+ final ChangeRecord cr = reader.readChangeRecord();
+ final int result = cr.accept(visitor, null);
if (result != 0 && !continueOnError.isPresent())
{
return result;
diff --git a/sdk/src/org/opends/sdk/ldif/AbstractLDIFReader.java b/sdk/src/org/opends/sdk/ldif/AbstractLDIFReader.java
index 8ff8698..418b08a 100644
--- a/sdk/src/org/opends/sdk/ldif/AbstractLDIFReader.java
+++ b/sdk/src/org/opends/sdk/ldif/AbstractLDIFReader.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
@@ -141,8 +141,11 @@
*/
public void close() throws IOException
{
- reader.close();
- reader = null;
+ if (reader != null)
+ {
+ reader.close();
+ reader = null;
+ }
}
diff --git a/sdk/src/org/opends/sdk/ldif/ChangeRecordReader.java b/sdk/src/org/opends/sdk/ldif/ChangeRecordReader.java
index 0e73da8..5364319 100644
--- a/sdk/src/org/opends/sdk/ldif/ChangeRecordReader.java
+++ b/sdk/src/org/opends/sdk/ldif/ChangeRecordReader.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
@@ -31,8 +31,7 @@
import java.io.Closeable;
import java.io.IOException;
-
-import org.opends.sdk.DecodeException;
+import java.util.NoSuchElementException;
@@ -46,10 +45,6 @@
* malformed change records and, if it is possible, how they are handled.
* <li>Any synchronization limitations.
* </ul>
- * <p>
- * TODO: LDIFInputStreamReader
- * <p>
- * TODO: SearchResultEntryReader
*/
public interface ChangeRecordReader extends Closeable
{
@@ -62,23 +57,35 @@
* @throws IOException
* If an unexpected IO error occurred while closing.
*/
+ @Override
void close() throws IOException;
/**
+ * Returns {@code true} if this reader contains another change record,
+ * blocking if necessary until either the next change record is available or
+ * the end of the stream is reached.
+ *
+ * @return {@code true} if this reader contains another change record.
+ * @throws IOException
+ * If an unexpected IO error occurred.
+ */
+ boolean hasNext() throws IOException;
+
+
+
+ /**
* Reads the next change record, blocking if necessary until a change record
* is available. If the next change record does not contain a change type then
* it will be treated as an {@code Add} change record.
*
- * @return The next change record, or {@code null} if there are no more change
- * records to be read.
- * @throws DecodeException
- * If the change record could not be decoded because it was
- * malformed.
+ * @return The next change record.
* @throws IOException
* If an unexpected IO error occurred while reading the change
* record.
+ * @throws NoSuchElementException
+ * If this reader does not contain any more change records.
*/
- ChangeRecord readChangeRecord() throws DecodeException, IOException;
+ ChangeRecord readChangeRecord() throws IOException, NoSuchElementException;
}
diff --git a/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java b/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java
index 17a0be2..8e6e353 100644
--- a/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java
+++ b/sdk/src/org/opends/sdk/ldif/ConnectionEntryReader.java
@@ -30,6 +30,7 @@
import java.io.InterruptedIOException;
+import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -60,21 +61,29 @@
*
* <pre>
* Connection connection = ...;
- * ConnectionEntryReader results = connection.search(
- * "dc=example,dc=com",
- * SearchScope.WHOLE_SUBTREE,
- * "(objectClass=person)");
- * SearchResultEntry entry;
+ * ConnectionEntryReader results = connection.search("dc=example,dc=com",
+ * SearchScope.WHOLE_SUBTREE, "(objectClass=person)");
* try
* {
- * while ((entry = results.readEntry()) != null)
+ * while (reader.hasNext())
* {
- * // Process search result entry.
+ * if (!reader.isReference())
+ * {
+ * SearchResultEntry entry = reader.readEntry();
+ *
+ * // Handle entry...
+ * }
+ * else
+ * {
+ * SearchResultReference ref = reader.readReference();
+ *
+ * // Handle continuation reference...
+ * }
* }
* }
- * catch (Exception e)
+ * catch (IOException e)
* {
- * // Handle exceptions
+ * // Handle exceptions...
* }
* finally
* {
@@ -181,6 +190,7 @@
private final BufferHandler buffer;
private final FutureResult<Result> future;
+ private Response nextResponse = null;
@@ -242,65 +252,69 @@
/**
- * Returns the next search result entry contained in the search results,
- * waiting if necessary until one becomes available.
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasNext() throws ErrorResultIOException,
+ InterruptedIOException
+ {
+ // Poll for the next response if needed.
+ final Response r = getNextResponse();
+ if (!(r instanceof Result))
+ {
+ // Entry or reference.
+ return true;
+ }
+
+ // Final result.
+ final Result result = (Result) r;
+ if (result.isSuccess())
+ {
+ return false;
+ }
+
+ final ErrorResultException e = ErrorResultException.wrap(result);
+ throw new ErrorResultIOException(e);
+ }
+
+
+
+ /**
+ * Waits for the next search result entry or reference to become available and
+ * returns {@code true} if it is a reference, or {@code false} if it is an
+ * entry.
*
- * @return The next search result entry, or {@code null} if there are no more
- * entries in the search results.
- * @throws SearchResultReferenceIOException
- * If the next search response was a search result reference. This
- * connection entry reader may still contain remaining search
- * results and references which can be retrieved using additional
- * calls to this method.
+ * @return {@code true} if the next search result is a reference, or
+ * {@code false} if it is an entry.
* @throws ErrorResultIOException
- * If the result code indicates that the search operation failed for
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation failed for
* some reason.
* @throws InterruptedIOException
* If the current thread was interrupted while waiting.
+ * @throws NoSuchElementException
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation succeeded.
*/
- @Override
- public SearchResultEntry readEntry() throws SearchResultReferenceIOException,
- ErrorResultIOException, InterruptedIOException
+ public boolean isReference() throws ErrorResultIOException,
+ InterruptedIOException, NoSuchElementException
{
- Response r;
- try
+ // Throws ErrorResultIOException if search returned error.
+ if (!hasNext())
{
- while ((r = buffer.responses.poll(50, TimeUnit.MILLISECONDS)) == null)
- {
- if (buffer.isInterrupted)
- {
- // The worker thread processing the result was interrupted so no
- // result will ever arrive. We don't want to hang this thread forever
- // while we wait, so terminate now.
- r = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR);
- break;
- }
- }
- }
- catch (final InterruptedException e)
- {
- throw new InterruptedIOException(e.getMessage());
+ // Search has completed successfully.
+ throw new NoSuchElementException();
}
+ // Entry or reference.
+ final Response r = nextResponse;
if (r instanceof SearchResultEntry)
{
- return (SearchResultEntry) r;
+ return false;
}
else if (r instanceof SearchResultReference)
{
- throw new SearchResultReferenceIOException((SearchResultReference) r);
- }
- else if (r instanceof Result)
- {
- final Result result = (Result) r;
- if (result.isSuccess())
- {
- return null;
- }
- else
- {
- throw new ErrorResultIOException(ErrorResultException.wrap(result));
- }
+ return true;
}
else
{
@@ -308,4 +322,108 @@
+ r.getClass().toString());
}
}
+
+
+
+ /**
+ * Waits for the next search result entry or reference to become available
+ * and, if it is an entry, returns it as a {@code SearchResultEntry}. If the
+ * next search response is a reference then this method will throw a
+ * {@code SearchResultReferenceIOException}.
+ *
+ * @return The next search result entry.
+ * @throws SearchResultReferenceIOException
+ * If the next search response was a search result reference. This
+ * connection entry reader may still contain remaining search
+ * results and references which can be retrieved using additional
+ * calls to this method.
+ * @throws ErrorResultIOException
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation failed for
+ * some reason.
+ * @throws InterruptedIOException
+ * If the current thread was interrupted while waiting.
+ * @throws NoSuchElementException
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation succeeded.
+ */
+ @Override
+ public SearchResultEntry readEntry() throws SearchResultReferenceIOException,
+ ErrorResultIOException, InterruptedIOException, NoSuchElementException
+ {
+ if (!isReference())
+ {
+ final SearchResultEntry entry = (SearchResultEntry) nextResponse;
+ nextResponse = null;
+ return entry;
+ }
+ else
+ {
+ final SearchResultReference reference = (SearchResultReference) nextResponse;
+ nextResponse = null;
+ throw new SearchResultReferenceIOException(reference);
+ }
+ }
+
+
+
+ /**
+ * Waits for the next search result entry or reference to become available
+ * and, if it is a reference, returns it as a {@code SearchResultReference}.
+ * If the next search response is an entry then this method will return
+ * {@code null}.
+ *
+ * @return The next search result reference, or {@code null} if the next
+ * response was a search result entry.
+ * @throws ErrorResultIOException
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation failed for
+ * some reason.
+ * @throws InterruptedIOException
+ * If the current thread was interrupted while waiting.
+ * @throws NoSuchElementException
+ * If there are no more search result entries or references and the
+ * search result code indicates that the search operation succeeded.
+ */
+ public SearchResultReference readReference() throws ErrorResultIOException,
+ InterruptedIOException, NoSuchElementException
+ {
+ if (isReference())
+ {
+ final SearchResultReference reference = (SearchResultReference) nextResponse;
+ nextResponse = null;
+ return reference;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+
+ private Response getNextResponse() throws InterruptedIOException
+ {
+ while (nextResponse == null)
+ {
+ try
+ {
+ nextResponse = buffer.responses.poll(50, TimeUnit.MILLISECONDS);
+ }
+ catch (final InterruptedException e)
+ {
+ throw new InterruptedIOException(e.getMessage());
+ }
+
+ if (nextResponse == null && buffer.isInterrupted)
+ {
+ // The worker thread processing the result was interrupted so no
+ // result will ever arrive. We don't want to hang this thread
+ // forever while we wait, so terminate now.
+ nextResponse = Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR);
+ break;
+ }
+ }
+ return nextResponse;
+ }
}
diff --git a/sdk/src/org/opends/sdk/ldif/EntryReader.java b/sdk/src/org/opends/sdk/ldif/EntryReader.java
index a42c293..688b827 100644
--- a/sdk/src/org/opends/sdk/ldif/EntryReader.java
+++ b/sdk/src/org/opends/sdk/ldif/EntryReader.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
@@ -31,8 +31,8 @@
import java.io.Closeable;
import java.io.IOException;
+import java.util.NoSuchElementException;
-import org.opends.sdk.DecodeException;
import org.opends.sdk.Entry;
@@ -46,10 +46,6 @@
* malformed change records and, if it is possible, how they are handled.
* <li>Any synchronization limitations.
* </ul>
- * <p>
- * TODO: LDIFInputStreamReader
- * <p>
- * TODO: SearchResultEntryReader
*/
public interface EntryReader extends Closeable
{
@@ -62,19 +58,32 @@
* @throws IOException
* If an unexpected IO error occurred while closing.
*/
+ @Override
void close() throws IOException;
/**
+ * Returns {@code true} if this reader contains another entry, blocking if
+ * necessary until either the next entry is available or the end of the stream
+ * is reached.
+ *
+ * @return {@code true} if this reader contains another entry.
+ * @throws IOException
+ * If an unexpected IO error occurred.
+ */
+ boolean hasNext() throws IOException;
+
+
+
+ /**
* Reads the next entry, blocking if necessary until an entry is available.
*
- * @return The next entry or {@code null} if there are no more entries to be
- * read.
- * @throws DecodeException
- * If the entry could not be decoded because it was malformed.
+ * @return The next entry.
* @throws IOException
* If an unexpected IO error occurred while reading the entry.
+ * @throws NoSuchElementException
+ * If this reader does not contain any more entries.
*/
- Entry readEntry() throws DecodeException, IOException;
+ Entry readEntry() throws IOException, NoSuchElementException;
}
diff --git a/sdk/src/org/opends/sdk/ldif/LDIFChangeRecordReader.java b/sdk/src/org/opends/sdk/ldif/LDIFChangeRecordReader.java
index 0825276..3a8fec6 100644
--- a/sdk/src/org/opends/sdk/ldif/LDIFChangeRecordReader.java
+++ b/sdk/src/org/opends/sdk/ldif/LDIFChangeRecordReader.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
@@ -37,6 +37,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
import org.opends.sdk.*;
import org.opends.sdk.requests.ModifyDNRequest;
@@ -79,9 +80,7 @@
final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldifLines);
try
{
- final ChangeRecord record = reader.readChangeRecord();
-
- if (record == null)
+ if (!reader.hasNext())
{
// No change record found.
final LocalizableMessage message = WARN_READ_LDIF_RECORD_NO_CHANGE_RECORD_FOUND
@@ -89,7 +88,9 @@
throw new LocalizedIllegalArgumentException(message);
}
- if (reader.readChangeRecord() != null)
+ final ChangeRecord record = reader.readChangeRecord();
+
+ if (reader.hasNext())
{
// Multiple change records found.
final LocalizableMessage message = WARN_READ_LDIF_RECORD_MULTIPLE_CHANGE_RECORDS_FOUND
@@ -115,6 +116,13 @@
+ private ChangeRecord nextChangeRecord = null;
+
+ // Poison used to indicate end of LDIF.
+ private static final ChangeRecord EOF = Requests.newAddRequest(DN.rootDN());
+
+
+
/**
* Creates a new LDIF change record reader whose source is the provided input
* stream.
@@ -169,6 +177,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void close() throws IOException
{
close0();
@@ -178,110 +187,37 @@
/**
* {@inheritDoc}
+ *
+ * @throws DecodeException
+ * If the change record could not be decoded because it was
+ * malformed.
*/
+ @Override
+ public boolean hasNext() throws DecodeException, IOException
+ {
+ return getNextChangeRecord() != EOF;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DecodeException
+ * If the entry could not be decoded because it was malformed.
+ */
+ @Override
public ChangeRecord readChangeRecord() throws DecodeException, IOException
{
- // Continue until an unfiltered entry is obtained.
- while (true)
+ if (!hasNext())
{
- LDIFRecord record = null;
-
- // Read the set of lines that make up the next entry.
- record = readLDIFRecord();
- if (record == null)
- {
- return null;
- }
-
- // Read the DN of the entry and see if it is one that should be
- // included in the import.
- DN entryDN;
- try
- {
- entryDN = readLDIFRecordDN(record);
- if (entryDN == null)
- {
- // Skip version record.
- continue;
- }
- }
- catch (final DecodeException e)
- {
- rejectLDIFRecord(record, e.getMessageObject());
- continue;
- }
-
- // Skip if branch containing the entry DN is excluded.
- if (isBranchExcluded(entryDN))
- {
- final LocalizableMessage message = LocalizableMessage
- .raw("Skipping entry because it is in excluded branch");
- skipLDIFRecord(record, message);
- continue;
- }
-
- ChangeRecord changeRecord = null;
- try
- {
- if (!record.iterator.hasNext())
- {
- // FIXME: improve error.
- final LocalizableMessage message = LocalizableMessage
- .raw("Missing changetype");
- throw DecodeException.error(message);
- }
-
- final KeyValuePair pair = new KeyValuePair();
- final String ldifLine = readLDIFRecordKeyValuePair(record, pair, false);
-
- if (!toLowerCase(pair.key).equals("changetype"))
- {
- // Default to add change record.
- changeRecord = parseAddChangeRecordEntry(entryDN, ldifLine, record);
- }
- else
- {
- final String changeType = toLowerCase(pair.value);
- if (changeType.equals("add"))
- {
- changeRecord = parseAddChangeRecordEntry(entryDN, null, record);
- }
- else if (changeType.equals("delete"))
- {
- changeRecord = parseDeleteChangeRecordEntry(entryDN, record);
- }
- else if (changeType.equals("modify"))
- {
- changeRecord = parseModifyChangeRecordEntry(entryDN, record);
- }
- else if (changeType.equals("modrdn"))
- {
- changeRecord = parseModifyDNChangeRecordEntry(entryDN, record);
- }
- else if (changeType.equals("moddn"))
- {
- changeRecord = parseModifyDNChangeRecordEntry(entryDN, record);
- }
- else
- {
- // FIXME: improve error.
- final LocalizableMessage message = ERR_LDIF_INVALID_CHANGETYPE_ATTRIBUTE
- .get(pair.value, "add, delete, modify, moddn, modrdn");
- throw DecodeException.error(message);
- }
- }
- }
- catch (final DecodeException e)
- {
- rejectLDIFRecord(record, e.getMessageObject());
- continue;
- }
-
- if (changeRecord != null)
- {
- return changeRecord;
- }
+ // LDIF reader has completed successfully.
+ throw new NoSuchElementException();
}
+
+ final ChangeRecord changeRecord = nextChangeRecord;
+ nextChangeRecord = null;
+ return changeRecord;
}
@@ -309,8 +245,8 @@
* change records that are read from LDIF. The default is {@code false}.
*
* @param excludeUserAttributes
- * {@code true} if all user attributes should be excluded, or {@code
- * false} otherwise.
+ * {@code true} if all user attributes should be excluded, or
+ * {@code false} otherwise.
* @return A reference to this {@code LDIFChangeRecordReader}.
*/
public LDIFChangeRecordReader setExcludeAllUserAttributes(
@@ -418,8 +354,8 @@
* records that are read from LDIF. The default is {@code true} .
*
* @param validateSchema
- * {@code true} if schema validation should be performed, or {@code
- * false} otherwise.
+ * {@code true} if schema validation should be performed, or
+ * {@code false} otherwise.
* @return A reference to this {@code LDIFChangeRecordReader}.
*/
public LDIFChangeRecordReader setValidateSchema(final boolean validateSchema)
@@ -430,6 +366,115 @@
+ private ChangeRecord getNextChangeRecord() throws DecodeException,
+ IOException
+ {
+ while (nextChangeRecord == null)
+ {
+ LDIFRecord record = null;
+
+ // Read the set of lines that make up the next entry.
+ record = readLDIFRecord();
+ if (record == null)
+ {
+ nextChangeRecord = EOF;
+ break;
+ }
+
+ // Read the DN of the entry and see if it is one that should be
+ // included in the import.
+ DN entryDN;
+ try
+ {
+ entryDN = readLDIFRecordDN(record);
+ if (entryDN == null)
+ {
+ // Skip version record.
+ continue;
+ }
+ }
+ catch (final DecodeException e)
+ {
+ rejectLDIFRecord(record, e.getMessageObject());
+ continue;
+ }
+
+ // Skip if branch containing the entry DN is excluded.
+ if (isBranchExcluded(entryDN))
+ {
+ final LocalizableMessage message = LocalizableMessage
+ .raw("Skipping entry because it is in excluded branch");
+ skipLDIFRecord(record, message);
+ continue;
+ }
+
+ ChangeRecord changeRecord = null;
+ try
+ {
+ if (!record.iterator.hasNext())
+ {
+ // FIXME: improve error.
+ final LocalizableMessage message = LocalizableMessage
+ .raw("Missing changetype");
+ throw DecodeException.error(message);
+ }
+
+ final KeyValuePair pair = new KeyValuePair();
+ final String ldifLine = readLDIFRecordKeyValuePair(record, pair, false);
+
+ if (!toLowerCase(pair.key).equals("changetype"))
+ {
+ // Default to add change record.
+ changeRecord = parseAddChangeRecordEntry(entryDN, ldifLine, record);
+ }
+ else
+ {
+ final String changeType = toLowerCase(pair.value);
+ if (changeType.equals("add"))
+ {
+ changeRecord = parseAddChangeRecordEntry(entryDN, null, record);
+ }
+ else if (changeType.equals("delete"))
+ {
+ changeRecord = parseDeleteChangeRecordEntry(entryDN, record);
+ }
+ else if (changeType.equals("modify"))
+ {
+ changeRecord = parseModifyChangeRecordEntry(entryDN, record);
+ }
+ else if (changeType.equals("modrdn"))
+ {
+ changeRecord = parseModifyDNChangeRecordEntry(entryDN, record);
+ }
+ else if (changeType.equals("moddn"))
+ {
+ changeRecord = parseModifyDNChangeRecordEntry(entryDN, record);
+ }
+ else
+ {
+ // FIXME: improve error.
+ final LocalizableMessage message = ERR_LDIF_INVALID_CHANGETYPE_ATTRIBUTE
+ .get(pair.value, "add, delete, modify, moddn, modrdn");
+ throw DecodeException.error(message);
+ }
+ }
+ }
+ catch (final DecodeException e)
+ {
+ rejectLDIFRecord(record, e.getMessageObject());
+ continue;
+ }
+
+ if (changeRecord != null)
+ {
+ nextChangeRecord = changeRecord;
+ }
+ }
+ return nextChangeRecord;
+ }
+
+
+
private ChangeRecord parseAddChangeRecordEntry(final DN entryDN,
final String lastLDIFLine, final LDIFRecord record)
throws DecodeException
@@ -581,8 +626,8 @@
{
// TODO: include line number.
final LocalizableMessage message = ERR_LDIF_INVALID_CHANGERECORD_ATTRIBUTE
- .get(attributeDescription2.toString(), attributeDescription
- .toString());
+ .get(attributeDescription2.toString(),
+ attributeDescription.toString());
throw DecodeException.error(message);
}
diff --git a/sdk/src/org/opends/sdk/ldif/LDIFEntryReader.java b/sdk/src/org/opends/sdk/ldif/LDIFEntryReader.java
index 4eb257c..b51cd73 100644
--- a/sdk/src/org/opends/sdk/ldif/LDIFEntryReader.java
+++ b/sdk/src/org/opends/sdk/ldif/LDIFEntryReader.java
@@ -22,24 +22,20 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
-import static com.sun.opends.sdk.messages.Messages.
- WARN_READ_LDIF_RECORD_MULTIPLE_CHANGE_RECORDS_FOUND;
-import static com.sun.opends.sdk.messages.Messages.
- WARN_READ_LDIF_RECORD_NO_CHANGE_RECORD_FOUND;
-import static com.sun.opends.sdk.messages.Messages.
- WARN_READ_LDIF_RECORD_UNEXPECTED_IO_ERROR;
+import static com.sun.opends.sdk.messages.Messages.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
import org.opends.sdk.*;
import org.opends.sdk.schema.Schema;
@@ -58,6 +54,11 @@
public final class LDIFEntryReader extends AbstractLDIFReader implements
EntryReader
{
+ // Poison used to indicate end of LDIF.
+ private static final Entry EOF = new LinkedHashMapEntry();
+
+
+
/**
* Parses the provided array of LDIF lines as a single LDIF entry.
*
@@ -77,9 +78,7 @@
final LDIFEntryReader reader = new LDIFEntryReader(ldifLines);
try
{
- final Entry entry = reader.readEntry();
-
- if (entry == null)
+ if (!reader.hasNext())
{
// No change record found.
final LocalizableMessage message = WARN_READ_LDIF_RECORD_NO_CHANGE_RECORD_FOUND
@@ -87,7 +86,9 @@
throw new LocalizedIllegalArgumentException(message);
}
- if (reader.readEntry() != null)
+ final Entry entry = reader.readEntry();
+
+ if (reader.hasNext())
{
// Multiple change records found.
final LocalizableMessage message = WARN_READ_LDIF_RECORD_MULTIPLE_CHANGE_RECORDS_FOUND
@@ -113,6 +114,10 @@
+ private Entry nextEntry = null;
+
+
+
/**
* Creates a new LDIF entry reader whose source is the provided input stream.
*
@@ -164,6 +169,7 @@
/**
* {@inheritDoc}
*/
+ @Override
public void close() throws IOException
{
close0();
@@ -173,75 +179,36 @@
/**
* {@inheritDoc}
+ *
+ * @throws DecodeException
+ * If the entry could not be decoded because it was malformed.
*/
+ @Override
+ public boolean hasNext() throws DecodeException, IOException
+ {
+ return getNextEntry() != EOF;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DecodeException
+ * If the entry could not be decoded because it was malformed.
+ */
+ @Override
public Entry readEntry() throws DecodeException, IOException
{
- // Continue until an unfiltered entry is obtained.
- while (true)
+ if (!hasNext())
{
- LDIFRecord record = null;
-
- // Read the set of lines that make up the next entry.
- record = readLDIFRecord();
- if (record == null)
- {
- return null;
- }
-
- // Read the DN of the entry and see if it is one that should be
- // included in the import.
- DN entryDN;
- try
- {
- entryDN = readLDIFRecordDN(record);
- if (entryDN == null)
- {
- // Skip version record.
- continue;
- }
- }
- catch (final DecodeException e)
- {
- rejectLDIFRecord(record, e.getMessageObject());
- continue;
- }
-
- // Skip if branch containing the entry DN is excluded.
- if (isBranchExcluded(entryDN))
- {
- final LocalizableMessage message = LocalizableMessage
- .raw("Skipping entry because it is in excluded branch");
- skipLDIFRecord(record, message);
- continue;
- }
-
- // Use an Entry for the AttributeSequence.
- final Entry entry = new LinkedHashMapEntry(entryDN);
- try
- {
- while (record.iterator.hasNext())
- {
- final String ldifLine = record.iterator.next();
- readLDIFRecordAttributeValue(record, ldifLine, entry);
- }
- }
- catch (final DecodeException e)
- {
- rejectLDIFRecord(record, e.getMessageObject());
- continue;
- }
-
- // Skip if the entry is excluded by any filters.
- if (isEntryExcluded(entry))
- {
- final LocalizableMessage message = LocalizableMessage
- .raw("Skipping entry due to exclusing filters");
- skipLDIFRecord(record, message);
- continue;
- }
-
- return entry;
+ // LDIF reader has completed successfully.
+ throw new NoSuchElementException();
}
+
+ final Entry entry = nextEntry;
+ nextEntry = null;
+ return entry;
}
@@ -269,8 +236,8 @@
* entries that are read from LDIF. The default is {@code false}.
*
* @param excludeUserAttributes
- * {@code true} if all user attributes should be excluded, or {@code
- * false} otherwise.
+ * {@code true} if all user attributes should be excluded, or
+ * {@code false} otherwise.
* @return A reference to this {@code LDIFEntryReader}.
*/
public LDIFEntryReader setExcludeAllUserAttributes(
@@ -414,8 +381,8 @@
* that are read from LDIF. The default is {@code true}.
*
* @param validateSchema
- * {@code true} if schema validation should be performed, or {@code
- * false} otherwise.
+ * {@code true} if schema validation should be performed, or
+ * {@code false} otherwise.
* @return A reference to this {@code LDIFEntryReader}.
*/
public LDIFEntryReader setValidateSchema(final boolean validateSchema)
@@ -424,4 +391,78 @@
return this;
}
+
+
+ private Entry getNextEntry() throws DecodeException, IOException
+ {
+ while (nextEntry == null)
+ {
+ LDIFRecord record = null;
+
+ // Read the set of lines that make up the next entry.
+ record = readLDIFRecord();
+ if (record == null)
+ {
+ nextEntry = EOF;
+ break;
+ }
+
+ // Read the DN of the entry and see if it is one that should be
+ // included in the import.
+ DN entryDN;
+ try
+ {
+ entryDN = readLDIFRecordDN(record);
+ if (entryDN == null)
+ {
+ // Skip version record.
+ continue;
+ }
+ }
+ catch (final DecodeException e)
+ {
+ rejectLDIFRecord(record, e.getMessageObject());
+ continue;
+ }
+
+ // Skip if branch containing the entry DN is excluded.
+ if (isBranchExcluded(entryDN))
+ {
+ final LocalizableMessage message = LocalizableMessage
+ .raw("Skipping entry because it is in excluded branch");
+ skipLDIFRecord(record, message);
+ continue;
+ }
+
+ // Use an Entry for the AttributeSequence.
+ final Entry entry = new LinkedHashMapEntry(entryDN);
+ try
+ {
+ while (record.iterator.hasNext())
+ {
+ final String ldifLine = record.iterator.next();
+ readLDIFRecordAttributeValue(record, ldifLine, entry);
+ }
+ }
+ catch (final DecodeException e)
+ {
+ rejectLDIFRecord(record, e.getMessageObject());
+ continue;
+ }
+
+ // Skip if the entry is excluded by any filters.
+ if (isEntryExcluded(entry))
+ {
+ final LocalizableMessage message = LocalizableMessage
+ .raw("Skipping entry due to exclusing filters");
+ skipLDIFRecord(record, message);
+ continue;
+ }
+
+ nextEntry = entry;
+ }
+
+ return nextEntry;
+ }
+
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
index 8e2e26f..2a6bba7 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
+++ b/sdk/tests/unit-tests-testng/src/org/opends/sdk/SynchronousConnectionTestCase.java
@@ -29,17 +29,18 @@
-import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
-import java.util.List;
+import java.util.NoSuchElementException;
-import org.opends.sdk.ldif.EntryReader;
+import org.opends.sdk.ldif.ConnectionEntryReader;
import org.opends.sdk.requests.Requests;
import org.opends.sdk.responses.BindResult;
import org.opends.sdk.responses.CompareResult;
import org.opends.sdk.responses.Result;
+import org.opends.sdk.responses.SearchResultEntry;
+import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -155,11 +156,27 @@
public void testSearchRequest() throws Exception
{
final SynchronousConnection con = new SynchronousConnection(asyncCon);
- final EntryReader reader = con.search(
+ final ConnectionEntryReader reader = con.search(
"uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
"objectclass=*", "cn");
- reader.readEntry();
- assertNull(reader.readEntry());
+ Assert.assertTrue(reader.hasNext());
+ Assert.assertFalse(reader.isReference());
+ Assert.assertTrue(reader.hasNext());
+ SearchResultEntry entry = reader.readEntry();
+ Assert.assertEquals(entry.getName(),
+ DN.valueOf("uid=user.0,ou=people,o=test"));
+ Assert.assertFalse(reader.hasNext());
+ try
+ {
+ reader.readEntry();
+ Assert
+ .fail("reader.readEntry() should have thrown NoSuchElementException");
+ }
+ catch (NoSuchElementException e)
+ {
+ // This is expected.
+ }
+ Assert.assertFalse(reader.hasNext());
}
// TODO: add more tests.
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/ldif/LDIFEntryReaderTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/ldif/LDIFEntryReaderTestCase.java
index 78c5b94..e4e345a 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/ldif/LDIFEntryReaderTestCase.java
+++ b/sdk/tests/unit-tests-testng/src/org/opends/sdk/ldif/LDIFEntryReaderTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.ldif;
@@ -32,9 +32,12 @@
import static org.testng.Assert.assertNotNull;
import java.io.FileInputStream;
+import java.util.NoSuchElementException;
-import org.opends.sdk.AbstractEntry;
+import org.opends.sdk.DN;
+import org.opends.sdk.Entry;
import org.opends.sdk.TestCaseUtils;
+import org.testng.Assert;
import org.testng.annotations.Test;
@@ -52,6 +55,45 @@
* If the test failed unexpectedly.
*/
@Test()
+ public void testEmpty() throws Exception
+ {
+ final String path = TestCaseUtils.createTempFile("");
+ final FileInputStream in = new FileInputStream(path);
+ final LDIFEntryReader reader = new LDIFEntryReader(in);
+ try
+ {
+ reader.setValidateSchema(false);
+
+ Assert.assertFalse(reader.hasNext());
+ Assert.assertFalse(reader.hasNext());
+ try
+ {
+ reader.readEntry();
+ Assert
+ .fail("reader.readEntry() should have thrown NoSuchElementException");
+ }
+ catch (NoSuchElementException e)
+ {
+ // This is expected.
+ }
+ Assert.assertFalse(reader.hasNext());
+ }
+ finally
+ {
+ reader.close();
+ }
+ }
+
+
+
+ /**
+ * Tests readEntry method of LDIFEntryReader class.See
+ * https://opends.dev.java.net/issues/show_bug.cgi?id=4545 for more details.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test()
public void testReadEntry() throws Exception
{
final String path = TestCaseUtils
@@ -80,9 +122,31 @@
"postalAddress: Aaccf Amar$01251 Chestnut Street$Panama City, DE 50369",
"description: This is the description for Aaccf Amar.");
final FileInputStream in = new FileInputStream(path);
- final LDIFEntryReader entryReader = new LDIFEntryReader(in);
- entryReader.setValidateSchema(false);
- final AbstractEntry entry = (AbstractEntry) entryReader.readEntry();
- assertNotNull(entry);
+ final LDIFEntryReader reader = new LDIFEntryReader(in);
+ try
+ {
+ reader.setValidateSchema(false);
+
+ Assert.assertTrue(reader.hasNext());
+ final Entry entry = reader.readEntry();
+ assertNotNull(entry);
+ Assert.assertEquals(entry.getName(),
+ DN.valueOf("uid=1,ou=people,dc=ucsf,dc=edu"));
+ Assert.assertFalse(reader.hasNext());
+ try
+ {
+ reader.readEntry();
+ Assert
+ .fail("reader.readEntry() should have thrown NoSuchElementException");
+ }
+ catch (NoSuchElementException e)
+ {
+ // This is expected.
+ }
+ }
+ finally
+ {
+ reader.close();
+ }
}
}
--
Gitblit v1.10.0