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

Jean-Noel Rouvignac
16.12.2014 cad91508db120a47ec3dd52f60085dfeee9c5c22
OPENDJ-1490 (CR-3724) Replicated server fails to start after upgrade due to missing ReplicationBackend class

Added upgrade tasks for:
- Removing 'dc=replicationchanges' backend
- Removing ACI for 'dc=replicationchanges'


Upgrade.java:
Registered upgrade tasks.

UpgradeTasks.java:
Added deleteConfigEntry().
Factorized code by extracting method perform0().
Fixed javadocs.

UpgradeUtils.java:
In updateConfigFile(), renamed lines parameter to ldifLines + changed String dn local variable to DN ldifDN + added support for deleting entries.

tools.properties:
Added messages for upgrade tasks.
Fixed typos.


admin_tool.properties:
Fixed typos.

SaltedMD5PasswordStorageSchemeTestCase.java:
Removed unused import.
6 files modified
240 ■■■■ changed files
opendj3-server-dev/src/messages/messages/admin_tool.properties 6 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/messages/messages/tools.properties 8 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/Upgrade.java 15 ●●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java 115 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java 79 ●●●● patch | view | raw | blame | history
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedMD5PasswordStorageSchemeTestCase.java 17 ●●●● patch | view | raw | blame | history
opendj3-server-dev/src/messages/messages/admin_tool.properties
@@ -641,12 +641,12 @@
 following suffixes:%n%s%nTo avoid a single point of failure at least two \
 replication servers must be configured.%nDo you want to continue?
INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE=You have decided to disable the \
 replication server (replication changelog).  At least one replicaton server \
 replication server (replication changelog).  At least one replication server \
 is required in a replication topology and this is the last replication server \
 for the following suffixes:%n%s%nReplication will be disabled for these \
 servers.
INFO_DISABLE_REPLICATION_DISABLE_IN_REMOTE_PROMPT=You have decided to disable \
 the replication server (replication changelog).  At least one replicaton \
 the replication server (replication changelog).  At least one replication \
 server is required in a replication topology and this is the last replication \
 server for the following suffixes:%n%s%nReplication will be disabled for \
 these servers.%nDo you want to continue?
@@ -688,7 +688,7 @@
INFO_REPLICATION_WARNING_NO_REPLICATION_SERVER_TO_DISABLE=There is no \
 replication server configured in '%s'.
INFO_REPLICATION_PROMPT_DISABLE_REPLICATION_SERVER=Do you want to disable the \
 replication server (changelog and replicatin port '%d') on the server?
 replication server (changelog and replication port '%d') on the server?
INFO_REPLICATION_CONFIRM_INITIALIZE_ADS=You chose to initialize the contents \
 of base DN %s on server %s with the contents in server %s.  This base DN is \
 used by the replication mechanism and by some administrative tools and it is \
opendj3-server-dev/src/messages/messages/tools.properties
@@ -2347,7 +2347,7 @@
 provided LDIF files must be conform to the server schema
WARN_LDIFDIFF_NO_CONFIG_FILE_1675=WARNING:  no configuration file was \
 provided as argument.  No schema check will be performed.  If this is being \
 called throught the '%s' command-line, verify that the script has not been \
 called through the '%s' command-line, verify that the script has not been \
 modified
INFO_LDAPAUTH_NON_EMPTY_PASSWORD_1676=You must provide a non-empty password \
to continue
@@ -2375,10 +2375,8 @@
INFO_LDIFIMPORT_THREAD_COUNT_PLACEHOLDER_1687={count}
ERR_LDIFIMPORT_CANNOT_PARSE_THREAD_COUNT_1688=The value %s for \
threadCount cannot be parsed: %s
INFO_LDAPSEARCH_PUBLIC_CHANGELOG_COOKIE_EXC_1689=# Public \
 changelog exchange control(%s): %s
INFO_ENCPW_DESCRIPTION_INPUT_PW_1690=The password to encode or to compare \
 against an encoded password is interactively asked to the user
INFO_ENCPW_INPUT_PWD_1_1691=Please enter the password :
@@ -2540,7 +2538,7 @@
INFO_UPGRADE_REBUILD_INDEX_ENDS_1840=Rebuild index task ends
INFO_UPGRADE_PERFORMING_POST_TASKS_1841=Performing post upgrade tasks
INFO_UPGRADE_POST_TASKS_COMPLETE_1842=Post upgrade tasks complete
ERR_UPGRADE_PERFORMING_POST_TASKS_FAIL_1843=An error occured during post \
ERR_UPGRADE_PERFORMING_POST_TASKS_FAIL_1843=An error occurred during post \
upgrade task. Process aborted. Please check log for further details
INFO_UPGRADE_REBUILD_INDEX_DECLINED_1844 =You have to rebuild the '%s' index \
manually to get a fully functional server
@@ -2589,3 +2587,5 @@
INFO_UPGRADE_TASK_10232_1_SUMMARY_10024=Deleting ds-cfg-default-debug-level attribute in File-Based Debug Logger
INFO_UPGRADE_TASK_10329_1_SUMMARY_10025=Updating ds-cfg-default-severity attribute in File-Based Error Logger
INFO_UPGRADE_TASK_10339_1_SUMMARY_10026=Updating ds-cfg-override-severity attribute in Replication Repair Logger
INFO_UPGRADE_TASK_10733_1_SUMMARY_10027=Removing 'dc=replicationchanges' backend
INFO_UPGRADE_TASK_10733_2_SUMMARY_10028=Removing ACI for 'dc=replicationchanges'
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/Upgrade.java
@@ -132,7 +132,7 @@
        "delete: objectClass",
        "objectClass: ds-cfg-file-based-access-log-publisher"));
    register ("2.5.0.7466",
    register("2.5.0.7466",
        renameSnmpSecurityConfig(INFO_UPGRADE_TASK_7466_SUMMARY.get()));
    register("2.5.0.7748",
@@ -353,6 +353,19 @@
             "add:ds-cfg-override-severity",
             "ds-cfg-override-severity: SYNC=INFO,ERROR,WARNING,NOTICE"));
    /** See OPENDJ-1490 and OPENDJ-1454 */
    register("2.7.0.10703",
        deleteConfigEntry(INFO_UPGRADE_TASK_10733_1_SUMMARY.get(),
        "dn: ds-cfg-backend-id=replicationChanges,cn=Backends,cn=config"),
        modifyConfigEntry(INFO_UPGRADE_TASK_10733_2_SUMMARY.get(),
        "(objectClass=ds-cfg-dsee-compat-access-control-handler)",
        "delete: ds-cfg-global-aci",
        "ds-cfg-global-aci: "
            + "(target=\"ldap:///dc=replicationchanges\")"
            + "(targetattr=\"*\")"
            + "(version 3.0; acl \"Replication backend access\"; "
            + "deny (all) userdn=\"ldap:///anyone\";)"));
    /*
     * All upgrades will refresh the server configuration schema and generate
     * a new upgrade folder.
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/UpgradeTasks.java
@@ -83,8 +83,8 @@
  static boolean isRebuildAllIndexesTaskAccepted = false;
  /**
   * Returns a new upgrade task which applies an LDIF record to all
   * configuration entries matching the provided filter.
   * Returns a new upgrade task which adds a config entry to the underlying
   * config file.
   *
   * @param summary
   *          The summary of this upgrade task.
@@ -205,6 +205,30 @@
  }
  /**
   * Returns a new upgrade task which deletes a config entry from the underlying
   * config file.
   *
   * @param summary
   *          The summary of this upgrade task.
   * @param dnInLDIF
   *          The dn to delete in the form of LDIF.
   * @return A new upgrade task which applies an LDIF record to all
   *         configuration entries matching the provided filter.
   */
  public static UpgradeTask deleteConfigEntry(final LocalizableMessage summary,
      final String dnInLDIF)
  {
    return new AbstractUpgradeTask()
    {
      @Override
      public void perform(final UpgradeContext context) throws ClientException
      {
        perform0(summary, null, ChangeOperationType.DELETE, context, dnInLDIF);
      }
    };
  }
  /**
   * Returns a new upgrade task which applies an LDIF record to all
   * configuration entries matching the provided filter.
   *
@@ -803,33 +827,7 @@
      {
        if (userConfirmation)
        {
          displayTaskLogInformation(summary.toString(), null, ldif);
          final ProgressNotificationCallback pnc =
              new ProgressNotificationCallback(0, summary, 20);
          context.notifyProgress(pnc);
          try
          {
            // TODO change the directory to the config if it exists.
            final File configFile =
                new File(configDirectory,
                    Installation.CURRENT_CONFIG_FILE_NAME);
            final int changeCount =
                updateConfigFile(configFile.getPath(), null,
                    ChangeOperationType.ADD, ldif);
            displayChangeCount(configFile.getPath(), changeCount);
            context.notifyProgress(pnc.setProgress(100));
          }
          catch (final Exception e)
          {
            manageTaskException(context,
                LocalizableMessage.raw(e.getMessage()), pnc);
          }
          perform0(summary, null, ChangeOperationType.ADD, context, ldif);
        }
      }
    };
@@ -907,37 +905,44 @@
      {
        if (userConfirmation)
        {
          displayTaskLogInformation(summary.toString(), filter, ldif);
          final ProgressNotificationCallback pnc =
              new ProgressNotificationCallback(0, summary, 20);
          context.notifyProgress(pnc);
          try
          {
            final File configFile =
                new File(configDirectory,
                    Installation.CURRENT_CONFIG_FILE_NAME);
            final int changeCount =
                updateConfigFile(configFile.getPath(), Filter.valueOf(filter),
                    ChangeOperationType.MODIFY, ldif);
            displayChangeCount(configFile.getPath(), changeCount);
            context.notifyProgress(pnc.setProgress(100));
          }
          catch (final Exception e)
          {
            manageTaskException(context,
                LocalizableMessage.raw(e.getMessage()), pnc);
          }
          perform0(summary, filter, ChangeOperationType.MODIFY, context, ldif);
        }
      }
    };
  }
  private static void perform0(final LocalizableMessage summary, final String filter,
      final ChangeOperationType changeOperationType,
      final UpgradeContext context, final String... ldif)
      throws ClientException
  {
    displayTaskLogInformation(summary.toString(), filter, ldif);
    final ProgressNotificationCallback pnc =
        new ProgressNotificationCallback(0, summary, 20);
    context.notifyProgress(pnc);
    try
    {
      final File configFile =
          new File(configDirectory, Installation.CURRENT_CONFIG_FILE_NAME);
      final Filter filterVal = filter != null ? Filter.valueOf(filter) : null;
      final int changeCount =
          updateConfigFile(configFile.getPath(), filterVal,
              changeOperationType, ldif);
      displayChangeCount(configFile.getPath(), changeCount);
      context.notifyProgress(pnc.setProgress(100));
    }
    catch (final Exception e)
    {
      manageTaskException(context, LocalizableMessage.raw(e.getMessage()), pnc);
    }
  }
  // Prevent instantiation.
  private UpgradeTasks()
opendj3-server-dev/src/server/org/opends/server/tools/upgrade/UpgradeUtils.java
@@ -56,6 +56,7 @@
import static org.opends.server.tools.upgrade.FileManager.deleteRecursively;
import static org.opends.server.tools.upgrade.FileManager.rename;
import static org.opends.server.tools.upgrade.Installation.*;
import static org.opends.server.util.ChangeOperationType.*;
/**
 * Common utility methods needed by the upgrade.
@@ -407,22 +408,23 @@
  /**
   * Updates the config file during the upgrade process.
   *
   *
   * @param configPath
   *          The original path to the file.
   * @param filter
   *          The filter to avoid files.
   *          The filter to select entries. Only useful for modify change type.
   * @param changeType
   *          The change type which must be applied to ldif lines.
   * @param lines
   * @param ldifLines
   *          The change record ldif lines.
   *          For ADD change type, the first line must be the dn.
   *          For DELETE change type, the first and only line must be the dn.
   * @throws IOException
   *           If an Exception occurs during the input output methods.
   * @return The changes number that have occurred.
   */
  static int updateConfigFile(final String configPath,
      final Filter filter, final ChangeOperationType changeType,
      final String... lines) throws IOException
      final String... ldifLines) throws IOException
  {
    final File original = new File(configPath);
    final File copyConfig =
@@ -445,54 +447,64 @@
      writer.writeComment(INFO_CONFIG_FILE_HEADER.get());
      writer.setWrapColumn(0);
      boolean alreadyExist = false;
      String dn = null;
      if (filter == null && changeType == ChangeOperationType.ADD)
      boolean entryAlreadyExist = false;
      DN ldifDN = null;
      if (filter == null && (changeType == ADD || changeType == DELETE))
      {
        // For an Add, the first line should start with dn:
        dn = lines[0].replaceFirst("dn: ","");
        // The first line should start with dn:
        ldifDN = DN.valueOf(ldifLines[0].replaceFirst("dn: ", ""));
      }
      final Matcher matcher =
          filter != null ? filter.matcher(schema) : Filter.alwaysFalse()
              .matcher(schema);
      final Filter f = filter != null ? filter : Filter.alwaysFalse();
      final Matcher matcher = f.matcher(schema);
      while (entryReader.hasNext())
      {
        Entry entry = entryReader.readEntry();
        final DN entryDN = entry.getName();
        // Searching for the related entries
        if (matcher.matches(entry) == ConditionResult.TRUE)
        if (changeType == MODIFY
            && matcher.matches(entry) == ConditionResult.TRUE)
        {
          try
          {
            final ModifyRequest mr =
                Requests.newModifyRequest(readLDIFLines(entry.getName(),
                    changeType, lines));
            final ModifyRequest mr = Requests.newModifyRequest(
                readLDIFLines(entryDN, changeType, ldifLines));
            entry = Entries.modifyEntryPermissive(entry, mr.getModifications());
            changeCount++;
            logger.debug(LocalizableMessage.raw(
                String.format("The following entry has been modified : %s",
                    entry.getName())));
                "The following entry has been modified : %s", entryDN));
          }
          catch (Exception ex)
          {
            logger.error(LocalizableMessage.raw(ex.getMessage()));
          }
        }
        if (dn != null // This is an ADD
            && entry.getName().equals(DN.valueOf(dn)))
        if (entryDN.equals(ldifDN))
        {
          logger.debug(LocalizableMessage.raw(String.format("Entry %s found", entry.getName())));
          alreadyExist = true;
          logger.debug(LocalizableMessage.raw("Entry %s found", entryDN));
          entryAlreadyExist = true;
          if (changeType == DELETE)
          {
            entry = null;
            changeCount++;
            logger.debug(LocalizableMessage.raw(
                "The following entry has been deleted : %s", entryDN));
          }
        }
        writer.writeEntry(entry);
        if (entry != null)
        {
          writer.writeEntry(entry);
        }
      }
      // If it's an ADD and the entry doesn't exist yet
      if (dn != null && !alreadyExist)
      if (changeType == ADD && !entryAlreadyExist)
      {
        final AddRequest ar = Requests.newAddRequest(lines);
        final AddRequest ar = Requests.newAddRequest(ldifLines);
        writer.writeEntry(ar);
        logger.debug(LocalizableMessage.raw(String.format("Entry successfully added %s in %s",
            dn, original.getAbsolutePath())));
        logger.debug(LocalizableMessage.raw("Entry successfully added %s in %s",
            ldifDN, original.getAbsolutePath()));
        changeCount++;
      }
    }
@@ -840,19 +852,12 @@
      final ChangeOperationType changeType, final String... lines)
  {
    final String[] modifiedLines = new String[lines.length + 2];
    int index = 0;
    if (changeType == ChangeOperationType.MODIFY)
    if (changeType == MODIFY)
    {
      modifiedLines[0] = "dn: " + dn;
      modifiedLines[1] = "changetype: modify";
      index = 2;
    }
    for (final String line : lines)
    {
      modifiedLines[index] = line;
      index++;
    }
    System.arraycopy(lines, 0, modifiedLines, 2, lines.length);
    return modifiedLines;
  }
opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedMD5PasswordStorageSchemeTestCase.java
@@ -26,20 +26,16 @@
 */
package org.opends.server.extensions;
import org.testng.annotations.Test;
import org.opends.server.admin.server.AdminTestCaseUtils;
import org.opends.server.admin.std.meta.SaltedMD5PasswordStorageSchemeCfgDefn;
import org.opends.server.admin.std.server.SaltedMD5PasswordStorageSchemeCfg;
import org.opends.server.api.PasswordStorageScheme;
import org.opends.server.schema.UserPasswordSyntax;
import org.forgerock.opendj.ldap.ByteString;
import static org.testng.Assert.*;
/**
 * A set of test cases for the salted MD5 password storage scheme.
 */
@@ -54,17 +50,12 @@
    super("cn=Salted MD5,cn=Password Storage Schemes,cn=config");
  }
  /**
   * Retrieves an initialized instance of this password storage scheme.
   *
   * @return  An initialized instance of this password storage scheme.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  protected PasswordStorageScheme getScheme()
         throws Exception
  protected PasswordStorageScheme getScheme() throws Exception
  {
    SaltedMD5PasswordStorageScheme scheme =
         new SaltedMD5PasswordStorageScheme();
@@ -83,12 +74,9 @@
  /**
   * Tests matching with a different salt size.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test
  public void testDifferentSaltSize()
    throws Exception {
  public void testDifferentSaltSize() throws Exception {
    SaltedMD5PasswordStorageScheme scheme =
      new SaltedMD5PasswordStorageScheme();
@@ -104,4 +92,3 @@
      ByteString.valueOf("so5s1vK3oEi4uL/oVY3bqs5LRlKjgMN+u4A4bw==")));
  }
}