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

Matthew Swift
14.13.2012 3a209faa6fafaa9ee7ba9640a850449062b4b78d
Fix OPENDJ-98: Searches on cn=monitor take a long time

Mostly rewrote this backend since there were so many bugs. The main change is to lazily compute the monitor entries only when they are needed.
1 files modified
1785 ■■■■ changed files
opends/src/server/org/opends/server/backends/MonitorBackend.java 1785 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -23,62 +23,34 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions copyright 2012 ForgeRock AS.
 */
package org.opends.server.backends;
import static org.opends.messages.BackendMessages.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.StaticUtils.getExceptionMessage;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.*;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.MonitorBackendCfg;
import org.opends.server.admin.std.server.MonitorProviderCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.MonitorProvider;
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.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.Attributes;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.IndexType;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFImportResult;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.*;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.LDIFWriter;
import org.opends.server.util.TimeThread;
@@ -87,14 +59,13 @@
/**
 * This class defines a backend to hold Directory Server monitor entries.  It
 * This class defines a backend to hold Directory Server monitor entries. It
 * will not actually store anything, but upon request will retrieve the
 * requested monitor and dynamically generate the associated entry.  It will
 * also construct a base monitor entry with some useful server-wide data.
 * requested monitor and dynamically generate the associated entry. It will also
 * construct a base monitor entry with some useful server-wide data.
 */
public class MonitorBackend
       extends Backend
       implements ConfigurationChangeListener<MonitorBackendCfg>
public class MonitorBackend extends Backend implements
    ConfigurationChangeListener<MonitorBackendCfg>
{
  /**
   * The tracer object for the debug logger.
@@ -106,7 +77,7 @@
  private ArrayList<Attribute> userDefinedAttributes;
  // The set of objectclasses that will be used in monitor entries.
  private HashMap<ObjectClass,String> monitorObjectClasses;
  private HashMap<ObjectClass, String> monitorObjectClasses;
  // The DN of the configuration entry for this backend.
  private DN configEntryDN;
@@ -126,13 +97,10 @@
  // 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;
  // The mapping between parent DNs and their immediate children.
  private HashMap<DN,HashSet<DN>> childDNs;
  /**
   * Creates a new backend with the provided information.  All backend
   * 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.
   */
@@ -142,95 +110,167 @@
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void configureBackend(Configuration config)
         throws ConfigException
  public void addEntry(final Entry entry, final AddOperation addOperation)
      throws DirectoryException
  {
    Validator.ensureNotNull(config);
    Validator.ensureTrue(config instanceof MonitorBackendCfg);
    MonitorBackendCfg cfg = (MonitorBackendCfg)config;
    ConfigEntry configEntry = DirectoryServer.getConfigEntry(cfg.dn());
    final Message message = ERR_MONITOR_ADD_NOT_SUPPORTED.get(String
        .valueOf(entry.getDN()));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
    // Make sure that a configuration entry was provided.  If not, then we will
    // not be able to complete initialization.
    if (configEntry == null)
    {
      Message message = ERR_MONITOR_CONFIG_ENTRY_NULL.get();
      throw new ConfigException(message);
    }
    configEntryDN = configEntry.getDN();
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
      final MonitorBackendCfg backendCfg)
  {
    ResultCode resultCode = ResultCode.SUCCESS;
    final boolean adminActionRequired = false;
    final ArrayList<Message> messages = new ArrayList<Message>();
    // Get the set of user-defined attributes for the configuration entry.  Any
    // attributes that we don't recognize will be included directly in the base
    // monitor entry.
    userDefinedAttributes = new ArrayList<Attribute>();
    for (List<Attribute> attrs :
         configEntry.getEntry().getUserAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isMonitorConfigAttribute(a))
        {
          userDefinedAttributes.add(a);
        }
      }
    }
    for (List<Attribute> attrs :
         configEntry.getEntry().getOperationalAttributes().values())
    {
      for (Attribute a : attrs)
      {
        if (! isMonitorConfigAttribute(a))
        {
          userDefinedAttributes.add(a);
        }
      }
    }
    // Construct the set of objectclasses to include in the base monitor entry.
    monitorObjectClasses = new LinkedHashMap<ObjectClass,String>(2);
    ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
    monitorObjectClasses.put(topOC, OC_TOP);
    ObjectClass monitorOC = DirectoryServer.getObjectClass(OC_MONITOR_ENTRY,
                                                           true);
    monitorObjectClasses.put(monitorOC, OC_MONITOR_ENTRY);
    // Define an empty sets for the supported controls and features.
    supportedControls = new HashSet<String>(0);
    supportedFeatures = new HashSet<String>(0);
    // Create the set of base DNs that we will handle.  In this case, it's just
    // the DN of the base monitor entry.
    // Check to see if there is a new set of user-defined attributes.
    final ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
    try
    {
      baseMonitorDN = DN.decode(DN_MONITOR_ROOT);
      final ConfigEntry configEntry = DirectoryServer
          .getConfigEntry(configEntryDN);
      for (final List<Attribute> attrs : configEntry.getEntry()
          .getUserAttributes().values())
      {
        for (final Attribute a : attrs)
        {
          if (!isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
      for (final List<Attribute> attrs : configEntry.getEntry()
          .getOperationalAttributes().values())
      {
        for (final Attribute a : attrs)
        {
          if (!isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
    }
    catch (Exception e)
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
          ERR_MONITOR_CANNOT_DECODE_MONITOR_ROOT_DN.get(getExceptionMessage(e));
      messages.add(ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY.get(
          String.valueOf(configEntryDN), stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
    }
    userDefinedAttributes = userAttrs;
    final Message message = INFO_MONITOR_USING_NEW_USER_ATTRS.get();
    messages.add(message);
    currentConfig = backendCfg;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void configureBackend(final Configuration config)
      throws ConfigException
  {
    Validator.ensureNotNull(config);
    Validator.ensureTrue(config instanceof MonitorBackendCfg);
    final MonitorBackendCfg cfg = (MonitorBackendCfg) config;
    final ConfigEntry configEntry = DirectoryServer.getConfigEntry(cfg.dn());
    // Make sure that a configuration entry was provided. If not, then we will
    // not be able to complete initialization.
    if (configEntry == null)
    {
      final Message message = ERR_MONITOR_CONFIG_ENTRY_NULL.get();
      throw new ConfigException(message);
    }
    configEntryDN = configEntry.getDN();
    // Get the set of user-defined attributes for the configuration entry. Any
    // attributes that we don't recognize will be included directly in the base
    // monitor entry.
    userDefinedAttributes = new ArrayList<Attribute>();
    for (final List<Attribute> attrs : configEntry.getEntry()
        .getUserAttributes().values())
    {
      for (final Attribute a : attrs)
      {
        if (!isMonitorConfigAttribute(a))
        {
          userDefinedAttributes.add(a);
        }
      }
    }
    for (final List<Attribute> attrs : configEntry.getEntry()
        .getOperationalAttributes().values())
    {
      for (final Attribute a : attrs)
      {
        if (!isMonitorConfigAttribute(a))
        {
          userDefinedAttributes.add(a);
        }
      }
    }
    // Construct the set of objectclasses to include in the base monitor entry.
    monitorObjectClasses = new LinkedHashMap<ObjectClass, String>(2);
    final ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
    monitorObjectClasses.put(topOC, OC_TOP);
    final ObjectClass monitorOC = DirectoryServer.getObjectClass(
        OC_MONITOR_ENTRY, true);
    monitorObjectClasses.put(monitorOC, OC_MONITOR_ENTRY);
    // Define an empty sets for the supported controls and features.
    supportedControls = new HashSet<String>(0);
    supportedFeatures = new HashSet<String>(0);
    // Create the set of base DNs that we will handle. In this case, it's just
    // the DN of the base monitor entry.
    try
    {
      baseMonitorDN = DN.decode(DN_MONITOR_ROOT);
    }
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      final Message message = ERR_MONITOR_CANNOT_DECODE_MONITOR_ROOT_DN
          .get(getExceptionMessage(e));
      throw new ConfigException(message, e);
    }
    // FIXME -- Deal with this more correctly.
    this.baseDNs = new DN[] { baseMonitorDN };
    currentConfig = cfg;
  }
@@ -240,32 +280,146 @@
   * {@inheritDoc}
   */
  @Override()
  public void initializeBackend()
         throws ConfigException, InitializationException
  public void createBackup(final BackupConfig backupConfig)
      throws DirectoryException
  {
    // Register with the Directory Server as a configurable component.
    currentConfig.addMonitorChangeListener(this);
    // This backend does not provide a backup/restore mechanism.
    final Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
    entryMap = new LinkedHashMap<DN,Entry>();
    childDNs = new HashMap<DN,HashSet<DN>>();
    this.initEntryMaps();
    // Register the monitor base as a private suffix.
  /**
   * {@inheritDoc}
   */
  @Override()
  public void deleteEntry(final DN entryDN,
      final DeleteOperation deleteOperation) throws DirectoryException
  {
    final Message message = ERR_MONITOR_DELETE_NOT_SUPPORTED.get(String
        .valueOf(entryDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean entryExists(final DN entryDN) throws DirectoryException
  {
    return getDIT().containsKey(entryDN);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void exportLDIF(final LDIFExportConfig exportConfig)
      throws DirectoryException
  {
    // TODO export-ldif reports nonsense for upTime etc.
    // Create the LDIF writer.
    LDIFWriter ldifWriter;
    try
    {
      DirectoryServer.registerBaseDN(baseMonitorDN, this, true);
      ldifWriter = new LDIFWriter(exportConfig);
    }
    catch (Exception e)
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
          baseMonitorDN.toString(), getExceptionMessage(e));
      throw new InitializationException(message, e);
      final Message message = ERR_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER
          .get(stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
          message);
    }
    // Write the base monitor entry to the LDIF.
    try
    {
      ldifWriter.writeEntry(getBaseMonitorEntry());
    }
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      try
      {
        ldifWriter.close();
      }
      catch (final Exception e2)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e2);
        }
      }
      final Message message = ERR_MONITOR_UNABLE_TO_EXPORT_BASE
          .get(stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
          message);
    }
    // Get all the monitor providers, convert them to entries, and write them to
    // LDIF.
    for (final MonitorProvider<?> monitorProvider : DirectoryServer
        .getMonitorProviders().values())
    {
      try
      {
        // TODO implementation of export is incomplete
      }
      catch (final Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        try
        {
          ldifWriter.close();
        }
        catch (final Exception e2)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e2);
          }
        }
        final Message message = ERR_MONITOR_UNABLE_TO_EXPORT_PROVIDER_ENTRY
            .get(monitorProvider.getMonitorInstanceName(),
                stackTraceToSingleLineString(e));
        throw new DirectoryException(
            DirectoryServer.getServerErrorResultCode(), message);
      }
    }
    // Close the monitor provider and return.
    try
    {
      ldifWriter.close();
    }
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
@@ -277,15 +431,12 @@
  @Override()
  public void finalizeBackend()
  {
    entryMap.clear();
    childDNs.clear();
    currentConfig.removeMonitorChangeListener(this);
    try
    {
      DirectoryServer.deregisterBaseDN(baseMonitorDN);
    }
    catch (Exception e)
    catch (final Exception e)
    {
      if (debugEnabled())
      {
@@ -297,33 +448,6 @@
  /**
   * Indicates whether the provided attribute is one that is used in the
   * configuration of this backend.
   *
   * @param  attribute  The attribute for which to make the determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute is one that is used in
   *          the configuration of this backend, <CODE>false</CODE> if not.
   */
  private boolean isMonitorConfigAttribute(Attribute attribute)
  {
    AttributeType attrType = attribute.getAttributeType();
    if (attrType.hasName(ATTR_COMMON_NAME) ||
        attrType.hasName(ATTR_BACKEND_ENABLED.toLowerCase()) ||
        attrType.hasName(ATTR_BACKEND_CLASS.toLowerCase()) ||
        attrType.hasName(ATTR_BACKEND_BASE_DN.toLowerCase()) ||
        attrType.hasName(ATTR_BACKEND_ID.toLowerCase()) ||
        attrType.hasName(ATTR_BACKEND_WRITABILITY_MODE.toLowerCase()))
    {
      return true;
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
@@ -338,12 +462,166 @@
   * {@inheritDoc}
   */
  @Override()
  public Entry getEntry(final DN entryDN) throws DirectoryException
  {
    // If the requested entry was null, then throw an exception.
    if (entryDN == null)
    {
      final Message message = ERR_MONITOR_GET_ENTRY_NULL.get();
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
          message);
    }
    // If the requested entry was the monitor base entry, then retrieve it
    // without constructing the DIT.
    if (entryDN.equals(baseMonitorDN))
    {
      return getBaseMonitorEntry();
    }
    // From now on we'll need the DIT.
    final Map<DN, MonitorProvider<?>> dit = getDIT();
    if (!dit.containsKey(entryDN))
    {
      final Message message = ERR_MONITOR_INVALID_BASE.get(
          String.valueOf(entryDN), String.valueOf(baseMonitorDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
    }
    // The DN is associated with a valid monitor/glue entry.
    return getEntry(entryDN, dit);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public long getEntryCount()
  {
    if (entryMap != null) {
      return entryMap.size();
    return getDIT().size();
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public HashSet<String> getSupportedControls()
  {
    return supportedControls;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public HashSet<String> getSupportedFeatures()
  {
    return supportedFeatures;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult hasSubordinates(final DN entryDN)
      throws DirectoryException
  {
    final NavigableMap<DN, MonitorProvider<?>> dit = getDIT();
    if (!dit.containsKey(entryDN))
    {
      return ConditionResult.UNDEFINED;
    }
    return -1;
    else
    {
      final DN nextDN = dit.higherKey(entryDN);
      if (nextDN == null || !nextDN.isDescendantOf(entryDN))
      {
        return ConditionResult.FALSE;
      }
      else
      {
        return ConditionResult.TRUE;
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LDIFImportResult importLDIF(final LDIFImportConfig importConfig)
      throws DirectoryException
  {
    // This backend does not support LDIF imports.
    final Message message = ERR_MONITOR_IMPORT_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializeBackend() throws ConfigException,
      InitializationException
  {
    // Register with the Directory Server as a configurable component.
    currentConfig.addMonitorChangeListener(this);
    // Register the monitor base as a private suffix.
    try
    {
      DirectoryServer.registerBaseDN(baseMonitorDN, this, true);
    }
    catch (final Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      final Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
          baseMonitorDN.toString(), getExceptionMessage(e));
      throw new InitializationException(message, e);
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      final MonitorBackendCfg backendCfg,
      final List<Message> unacceptableReasons)
  {
    // We'll pretty much accept anything here as long as it isn't one of our
    // private attributes.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isIndexed(final AttributeType attributeType,
      final IndexType indexType)
  {
    // All searches in this backend will always be considered indexed.
    return true;
  }
@@ -364,120 +642,42 @@
   * {@inheritDoc}
   */
  @Override()
  public boolean isIndexed(AttributeType attributeType, IndexType indexType)
  public long numSubordinates(final DN entryDN, final boolean subtree)
      throws DirectoryException
  {
    // All searches in this backend will always be considered indexed.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public ConditionResult hasSubordinates(DN entryDN)
         throws DirectoryException
  {
    long ret = numSubordinates(entryDN, false);
    if(ret < 0)
    final NavigableMap<DN, MonitorProvider<?>> dit = getDIT();
    if (!dit.containsKey(entryDN))
    {
      return ConditionResult.UNDEFINED;
    }
    else if(ret == 0)
    {
      return ConditionResult.FALSE;
    }
    else
    {
      return ConditionResult.TRUE;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public long numSubordinates(DN entryDN, boolean subtree)
         throws DirectoryException {
    Set<DN> children = childDNs.get(entryDN);
    if (children == null)
    {
      if(entryMap.get(entryDN) != null)
      {
        // The entry does exist but just no children.
        return 0;
      }
      return -1;
    }
    if(!subtree)
    {
      return children.size();
      return -1L;
    }
    else
    {
      long count = 0;
      for(DN child : children)
      final int childDNSize = entryDN.getNumComponents() + 1;
      for (final DN dn : dit.tailMap(entryDN, false).navigableKeySet())
      {
        count += numSubordinates(child, true);
        count++;
        if (!dn.isDescendantOf(entryDN))
        {
          break;
        }
        else if (subtree || dn.getNumComponents() == childDNSize)
        {
          count++;
        }
      }
      return count;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public Entry getEntry(DN entryDN)
         throws DirectoryException
  @Override
  public void preloadEntryCache() throws UnsupportedOperationException
  {
    // If the requested entry was null, then throw an exception.
    if (entryDN == null)
    {
      Message message = ERR_MONITOR_GET_ENTRY_NULL.get();
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    // If the requested entry was the monitor base entry, then retrieve it.
    if (entryDN.equals(baseMonitorDN))
    {
      return getBaseMonitorEntry();
    }
    if (!isATreeNode(entryDN)) {
      Message message = ERR_MONITOR_INVALID_BASE.get(
          String.valueOf(entryDN), String.valueOf(baseMonitorDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
    }
    // Get the RDN for the requested DN and make sure it is single-valued.
    RDN entryRDN = entryDN.getRDN();
    if (entryRDN.isMultiValued())
    {
      Message message =
          ERR_MONITOR_MULTIVALUED_RDN.get(String.valueOf(entryDN));
      throw new DirectoryException(
              ResultCode.NO_SUCH_OBJECT, message, baseMonitorDN, null);
    }
    String rdnValue = getRdns(entryDN);
    MonitorProvider<? extends MonitorProviderCfg> monitorProvider =
         DirectoryServer.getMonitorProvider(rdnValue.toLowerCase());
    // Could be a monitor branch
    if (monitorProvider == null) {
       return getBranchMonitorEntry(entryDN);
    }
    // Take the data from the monitor provider and stuff it into an entry.
    return getMonitorEntry(entryDN, monitorProvider);
    throw new UnsupportedOperationException("Operation not supported.");
  }
@@ -486,10 +686,193 @@
   * {@inheritDoc}
   */
  @Override()
  public boolean entryExists(DN entryDN)
         throws DirectoryException
  public void removeBackup(final BackupDirectory backupDirectory,
      final String backupID) throws DirectoryException
  {
    return this.isATreeNode(entryDN);
    // This backend does not provide a backup/restore mechanism.
    final Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void renameEntry(final DN currentDN, final Entry entry,
      final ModifyDNOperation modifyDNOperation) throws DirectoryException
  {
    final Message message = ERR_MONITOR_MODIFY_DN_NOT_SUPPORTED.get(String
        .valueOf(currentDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void replaceEntry(final Entry oldEntry, final Entry newEntry,
      final ModifyOperation modifyOperation) throws DirectoryException
  {
    final Message message = ERR_MONITOR_MODIFY_NOT_SUPPORTED.get(
        String.valueOf(newEntry.getDN()), String.valueOf(configEntryDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void restoreBackup(final RestoreConfig restoreConfig)
      throws DirectoryException
  {
    // This backend does not provide a backup/restore mechanism.
    final Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void search(final SearchOperation searchOperation)
      throws DirectoryException
  {
    // Get the base DN, scope, and filter for the search.
    final DN baseDN = searchOperation.getBaseDN();
    final SearchScope scope = searchOperation.getScope();
    final SearchFilter filter = searchOperation.getFilter();
    // Compute the current monitor DIT.
    final NavigableMap<DN, MonitorProvider<?>> dit = getDIT();
    // Resolve the base entry and return no such object if it does not exist.
    if (!dit.containsKey(baseDN))
    {
      // Not found, so find the nearest match.
      DN matchedDN = baseDN.getParent();
      while (matchedDN != null)
      {
        if (dit.containsKey(matchedDN))
        {
          break;
        }
        matchedDN = matchedDN.getParent();
      }
      final Message message = ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String
          .valueOf(baseDN));
      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message,
          matchedDN, null);
    }
    // Walk through all entries and send the ones that match.
    for (final Map.Entry<DN, MonitorProvider<?>> e : dit.tailMap(baseDN)
        .entrySet())
    {
      final DN dn = e.getKey();
      if (dn.matchesBaseAndScope(baseDN, scope))
      {
        final Entry entry = getEntry(dn, dit);
        if (filter.matchesEntry(entry))
        {
          searchOperation.returnEntry(entry, null);
        }
      }
      else if (scope == SearchScope.BASE_OBJECT || !dn.isDescendantOf(baseDN))
      {
        // No more entries will be in scope.
        break;
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsBackup()
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsBackup(final BackupConfig backupConfig,
      final StringBuilder unsupportedReason)
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsLDIFExport()
  {
    // We can export all the monitor entries as a point-in-time snapshot.
    // TODO implementation of export is incomplete
    // TODO export-ldif reports nonsense for upTime etc.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsLDIFImport()
  {
    // This backend does not support LDIF imports.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsRestore()
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * Creates an attribute for a monitor entry with the following criteria.
   *
   * @param name
   *          The name for the attribute.
   * @param lowerName
   *          The name for the attribute formatted in all lowercase characters.
   * @param value
   *          The value to use for the attribute.
   * @return The constructed attribute.
   */
  private Attribute createAttribute(final String name, final String lowerName,
      final String value)
  {
    return Attributes.create(name, value);
  }
@@ -497,130 +880,110 @@
  /**
   * Retrieves the base monitor entry for the Directory Server.
   *
   * @return  The base monitor entry for the Directory Server.
   * @return The base monitor entry for the Directory Server.
   */
  public Entry getBaseMonitorEntry()
  private Entry getBaseMonitorEntry()
  {
    HashMap<ObjectClass,String> monitorClasses =
         new LinkedHashMap<ObjectClass,String>(3);
    final HashMap<ObjectClass, String> monitorClasses =
        new LinkedHashMap<ObjectClass, String>(3);
    monitorClasses.putAll(monitorObjectClasses);
    ObjectClass extensibleObjectOC =
         DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC, true);
    final ObjectClass extensibleObjectOC = DirectoryServer.getObjectClass(
        OC_EXTENSIBLE_OBJECT_LC, true);
    monitorClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT);
    HashMap<AttributeType,List<Attribute>> monitorUserAttrs =
         new LinkedHashMap<AttributeType,List<Attribute>>();
    HashMap<AttributeType,List<Attribute>> monitorOperationalAttrs =
         new LinkedHashMap<AttributeType,List<Attribute>>();
    final HashMap<AttributeType, List<Attribute>> monitorUserAttrs =
        new LinkedHashMap<AttributeType, List<Attribute>>();
    final HashMap<AttributeType, List<Attribute>> monitorOperationalAttrs =
        new LinkedHashMap<AttributeType, List<Attribute>>();
    // Add the "cn" attribute.
    Attribute cnAttr = createAttribute(ATTR_COMMON_NAME, ATTR_COMMON_NAME,
                                       "monitor");
    ArrayList<Attribute> cnList = new ArrayList<Attribute>(1);
    final Attribute cnAttr = createAttribute(ATTR_COMMON_NAME,
        ATTR_COMMON_NAME, "monitor");
    final ArrayList<Attribute> cnList = new ArrayList<Attribute>(1);
    cnList.add(cnAttr);
    monitorUserAttrs.put(cnAttr.getAttributeType(), cnList);
    // Add the server product name.
    Attribute productNameAttr = createAttribute(ATTR_PRODUCT_NAME,
                                                ATTR_PRODUCT_NAME_LC,
                                                DynamicConstants.PRODUCT_NAME);
    ArrayList<Attribute> productNameList = new ArrayList<Attribute>(1);
    final Attribute productNameAttr = createAttribute(ATTR_PRODUCT_NAME,
        ATTR_PRODUCT_NAME_LC, DynamicConstants.PRODUCT_NAME);
    final ArrayList<Attribute> productNameList = new ArrayList<Attribute>(1);
    productNameList.add(productNameAttr);
    monitorUserAttrs.put(productNameAttr.getAttributeType(), productNameList);
    // Add the vendor name.
    Attribute vendorNameAttr = createAttribute(ATTR_VENDOR_NAME,
                                               ATTR_VENDOR_NAME_LC,
                                               SERVER_VENDOR_NAME);
    ArrayList<Attribute> vendorNameList = new ArrayList<Attribute>(1);
    final Attribute vendorNameAttr = createAttribute(ATTR_VENDOR_NAME,
        ATTR_VENDOR_NAME_LC, SERVER_VENDOR_NAME);
    final ArrayList<Attribute> vendorNameList = new ArrayList<Attribute>(1);
    vendorNameList.add(vendorNameAttr);
    monitorUserAttrs.put(vendorNameAttr.getAttributeType(), vendorNameList);
    // Add the vendor version.
    Attribute versionAttr = createAttribute(ATTR_VENDOR_VERSION,
                                            ATTR_VENDOR_VERSION_LC,
                                            DirectoryServer.getVersionString());
    ArrayList<Attribute> versionList = new ArrayList<Attribute>(1);
    final Attribute versionAttr = createAttribute(ATTR_VENDOR_VERSION,
        ATTR_VENDOR_VERSION_LC, DirectoryServer.getVersionString());
    final ArrayList<Attribute> versionList = new ArrayList<Attribute>(1);
    versionList.add(versionAttr);
    monitorUserAttrs.put(versionAttr.getAttributeType(), versionList);
    // Add the server startup time.
    Attribute startTimeAttr =
         createAttribute(ATTR_START_TIME, ATTR_START_TIME_LC,
                         DirectoryServer.getStartTimeUTC());
    ArrayList<Attribute> startTimeList = new ArrayList<Attribute>(1);
    final Attribute startTimeAttr = createAttribute(ATTR_START_TIME,
        ATTR_START_TIME_LC, DirectoryServer.getStartTimeUTC());
    final ArrayList<Attribute> startTimeList = new ArrayList<Attribute>(1);
    startTimeList.add(startTimeAttr);
    monitorUserAttrs.put(startTimeAttr.getAttributeType(), startTimeList);
    // Add the current time.
    Attribute currentTimeAttr =
         createAttribute(ATTR_CURRENT_TIME, ATTR_CURRENT_TIME_LC,
                         TimeThread.getGMTTime());
    ArrayList<Attribute> currentTimeList = new ArrayList<Attribute>(1);
    final Attribute currentTimeAttr = createAttribute(ATTR_CURRENT_TIME,
        ATTR_CURRENT_TIME_LC, TimeThread.getGMTTime());
    final ArrayList<Attribute> currentTimeList = new ArrayList<Attribute>(1);
    currentTimeList.add(currentTimeAttr);
    monitorUserAttrs.put(currentTimeAttr.getAttributeType(), currentTimeList);
    // Add the uptime as a human-readable string.
    long upSeconds =
         ((System.currentTimeMillis() - DirectoryServer.getStartTime()) / 1000);
    long upDays = (upSeconds / 86400);
    long upSeconds = ((System.currentTimeMillis() - DirectoryServer
        .getStartTime()) / 1000);
    final long upDays = (upSeconds / 86400);
    upSeconds %= 86400;
    long upHours = (upSeconds / 3600);
    final long upHours = (upSeconds / 3600);
    upSeconds %= 3600;
    long upMinutes = (upSeconds / 60);
    final long upMinutes = (upSeconds / 60);
    upSeconds %= 60;
    Message upTimeStr =
        INFO_MONITOR_UPTIME.get(upDays, upHours, upMinutes, upSeconds);
    Attribute upTimeAttr = createAttribute(ATTR_UP_TIME, ATTR_UP_TIME_LC,
                                           upTimeStr.toString());
    ArrayList<Attribute> upTimeList = new ArrayList<Attribute>(1);
    final Message upTimeStr = INFO_MONITOR_UPTIME.get(upDays, upHours,
        upMinutes, upSeconds);
    final Attribute upTimeAttr = createAttribute(ATTR_UP_TIME, ATTR_UP_TIME_LC,
        upTimeStr.toString());
    final ArrayList<Attribute> upTimeList = new ArrayList<Attribute>(1);
    upTimeList.add(upTimeAttr);
    monitorUserAttrs.put(upTimeAttr.getAttributeType(), upTimeList);
    // Add the number of connections currently established.
    long currentConns = DirectoryServer.getCurrentConnections();
    Attribute currentConnsAttr = createAttribute(ATTR_CURRENT_CONNS,
                                                 ATTR_CURRENT_CONNS_LC,
                                                 String.valueOf(currentConns));
    ArrayList<Attribute> currentConnsList = new ArrayList<Attribute>(1);
    final long currentConns = DirectoryServer.getCurrentConnections();
    final Attribute currentConnsAttr = createAttribute(ATTR_CURRENT_CONNS,
        ATTR_CURRENT_CONNS_LC, String.valueOf(currentConns));
    final ArrayList<Attribute> currentConnsList = new ArrayList<Attribute>(1);
    currentConnsList.add(currentConnsAttr);
    monitorUserAttrs.put(currentConnsAttr.getAttributeType(), currentConnsList);
    // Add the maximum number of connections established at one time.
    long maxConns = DirectoryServer.getMaxConnections();
    Attribute maxConnsAttr = createAttribute(ATTR_MAX_CONNS,
                                             ATTR_MAX_CONNS_LC,
                                             String.valueOf(maxConns));
    ArrayList<Attribute> maxConnsList = new ArrayList<Attribute>(1);
    final long maxConns = DirectoryServer.getMaxConnections();
    final Attribute maxConnsAttr = createAttribute(ATTR_MAX_CONNS,
        ATTR_MAX_CONNS_LC, String.valueOf(maxConns));
    final ArrayList<Attribute> maxConnsList = new ArrayList<Attribute>(1);
    maxConnsList.add(maxConnsAttr);
    monitorUserAttrs.put(maxConnsAttr.getAttributeType(), maxConnsList);
    // Add the total number of connections the server has accepted.
    long totalConns = DirectoryServer.getTotalConnections();
    Attribute totalConnsAttr = createAttribute(ATTR_TOTAL_CONNS,
                                               ATTR_TOTAL_CONNS_LC,
                                               String.valueOf(totalConns));
    ArrayList<Attribute> totalConnsList = new ArrayList<Attribute>(1);
    final long totalConns = DirectoryServer.getTotalConnections();
    final Attribute totalConnsAttr = createAttribute(ATTR_TOTAL_CONNS,
        ATTR_TOTAL_CONNS_LC, String.valueOf(totalConns));
    final ArrayList<Attribute> totalConnsList = new ArrayList<Attribute>(1);
    totalConnsList.add(totalConnsAttr);
    monitorUserAttrs.put(totalConnsAttr.getAttributeType(), totalConnsList);
    // Add all the user-defined attributes.
    for (Attribute a : userDefinedAttributes)
    for (final Attribute a : userDefinedAttributes)
    {
      AttributeType type = a.getAttributeType();
      final AttributeType type = a.getAttributeType();
      if (type.isOperational())
      {
@@ -652,95 +1015,167 @@
      }
    }
    // Construct and return the entry.
    Entry e = new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
                        monitorOperationalAttrs);
    final Entry e = new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
        monitorOperationalAttrs);
    e.processVirtualAttributes();
    return e;
  }
   /**
   * Retrieves the branch monitor entry for the Directory Server.
   * @param dn to get.
   * @return  The branch monitor entry for the Directory Server.
   */
  public Entry getBranchMonitorEntry(DN dn) {
    HashMap<ObjectClass,String> monitorClasses =
         new LinkedHashMap<ObjectClass,String>(3);
  /**
   * Retrieves the branch monitor entry for the Directory Server.
   *
   * @param dn
   *          to get.
   * @return The branch monitor entry for the Directory Server.
   */
  private Entry getBranchMonitorEntry(final DN dn)
  {
    final HashMap<ObjectClass, String> monitorClasses =
        new LinkedHashMap<ObjectClass, String>(3);
    monitorClasses.putAll(monitorObjectClasses);
    ObjectClass monitorOC = DirectoryServer.getObjectClass(OC_MONITOR_BRANCH,
                                                           true);
    final ObjectClass monitorOC = DirectoryServer.getObjectClass(
        OC_MONITOR_BRANCH, true);
    monitorClasses.put(monitorOC, OC_MONITOR_BRANCH);
    HashMap<AttributeType,List<Attribute>> monitorUserAttrs =
      new LinkedHashMap<AttributeType,List<Attribute>>();
    final HashMap<AttributeType, List<Attribute>> monitorUserAttrs =
        new LinkedHashMap<AttributeType, List<Attribute>>();
    RDN rdn = dn.getRDN();
    final RDN rdn = dn.getRDN();
    if (rdn != null)
    {
      // Add the RDN values
      for (int i=0; i<rdn.getNumValues(); i++)
      for (int i = 0; i < rdn.getNumValues(); i++)
      {
        AttributeType attributeType = rdn.getAttributeType(i);
        AttributeValue value = rdn.getAttributeValue(attributeType);
        Attribute attr = Attributes.create(attributeType, value);
        List<Attribute> attrList = new ArrayList<Attribute>(1);
        final AttributeType attributeType = rdn.getAttributeType(i);
        final AttributeValue value = rdn.getAttributeValue(attributeType);
        final Attribute attr = Attributes.create(attributeType, value);
        final List<Attribute> attrList = new ArrayList<Attribute>(1);
        attrList.add(attr);
        monitorUserAttrs.put(attributeType, attrList);
      }
    }
    // Construct and return the entry.
    Entry  e = new Entry(dn, monitorClasses, monitorUserAttrs, null);
    final Entry e = new Entry(dn, monitorClasses, monitorUserAttrs, null);
    e.processVirtualAttributes();
    return e;
  }
  /**
   * Generates and returns a monitor entry based on the contents of the
   * provided monitor provider.
   * Returns a map containing records for each DN in the monitor backend's DIT.
   * Each record maps the entry DN to the associated monitor provider, or
   * {@code null} if the entry is a glue (branch) entry.
   *
   * @param  entryDN          The DN to use for the entry.
   * @param  monitorProvider  The monitor provider to use to obtain the
   *                          information for the entry.
   *
   * @return  The monitor entry generated from the information in the provided
   *          monitor provider.
   * @return A map containing records for each DN in the monitor backend's DIT.
   */
  private Entry getMonitorEntry(DN entryDN,
                     MonitorProvider<? extends MonitorProviderCfg>
                          monitorProvider)
  private NavigableMap<DN, MonitorProvider<?>> getDIT()
  {
    HashMap<ObjectClass,String> monitorClasses =
         new LinkedHashMap<ObjectClass,String>(3);
    final NavigableMap<DN, MonitorProvider<?>> dit =
        new TreeMap<DN, MonitorProvider<?>>();
    for (final MonitorProvider<?> monitorProvider : DirectoryServer
        .getMonitorProviders().values())
    {
      DN dn = DirectoryServer.getMonitorProviderDN(monitorProvider);
      dit.put(dn, monitorProvider);
      // Added glue records.
      for (dn = dn.getParent(); dn != null; dn = dn.getParent())
      {
        if (dit.containsKey(dn))
        {
          break;
        }
        else
        {
          dit.put(dn, null);
        }
      }
    }
    return dit;
  }
  /**
   * Creates the monitor entry having the specified DN.
   *
   * @param entryDN
   *          The name of the monitor entry.
   * @param dit
   *          The monitor DIT.
   * @return Returns the monitor entry having the specified DN.
   */
  private Entry getEntry(final DN entryDN,
      final Map<DN, MonitorProvider<?>> dit)
  {
    // Get the monitor provider.
    final MonitorProvider<?> monitorProvider = dit.get(entryDN);
    if (monitorProvider != null)
    {
      return getMonitorEntry(entryDN, monitorProvider);
    }
    else if (entryDN.equals(baseMonitorDN))
    {
      // The monitor base entry needs special treatment.
      return getBaseMonitorEntry();
    }
    else
    {
      // Create a generic glue branch entry.
      return getBranchMonitorEntry(entryDN);
    }
  }
  /**
   * Generates and returns a monitor entry based on the contents of the provided
   * monitor provider.
   *
   * @param entryDN
   *          The DN to use for the entry.
   * @param monitorProvider
   *          The monitor provider to use to obtain the information for the
   *          entry.
   * @return The monitor entry generated from the information in the provided
   *         monitor provider.
   */
  private Entry getMonitorEntry(final DN entryDN,
      final MonitorProvider<?> monitorProvider)
  {
    final HashMap<ObjectClass, String> monitorClasses =
        new LinkedHashMap<ObjectClass, String>(
        3);
    monitorClasses.putAll(monitorObjectClasses);
    ObjectClass monitorOC = monitorProvider.getMonitorObjectClass();
    final ObjectClass monitorOC = monitorProvider.getMonitorObjectClass();
    monitorClasses.put(monitorOC, monitorOC.getPrimaryName());
    List<Attribute> monitorAttrs = monitorProvider.getMonitorData();
    HashMap<AttributeType,List<Attribute>> attrMap =
         new LinkedHashMap<AttributeType,List<Attribute>>(
                  monitorAttrs.size()+1);
    final List<Attribute> monitorAttrs = monitorProvider.getMonitorData();
    final HashMap<AttributeType, List<Attribute>> attrMap =
        new LinkedHashMap<AttributeType, List<Attribute>>(
          monitorAttrs.size() + 1);
    // Make sure to include the RDN attribute.
    RDN            entryRDN = entryDN.getRDN();
    AttributeType  rdnType  = entryRDN.getAttributeType(0);
    AttributeValue rdnValue = entryRDN.getAttributeValue(0);
    final RDN entryRDN = entryDN.getRDN();
    final AttributeType rdnType = entryRDN.getAttributeType(0);
    final AttributeValue rdnValue = entryRDN.getAttributeValue(0);
    Attribute rdnAttr = Attributes.create(rdnType, rdnValue);
    ArrayList<Attribute> rdnList = new ArrayList<Attribute>(1);
    final Attribute rdnAttr = Attributes.create(rdnType, rdnValue);
    final ArrayList<Attribute> rdnList = new ArrayList<Attribute>(1);
    rdnList.add(rdnAttr);
    attrMap.put(rdnType, rdnList);
    // Take the rest of the information from the monitor data.
    for (Attribute a : monitorAttrs)
    for (final Attribute a : monitorAttrs)
    {
      AttributeType type = a.getAttributeType();
      final AttributeType type = a.getAttributeType();
      List<Attribute> attrs = attrMap.get(type);
      if (attrs == null)
@@ -755,575 +1190,37 @@
      }
    }
    Entry e = new Entry(entryDN, monitorClasses, attrMap,
                        new HashMap<AttributeType,List<Attribute>>(0));
    final Entry e = new Entry(entryDN, monitorClasses, attrMap,
        new HashMap<AttributeType, List<Attribute>>(0));
    e.processVirtualAttributes();
    return e;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void addEntry(Entry entry, AddOperation addOperation)
         throws DirectoryException
  {
    Message message =
        ERR_MONITOR_ADD_NOT_SUPPORTED.get(String.valueOf(entry.getDN()));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
         throws DirectoryException
  {
    Message message =
        ERR_MONITOR_DELETE_NOT_SUPPORTED.get(String.valueOf(entryDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void replaceEntry(Entry oldEntry, Entry newEntry,
      ModifyOperation modifyOperation) throws DirectoryException
  {
    Message message = ERR_MONITOR_MODIFY_NOT_SUPPORTED.get(
        String.valueOf(newEntry.getDN()), String.valueOf(configEntryDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void renameEntry(DN currentDN, Entry entry,
                                   ModifyDNOperation modifyDNOperation)
         throws DirectoryException
  {
    Message message =
        ERR_MONITOR_MODIFY_DN_NOT_SUPPORTED.get(String.valueOf(currentDN));
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public synchronized void search(SearchOperation searchOperation)
         throws DirectoryException
  {
    // Get the base DN, scope, and filter for the search.
    DN           baseDN = searchOperation.getBaseDN();
    SearchScope  scope  = searchOperation.getScope();
    SearchFilter filter = searchOperation.getFilter();
    // Make sure the base entry exists if it's supposed to be in this backend.
    this.initEntryMaps();
    Entry baseEntry = entryMap.get(baseDN);
    if ((baseEntry == null) && handlesEntry(baseDN))
    {
      DN matchedDN = baseDN.getParentDNInSuffix();
      while (matchedDN != null)
      {
        if (entryMap.containsKey(matchedDN))
        {
          break;
        }
        matchedDN = matchedDN.getParentDNInSuffix();
      }
      Message message =
          ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(baseDN));
      throw new DirectoryException(
              ResultCode.NO_SUCH_OBJECT, message, matchedDN, null);
    }
    if (baseEntry != null)
    {
      baseEntry = baseEntry.duplicate(true);
    }
    // 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)
    {
      if (filter.matchesEntry(baseEntry))
      {
        searchOperation.returnEntry(baseEntry, null);
      }
    }
    else
    {
      // Walk through all entries and send the ones that match.
      for (Entry e : entryMap.values()) {
        boolean matched = filter.matchesEntry(e);
        if (matched) {
            if (e.matchesBaseAndScope(baseDN, scope)==true) {
                searchOperation.returnEntry(e, null);
            }
        }
      }
    }
  }
  // Build the internal monitor tree (entries, and children)
  private synchronized void initEntryMaps() {
      this.entryMap.clear();
      this.childDNs.clear();
      for (MonitorProvider<? extends MonitorProviderCfg> monitorProvider :
           DirectoryServer.getMonitorProviders().values()) {
            try {
                DN providerdn =
                        DirectoryServer.getMonitorProviderDN(monitorProvider);
                if (!entryMap.containsKey(providerdn)) {
                  getAndAddParentInMaps(providerdn);
                  Entry entry = getEntry(providerdn);
                  entryMap.put(providerdn, entry);
              }
            } catch (Exception ex) {
              if (debugEnabled()) {
                TRACER.debugCaught(DebugLogLevel.ERROR, ex);
              }
            }
      }
  }
 /**
  * Recursively builds the entryMap and the childDN Map.
  */
  private void getAndAddParentInMaps(DN providerDN)
  {
    try
    {
      DN parentdn = providerDN.getParentDNInSuffix();
      DN child = providerDN;
      if ((parentdn != null) && (!entryMap.containsKey(parentdn)))
      {
        getAndAddParentInMaps(parentdn);
        entryMap.put(parentdn, getEntry(parentdn));
      }
      if (childDNs.containsKey(parentdn))
      {
        HashSet<DN> children = childDNs.get(parentdn);
        children.add(child);
        childDNs.put(parentdn, children);
      }
      else
      {
        HashSet<DN> children = new HashSet<DN>();
        children.add(child);
        childDNs.put(parentdn, children);
      }
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public HashSet<String> getSupportedControls()
  {
    return supportedControls;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public HashSet<String> getSupportedFeatures()
  {
    return supportedFeatures;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsLDIFExport()
  {
    // We can export all the monitor entries as a point-in-time snapshot.
    // TODO implementation of export is incomplete
    // TODO export-ldif reports nonsense for upTime etc.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void exportLDIF(LDIFExportConfig exportConfig)
         throws DirectoryException
  {
    // TODO export-ldif reports nonsense for upTime etc.
    // Create the LDIF writer.
    LDIFWriter ldifWriter;
    try
    {
      ldifWriter = new LDIFWriter(exportConfig);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message = ERR_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER.get(
          stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    // Write the base monitor entry to the LDIF.
    try
    {
      ldifWriter.writeEntry(getBaseMonitorEntry());
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      try
      {
        ldifWriter.close();
      }
      catch (Exception e2)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e2);
        }
      }
      Message message = ERR_MONITOR_UNABLE_TO_EXPORT_BASE.get(
          stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    // Get all the monitor providers, convert them to entries, and write them to
    // LDIF.
    for (MonitorProvider<?> monitorProvider :
         DirectoryServer.getMonitorProviders().values())
    {
      try
      {
        // TODO implementation of export is incomplete
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        try
        {
          ldifWriter.close();
        }
        catch (Exception e2)
        {
          if (debugEnabled())
          {
            TRACER.debugCaught(DebugLogLevel.ERROR, e2);
          }
        }
        Message message = ERR_MONITOR_UNABLE_TO_EXPORT_PROVIDER_ENTRY.
            get(monitorProvider.getMonitorInstanceName(),
                stackTraceToSingleLineString(e));
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message);
      }
    }
    // Close the monitor provider and return.
    try
    {
      ldifWriter.close();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsLDIFImport()
  {
    // This backend does not support LDIF imports.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
         throws DirectoryException
  {
    // This backend does not support LDIF imports.
    Message message = ERR_MONITOR_IMPORT_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsBackup()
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsBackup(BackupConfig backupConfig,
                                StringBuilder unsupportedReason)
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void createBackup(BackupConfig backupConfig)
         throws DirectoryException
  {
    // This backend does not provide a backup/restore mechanism.
    Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void removeBackup(BackupDirectory backupDirectory,
                           String backupID)
         throws DirectoryException
  {
    // This backend does not provide a backup/restore mechanism.
    Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean supportsRestore()
  {
    // This backend does not provide a backup/restore mechanism.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void restoreBackup(RestoreConfig restoreConfig)
         throws DirectoryException
  {
    // This backend does not provide a backup/restore mechanism.
    Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
       MonitorBackendCfg backendCfg,
       List<Message> unacceptableReasons)
  {
    // We'll pretty much accept anything here as long as it isn't one of our
    // private attributes.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
                                 MonitorBackendCfg backendCfg)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<Message> messages            = new ArrayList<Message>();
    // Check to see if there is a new set of user-defined attributes.
    ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
    try
    {
      ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN);
      for (List<Attribute> attrs :
           configEntry.getEntry().getUserAttributes().values())
      {
        for (Attribute a : attrs)
        {
          if (! isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
      for (List<Attribute> attrs :
           configEntry.getEntry().getOperationalAttributes().values())
      {
        for (Attribute a : attrs)
        {
          if (! isMonitorConfigAttribute(a))
          {
            userAttrs.add(a);
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      messages.add(ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY.get(
              String.valueOf(configEntryDN),
              stackTraceToSingleLineString(e)));
      resultCode = DirectoryServer.getServerErrorResultCode();
    }
    userDefinedAttributes = userAttrs;
    Message message = INFO_MONITOR_USING_NEW_USER_ATTRS.get();
    messages.add(message);
    currentConfig = backendCfg;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void preloadEntryCache() throws UnsupportedOperationException {
    throw new UnsupportedOperationException("Operation not supported.");
  }
  // Private Methods
  private boolean isATreeNode(DN dn) {
      boolean found=false;
      if (dn.equals(baseMonitorDN)) {
          return true;
      }
      for (MonitorProvider<? extends MonitorProviderCfg> monitorProvider :
           DirectoryServer.getMonitorProviders().values()) {
         DN providerDN = DirectoryServer.getMonitorProviderDN(monitorProvider);
         if ((dn.isAncestorOf(providerDN)) ||
                 (dn.equals(providerDN))) {
             found=true;
             return (found);
         }
      }
      return (found);
  }
  private String getRdns(DN dn) {
      int index = dn.toString().lastIndexOf(",");
      return dn.toString().substring(3, index);
  }
   /**
   * Creates an attribute for a monitor entry with the following criteria.
   * Indicates whether the provided attribute is one that is used in the
   * configuration of this backend.
   *
   * @param  name       The name for the attribute.
   * @param  lowerName  The name for the attribute formatted in all lowercase
   *                    characters.
   * @param  value      The value to use for the attribute.
   *
   * @return  The constructed attribute.
   * @param attribute
   *          The attribute for which to make the determination.
   * @return <CODE>true</CODE> if the provided attribute is one that is used in
   *         the configuration of this backend, <CODE>false</CODE> if not.
   */
  private Attribute createAttribute(String name, String lowerName,
                                    String value) {
    return Attributes.create(name, value);
  private boolean isMonitorConfigAttribute(final Attribute attribute)
  {
    final AttributeType attrType = attribute.getAttributeType();
    if (attrType.hasName(ATTR_COMMON_NAME)
        || attrType.hasName(ATTR_BACKEND_ENABLED.toLowerCase())
        || attrType.hasName(ATTR_BACKEND_CLASS.toLowerCase())
        || attrType.hasName(ATTR_BACKEND_BASE_DN.toLowerCase())
        || attrType.hasName(ATTR_BACKEND_ID.toLowerCase())
        || attrType.hasName(ATTR_BACKEND_WRITABILITY_MODE.toLowerCase()))
    {
      return true;
    }
    return false;
  }
}