Skip to content

Commit f7871b9

Browse files
committed
Test for monitoring of Embedded GlassFish via JMX
1 parent da7c941 commit f7871b9

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

appserver/tests/embedded/runnable/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@
5151
<groupId>org.glassfish.main.extras</groupId>
5252
<artifactId>glassfish-embedded-web</artifactId>
5353
</dependency>
54+
<dependency>
55+
<groupId>org.glassfish.main.flashlight</groupId>
56+
<artifactId>flashlight-agent</artifactId>
57+
<version>${project.version}</version>
58+
</dependency>
5459
</dependencies>
5560

5661
<build>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
package org.glassfish.tests.embedded.runnable.app;
17+
18+
import jakarta.servlet.ServletException;
19+
import jakarta.servlet.annotation.WebServlet;
20+
import jakarta.servlet.http.HttpServlet;
21+
import jakarta.servlet.http.HttpServletRequest;
22+
import jakarta.servlet.http.HttpServletResponse;
23+
24+
import java.io.IOException;
25+
import java.util.logging.Logger;
26+
27+
/**
28+
* @author Ondro Mihalyi
29+
*/
30+
@WebServlet("/monitoring")
31+
public class MonitoringApp extends HttpServlet {
32+
33+
private static final Logger LOG = Logger.getLogger(MonitoringApp.class.getName());
34+
35+
@Override
36+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
37+
LOG.info("Monitoring servlet accessed");
38+
resp.getWriter().println("Monitoring test servlet");
39+
}
40+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
package org.glassfish.tests.embedded.runnable;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Optional;
23+
import java.util.Set;
24+
import java.util.concurrent.TimeUnit;
25+
import java.util.logging.Logger;
26+
27+
import javax.management.MBeanServerConnection;
28+
import javax.management.ObjectName;
29+
import javax.management.remote.JMXConnector;
30+
import javax.management.remote.JMXConnectorFactory;
31+
import javax.management.remote.JMXServiceURL;
32+
33+
import org.glassfish.tests.embedded.runnable.TestArgumentProviders.GfEmbeddedJarNameProvider;
34+
import org.glassfish.tests.embedded.runnable.app.MonitoringApp;
35+
import org.jboss.shrinkwrap.api.ShrinkWrap;
36+
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
37+
import org.jboss.shrinkwrap.api.spec.WebArchive;
38+
import org.junit.jupiter.params.ParameterizedTest;
39+
import org.junit.jupiter.params.provider.ArgumentsSource;
40+
41+
import static org.glassfish.tests.embedded.runnable.ShrinkwrapUtils.logArchiveContent;
42+
import static org.junit.jupiter.api.Assertions.assertNotNull;
43+
import static org.junit.jupiter.api.Assertions.assertTrue;
44+
45+
/**
46+
* @author Ondro Mihalyi
47+
*/
48+
public class MonitoringTest {
49+
50+
private static final Logger LOG = Logger.getLogger(MonitoringTest.class.getName());
51+
private static final int JMX_PORT = 8686;
52+
private static final int WAIT_SECONDS = 10;
53+
54+
@ParameterizedTest
55+
@ArgumentsSource(GfEmbeddedJarNameProvider.class)
56+
void testJmxMonitoringWithFlashlightAgent(String gfEmbeddedJarName) throws Exception {
57+
File warFile = null;
58+
Process gfEmbeddedProcess = null;
59+
JMXConnector jmxConnector = null;
60+
try {
61+
warFile = createMonitoringApp();
62+
63+
gfEmbeddedProcess = startGlassFishWithJmx(gfEmbeddedJarName, warFile);
64+
65+
jmxConnector = connectToJmx();
66+
MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
67+
68+
bootAmxAndWait(mbsc);
69+
70+
verifyMonitoringMBeans(mbsc);
71+
72+
} finally {
73+
cleanup(jmxConnector, gfEmbeddedProcess, warFile);
74+
}
75+
}
76+
77+
private File createMonitoringApp() throws Exception {
78+
WebArchive war = ShrinkWrap.create(WebArchive.class, "monitoring-test.war")
79+
.addClass(MonitoringApp.class);
80+
81+
File warFile = File.createTempFile("monitoring-test", ".war");
82+
war.as(ZipExporter.class).exportTo(warFile, true);
83+
logArchiveContent(war, "monitoring-test.war", LOG::info);
84+
return warFile;
85+
}
86+
87+
private File createMonitoringPropertiesFile() throws Exception {
88+
File propertiesFile = File.createTempFile("monitoring", ".properties");
89+
java.nio.file.Files.write(propertiesFile.toPath(), List.of(
90+
"configs.config.server-config.monitoring-service.module-monitoring-levels.http-service=HIGH",
91+
"configs.config.server-config.monitoring-service.module-monitoring-levels.thread-pool=HIGH"
92+
));
93+
return propertiesFile;
94+
}
95+
96+
private Process startGlassFishWithJmx(String gfEmbeddedJarName, File warFile) throws IOException {
97+
List<String> arguments = new ArrayList<>();
98+
arguments.add(ProcessHandle.current().info().command().get());
99+
arguments.addAll(List.of(
100+
"-javaagent:flashlight-agent.jar",
101+
"-Dcom.sun.management.jmxremote",
102+
"-Dcom.sun.management.jmxremote.port=" + JMX_PORT,
103+
"-Dcom.sun.management.jmxremote.authenticate=false",
104+
"-Dcom.sun.management.jmxremote.ssl=false",
105+
"-jar", gfEmbeddedJarName,
106+
"--noPort",
107+
"enable-monitoring --modules http-service",
108+
warFile.getAbsolutePath()
109+
));
110+
111+
return new ProcessBuilder()
112+
.redirectOutput(ProcessBuilder.Redirect.PIPE)
113+
.redirectError(ProcessBuilder.Redirect.PIPE)
114+
.command(arguments)
115+
.start();
116+
}
117+
118+
private JMXConnector connectToJmx() throws Exception {
119+
JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + JMX_PORT + "/jmxrmi");
120+
121+
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
122+
try {
123+
return JMXConnectorFactory.connect(serviceURL, null);
124+
} catch (Exception e) {
125+
Thread.sleep(500);
126+
}
127+
}
128+
throw new IllegalStateException("Could not connect to JMX in " + WAIT_SECONDS + " seconds");
129+
}
130+
131+
private void bootAmxAndWait(MBeanServerConnection mbsc) throws Exception {
132+
ObjectName bootAMXObjectName = new ObjectName("amx-support:type=boot-amx");
133+
assertTrue(mbsc.isRegistered(bootAMXObjectName), "bootAMX is registered");
134+
135+
mbsc.invoke(bootAMXObjectName, "bootAMX", null, null);
136+
137+
// Wait for AMX runtime to be available
138+
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
139+
Set<ObjectName> runtimeBeans = mbsc.queryNames(new ObjectName("amx:pp=/,type=runtime"), null);
140+
if (!runtimeBeans.isEmpty()) {
141+
return;
142+
}
143+
Thread.sleep(500);
144+
}
145+
throw new IllegalStateException("AMX runtime not available after " + WAIT_SECONDS + " seconds");
146+
}
147+
148+
private void verifyMonitoringMBeans(MBeanServerConnection mbsc) throws Exception {
149+
Set<ObjectName> requestBeans = mbsc.queryNames(new ObjectName("amx:type=request-mon,*"), null);
150+
assertTrue(!requestBeans.isEmpty(), "Request monitoring MBean should be present");
151+
152+
// Verify we can read monitoring data
153+
ObjectName requestBean = requestBeans.iterator().next();
154+
assertNotNull(mbsc.getAttribute(requestBean, "countrequests"), "Should be able to read request count");
155+
}
156+
157+
private void cleanup(JMXConnector jmxConnector, Process process, File... files) throws InterruptedException {
158+
if (jmxConnector != null) try { jmxConnector.close(); } catch (Exception ignored) {}
159+
if (process != null && process.isAlive()) {
160+
process.destroyForcibly();
161+
process.waitFor(5, TimeUnit.SECONDS);
162+
}
163+
cleanupFiles(files);
164+
}
165+
166+
private void cleanupFiles(File... files) {
167+
for (File file : files) {
168+
Optional.ofNullable(file).ifPresent(File::delete);
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)