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

Valery Kharseko
18 hours ago 12a5e3a1dfca3b334edd9378875464c80870fc4f
Fix JMX port-release test race on Linux (#1267)

The JMX port-release regression test added in #1264 can connect
successfully immediately after handler shutdown on Linux: the JDK may
defer socket disposal until a pending accept operation finishes. All
five Ubuntu jobs in [run
41635504530](https://github.com/OpenIdentityPlatform/OpenDJ/actions/runs/41635504530)
failed this check.

Retry the port-release assertion using the existing `TestTimer`,
checking both connection refusal and successful rebinding in the same
attempt. Retry `BindException` as well; propagate other I/O errors. The
timer permits five seconds of total sleep in 10 ms steps; individual
connection attempts retain their existing timeout. Production code is
unchanged.

Follow-up to #1264, the implementation for #1261.

Validation:

- The original test fails on Linux / JDK 11 with the stopped registry
port still accepting connections.
- The updated class passes all 10 tests on Linux / JDK 11, 21 and 27,
with no skips.
- Maven reactor `-Pprecommit verify` with the full JMX package passes:
55 passed, 1 skipped, including Javadoc. The skip is the existing
wildcard-hostname case on the local loopback hostname.
- Removing the production `close(connectorSocketFactory)` call in a
separately compiled mutant makes all four port-release cases fail. The
retry still detects the actual leak.
- `git diff --check` passes. The full repository test suite was not run
locally.

```sh
mvn -o -Pprecommit verify -pl opendj-server-legacy -am \
'-Dit.test=org/opends/server/protocols/jmx/**' \
-Dfailsafe.failIfNoSpecifiedTests=false \
-Dtest=NoTestsSelected -Dsurefire.failIfNoSpecifiedTests=false
```
1 files modified
29 ■■■■■ changed files
opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java 29 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/protocols/jmx/JmxListenAddressTestCase.java
@@ -15,11 +15,14 @@
 */
package org.opends.server.protocols.jmx;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.*;
import static org.opends.server.TestCaseUtils.*;
import static org.testng.Assert.*;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -49,6 +52,7 @@
import org.opends.server.extensions.InitializationUtils;
import org.opends.server.types.Entry;
import org.opends.server.types.HostPort;
import org.opends.server.util.TestTimer;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -271,14 +275,25 @@
    return port;
  }
  private static void assertPortReleased(InetAddress address, int port) throws IOException
  private static void assertPortReleased(InetAddress address, int port) throws Exception
  {
    assertFalse(isAcceptingConnections(address, port), "The stopped RMI listener still accepts connections on " + port);
    try (ServerSocket socket = new ServerSocket())
    {
      socket.setReuseAddress(true);
      socket.bind(new InetSocketAddress(address, port));
    }
    // The JDK may defer closing the socket until its accept thread exits the pending I/O.
    // On Linux, close() can therefore return before the port stops accepting connections.
    new TestTimer.Builder().maxSleep(5, SECONDS).sleepTimes(10, MILLISECONDS).toTimer()
        .repeatUntilSuccess(() ->
        {
          assertFalse(isAcceptingConnections(address, port),
              "The stopped RMI listener still accepts connections on " + port);
          try (ServerSocket socket = new ServerSocket())
          {
            socket.setReuseAddress(true);
            socket.bind(new InetSocketAddress(address, port));
          }
          catch (BindException e)
          {
            throw new AssertionError("The stopped RMI listener's port cannot be rebound: " + port, e);
          }
        });
  }
  /** A change that restarts the RMI connector keeps the listen address that is reported. */