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

Nicolas Capponi
22.33.2014 1c30f85c74cad5f3a868f2054651e86e5532eb48
Checkpoint commit for OPENDJ-1206 Create a new ReplicationBackend/ChangelogBackend 
to support cn=changelog
CR-4439

Implementation of an additional validation of provided cookie when searching changelog
in cookie mode.

Check for each state of the provided cookie that the csn is not older than the oldest CSN
available in each domain. If this is not the case, then an error
ERR_RESYNC_REQUIRED_TOO_OLD_DOMAIN_IN_PROVIDED_COOKIE is returned.

This check was done previously in the ECLServerHandler class but was not ported when
writing ChangelogBackend. It also means I'm re-adding some methods deleted by the
previous big clean done by Jean-Noel.

Changes:
* ReplicationServer.java :
- add the check to validateServerState() method
- refactor validateServerState() method for better readability
* ReplicationServerDomain.java :
- re-add getOldestState() method
* ReplicationDomainDB. java :
- re-add getDomainOldestCSNs(DN) method
* FileChangelogDB.java, JEChangelogDB.java :
- re-add implementation of getDomainOldestCSNs(DN) method
1 files modified
123 ■■■■ changed files
opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java 123 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -669,7 +669,7 @@
   * parameter.
   *
   * @param baseDN
   *          The base Dn for which the ReplicationServerDomain must be
   *          The base DN for which the ReplicationServerDomain must be
   *          returned.
   * @return The ReplicationServerDomain associated to the base DN given in
   *         parameter.
@@ -692,70 +692,123 @@
  }
  /**
   * Validate that provided state is coherent with this replication server,
   * Validate that provided cookie is coherent with this replication server,
   * when ignoring the provided set of DNs.
   * <p>
   * The state is coherent if and only if it exactly has the set of DNs corresponding to
   * the replication domains.
   * The cookie is coherent if and only if it exactly has the set of DNs corresponding to
   * the replication domains, and the states in the cookie are not older than oldest states
   * in the server.
   *
   * @param state
   * @param cookie
   *            The multi domain state (cookie) to validate.
   * @param ignoredBaseDNs
   *            The set of DNs to ignore when validating
   * @throws DirectoryException
   *            If the state is not valid
   *            If the cookie is not valid
   */
  public void validateServerState(MultiDomainServerState state, Set<DN> ignoredBaseDNs) throws DirectoryException
  public void validateCookie(MultiDomainServerState cookie, Set<DN> ignoredBaseDNs) throws DirectoryException
  {
    // Build the two sets of DNs to compare
    final Set<DN> activeServerDomains = new HashSet<DN>();
    final Set<DN> activeDomains = getDNsOfActiveDomainsInServer(ignoredBaseDNs);
    final Set<DN> cookieDomains = getDNsOfCookie(cookie);
    checkNoActiveDomainIsMissingInCookie(cookie, activeDomains, cookieDomains);
    checkNoUnknownDomainIsProvidedInCookie(cookie, activeDomains, cookieDomains);
    checkCookieIsNotOutdated(cookie, activeDomains);
  }
  private Set<DN> getDNsOfCookie(MultiDomainServerState cookie)
  {
    final Set<DN> cookieDomains = new HashSet<DN>();
    for (final DN dn : cookie)
    {
      cookieDomains.add(dn);
    }
    return cookieDomains;
  }
  private Set<DN> getDNsOfActiveDomainsInServer(final Set<DN> ignoredBaseDNs) throws DirectoryException
  {
    final Set<DN> activeDomains = new HashSet<DN>();
    for (final DN dn : getDomainDNs(ignoredBaseDNs))
    {
      final ServerState lastServerState = getReplicationServerDomain(dn).getLatestServerState();
      if (!lastServerState.isEmpty())
      {
         activeServerDomains.add(dn);
         activeDomains.add(dn);
      }
    }
    final Set<DN> stateDomains = new HashSet<DN>();
    for (final DN dn : state)
    {
      stateDomains.add(dn);
    return activeDomains;
    }
    // The two sets of DNs are expected to be the same. Check this.
    final Set<DN> domainsCopy = new HashSet<DN>(activeServerDomains);
    final Set<DN> stateDomainsCopy = new HashSet<DN>(stateDomains);
    domainsCopy.removeAll(stateDomains);
    if (!domainsCopy.isEmpty())
  private void checkNoUnknownDomainIsProvidedInCookie(final MultiDomainServerState cookie, final Set<DN> activeDomains,
      final Set<DN> cookieDomains) throws DirectoryException
    {
      final StringBuilder missingDomains = new StringBuilder();
      for (DN dn : domainsCopy)
    if (!activeDomains.containsAll(cookieDomains))
      {
        missingDomains.append(dn).append(":;");
      }
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
          ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE.get(
              missingDomains, "<" + state.toString() + missingDomains + ">"));
    }
    stateDomainsCopy.removeAll(activeServerDomains);
    if (!stateDomainsCopy.isEmpty())
    {
      final StringBuilder startState = new StringBuilder();
      for (DN dn : activeServerDomains) {
        startState.append(dn).append(":").append(state.getServerState(dn).toString()).append(";");
      final Set<DN> unknownCookieDomains = new HashSet<DN>(cookieDomains);
      unknownCookieDomains.removeAll(activeDomains);
      final StringBuilder currentStartingCookie = new StringBuilder();
      for (DN domainDN : activeDomains) {
        currentStartingCookie.append(domainDN).append(":").append(cookie.getServerState(domainDN)).append(";");
      }
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
          ERR_RESYNC_REQUIRED_UNKNOWN_DOMAIN_IN_PROVIDED_COOKIE.get(
              stateDomainsCopy.toString(), startState));
              unknownCookieDomains.toString(), currentStartingCookie));
    }
  }
  private void checkNoActiveDomainIsMissingInCookie(final MultiDomainServerState cookie, final Set<DN> activeDomains,
      final Set<DN> cookieDomains) throws DirectoryException
  {
    if (!cookieDomains.containsAll(activeDomains))
    {
      final Set<DN> missingActiveDomains = new HashSet<DN>(activeDomains);
      missingActiveDomains.removeAll(cookieDomains);
      final StringBuilder missingDomains = new StringBuilder();
      for (DN domainDN : missingActiveDomains)
      {
        missingDomains.append(domainDN).append(":;");
      }
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
          ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE.get(
              missingDomains, "<" + cookie + missingDomains + ">"));
    }
  }
  private void checkCookieIsNotOutdated(final MultiDomainServerState cookie, final Set<DN> activeDomains)
      throws DirectoryException
  {
    for (DN dn : activeDomains)
    {
      if (isCookieOutdatedForDomain(cookie, dn))
      {
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            ERR_RESYNC_REQUIRED_TOO_OLD_DOMAIN_IN_PROVIDED_COOKIE.get(dn.toString()));
      }
    }
  }
  /** Check that provided cookie is not outdated compared to the oldest state of a domain. */
  private boolean isCookieOutdatedForDomain(MultiDomainServerState cookie, DN domainDN)
  {
    final ServerState domainOldestState = getReplicationServerDomain(domainDN).getOldestState();
    final ServerState providedState = cookie.getServerState(domainDN);
    for (final CSN oldestCsn : domainOldestState)
    {
      final CSN providedCsn = providedState.getCSN(oldestCsn.getServerId());
      if (providedCsn != null && providedCsn.isOlderThan(oldestCsn))
      {
        return true;
      }
    }
    return false;
  }
  /**
   * Get the ReplicationServerDomain associated to the base DN given in
   * parameter.
   *
   * @param baseDN The base Dn for which the ReplicationServerDomain must be
   * @param baseDN The base DN for which the ReplicationServerDomain must be
   * returned.
   * @param create Specifies whether to create the ReplicationServerDomain if
   *        it does not already exist.