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

Valery Kharseko
yesterday a4a5cd8e4c89ba459c1105f4b25a499895a3a571
refs
author Valery Kharseko <vharseko@3a-systems.ru>
Friday, October 9, 2026 09:20 +0200
committer GitHub <noreply@github.com>
Friday, October 9, 2026 09:20 +0200
commita4a5cd8e4c89ba459c1105f4b25a499895a3a571
tree 1539ce6d68caa679dabc577e2587536d83767106 tree | zip | gz
parent 3f4deb91789189521d577457bd6da27de8fd75b1 view | diff
[#1180] Report the fail-over failure of the Grizzly proxy listener tests instead of timing out (#1181)

Fixes #1180

### Problem

`GrizzlyLDAPListenerTestCase#testLDAPListenerProxyDuringHandleAccept`
timed out on `windows-latest` / JDK 26 with a bare `TimeoutException` at
the first wait (line 605).

The overridden `handleAccept` connects to an offline port and then to
the online listener, and resolves the proxy's `context` only after both
succeed. It runs synchronously on a selector thread of the server
transport. Any exception before the last line therefore left that
`context` unresolved. The causes could be: the offline connection
unexpectedly succeeding, a `TimeoutResultException` (which is not a
`ConnectionException`), a failed online connection, or an unchecked
exception such as a failed loopback bind in `findFreeSocketAddress()`.
In each case the listener only closed the client connection, and the
test waited out its 10 seconds without a cause. The class runs with
logging at `SEVERE`, so the job log shows nothing either. Every way the
fail-over can fail looks the same as the CI failure, so the run cannot
tell us which one happened.

`testLDAPListenerProxyDuringHandleBind` had the same blind spot in a
different form. It failed the bind with a plain `Result`, which the
listener cannot encode as a bind response (`ClassCastException:
ResultImpl cannot be cast to BindResult` inside Grizzly). The connection
was closed and the client saw only `Server Connection Closed`, or the
test hung until its `timeOut`.

### Fix (test only)

- The overridden `handleAccept` resolves the proxy's `context` with the
failure before rethrowing it, unchecked exceptions included, so the
assert fails at once with the actual cause.
- The first wait is 30 s instead of 10 s. `handleAccept` makes two
nested connections before it resolves the context, and each may take up
to the default connect timeout (10 s). A shorter wait would again expire
before a failed attempt can report itself. Because a failure is now
reported as soon as it happens, the longer window does not hide
anything.
- The bind variant fails the bind with a `BindResult`, for unchecked
exceptions too. Only the diagnostic message of that result reaches the
client, never its cause, so the message carries the nested cause.
- Both tests share one `failOverToOnlineServer` helper. It closes the
offline and online connection factories (previously leaked). It connects
to the numeric address of the offline port: the address of the reserved
socket keeps the host name `localhost`, so neither `getHostName()` nor
`getHostString()` gives a numeric host.
- The listener addresses (the online server and both proxies) have no
host name, so `getHostName()` reverse-resolved them, for the online
server inside `handleAccept`, outside the connect timeout. They are now
used through `getHostString()`.
- The client connection factories are closed in `finally`. In
`...DuringHandleAccept`, `connection.close()` moved into `finally`, the
`isClosed` wait is bounded, and a `@Test(timeOut = 60000)` was added.
The `timeOut` of `...DuringHandleBind` goes from 10 s to 60 s, for the
same reason as the longer wait.

The root cause on Windows is still unknown: it cannot be reproduced
locally. This change makes the next occurrence name it. I also tried
reserving the offline port with a socket that is bound but not
listening, to rule out the port being reused. On macOS such a port does
not refuse connections: the connect times out instead. So the offline
port is still picked with `findFreeSocketAddress()`.

### Verification

Each mutant was run against both tests, before (BASE of the PR) and
after:

| Mutant | Before | After |
|---|---|---|
| offline address = the listening online one | accept: bare
`TimeoutException` after 11.2 s; bind: `Server Connection Closed`,
`ClassCastException` in the log | both fail in about 1 s with
`Connection to offline server succeeded unexpectedly` |
| online port = a free port (connect refused) | — | accept fails in 1 s,
with `Connection refused` in the cause chain; bind fails in 0.4 s with
`Unexpected exception when connecting to online server:
...ConnectionException: Connect Error: Connection refused` |
| `RuntimeException` at the start of the helper | — | accept fails in 1
s with `mutant: loopback bind failed`; bind fails in 0.5 s with
`java.lang.RuntimeException: mutant: loopback bind failed` |

Against the first commit of this PR, the last two mutants gave: a bind
message without the cause (`Unexpected exception when connecting to
online server`); and for the `RuntimeException`, a bare
`TimeoutException` after 31 s in the accept test and `Server Connection
Closed` in the bind test.

Without a mutant: all of `opendj-grizzly` passes, 1040 tests in 9
classes with 0 failures (`mvn verify`, checkstyle included).
1 files modified
159 ■■■■■ changed files
opendj-grizzly/src/test/java/org/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java 159 ●●●●● diff | view | raw | blame | history