EmbeddedOpenDJTest#testOpenDJ failed on windows-latest / Java 26 while every other job of the same run — including windows-latest / Java 11 — was green:
[ERROR] EmbeddedOpenDJTest.testOpenDJ:91 expected [false] but found [true]
java.lang.AssertionError: expected [false] but found [true]
at org.testng.Assert.assertFalse(Assert.java:75)
at org.openidentityplatform.opendj.embedded.EmbeddedOpenDJTest.testOpenDJ(EmbeddedOpenDJTest.java:91)
Run: https://github.com/OpenIdentityPlatform/OpenDJ/actions/runs/30554186279/job/90910350398 (observed on PR #793, whose diff does not touch opendj-embedded; the same job passed on the previous commit of that PR and on PRs #790, #791, #795).
What actually fails
Everything the test exercises works — the server starts, the LDIF import, the export, the bind and the search all succeed, and the shutdown completes cleanly:
15:05:16.685 [main] INFO o.o.s.backends.pluggable.BackendImpl - The backend userRoot is now taken offline
15:05:16.692 [main] INFO o.opends.server.core.DirectoryServer - The Directory Server is now stopped
The only failing step is the assertion added in #763, which checks that the per-instance temporary directory is gone after close():
https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java#L86-L92
//stop OpenDJ
embeddedOpenDJ.close();
assertFalse(embeddedOpenDJ.isRunning());
//the per-instance temporary directory is deleted on close
assertFalse(serverRoot.exists());
assertFalse(serverRoot.getParentFile().exists());
Cause
EmbeddedOpenDJ.close() deletes the instance directory with a single best-effort call, immediately after server.stop() returns:
https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java#L166-L178
@Override
public void close() {
if (server.isRunning()) {
...
server.stop(...);
}
// close() is also registered as a shutdown hook, so deletion must stay idempotent
FileUtils.deleteQuietly(instanceDirectory);
}
On Windows a file cannot be deleted while any handle to it is still open, and server.stop() returning does not guarantee that every handle — backend files, the JSON audit event handler, server.out, the extracted opendj.zip — has already been released; some are closed by background threads shortly afterwards. When that race is lost, deleteQuietly fails, swallows the exception, and the directory survives. POSIX platforms are unaffected because unlinking an open file is allowed there, which is why only the Windows jobs can ever see this.
So the assertion is not wrong — it reports a real, Windows-only defect that is normally invisible:
- every embedded instance can leak its whole temporary directory (an extracted server root, backend data included) into
%TEMP% on Windows;
close() gives the caller no indication that the cleanup failed, since deleteQuietly discards the error.
Suggested fix
In EmbeddedOpenDJ.close(), retry the deletion for a short bounded period instead of attempting it once, so that handles released just after shutdown are still picked up; log a warning if the directory is still there when the retries are exhausted, and register the leftovers for deletion on JVM exit as a last resort. Keeping the operation idempotent matters — close() is also registered as a shutdown hook.
With that in place the existing assertions in EmbeddedOpenDJTest become deterministic on Windows and can stay as they are.
EmbeddedOpenDJTest#testOpenDJfailed onwindows-latest/ Java 26 while every other job of the same run — includingwindows-latest/ Java 11 — was green:Run: https://github.com/OpenIdentityPlatform/OpenDJ/actions/runs/30554186279/job/90910350398 (observed on PR #793, whose diff does not touch
opendj-embedded; the same job passed on the previous commit of that PR and on PRs #790, #791, #795).What actually fails
Everything the test exercises works — the server starts, the LDIF import, the export, the bind and the search all succeed, and the shutdown completes cleanly:
The only failing step is the assertion added in #763, which checks that the per-instance temporary directory is gone after
close():https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java#L86-L92
Cause
EmbeddedOpenDJ.close()deletes the instance directory with a single best-effort call, immediately afterserver.stop()returns:https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java#L166-L178
On Windows a file cannot be deleted while any handle to it is still open, and
server.stop()returning does not guarantee that every handle — backend files, the JSON audit event handler,server.out, the extractedopendj.zip— has already been released; some are closed by background threads shortly afterwards. When that race is lost,deleteQuietlyfails, swallows the exception, and the directory survives. POSIX platforms are unaffected because unlinking an open file is allowed there, which is why only the Windows jobs can ever see this.So the assertion is not wrong — it reports a real, Windows-only defect that is normally invisible:
%TEMP%on Windows;close()gives the caller no indication that the cleanup failed, sincedeleteQuietlydiscards the error.Suggested fix
In
EmbeddedOpenDJ.close(), retry the deletion for a short bounded period instead of attempting it once, so that handles released just after shutdown are still picked up; log a warning if the directory is still there when the retries are exhausted, and register the leftovers for deletion on JVM exit as a last resort. Keeping the operation idempotent matters —close()is also registered as a shutdown hook.With that in place the existing assertions in
EmbeddedOpenDJTestbecome deterministic on Windows and can stay as they are.