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

Valery Kharseko
3 days ago 4300052ea147c776119033f3c46b1286af8bd4af
[#851] Add upgrade task for RFC 5805 transaction extended operation handlers (#852)
2 files modified
1 files added
186 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java 28 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/tool.properties 3 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java 155 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java
@@ -79,6 +79,25 @@
  private static final NavigableMap<BuildVersion, List<UpgradeTask>> TASKS = new TreeMap<>();
  private static final List<UpgradeTask> MANDATORY_TASKS = new LinkedList<>();
  /* Payloads of the issue #851 upgrade task, mirroring the fresh-install config.ldif template.
   * Package-private so UpgradeUtilsTestCase applies exactly what the task applies. */
  static final String[] START_TRANSACTION_HANDLER_ENTRY = {
      "dn: cn=Start Transaction,cn=Extended Operations,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-extended-operation-handler",
      "objectClass: ds-cfg-start-transaction-extended-operation-handler",
      "cn: Start Transaction",
      "ds-cfg-java-class: org.opends.server.extensions.StartTransactionExtendedOperation",
      "ds-cfg-enabled: true" };
  static final String[] END_TRANSACTION_HANDLER_ENTRY = {
      "dn: cn=End Transaction,cn=Extended Operations,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-extended-operation-handler",
      "objectClass: ds-cfg-end-transaction-extended-operation-handler",
      "cn: End Transaction",
      "ds-cfg-java-class: org.opends.server.extensions.EndTransactionExtendedOperation",
      "ds-cfg-enabled: true" };
  static
  {
    // @formatter:off
@@ -610,6 +629,15 @@
    register("4.0.0", moveSubordinateBaseDnToGlobalConfiguration());
    register("4.0.0", removeTools("ldif-diff", "make-ldif", "dsjavaproperties"));
    /* See issue #851: RFC 5805 LDAP transactions (#462, shipped in 4.10.0) added these entries to
     * the fresh-install config.ldif template only, leaving upgraded instances without them.
     * Keyed at 5.2.0 (the release shipping this task) rather than 4.10.0 so that instances already
     * upgraded to 4.10.0-5.1.x without the entries are healed too; addConfigEntry is idempotent. */
    register("5.2.0",
        addConfigEntry(INFO_UPGRADE_TASK_ADD_TRANSACTION_EXTENDED_OPERATIONS.get(),
            START_TRANSACTION_HANDLER_ENTRY),
        addConfigEntry(END_TRANSACTION_HANDLER_ENTRY));
    /*
     * See issue #746. Builds before #661 (fixed in 5.1.2) shipped a duplicate
     * org.openidentityplatform.opendj.opendj-server-legacy.jar alongside opendj.jar in lib/.
opendj-server-legacy/src/messages/org/opends/messages/tool.properties
@@ -12,6 +12,7 @@
#
# Copyright 2006-2010 Sun Microsystems, Inc.
# Portions Copyright 2011-2016 ForgeRock AS.
# Portions Copyright 2026 3A Systems, LLC.
@@ -2699,3 +2700,5 @@
 Root DSE configuration
INFO_UPGRADE_TASK_ADD_SUBORDINATE_BASE_DN_TO_GLOBAL_CONFIG=Adding subordinate-base-dn attribute to \
 Global configuration
INFO_UPGRADE_TASK_ADD_TRANSACTION_EXTENDED_OPERATIONS=Adding configuration for LDAP transactions \
 extended operation handlers (RFC 5805)
opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java
New file
@@ -0,0 +1,155 @@
/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.tools.upgrade;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldif.LDIFEntryReader;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.util.ChangeOperationType;
import org.opends.server.util.StaticUtils;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
 * Tests that the issue #851 upgrade task payloads, applied through
 * {@link UpgradeUtils#updateConfigFile}, add the RFC 5805 transaction extended operation handler
 * entries exactly once and match the fresh-install template.
 */
@SuppressWarnings("javadoc")
@Test(groups = { "precommit", "tools" }, sequential = true)
public class UpgradeUtilsTestCase extends DirectoryServerTestCase
{
  /** Unknown config attributes must not fail parsing, as in the upgrade tool's own schema. */
  private final Schema schema = Schema.getCoreSchema().asNonStrictSchema();
  /** The config.ldif template a fresh install starts from. */
  private File freshInstallTemplate()
  {
    return new File(TestCaseUtils.getBuildRoot(), "resource/config/config.ldif");
  }
  /** The upgrade task must apply exactly what a fresh install ships. */
  @Test
  public void testTaskPayloadsMirrorFreshInstallTemplate() throws Exception
  {
    final List<Entry> template = readEntries(freshInstallTemplate());
    assertEntryPresentOnce(template, Upgrade.START_TRANSACTION_HANDLER_ENTRY);
    assertEntryPresentOnce(template, Upgrade.END_TRANSACTION_HANDLER_ENTRY);
  }
  @Test
  public void testAddTransactionHandlersAppliesOnceAndIsIdempotent() throws Exception
  {
    final File tempDir = TestCaseUtils.createTemporaryDirectory("upgradeTask851");
    try
    {
      // Simulates an instance upgraded from a pre-4.10.0 version: the fresh-install
      // template with the two transaction handler entries missing.
      final File config = new File(tempDir, "config.ldif");
      writeConfigWithoutTransactionEntries(config);
      assertEquals(applyAdd(config, Upgrade.START_TRANSACTION_HANDLER_ENTRY), 1);
      assertEquals(applyAdd(config, Upgrade.END_TRANSACTION_HANDLER_ENTRY), 1);
      assertEquals(applyAdd(config, Upgrade.START_TRANSACTION_HANDLER_ENTRY), 0);
      assertEquals(applyAdd(config, Upgrade.END_TRANSACTION_HANDLER_ENTRY), 0);
      final List<Entry> entries = readEntries(config);
      assertEntryPresentOnce(entries, Upgrade.START_TRANSACTION_HANDLER_ENTRY);
      assertEntryPresentOnce(entries, Upgrade.END_TRANSACTION_HANDLER_ENTRY);
    }
    finally
    {
      StaticUtils.recursiveDelete(tempDir);
    }
  }
  private int applyAdd(final File config, final String... ldifLines) throws Exception
  {
    return UpgradeUtils.updateConfigFile(config, null, ChangeOperationType.ADD, ldifLines);
  }
  private void writeConfigWithoutTransactionEntries(final File config) throws Exception
  {
    final DN startDN = dnOf(Upgrade.START_TRANSACTION_HANDLER_ENTRY);
    final DN endDN = dnOf(Upgrade.END_TRANSACTION_HANDLER_ENTRY);
    int removed = 0;
    try (LDIFEntryReader reader =
            new LDIFEntryReader(new FileInputStream(freshInstallTemplate())).setSchema(schema);
        LDIFEntryWriter writer = new LDIFEntryWriter(new FileOutputStream(config)))
    {
      while (reader.hasNext())
      {
        final Entry entry = reader.readEntry();
        if (entry.getName().equals(startDN) || entry.getName().equals(endDN))
        {
          removed++;
          continue;
        }
        writer.writeEntry(entry);
      }
    }
    assertEquals(removed, 2, "fresh-install template no longer ships the transaction entries");
  }
  private void assertEntryPresentOnce(final List<Entry> entries, final String... ldifLines)
      throws Exception
  {
    final DN dn = dnOf(ldifLines);
    final List<Entry> matches = new ArrayList<>();
    for (final Entry entry : entries)
    {
      if (entry.getName().equals(dn))
      {
        matches.add(entry);
      }
    }
    assertEquals(matches.size(), 1, "expected exactly one entry " + dn);
    try (LDIFEntryReader reader = new LDIFEntryReader(ldifLines).setSchema(schema))
    {
      assertEquals(matches.get(0), reader.readEntry());
    }
  }
  private DN dnOf(final String... ldifLines)
  {
    return DN.valueOf(ldifLines[0].replaceFirst("dn: ", ""), schema);
  }
  private List<Entry> readEntries(final File ldifFile) throws Exception
  {
    final List<Entry> entries = new ArrayList<>();
    try (LDIFEntryReader reader = new LDIFEntryReader(new FileInputStream(ldifFile)).setSchema(schema))
    {
      while (reader.hasNext())
      {
        entries.add(reader.readEntry());
      }
    }
    return entries;
  }
}