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

Valery Kharseko
17 hours ago 6e6cbc7128ebc845f968ae95be36be29a0a05ef8
refs
author Valery Kharseko <vharseko@3a-systems.ru>
Monday, October 5, 2026 14:41 +0200
committer GitHub <noreply@github.com>
Monday, October 5, 2026 14:41 +0200
commit6e6cbc7128ebc845f968ae95be36be29a0a05ef8
tree b105fdf8ec950cedaf19e05d6a8214215462813a tree | zip | gz
parent 10fc27e672753c6718197cf36da388a5d66bd285 view | diff
[#1160] Report what start-ds printed when the server fails to start, and dump the quicksetup test instance's logs on a failed build (#1169)

Fixes #1160

### Problem

`ServerControllerTest#testStartServer` failed once on CI with `Error
Starting Directory Server. Error code: 1.` and nothing else. The cause
could not be recovered:

- `ServerController.startServerViaAnotherProcess()` threw
`INFO_ERROR_STARTING_SERVER_CODE` with the exit code only. The two
`StartReader`s did read every line `start-ds` printed, but passed them
only to `logger.info` and to the `application` listeners (none in the
test).
- The "Dump the server logs of a failed test" step read only
`target/package/opendj*/logs`, not the quicksetup tests' own instance
under `build/unit-tests/quicksetup/OpenDS`.

`start-ds` exits with 1 in one branch only: `WaitForFileDelete` returned
0, yet the second `--checkStartability` did not report a running server
(98). Before that, `WaitForFileDelete --logFile` prints
`logs/server.out` of the server it waits for to stdout. So the server's
own account of why it stopped already reaches `StartReader`; it just
never reached the exception.

### Change

- `ServerController`: both `StartReader`s append to one shared tail of
the last 100 lines of stdout and stderr (guarded by its own monitor). On
a non-zero exit, the controller waits up to 5 s for each reader to drain
what `start-ds` printed. `waitFor()` returns as soon as the process has
exited, while the readers may still be behind on what is left in the
pipes. A line that a child of `start-ds` prints after the exit arrives
only if the reader is blocked in `read()` at that moment: otherwise the
JDK drains what the pipe holds and closes it
(`ProcessPipeInputStream.processExited()`). Then, if nobody has seen
those lines, it appends them to the old message:
```
Error Starting Directory Server. Error code: 1.
Last lines of the start command output:
<lines>
```
The first line is the translated `INFO_ERROR_STARTING_SERVER_CODE`; only
the new `INFO_ERROR_STARTING_SERVER_OUTPUT` part is untranslated.
- "Nobody has seen them" means the start was called with
`suppressOutput` or without an `application`. Otherwise the readers have
already shown every line to the listeners (`setup --verbose`, the
uninstall CLI and GUI), and the error keeps the old message instead of
repeating up to 100 lines.
- A start command that printed nothing keeps the old message. A
successful start is unchanged.
- `.github/workflows/build.yml`: the dump step also prints
`opendj-server-legacy/build/unit-tests/quicksetup/OpenDS/logs/{server.out,errors}`.
- New `ServerControllerStartFailureTest`: the installation is a bare
temporary directory whose start command (`bin/start-ds`, or
`bat/start-ds.bat` on Windows) is a stand-in script that prints and
exits with 1. Six cases:
- both streams reach the message;
- out of 150 lines, only the last 100 do;
- a line printed by a background child after the start command has
exited reaches the message (skipped on Windows). The script prints a
line and sleeps for 1 s before it exits, so the stdout reader is already
blocked in `read()` and the JDK waits for that read instead of closing
the pipe;
- a silent failure keeps the old message;
- with an application whose listener is not suppressed, the listener
gets both lines and the message stays the old one;
- with the same application and `suppressOutput`, the listener gets
nothing and the message carries both lines.

Every expected message is built from `INFO_ERROR_STARTING_SERVER_CODE`,
not from its English text, so the cases also pass under a translated
default locale.

This PR does not fix the flake itself. It makes the next occurrence name
its cause, which item 3 of the issue waits for.

### Testing

- `mvn -Pprecommit verify -pl opendj-server-legacy -am
-Dit.test=ServerControllerStartFailureTest,ServerControllerTest`: BUILD
SUCCESS. `ServerControllerStartFailureTest` 6/6, `ServerControllerTest`
2/2. `attach-javadocs` (doclint) passed.
- RED, by running the new test with TestNG directly against mutants of
`ServerController` (each one goes red in the case named for it):
- both drain `join`s deleted:
`failedStartReportsWhatIsPrintedAfterTheExit`, in every run. On Linux it
also fails other cases from time to time, since without the joins the
message can also miss lines printed before the exit (in
`eclipse-temurin:21-jdk`, over 20 runs:
`failedStartReportsBothOutputStreams` 19,
`failedStartReportsOnlyTheLastLines` 11,
`failedStartShownToTheListenersKeepsTheExitCodeMessage` 9);
- `removeFirst` dropped (no tail bound):
`failedStartReportsOnlyTheLastLines`;
- output always reported:
`failedStartShownToTheListenersKeepsTheExitCodeMessage`;
- output reported only without an application:
`failedStartHiddenFromTheListenersReportsBothOutputStreams`.
- Locale: the failsafe classpath holds only `opendj.jar`, whose bundle
is the English base; the translations ship in `lib/opendj_<lang>.jar`.
With `opendj_fr.jar` and `opendj_ja.jar` added to the classpath (as an
IDE run with `target/classes` has them), the new test passes under
`-Duser.language=en`, `fr` and `ja`. The previous version of the test
fails 2 cases under `fr` and `ja` against this code.
- Linux: this case failed on CI at b35afb762c (`build-maven
(ubuntu-latest, 21)` and `(ubuntu-latest, 25)`, run 37187761859). Its
stand-in exited at once, so the line of the child was lost whenever the
reader was not yet blocked in `read()` at the exit. In
`eclipse-temurin:21-jdk` that version failed in 5 of 20 runs, then in 0
of the next 30. The current version passes 20 of 20 in
`eclipse-temurin:21-jdk` (and 30 of 30 in a second series) and in
`eclipse-temurin:25-jdk`, and 20 of 20 on macOS with JDK 21. With both
joins deleted, it fails in every run: 10 of 10 on each, and 20 of 20 in
the second series on 21.
- Windows: not run. CI runs failsafe only on the Linux legs (`-P
precommit` is set only when `runner.os == 'Linux'`), and I have no
Windows host, so the `.bat` stand-ins (`@for /L`, `1>&2`, `@exit /b 1`,
CRLF) have not run anywhere.
3 files modified
1 files added
353 ■■■■■ changed files
.github/workflows/build.yml 6 ●●●●● diff | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java 71 ●●●●● diff | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/quickSetup.properties 1 ●●●●● diff | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/quicksetup/util/ServerControllerStartFailureTest.java 275 ●●●●● diff | view | raw | blame | history