From fd84642bc454b2ec3a6c38a0d27054cde3b44d85 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Mon, 27 Jul 2026 13:03:31 +0000
Subject: [PATCH] [#766] Fix flaky GrizzlyLDAPListenerTestCase load-balancer tests (#767)

---
 opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java |   60 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
index 29dd804..89b7787 100644
--- a/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
+++ b/opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
@@ -28,7 +28,9 @@
 import static org.forgerock.util.Options.defaultOptions;
 import static org.mockito.Mockito.mock;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.ServerSocket;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.concurrent.CountDownLatch;
@@ -187,6 +189,18 @@
         }
     }
 
+    /**
+     * Binds a server socket on a free loopback port and keeps it bound so that the port cannot be
+     * handed out again to a listener which is created later. The socket must be closed before
+     * connection attempts which expect a connection failure are performed against its address.
+     */
+    private static ServerSocket reserveSocketAddress() throws IOException {
+        final ServerSocket socket = new ServerSocket();
+        socket.setReuseAddress(true);
+        socket.bind(loopbackWithDynamicPort());
+        return socket;
+    }
+
     /** Disables logging before the tests. */
     @BeforeClass
     public void disableLogging() {
@@ -285,14 +299,19 @@
                         onlineServerConnectionFactory));
         final InetSocketAddress onlineAddr = onlineServerListener.firstSocketAddress();
 
+        // Reserve the offline server ports, keeping them bound until the proxy listener is
+        // created: a port released too early could be handed out to the proxy listener itself,
+        // making the load balancer connect back to the proxy instead of the online server.
+        final ServerSocket offlineServerSocket1 = reserveSocketAddress();
+        final ServerSocket offlineServerSocket2 = reserveSocketAddress();
         try {
             // Connection pool and load balancing tests.
-            InetSocketAddress offlineAddress1 = findFreeSocketAddress();
+            InetSocketAddress offlineAddress1 = (InetSocketAddress) offlineServerSocket1.getLocalSocketAddress();
             final ConnectionFactory offlineServer1 =
                     Connections.newNamedConnectionFactory(new LDAPConnectionFactory(
                             offlineAddress1.getHostName(),
                             offlineAddress1.getPort()), "offline1");
-            InetSocketAddress offlineAddress2 = findFreeSocketAddress();
+            InetSocketAddress offlineAddress2 = (InetSocketAddress) offlineServerSocket2.getLocalSocketAddress();
             final ConnectionFactory offlineServer2 =
                     Connections.newNamedConnectionFactory(new LDAPConnectionFactory(
                             offlineAddress2.getHostName(),
@@ -329,11 +348,15 @@
                     new ServerConnectionFactoryAdapter(Options.defaultOptions().get(LDAP_DECODE_OPTIONS),
                             proxyServerConnectionFactory));
             final InetSocketAddress proxyAddr = proxyListener.firstSocketAddress();
+            // Release the reserved ports now that every listener is bound: connection attempts
+            // to the offline addresses must fail with a connection error.
+            offlineServerSocket1.close();
+            offlineServerSocket2.close();
+            final LDAPConnectionFactory proxyClientFactory =
+                    new LDAPConnectionFactory(proxyAddr.getHostName(), proxyAddr.getPort());
             try {
                 // Connect and close.
-                final Connection connection =
-                        new LDAPConnectionFactory(proxyAddr.getHostName(),
-                            proxyAddr.getPort()).getConnection();
+                final Connection connection = proxyClientFactory.getConnection();
 
                 assertThat(proxyServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull();
                 assertThat(onlineServerConnection.context.get(10, TimeUnit.SECONDS)).isNotNull();
@@ -343,9 +366,13 @@
 
                 proxyServerConnection.isClosed.await();
             } finally {
+                proxyClientFactory.close();
+                loadBalancer.close();
                 proxyListener.close();
             }
         } finally {
+            offlineServerSocket1.close();
+            offlineServerSocket2.close();
             onlineServerListener.close();
         }
     }
@@ -368,14 +395,19 @@
                         onlineServerConnectionFactory));
         final InetSocketAddress onlineServerAddr = onlineServerListener.firstSocketAddress();
 
+        // Reserve the offline server ports, keeping them bound until the proxy listener is
+        // created: a port released too early could be handed out to the proxy listener itself,
+        // making the load balancer connect back to the proxy instead of the online server.
+        final ServerSocket offlineServerSocket1 = reserveSocketAddress();
+        final ServerSocket offlineServerSocket2 = reserveSocketAddress();
         try {
             // Connection pool and load balancing tests.
-            InetSocketAddress offlineAddress1 = findFreeSocketAddress();
+            InetSocketAddress offlineAddress1 = (InetSocketAddress) offlineServerSocket1.getLocalSocketAddress();
             final ConnectionFactory offlineServer1 =
                     Connections.newNamedConnectionFactory(
                             new LDAPConnectionFactory(offlineAddress1.getHostName(),
                                     offlineAddress1.getPort()), "offline1");
-            InetSocketAddress offlineAddress2 = findFreeSocketAddress();
+            InetSocketAddress offlineAddress2 = (InetSocketAddress) offlineServerSocket2.getLocalSocketAddress();
             final ConnectionFactory offlineServer2 =
                     Connections.newNamedConnectionFactory(
                             new LDAPConnectionFactory(offlineAddress2.getHostName(),
@@ -421,11 +453,15 @@
                             proxyServerConnectionFactory));
             final InetSocketAddress proxyAddr = (InetSocketAddress) proxyListener.firstSocketAddress();
 
+            // Release the reserved ports now that every listener is bound: connection attempts
+            // to the offline addresses must fail with a connection error.
+            offlineServerSocket1.close();
+            offlineServerSocket2.close();
+            final LDAPConnectionFactory proxyClientFactory =
+                    new LDAPConnectionFactory(proxyAddr.getHostName(), proxyAddr.getPort());
             try {
                 // Connect, bind, and close.
-                final Connection connection =
-                        new LDAPConnectionFactory(proxyAddr.getHostName(),
-                            proxyAddr.getPort()).getConnection();
+                final Connection connection = proxyClientFactory.getConnection();
                 try {
                     connection.bind("cn=test", "password".toCharArray());
 
@@ -439,9 +475,13 @@
                 // Wait for connect/close to complete.
                 proxyServerConnection.isClosed.await();
             } finally {
+                proxyClientFactory.close();
+                loadBalancer.close();
                 proxyListener.close();
             }
         } finally {
+            offlineServerSocket1.close();
+            offlineServerSocket2.close();
             onlineServerListener.close();
         }
     }

--
Gitblit v1.10.0