From 4300052ea147c776119033f3c46b1286af8bd4af Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 05 Aug 2026 17:02:21 +0000
Subject: [PATCH] [#851] Add upgrade task for RFC 5805 transaction extended operation handlers (#852)
---
opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java | 28 +++++++
opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java | 155 ++++++++++++++++++++++++++++++++++++++
opendj-server-legacy/src/messages/org/opends/messages/tool.properties | 3
3 files changed, 186 insertions(+), 0 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java
index 7b67cde..10b0590 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tools/upgrade/Upgrade.java
+++ b/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/.
diff --git a/opendj-server-legacy/src/messages/org/opends/messages/tool.properties b/opendj-server-legacy/src/messages/org/opends/messages/tool.properties
index b488fc1..c03e0bc 100644
--- a/opendj-server-legacy/src/messages/org/opends/messages/tool.properties
+++ b/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)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java
new file mode 100644
index 0000000..fb9eed1
--- /dev/null
+++ b/opendj-server-legacy/src/test/java/org/opends/server/tools/upgrade/UpgradeUtilsTestCase.java
@@ -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;
+ }
+}
--
Gitblit v1.10.0