From a2c7a1aafbd30c89e1c857ec8574080dcd83aa52 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Fri, 31 Jul 2026 17:01:00 +0000
Subject: [PATCH] [#794] Wait for the listen port to be open before returning from connection handler start (#796)
---
opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java | 53 +++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
index 27581c4..482b318 100644
--- a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
+++ b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
@@ -190,6 +190,13 @@
*/
private final Object waitListen = new Object();
+ /**
+ * Condition predicate for {@link #waitListen}: set once the handler thread has attempted to open the listen socket
+ * (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;
@@ -680,6 +687,38 @@
}
/**
+ * {@inheritDoc}
+ * <p>
+ * Deliberately left unsynchronized, unlike the overridden {@link Thread#start()}: the state this method touches is
+ * guarded by the {@link #waitListen} monitor, and holding the thread's own monitor across {@code waitListen.wait()}
+ * would block {@link Thread#join()}.
+ */
+ @Override
+ public void start() {
+ // The Directory Server start process should only return when the connection handler port is fully opened
+ // and working. The start method therefore needs to wait for the created thread.
+ synchronized (waitListen) {
+ super.start();
+
+ final long deadlineNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(LISTEN_TIMEOUT_MS);
+ try {
+ while (!listenAttempted) {
+ final long remainingMs = TimeUnit.NANOSECONDS.toMillis(deadlineNanos - System.nanoTime());
+ if (remainingMs <= 0) {
+ logger.error(ERR_CONNHANDLER_TIMEOUT_WAITING_FOR_LISTENER, handlerName,
+ TimeUnit.MILLISECONDS.toSeconds(LISTEN_TIMEOUT_MS));
+ break;
+ }
+ waitListen.wait(remainingMs);
+ }
+ } catch (InterruptedException e) {
+ // If something interrupted the start its probably better to return ASAP.
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
* Operates in a loop, accepting new connections and ensuring that requests on those connections are handled
* properly.
*/
@@ -702,6 +741,7 @@
// so notify here to allow the server startup to complete.
synchronized (waitListen) {
starting = false;
+ listenAttempted = true;
waitListen.notifyAll();
}
}
@@ -717,12 +757,6 @@
}
try {
- // At this point, the connection Handler either started correctly or failed
- // to start but the start process should be notified and resume its work in any cases.
- synchronized (waitListen) {
- waitListen.notifyAll();
- }
-
// If we have gotten here, then we are about to start listening
// for the first time since startup or since we were previously disabled.
startListener();
@@ -750,6 +784,13 @@
} else {
lastIterationFailed = true;
}
+ } finally {
+ // At this point, the connection handler either started listening or failed to do so,
+ // but the start process should be notified and resume its work in any cases.
+ synchronized (waitListen) {
+ listenAttempted = true;
+ waitListen.notifyAll();
+ }
}
}
--
Gitblit v1.10.0