From 0b1a22ba7ad91e04cef8999ac98adcd7cde7d5a1 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 02 Aug 2013 22:45:31 +0000
Subject: [PATCH] Fix OPENDJ-1110: Minor improvements to the SDK MemoryBackend to make it more usable in unit tests.

---
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java |  165 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 145 insertions(+), 20 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
index f0ce342..f2a89ef 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
@@ -33,6 +33,7 @@
 import static org.forgerock.opendj.ldap.responses.Responses.newSearchResultEntry;
 
 import java.io.IOException;
+import java.util.Collection;
 import java.util.NavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 
@@ -81,7 +82,7 @@
  * <li>indexing
  * </ul>
  * This class can be used in conjunction with the factories defined in
- * {@link Connections} to create simpler servers as well as mock LDAP
+ * {@link Connections} to create simple servers as well as mock LDAP
  * connections. For example, to create a mock LDAP connection factory:
  *
  * <pre>
@@ -89,12 +90,20 @@
  * Connection connection = newInternalConnectionFactory(newServerConnectionFactory(backend), null)
  *         .getConnection();
  * </pre>
+ *
+ * To create a simple LDAP server listening on 0.0.0.0:1389:
+ *
+ * <pre>
+ * MemoryBackend backend = new MemoryBackend();
+ * LDAPListener listener = new LDAPListener(1389, Connections
+ *         .&lt;LDAPClientContext&gt; newServerConnectionFactory(backend));
+ * </pre>
  */
 public final class MemoryBackend implements RequestHandler<RequestContext> {
     private final DecodeOptions decodeOptions;
     private final ConcurrentSkipListMap<DN, Entry> entries = new ConcurrentSkipListMap<DN, Entry>();
-    private final Object writeLock = new Object();
     private final Schema schema;
+    private final Object writeLock = new Object();
 
     /**
      * Creates a new empty memory backend which will use the default schema.
@@ -110,7 +119,8 @@
      * @param reader
      *            The entry reader.
      * @throws IOException
-     *             If an unexpected IO error occurred while reading the entries.
+     *             If an unexpected IO error occurred while reading the entries,
+     *             or if duplicate entries are detected.
      */
     public MemoryBackend(final EntryReader reader) throws IOException {
         this(Schema.getDefaultSchema(), reader);
@@ -136,28 +146,86 @@
      * @param reader
      *            The entry reader.
      * @throws IOException
-     *             If an unexpected IO error occurred while reading the entries.
+     *             If an unexpected IO error occurred while reading the entries,
+     *             or if duplicate entries are detected.
      */
     public MemoryBackend(final Schema schema, final EntryReader reader) throws IOException {
         this.schema = schema;
         this.decodeOptions = new DecodeOptions().setSchema(schema);
-        if (reader != null) {
-            try {
-                while (reader.hasNext()) {
-                    final Entry entry = reader.readEntry();
-                    final DN dn = entry.getName();
-                    if (entries.containsKey(dn)) {
-                        throw new ErrorResultIOException(newErrorResult(
-                                ResultCode.ENTRY_ALREADY_EXISTS, "Attempted to add the entry '"
-                                        + dn.toString() + "' multiple times"));
-                    } else {
-                        entries.put(dn, entry);
-                    }
-                }
-            } finally {
-                reader.close();
-            }
+        load(reader, false);
+    }
+
+    /**
+     * Clears the contents of this memory backend so that it does not contain
+     * any entries.
+     *
+     * @return This memory backend.
+     */
+    public MemoryBackend clear() {
+        synchronized (writeLock) {
+            entries.clear();
         }
+        return this;
+    }
+
+    /**
+     * Returns {@code true} if the named entry exists in this memory backend.
+     *
+     * @param dn
+     *            The name of the entry.
+     * @return {@code true} if the named entry exists in this memory backend.
+     */
+    public boolean contains(final DN dn) {
+        return get(dn) != null;
+    }
+
+    /**
+     * Returns {@code true} if the named entry exists in this memory backend.
+     *
+     * @param dn
+     *            The name of the entry.
+     * @return {@code true} if the named entry exists in this memory backend.
+     */
+    public boolean contains(final String dn) {
+        return get(dn) != null;
+    }
+
+    /**
+     * Returns the named entry contained in this memory backend, or {@code null}
+     * if it does not exist.
+     *
+     * @param dn
+     *            The name of the entry to be returned.
+     * @return The named entry.
+     */
+    public Entry get(final DN dn) {
+        return entries.get(dn);
+    }
+
+    /**
+     * Returns the named entry contained in this memory backend, or {@code null}
+     * if it does not exist.
+     *
+     * @param dn
+     *            The name of the entry to be returned.
+     * @return The named entry.
+     */
+    public Entry get(final String dn) {
+        return get(DN.valueOf(dn, schema));
+    }
+
+    /**
+     * Returns a collection containing all of the entries in this memory
+     * backend. The returned collection is backed by this memory backend, so
+     * changes to the collection are reflected in this memory backend and
+     * vice-versa. The returned collection supports entry removal, iteration,
+     * and is thread safe, but it does not support addition of new entries.
+     *
+     * @return A collection containing all of the entries in this memory
+     *         backend.
+     */
+    public Collection<Entry> getAll() {
+        return entries.values();
     }
 
     @Override
@@ -365,6 +433,63 @@
         }
     }
 
+    /**
+     * Returns {@code true} if this memory backend does not contain any entries.
+     *
+     * @return {@code true} if this memory backend does not contain any entries.
+     */
+    public boolean isEmpty() {
+        return entries.isEmpty();
+    }
+
+    /**
+     * Reads all of the entries from the provided entry reader and adds them to
+     * the content of this memory backend.
+     *
+     * @param reader
+     *            The entry reader.
+     * @param overwrite
+     *            {@code true} if existing entries should be replaced, or
+     *            {@code false} if an error should be returned when duplicate
+     *            entries are encountered.
+     * @return This memory backend.
+     * @throws IOException
+     *             If an unexpected IO error occurred while reading the entries,
+     *             or if duplicate entries are detected and {@code overwrite} is
+     *             {@code false}.
+     */
+    public MemoryBackend load(final EntryReader reader, final boolean overwrite) throws IOException {
+        synchronized (writeLock) {
+            if (reader != null) {
+                try {
+                    while (reader.hasNext()) {
+                        final Entry entry = reader.readEntry();
+                        final DN dn = entry.getName();
+                        if (!overwrite && entries.containsKey(dn)) {
+                            throw new ErrorResultIOException(newErrorResult(
+                                    ResultCode.ENTRY_ALREADY_EXISTS, "Attempted to add the entry '"
+                                            + dn.toString() + "' multiple times"));
+                        } else {
+                            entries.put(dn, entry);
+                        }
+                    }
+                } finally {
+                    reader.close();
+                }
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Returns the number of entries contained in this memory backend.
+     *
+     * @return The number of entries contained in this memory backend.
+     */
+    public int size() {
+        return entries.size();
+    }
+
     private <R extends Result> R addResultControls(final Request request, final Entry before,
             final Entry after, final R result) throws ErrorResultException {
         try {

--
Gitblit v1.10.0