/** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(BackupBackendCfg cfg) {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();

    Set<String> values = cfg.getBackupDirectory();
    backupDirectories = new LinkedHashSet<File>(values.size());
    for (String s : values) {
      backupDirectories.add(getFileForPath(s));
    }

    currentConfig = cfg;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /** {@inheritDoc} */
  @Override
  public void search(SearchOperation searchOperation) throws DirectoryException {
    // Get the base entry for the search, if possible.  If it doesn't exist,
    // then this will throw an exception.
    DN baseDN = searchOperation.getBaseDN();
    Entry baseEntry = getEntry(baseDN);

    // Look at the base DN and see if it's the backup base DN, a backup
    // directory entry DN, or a backup entry DN.
    DN parentDN;
    SearchScope scope = searchOperation.getScope();
    SearchFilter filter = searchOperation.getFilter();
    if (backupBaseDN.equals(baseDN)) {
      if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE)
          && filter.matchesEntry(baseEntry)) {
        searchOperation.returnEntry(baseEntry, null);
      }

      if (scope != SearchScope.BASE_OBJECT && !backupDirectories.isEmpty()) {
        AttributeType backupPathType =
            DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true);
        for (File f : backupDirectories) {
          // Check to see if the descriptor file exists.  If not, then skip this
          // backup directory.
          File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE);
          if (!descriptorFile.exists()) {
            continue;
          }

          DN backupDirDN = makeChildDN(backupBaseDN, backupPathType, f.getAbsolutePath());

          Entry backupDirEntry;
          try {
            backupDirEntry = getBackupDirectoryEntry(backupDirDN);
          } catch (Exception e) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }

            continue;
          }

          if (filter.matchesEntry(backupDirEntry)) {
            searchOperation.returnEntry(backupDirEntry, null);
          }

          if (scope != SearchScope.SINGLE_LEVEL) {
            List<Attribute> attrList = backupDirEntry.getAttribute(backupPathType);
            if (attrList != null && !attrList.isEmpty()) {
              for (AttributeValue v : attrList.get(0)) {
                try {
                  BackupDirectory backupDirectory =
                      BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString());
                  AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, true);
                  for (String backupID : backupDirectory.getBackups().keySet()) {
                    DN backupEntryDN = makeChildDN(backupDirDN, idType, backupID);
                    Entry backupEntry = getBackupEntry(backupEntryDN);
                    if (filter.matchesEntry(backupEntry)) {
                      searchOperation.returnEntry(backupEntry, null);
                    }
                  }
                } catch (Exception e) {
                  if (debugEnabled()) {
                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
                  }

                  continue;
                }
              }
            }
          }
        }
      }
    } else if (backupBaseDN.equals(parentDN = baseDN.getParentDNInSuffix())) {
      Entry backupDirEntry = getBackupDirectoryEntry(baseDN);

      if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE)
          && filter.matchesEntry(backupDirEntry)) {
        searchOperation.returnEntry(backupDirEntry, null);
      }

      if (scope != SearchScope.BASE_OBJECT) {
        AttributeType t = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true);
        List<Attribute> attrList = backupDirEntry.getAttribute(t);
        if (attrList != null && !attrList.isEmpty()) {
          for (AttributeValue v : attrList.get(0)) {
            try {
              BackupDirectory backupDirectory =
                  BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString());
              AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, true);
              for (String backupID : backupDirectory.getBackups().keySet()) {
                DN backupEntryDN = makeChildDN(baseDN, idType, backupID);
                Entry backupEntry = getBackupEntry(backupEntryDN);
                if (filter.matchesEntry(backupEntry)) {
                  searchOperation.returnEntry(backupEntry, null);
                }
              }
            } catch (Exception e) {
              if (debugEnabled()) {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
              }

              continue;
            }
          }
        }
      }
    } else {
      if (parentDN == null || !backupBaseDN.equals(parentDN.getParentDNInSuffix())) {
        Message message = ERR_BACKUP_NO_SUCH_ENTRY.get(String.valueOf(backupBaseDN));
        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
      }

      if (scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE) {
        Entry backupEntry = getBackupEntry(baseDN);
        if (backupEntry == null) {
          Message message = ERR_BACKUP_NO_SUCH_ENTRY.get(String.valueOf(backupBaseDN));
          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
        }

        if (filter.matchesEntry(backupEntry)) {
          searchOperation.returnEntry(backupEntry, null);
        }
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void initializeBackend() throws ConfigException, InitializationException {
    // Create the set of base DNs that we will handle.  In this case, it's just
    // the DN of the base backup entry.
    try {
      backupBaseDN = DN.decode(DN_BACKUP_ROOT);
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }

      Message message =
          ERR_BACKEND_CANNOT_DECODE_BACKEND_ROOT_DN.get(getExceptionMessage(e), getBackendID());
      throw new InitializationException(message, e);
    }

    // FIXME -- Deal with this more correctly.
    this.baseDNs = new DN[] {backupBaseDN};

    // Determine the set of backup directories that we will use by default.
    Set<String> values = currentConfig.getBackupDirectory();
    backupDirectories = new LinkedHashSet<File>(values.size());
    for (String s : values) {
      backupDirectories.add(getFileForPath(s));
    }

    // Construct the backup base entry.
    LinkedHashMap<ObjectClass, String> objectClasses = new LinkedHashMap<ObjectClass, String>(2);
    objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);

    ObjectClass untypedOC = DirectoryServer.getObjectClass(OC_UNTYPED_OBJECT_LC, true);
    objectClasses.put(untypedOC, OC_UNTYPED_OBJECT);

    LinkedHashMap<AttributeType, List<Attribute>> opAttrs =
        new LinkedHashMap<AttributeType, List<Attribute>>(0);
    LinkedHashMap<AttributeType, List<Attribute>> userAttrs =
        new LinkedHashMap<AttributeType, List<Attribute>>(1);

    RDN rdn = backupBaseDN.getRDN();
    int numAVAs = rdn.getNumValues();
    for (int i = 0; i < numAVAs; i++) {
      AttributeType attrType = rdn.getAttributeType(i);
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(Attributes.create(attrType, rdn.getAttributeValue(i)));

      userAttrs.put(attrType, attrList);
    }

    backupBaseEntry = new Entry(backupBaseDN, objectClasses, userAttrs, opAttrs);

    currentConfig.addBackupChangeListener(this);

    // Register the backup base as a private suffix.
    try {
      DirectoryServer.registerBaseDN(backupBaseDN, this, true);
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }

      Message message =
          ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(backupBaseDN.toString(), getExceptionMessage(e));
      throw new InitializationException(message, e);
    }
  }