mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
15.28.2006 450ead635bca4c269cba0f9a69c3bab7426e1915
Add a new memory-based backend that may be used for temporary storage of
volatile data. It has also been integrated with the testing framework so that
it is available for use in test cases.

OpenDS Issue Number: 642
1 files added
2 files modified
1008 ■■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java 780 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java 166 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java 62 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java
New file
@@ -0,0 +1,780 @@
/*
 * 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/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 */
package org.opends.server.backends;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import org.opends.server.api.Backend;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.Control;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchScope;
import org.opends.server.types.SearchFilter;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.LDIFWriter;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
/**
 * This class defines a very simple backend that stores its information in
 * memory.  This is primarily intended for testing purposes with small data
 * sets, as it does not have any indexing mechanism such as would be required to
 * achieve high performance with large data sets.  It is also heavily
 * synchronized for simplicity at the expense of performance, rather than
 * providing a more fine-grained locking mechanism.
 * <BR><BR>
 * Entries stored in this backend are held in a
 * <CODE>LinkedHashMap&lt;DN,Entry&gt;</CODE> object, which ensures that the
 * order in which you iterate over the entries is the same as the order in which
 * they were inserted.  By combining this with the constraint that no entry can
 * be added before its parent, you can ensure that iterating through the entries
 * will always process the parent entries before their children, which is
 * important for both search result processing and LDIF exports.
 * <BR><BR>
 * As mentioned above, no data indexing is performed, so all non-baseObject
 * searches require iteration through the entire data set.  If this is to become
 * a more general-purpose backend, then additional
 * <CODE>HashMap&lt;ByteString,Set&lt;DN&gt;&gt;</CODE> objects could be used
 * to provide that capability.
 * <BR><BR>
 * There is actually one index that does get maintained within this backend,
 * which is a mapping between the DN of an entry and the DNs of any immediate
 * children of that entry.  This is needed to efficiently determine whether an
 * entry has any children (which must not be the case for delete operations).
 * Modify DN operations are not currently allowed, but if such support is added
 * in the future, then this mapping would play an integral role in that process
 * as well.
 */
public class MemoryBackend
       extends Backend
{
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.backends.MemoryBackend";
  // The base DNs for this backend.
  private DN[] baseDNs;
  // The mapping between parent DNs and their immediate children.
  private HashMap<DN,HashSet<DN>> childDNs;
  // The base DNs for this backend, in a hash set.
  private HashSet<DN> baseDNSet;
  // The set of supported controls for this backend.
  private HashSet<String> supportedControls;
  // The set of supported features for this backend.
  private HashSet<String> supportedFeatures;
  // The mapping between entry DNs and the corresponding entries.
  private LinkedHashMap<DN,Entry> entryMap;
  /**
   * Creates a new backend with the provided information.  All backend
   * implementations must implement a default constructor that use
   * <CODE>super()</CODE> to invoke this constructor.
   */
  public MemoryBackend()
  {
    super();
    assert debugConstructor(CLASS_NAME);
    // Perform all initialization in initializeBackend.
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void initializeBackend(ConfigEntry configEntry,
                                             DN[] baseDNs)
         throws ConfigException
  {
    assert debugEnter(CLASS_NAME, "initializeBackend",
                      String.valueOf(configEntry), String.valueOf(baseDNs));
    // We won't support anything other than exactly one base DN in this
    // implementation.  If we were to add such support in the future, we would
    // likely want to separate the data for each base DN into a separate entry
    // map.
    if ((baseDNs == null) || (baseDNs.length != 1))
    {
      int    msgID   = MSGID_MEMORYBACKEND_REQUIRE_EXACTLY_ONE_BASE;
      String message = getMessage(msgID);
      throw new ConfigException(msgID, message);
    }
    this.baseDNs = baseDNs;
    baseDNSet = new HashSet<DN>();
    for (DN dn : baseDNs)
    {
      baseDNSet.add(dn);
    }
    entryMap = new LinkedHashMap<DN,Entry>();
    childDNs = new HashMap<DN,HashSet<DN>>();
    supportedControls = new HashSet<String>();
    supportedFeatures = new HashSet<String>();
    for (DN dn : baseDNs)
    {
      DirectoryServer.registerSuffix(dn, this);
    }
  }
  /**
   * Removes any data that may have been stored in this backend.
   */
  public synchronized void clearMemoryBackend()
  {
    entryMap.clear();
    childDNs.clear();
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void finalizeBackend()
  {
    assert debugEnter(CLASS_NAME, "finalizeBackend");
    clearMemoryBackend();
  }
  /**
   * {@inheritDoc}
   */
  public DN[] getBaseDNs()
  {
    assert debugEnter(CLASS_NAME, "getBaseDNs");
    return baseDNs;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isLocal()
  {
    assert debugEnter(CLASS_NAME, "isLocal");
    // For the purposes of this method, this is a local backend.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized Entry getEntry(DN entryDN)
  {
    assert debugEnter(CLASS_NAME, "getEntry", String.valueOf(entryDN));
    return entryMap.get(entryDN);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized boolean entryExists(DN entryDN)
  {
    assert debugEnter(CLASS_NAME, "entryExists", String.valueOf(entryDN));
    return entryMap.containsKey(entryDN);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void addEntry(Entry entry, AddOperation addOperation)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addEntry", String.valueOf(entry),
                      String.valueOf(addOperation));
    // See if the target entry already exists.  If so, then fail.
    DN entryDN = entry.getDN();
    if (entryMap.containsKey(entryDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS;
      String message = getMessage(msgID, String.valueOf(entryDN));
      throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message,
                                   msgID);
    }
    // If the entry is one of the base DNs, then add it.
    if (baseDNSet.contains(entryDN))
    {
      entryMap.put(entryDN, entry);
      return;
    }
    // Get the parent DN and ensure that it exists in the backend.
    DN parentDN = entryDN.getParent();
    if (parentDN == null)
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_BELONG;
      String message = getMessage(msgID, String.valueOf(entryDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
    else if (! entryMap.containsKey(parentDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_PARENT_DOESNT_EXIST;
      String message = getMessage(msgID, String.valueOf(entryDN),
                                  String.valueOf(parentDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
    entryMap.put(entryDN, entry);
    HashSet<DN> children = childDNs.get(parentDN);
    if (children == null)
    {
      children = new HashSet<DN>();
      childDNs.put(parentDN, children);
    }
    children.add(entryDN);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void deleteEntry(DN entryDN,
                                       DeleteOperation deleteOperation)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "deleteEntry", String.valueOf(entryDN),
                      String.valueOf(deleteOperation));
    // Make sure the entry exists.  If not, then throw an exception.
    if (! entryMap.containsKey(entryDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST;
      String message = getMessage(msgID, String.valueOf(entryDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
    // Make sure the entry doesn't have any children.  If it does, then throw an
    // exception.
    HashSet<DN> children = childDNs.get(entryDN);
    if ((children != null) && (! children.isEmpty()))
    {
      int    msgID   = MSGID_MEMORYBACKEND_CANNOT_DELETE_ENTRY_WITH_CHILDREN;
      String message = getMessage(msgID, String.valueOf(entryDN));
      throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_NONLEAF, message,
                                   msgID);
    }
    // Remove the entry from the backend.  Also remove the reference to it from
    // its parent, if applicable.
    childDNs.remove(entryDN);
    entryMap.remove(entryDN);
    DN parentDN = entryDN.getParent();
    if (parentDN != null)
    {
      HashSet<DN> parentsChildren = childDNs.get(parentDN);
      if (parentsChildren != null)
      {
        parentsChildren.remove(entryDN);
        if (parentsChildren.isEmpty())
        {
          childDNs.remove(parentDN);
        }
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void replaceEntry(Entry entry,
                                        ModifyOperation modifyOperation)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "replaceEntry", String.valueOf(entry),
                      String.valueOf(modifyOperation));
    // Make sure the entry exists.  If not, then throw an exception.
    DN entryDN = entry.getDN();
    if (! entryMap.containsKey(entryDN))
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST;
      String message = getMessage(msgID, String.valueOf(entryDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
    }
    // Replace the old entry with the new one.
    entryMap.put(entryDN, entry);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void renameEntry(DN currentDN, Entry entry,
                                       ModifyDNOperation modifyDNOperation)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "renameEntry", String.valueOf(currentDN),
                      String.valueOf(entry), String.valueOf(modifyDNOperation));
    // FIXME -- Consider adding support for this in the future.
    int    msgID   = MSGID_MEMORYBACKEND_MODDN_NOT_SUPPORTED;
    String message = getMessage(msgID);
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void search(SearchOperation searchOperation)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "search", String.valueOf(searchOperation));
    // Get the base DN, scope, and filter for the search.
    DN           baseDN = searchOperation.getBaseDN();
    SearchScope  scope  = searchOperation.getScope();
    SearchFilter filter = searchOperation.getFilter();
    // If it's a base-level search, then just get that entry and return it if it
    // matches the filter.
    if (scope == SearchScope.BASE_OBJECT)
    {
      Entry entry = entryMap.get(baseDN);
      if (entry == null)
      {
        int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST;
        String message = getMessage(msgID, String.valueOf(baseDN));
        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
      }
      if (filter.matchesEntry(entry))
      {
        searchOperation.returnEntry(entry, new LinkedList<Control>());
      }
    }
    else
    {
      // Walk through all entries and send the ones that match.
      for (Entry e : entryMap.values())
      {
        if (e.matchesBaseAndScope(baseDN, scope) && filter.matchesEntry(e))
        {
          searchOperation.returnEntry(e, new LinkedList<Control>());
        }
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public HashSet<String> getSupportedControls()
  {
    assert debugEnter(CLASS_NAME, "getSupportedControls");
    return supportedControls;
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsControl(String controlOID)
  {
    assert debugEnter(CLASS_NAME, "supportsControl",
                      String.valueOf(controlOID));
    // This backend does not provide any special control support.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public HashSet<String> getSupportedFeatures()
  {
    assert debugEnter(CLASS_NAME, "getSupportedFeatures");
    return supportedFeatures;
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsFeature(String featureOID)
  {
    assert debugEnter(CLASS_NAME, "supportsFeature",
                      String.valueOf(featureOID));
    // This backend does not provide any special feature support.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsLDIFExport()
  {
    assert debugEnter(CLASS_NAME, "supportsLDIFExport");
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void exportLDIF(ConfigEntry configEntry, DN[] baseDNs,
                                      LDIFExportConfig exportConfig)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "exportLDIF", String.valueOf(exportConfig));
    // Create the LDIF writer.
    LDIFWriter ldifWriter;
    try
    {
      ldifWriter = new LDIFWriter(exportConfig);
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "exportLDIF", e);
      int    msgID   = MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_WRITER;
      String message = getMessage(msgID, String.valueOf(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    // Walk through all the entries and write them to LDIF.
    DN entryDN = null;
    try
    {
      for (Entry entry : entryMap.values())
      {
        entryDN = entry.getDN();
        ldifWriter.writeEntry(entry);
      }
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_MEMORYBACKEND_CANNOT_WRITE_ENTRY_TO_LDIF;
      String message = getMessage(msgID, String.valueOf(entryDN),
                                  String.valueOf(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    finally
    {
      try
      {
        ldifWriter.close();
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "exportLDIF", e);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsLDIFImport()
  {
    assert debugEnter(CLASS_NAME, "supportsLDIFImport");
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public synchronized void importLDIF(ConfigEntry configEntry, DN[] baseDNs,
                                      LDIFImportConfig importConfig)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "importLDIF", String.valueOf(importConfig));
    clearMemoryBackend();
    LDIFReader reader;
    try
    {
      reader = new LDIFReader(importConfig);
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_READER;
      String message = getMessage(msgID, String.valueOf(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    try
    {
      while (true)
      {
        Entry e = null;
        try
        {
          e = reader.readEntry();
        }
        catch (LDIFException le)
        {
          if (! le.canContinueReading())
          {
            int    msgID   = MSGID_MEMORYBACKEND_ERROR_READING_LDIF;
            String message = getMessage(msgID, String.valueOf(e));
            throw new DirectoryException(
                           DirectoryServer.getServerErrorResultCode(),
                           message, msgID, le);
          }
          else
          {
            continue;
          }
        }
        try
        {
          addEntry(e, null);
        }
        catch (DirectoryException de)
        {
          reader.rejectLastEntry(de.getErrorMessage());
        }
      }
    }
    catch (DirectoryException de)
    {
      throw de;
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_MEMORYBACKEND_ERROR_DURING_IMPORT;
      String message = getMessage(msgID, String.valueOf(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    finally
    {
      reader.close();
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsBackup()
  {
    assert debugEnter(CLASS_NAME, "supportsBackup");
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsBackup(BackupConfig backupConfig,
                                StringBuilder unsupportedReason)
  {
    assert debugEnter(CLASS_NAME, "supportsBackup");
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public void createBackup(ConfigEntry configEntry, BackupConfig backupConfig)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "createBackup", String.valueOf(backupConfig));
    int    msgID   = MSGID_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED;
    String message = getMessage(msgID);
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  public void removeBackup(BackupDirectory backupDirectory,
                           String backupID)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeBackup",
                      String.valueOf(backupDirectory),
                      String.valueOf(backupID));
    int    msgID   = MSGID_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED;
    String message = getMessage(msgID);
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
  /**
   * {@inheritDoc}
   */
  public boolean supportsRestore()
  {
    assert debugEnter(CLASS_NAME, "supportsRestore");
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public void restoreBackup(ConfigEntry configEntry,
                            RestoreConfig restoreConfig)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "restoreBackup",
                      String.valueOf(restoreConfig));
    int    msgID   = MSGID_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED;
    String message = getMessage(msgID);
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                 msgID);
  }
}
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -2049,6 +2049,137 @@
  /**
   * The message ID for the message that will be used if an attempt is made to
   * initialize a memory backend with zero or multiple base DNs.  This does not
   * take any arguments.
   */
  public static final int MSGID_MEMORYBACKEND_REQUIRE_EXACTLY_ONE_BASE =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 192;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add an entry that already exists.  This takes a single argument, which is
   * the DN of the target entry.
   */
  public static final int MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 193;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add an entry that doesn't belong in the backend.  This takes a single
   * argument, which is the DN of the target entry.
   */
  public static final int MSGID_MEMORYBACKEND_ENTRY_DOESNT_BELONG =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 194;
  /**
   * The message ID for the message that will be used if an entry cannot be
   * added because the parent does not exist.  This takes two arguments, which
   * are the entry DN and the parent DN.
   */
  public static final int MSGID_MEMORYBACKEND_PARENT_DOESNT_EXIST =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 195;
  /**
   * The message ID for the message that will be used if an operation targets an
   * entry that doesn't exist.  This takes a single argument, which is the DN of
   * the entry.
   */
  public static final int MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 196;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * delete an entry that has one or more children.  This takes a single
   * argument, which is the DN of the entry.
   */
  public static final int
       MSGID_MEMORYBACKEND_CANNOT_DELETE_ENTRY_WITH_CHILDREN =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 197;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * perform an unsupported modify DN operation.  This does not take any
   * arguments.
   */
  public static final int MSGID_MEMORYBACKEND_MODDN_NOT_SUPPORTED =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 198;
  /**
   * The message ID for the message that will be used if an error occurs while
   * creating an LDIF writer.  This takes a single argument, which is a message
   * explaining the problem that occurred.
   */
  public static final int MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_WRITER =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 199;
  /**
   * The message ID for the message that will be used if an error occurs while
   * writing an entry to LDIF.  This takes two arguments, which are the entry DN
   * and a message explaining the problem that occurred.
   */
  public static final int MSGID_MEMORYBACKEND_CANNOT_WRITE_ENTRY_TO_LDIF =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 200;
  /**
   * The message ID for the message that will be used if an error occurs while
   * creating an LDIF reader.  This takes a single argument, which is a message
   * explaining the problem that occurred.
   */
  public static final int MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_READER =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 201;
  /**
   * The message ID for the message that will be used if an error occurs while
   * reading an entry from LDIF.  This takes a single argument, which is a
   * message explaining the problem that occurred.
   */
  public static final int MSGID_MEMORYBACKEND_ERROR_READING_LDIF =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 202;
  /**
   * The message ID for the message that will be used if an error occurs during
   * an LDIF import.  This takes a single argument, which is a message
   * explaining the problem that occurred.
   */
  public static final int MSGID_MEMORYBACKEND_ERROR_DURING_IMPORT =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 203;
  /**
   * The message ID for the message that will be used to indicate that backup
   * and restore operations are not supported in the memory-based backend.  This
   * does not take any arguments.
   */
  public static final int MSGID_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 204;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -2811,6 +2942,41 @@
    registerMessage(MSGID_BACKUP_BACKUP_AND_RESTORE_NOT_SUPPORTED,
                    "Backup and restore operations are not supported in " +
                    "the backup backend.");
    registerMessage(MSGID_MEMORYBACKEND_REQUIRE_EXACTLY_ONE_BASE,
                    "Exactly one base DN must be provided for use with the " +
                    "memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS,
                    "Entry %s already exists in the memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_ENTRY_DOESNT_BELONG,
                    "Entry %s does not belong in the memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_PARENT_DOESNT_EXIST,
                    "Unable to add entry %s because its parent entry %s does " +
                    "not exist in the memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST,
                    "Entry %s does not exist in the memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_CANNOT_DELETE_ENTRY_WITH_CHILDREN,
                    "Cannot delete entry %s because it has one or more " +
                    "subordinate entries.");
    registerMessage(MSGID_MEMORYBACKEND_MODDN_NOT_SUPPORTED,
                    "Modify DN operations are not supported in the " +
                    "memory-based backend.");
    registerMessage(MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_WRITER,
                    "Unable to create an LDIF writer:  %s.");
    registerMessage(MSGID_MEMORYBACKEND_CANNOT_WRITE_ENTRY_TO_LDIF,
                    "Cannot write entry %s to LDIF:  %s.");
    registerMessage(MSGID_MEMORYBACKEND_CANNOT_CREATE_LDIF_READER,
                    "Unable to create an LDIF reader:  %s.");
    registerMessage(MSGID_MEMORYBACKEND_ERROR_READING_LDIF,
                    "An unrecoverable error occurred while reading from " +
                    "LDIF:  %s.");
    registerMessage(MSGID_MEMORYBACKEND_ERROR_DURING_IMPORT,
                    "An unexpected error occurred while processing the " +
                    "import:  %s.");
    registerMessage(MSGID_MEMORYBACKEND_BACKUP_RESTORE_NOT_SUPPORTED,
                    "The memory-based backend does not support backup or " +
                    "restore operations.");
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -40,14 +40,18 @@
import java.util.List;
import java.util.ArrayList;
import org.opends.server.backends.MemoryBackend;
import org.opends.server.config.ConfigException;
import org.opends.server.config.ConfigFileHandler;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.InitializationException;
import org.opends.server.loggers.Error;
import org.opends.server.loggers.Debug;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines some utility functions which can be used by test
@@ -61,9 +65,23 @@
       "org.opends.server.BuildRoot";
  /**
   * Indicates whether the server has already been started.
   * The string representation of the DN that will be used as the base entry for
   * the test backend.  This must not be changed, as there are a number of test
   * cases that depend on this specific value of "o=test".
   */
  private static boolean serverStarted = false;
  public static final String TEST_ROOT_DN_STRING = "o=test";
  /**
   * Indicates whether the server has already been started.  The value of this
   * constant must not be altered by anything outside the
   * <CODE>startServer</CODE> method.
   */
  public static boolean SERVER_STARTED = false;
  /**
   * The memory-based backend configured for use in the server.
   */
  private static MemoryBackend memoryBackend = null;
  /**
   * Starts the Directory Server so that it will be available for use while
@@ -83,7 +101,7 @@
  public static void startServer()
         throws IOException, InitializationException, ConfigException
  {
    if (serverStarted)
    if (SERVER_STARTED)
    {
      return;
    }
@@ -140,7 +158,43 @@
    Error.removeAllErrorLoggers(false);
    Debug.removeAllDebugLoggers(false);
    directoryServer.startServer();
    serverStarted = true;
    SERVER_STARTED = true;
  }
  /**
   * Initializes a memory-based backend that may be used to perform operations
   * while testing the server.  This will ensure that the memory backend is
   * created in the server if it does not yet exist, and that it is empty.  Note
   * that the base DN for the test backend will always be "o=test", and it must
   * not be changed.  It is acceptable for test cases using this backend to
   * hard-code their sample data to use this base DN, although they may still
   * reference the <CODE>TEST_ROOT_DN_STRING</CODE> constant if they wish.
   *
   * @param  createBaseEntry  Indicate whether to automatically create the base
   *                          entry and add it to the backend.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public static void initializeTestBackend(boolean createBaseEntry)
         throws Exception
  {
    startServer();
    DN baseDN = DN.decode(TEST_ROOT_DN_STRING);
    if (memoryBackend == null)
    {
      memoryBackend = new MemoryBackend();
      memoryBackend.initializeBackend(null, new DN[] { baseDN });
      DirectoryServer.registerBackend(memoryBackend);
    }
    memoryBackend.clearMemoryBackend();
    if (createBaseEntry)
    {
      Entry e = createEntry(baseDN);
      memoryBackend.addEntry(e, null);
    }
  }
  /**