This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 9ba5512204 Fix heap-dump test OOM by not buffering a full JVM dump
through MockRestClient
9ba5512204 is described below
commit 9ba55122046f02a4b59220fd803d3b859b66d9e2
Author: James Bognar <[email protected]>
AuthorDate: Thu Jun 18 14:10:25 2026 -0400
Fix heap-dump test OOM by not buffering a full JVM dump through
MockRestClient
---
.../juneau/rest/server/management/Dumps_Test.java | 52 ++++++++++++++++++----
.../juneau/rest/server/management/DumpsMixin.java | 19 +++++++-
.../rest/server/management/DumpsResource.java | 19 +++++++-
3 files changed, 80 insertions(+), 10 deletions(-)
diff --git
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/management/Dumps_Test.java
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/management/Dumps_Test.java
index feb2d64765..0a49655924 100644
---
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/management/Dumps_Test.java
+++
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/management/Dumps_Test.java
@@ -112,10 +112,33 @@ class Dumps_Test extends TestBase {
c.get("/threaddump").run().assertStatus(200).assertContent().asString().isNotEmpty();
}
- @Test void c02_heapDumpEnabled() throws Exception {
- var c = MockRestClient.buildLax(B.class);
- var bytes =
c.get("/heapdump").run().assertStatus(200).getContent().asBytes();
- assertTrue(bytes.length > 0, "Heap dump body should be
non-empty");
+ // The enabled-path heap-dump handler body (resolve stream -> set
headers -> stream it back) is covered with a
+ // FAKE DumpsManager that returns a tiny in-memory stream instead of a
real JVM heap dump. We must NOT drive a
+ // REAL enabled /heapdump through MockRestClient: MockServletResponse
buffers the whole response in memory, so a
+ // full JVM heap dump would OOM the constrained CI fork. (Real
dumpHeap content is covered by worker test a03.)
+
+ /** A DumpsManager that enables heap dumps and returns a tiny fixed
stream — no real (huge) JVM dump. */
+ public static class FakeHeapDumpManager extends DumpsManager {
+ @Override public DumpsSettings resolveSettings(RestContext
context) {
+ return DumpsSettings.create().enableHeapDump().build();
+ }
+ @Override public InputStream heapDumpStream(boolean live) {
+ return new ByteArrayInputStream("JAVA PROFILE
1.0.2\0".getBytes());
+ }
+ }
+
+ @Rest(mixins={DumpsMixin.class})
+ public static class HF extends BasicRestServlet {
+ private static final long serialVersionUID = 1L;
+ @Bean public DumpsManager dumpsManager() { return new
FakeHeapDumpManager(); }
+ }
+
+ @Test void c03_heapDumpEnabled_handlerBody_fakeManager() throws
Exception {
+ var c = MockRestClient.buildLax(HF.class);
+ var body = c.get("/heapdump").run().assertStatus(200)
+
.assertHeader("Content-Type").is("application/octet-stream")
+ .getContent().asString();
+ assertTrue(body.startsWith("JAVA PROFILE"), "Fake heap-dump
stream should be returned verbatim");
}
//
=================================================================================
@@ -141,10 +164,23 @@ class Dumps_Test extends TestBase {
c.get("/dumps/threaddump").run().assertStatus(200).assertContent().asString().isNotEmpty();
}
- @Test void d02_resourceHeapDumpEnabled() throws Exception {
- var c = MockRestClient.buildLax(D.class);
- var bytes =
c.get("/dumps/heapdump").run().assertStatus(200).getContent().asBytes();
- assertTrue(bytes.length > 0, "Heap dump body should be
non-empty");
+ // Resource-flavor enabled heap-dump handler body via the fake manager
(no real JVM dump — see the mixin note).
+ @Rest(path="/dumps")
+ public static class FakeHeapChild extends DumpsResource {
+ @Bean public DumpsManager dumpsManager() { return new
FakeHeapDumpManager(); }
+ }
+
+ @Rest(children={FakeHeapChild.class})
+ public static class DF extends BasicRestServlet {
+ private static final long serialVersionUID = 1L;
+ }
+
+ @Test void d02_resourceHeapDumpEnabled_handlerBody_fakeManager() throws
Exception {
+ var c = MockRestClient.buildLax(DF.class);
+ var body = c.get("/dumps/heapdump").run().assertStatus(200)
+
.assertHeader("Content-Type").is("application/octet-stream")
+ .getContent().asString();
+ assertTrue(body.startsWith("JAVA PROFILE"), "Fake heap-dump
stream should be returned verbatim");
}
// A child with no DumpsSettings bean -> deny-by-default on both ops.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsMixin.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsMixin.java
index 5446627339..4353f45a08 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsMixin.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsMixin.java
@@ -45,7 +45,22 @@ import org.apache.juneau.rest.server.servlet.*;
@Rest
public class DumpsMixin extends RestMixin {
- private final DumpsManager manager = new DumpsManager();
+ private final DumpsManager defaultManager = new DumpsManager();
+
+ /**
+ * Returns the {@link DumpsManager} worker, resolved from the host bean
store when a consumer registers one,
+ * else a built-in default. Resolving through the bean store lets
consumers (and tests) supply an alternate
+ * worker without subclassing.
+ *
+ * @param req The HTTP request (its context's bean store is searched).
+ * @return The resolved manager; never <jk>null</jk>.
+ */
+ @SuppressWarnings({
+ "resource" // The bean store is owned by the RestContext; this
only borrows a bean and must not close it.
+ })
+ protected DumpsManager manager(RestRequest req) {
+ return
req.getContext().getBeanStore().getBean(DumpsManager.class).orElse(defaultManager);
+ }
/**
* [GET /threaddump] - Full thread dump from the {@link
java.lang.management.ThreadMXBean ThreadMXBean}.
@@ -63,6 +78,7 @@ public class DumpsMixin extends RestMixin {
description="Renders a full thread dump from the JVM
ThreadMXBean. Disabled by default; opt in via DumpsSettings."
)
public String getThreadDump(RestRequest req) {
+ var manager = manager(req);
if (!
manager.resolveSettings(req.getContext()).isThreadDumpEnabled())
throw new Forbidden("The /threaddump endpoint is
disabled. Register a DumpsSettings bean with threadDump enabled to use it.");
return manager.threadDump();
@@ -91,6 +107,7 @@ public class DumpsMixin extends RestMixin {
"resource" // The returned stream is handed off to the
framework's InputStreamProcessor, which pipes then closes it (and the backing
temp file self-deletes on close).
})
public InputStream getHeapDump(RestRequest req, RestResponse res)
throws IOException {
+ var manager = manager(req);
if (!
manager.resolveSettings(req.getContext()).isHeapDumpEnabled())
throw new Forbidden("The /heapdump endpoint is
disabled. Register a DumpsSettings bean with heapDump enabled to use it.");
var stream = manager.heapDumpStream(true);
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsResource.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsResource.java
index c9b26c6aa5..0da9039996 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsResource.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/management/DumpsResource.java
@@ -46,7 +46,22 @@ import org.apache.juneau.rest.server.servlet.*;
@Rest(path="/dumps")
public class DumpsResource extends BasicRestResource {
- private final DumpsManager manager = new DumpsManager();
+ private final DumpsManager defaultManager = new DumpsManager();
+
+ /**
+ * Returns the {@link DumpsManager} worker, resolved from the bean
store when a consumer registers one, else a
+ * built-in default. Resolving through the bean store lets consumers
(and tests) supply an alternate worker
+ * without subclassing.
+ *
+ * @param req The HTTP request (its context's bean store is searched).
+ * @return The resolved manager; never <jk>null</jk>.
+ */
+ @SuppressWarnings({
+ "resource" // The bean store is owned by the RestContext; this
only borrows a bean and must not close it.
+ })
+ protected DumpsManager manager(RestRequest req) {
+ return
req.getContext().getBeanStore().getBean(DumpsManager.class).orElse(defaultManager);
+ }
/**
* [GET /threaddump] - Full thread dump from the {@link
java.lang.management.ThreadMXBean ThreadMXBean}.
@@ -61,6 +76,7 @@ public class DumpsResource extends BasicRestResource {
description="Renders a full thread dump from the JVM
ThreadMXBean. Disabled by default; opt in via DumpsSettings."
)
public String getThreadDump(RestRequest req) {
+ var manager = manager(req);
if (!
manager.resolveSettings(req.getContext()).isThreadDumpEnabled())
throw new Forbidden("The /threaddump endpoint is
disabled. Register a DumpsSettings bean with threadDump enabled to use it.");
return manager.threadDump();
@@ -85,6 +101,7 @@ public class DumpsResource extends BasicRestResource {
"resource" // The returned stream is handed off to the
framework's InputStreamProcessor, which pipes then closes it (and the backing
temp file self-deletes on close).
})
public InputStream getHeapDump(RestRequest req, RestResponse res)
throws IOException {
+ var manager = manager(req);
if (!
manager.resolveSettings(req.getContext()).isHeapDumpEnabled())
throw new Forbidden("The /heapdump endpoint is
disabled. Register a DumpsSettings bean with heapDump enabled to use it.");
var stream = manager.heapDumpStream(true);