From f70033e6e7a8456a8a892c4b48663cde7de82712 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 21 Jul 2026 07:25:00 +0000
Subject: [PATCH] [#755] wait for the LDAP listener after in-process server (re)start (#756)
---
opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java | 38 +++++++++++++++++++++++++++++++++++++-
opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java | 17 ++++++++++++++++-
2 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index c584f27..37a3256 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -13,6 +13,7 @@
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems, LLC.
*/
package org.opends.server.protocols.ldap;
@@ -183,6 +184,14 @@
*/
private final Object waitListen = new Object();
+ /**
+ * Condition predicate for {@link #waitListen}: set once the selector thread
+ * has attempted to open the listen channels (successfully or not). Guarded
+ * by the {@link #waitListen} monitor; protects the start method against
+ * spurious wakeups.
+ */
+ private boolean listenAttempted;
+
/** The friendly name of this connection handler. */
private String friendlyName;
@@ -814,12 +823,16 @@
try
{
- waitListen.wait();
+ while (!listenAttempted)
+ {
+ waitListen.wait();
+ }
}
catch (InterruptedException e)
{
// If something interrupted the start its probably better
// to return ASAP.
+ Thread.currentThread().interrupt();
}
}
}
@@ -859,6 +872,7 @@
synchronized (waitListen)
{
starting = false;
+ listenAttempted = true;
waitListen.notify();
}
}
@@ -882,6 +896,7 @@
// should be notified and resume its work in any cases.
synchronized (waitListen)
{
+ listenAttempted = true;
waitListen.notify();
}
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
index c7a835b..9f220fa 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/TestCaseUtils.java
@@ -14,7 +14,7 @@
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2016 ForgeRock AS.
* Portions Copyright 2013 Manuel Gaupp
- * Portions Copyright 2018-2025 3A Systems, LLC
+ * Portions Copyright 2018-2026 3A Systems, LLC
*/
package org.opends.server;
@@ -68,6 +68,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.LogManager;
@@ -119,6 +120,7 @@
import org.opends.server.util.BuildVersion;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.LDIFReader;
+import org.opends.server.util.TestTimer;
import com.forgerock.opendj.util.OperatingSystem;
@@ -327,6 +329,7 @@
setupLoggers();
writeBuildInfoFile();
server.start();
+ waitForLdapListener();
assertTrue(InvocationCounterPlugin.startupCalled());
// Save config.ldif for when we restart the server
backupServerConfigLdif();
@@ -607,6 +610,7 @@
restoreServerConfigLdif();
server.start();
+ waitForLdapListener();
clearJEBackends();
initializeTestBackend(true);
@@ -626,6 +630,38 @@
}
/**
+ * Waits until the LDAP connection handler actually accepts TCP connections.
+ * {@code LDAPConnectionHandler.start()} may return before the listen
+ * channel is bound (e.g. after an early wakeup of its wait-for-listen
+ * handshake), so the very first connection made right after a server
+ * (re)start could otherwise be refused.
+ */
+ private static void waitForLdapListener() throws Exception
+ {
+ new TestTimer.Builder()
+ .maxSleep(10, TimeUnit.SECONDS)
+ .sleepTimes(100, TimeUnit.MILLISECONDS)
+ .toTimer()
+ .repeatUntilSuccess(new TestTimer.CallableVoid()
+ {
+ @Override
+ public void call() throws Exception
+ {
+ try (Socket s = new Socket())
+ {
+ s.connect(new InetSocketAddress("127.0.0.1", getServerLdapPort()), 500);
+ }
+ catch (IOException e)
+ {
+ // repeatUntilSuccess() only retries on assertion errors.
+ throw new AssertionError("LDAP port " + getServerLdapPort()
+ + " is not accepting connections yet", e);
+ }
+ }
+ });
+ }
+
+ /**
* Returns the embedded server used for tests.
*
* @return the embedded server.
--
Gitblit v1.10.0