Skip to content

Commit 28dd44c

Browse files
committed
Test improvement - connect to JMX via socket instead of port
Disable test for Embedded Web - it doesn't support AMX because it doesn't include glassfish-mbeanserver artifact.
1 parent be9906c commit 28dd44c

File tree

4 files changed

+139
-69
lines changed

4 files changed

+139
-69
lines changed

appserver/tests/embedded/runnable/src/test/java/org/glassfish/tests/embedded/runnable/GfEmbeddedUtils.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,22 @@ public static Process runGlassFishEmbedded(String glassfishEmbeddedJarName, Stri
5353
return runGlassFishEmbedded(glassfishEmbeddedJarName, List.of(), additionalArguments);
5454
}
5555

56-
public static Process runGlassFishEmbedded(String glassfishEmbeddedJarName, List<String> jvmOpts, String... additionalArguments) throws IOException {
56+
public static Process runGlassFishEmbedded(String glassfishEmbeddedJarName, List<String> jvmOpts, String...
57+
additionalArguments) throws IOException {
58+
return runGlassFishEmbedded(glassfishEmbeddedJarName, false, jvmOpts, additionalArguments);
59+
}
60+
61+
public static Process runGlassFishEmbedded(String glassfishEmbeddedJarName, boolean keepRunning, List<String> jvmOpts, String... additionalArguments) throws IOException {
5762
List<String> arguments = new ArrayList<>();
5863
arguments.add(ProcessHandle.current().info().command().get());
5964
addDebugArgsIfDebugEnabled(arguments);
6065
arguments.addAll(jvmOpts);
6166
arguments.addAll(List.of(
6267
"-jar", glassfishEmbeddedJarName,
63-
"--noPort",
64-
"--stop"));
68+
"--noPort"));
69+
if (!keepRunning) {
70+
arguments.add("--stop");
71+
}
6572
for (String argument : additionalArguments) {
6673
arguments.add(argument);
6774
}

appserver/tests/embedded/runnable/src/test/java/org/glassfish/tests/embedded/runnable/MonitoringTest.java

Lines changed: 77 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,141 +15,152 @@
1515
*/
1616
package org.glassfish.tests.embedded.runnable;
1717

18+
import com.sun.tools.attach.VirtualMachine;
19+
import com.sun.tools.attach.VirtualMachineDescriptor;
20+
1821
import java.io.File;
1922
import java.io.IOException;
20-
import java.util.ArrayList;
2123
import java.util.List;
2224
import java.util.Optional;
25+
import java.util.Properties;
2326
import java.util.Set;
2427
import java.util.concurrent.TimeUnit;
2528
import java.util.logging.Logger;
2629

30+
import javax.management.JMException;
2731
import javax.management.MBeanServerConnection;
2832
import javax.management.ObjectName;
2933
import javax.management.remote.JMXConnector;
3034
import javax.management.remote.JMXConnectorFactory;
3135
import javax.management.remote.JMXServiceURL;
3236

3337
import org.glassfish.tests.embedded.runnable.TestArgumentProviders.GfEmbeddedJarNameProvider;
38+
import org.jboss.shrinkwrap.api.ShrinkWrap;
39+
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
40+
import org.jboss.shrinkwrap.api.spec.WebArchive;
3441
import org.junit.jupiter.params.ParameterizedTest;
3542
import org.junit.jupiter.params.provider.ArgumentsSource;
3643

44+
import static java.util.logging.Level.WARNING;
45+
import static org.glassfish.tests.embedded.runnable.GfEmbeddedUtils.runGlassFishEmbedded;
46+
import static org.glassfish.tests.embedded.runnable.ShrinkwrapUtils.logArchiveContent;
47+
import static org.glassfish.tests.embedded.runnable.TestUtils.waitFor;
3748
import static org.junit.jupiter.api.Assertions.assertNotNull;
38-
import static org.junit.jupiter.api.Assertions.assertTrue;
49+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
3950

4051
/**
4152
* @author Ondro Mihalyi
4253
*/
4354
public class MonitoringTest {
4455

4556
private static final Logger LOG = Logger.getLogger(MonitoringTest.class.getName());
46-
private static final int JMX_PORT = 8686;
47-
private static final int WAIT_SECONDS = 30;
4857

4958
@ParameterizedTest
5059
@ArgumentsSource(GfEmbeddedJarNameProvider.class)
5160
void testJmxMonitoringWithFlashlightAgent(String gfEmbeddedJarName) throws Exception {
61+
assumeTrue(!gfEmbeddedJarName.endsWith("web.jar"),
62+
"AMX is not supported by glassfish-embedded-web.jar, skipping this test scenario");
5263
Process gfEmbeddedProcess = null;
5364
JMXConnector jmxConnector = null;
65+
File warFile = null;
5466
try {
55-
gfEmbeddedProcess = startGlassFishWithJmx(gfEmbeddedJarName);
67+
// an app needs to be deployed to initialize request monitoring
68+
warFile = createEmptyApp();
69+
gfEmbeddedProcess = startGlassFishWithJmx(gfEmbeddedJarName, warFile);
5670

5771
jmxConnector = connectToJmx();
5872
MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
5973

60-
bootAmxAndWait(mbsc);
74+
bootAmx(mbsc);
6175

6276
verifyMonitoringMBeans(mbsc);
6377

6478
} finally {
65-
cleanup(jmxConnector, gfEmbeddedProcess);
79+
cleanup(jmxConnector, gfEmbeddedProcess, warFile);
6680
}
6781
}
6882

69-
private File createMonitoringPropertiesFile() throws Exception {
70-
File propertiesFile = File.createTempFile("monitoring", ".properties");
71-
java.nio.file.Files.write(propertiesFile.toPath(), List.of(
72-
"configs.config.server-config.monitoring-service.module-monitoring-levels.http-service=HIGH",
73-
"configs.config.server-config.monitoring-service.module-monitoring-levels.thread-pool=HIGH"
74-
));
75-
return propertiesFile;
76-
}
83+
private File createEmptyApp() throws Exception {
84+
WebArchive war = ShrinkWrap.create(WebArchive.class, "empty-app.war")
85+
.addAsWebInfResource("", "beans.xml");
7786

78-
private Process startGlassFishWithJmx(String gfEmbeddedJarName) throws IOException {
79-
List<String> arguments = new ArrayList<>();
80-
arguments.add(ProcessHandle.current().info().command().get());
81-
arguments.addAll(List.of(
82-
"-javaagent:flashlight-agent.jar",
83-
"-Dcom.sun.management.jmxremote",
84-
"-Dcom.sun.management.jmxremote.port=" + JMX_PORT,
85-
"-Dcom.sun.management.jmxremote.authenticate=false",
86-
"-Dcom.sun.management.jmxremote.ssl=false",
87-
"-jar", gfEmbeddedJarName,
88-
"--noPort",
89-
"enable-monitoring --modules http-service"
90-
));
91-
92-
return new ProcessBuilder()
93-
.redirectOutput(ProcessBuilder.Redirect.PIPE)
94-
.redirectError(ProcessBuilder.Redirect.PIPE)
95-
.command(arguments)
96-
.start();
87+
File warFile = File.createTempFile("empty-app", ".war");
88+
war.as(ZipExporter.class).exportTo(warFile, true);
89+
logArchiveContent(war, "empty-app.war", LOG::info);
90+
return warFile;
9791
}
9892

99-
private JMXConnector connectToJmx() throws Exception {
100-
JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + JMX_PORT + "/jmxrmi");
93+
private Process startGlassFishWithJmx(String gfEmbeddedJarName, File warFile) throws IOException {
94+
return runGlassFishEmbedded(gfEmbeddedJarName, true,
95+
List.of("-Dcom.sun.management.jmxremote",
96+
"-javaagent:flashlight-agent.jar"),
97+
"enable-monitoring --modules http-service",
98+
warFile.getAbsolutePath());
99+
}
101100

102-
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
101+
private JMXConnector connectToJmx() throws InterruptedException {
102+
return waitFor("JMX connector", () -> {
103+
VirtualMachine vm = null;
104+
String connectorAddress = null;
103105
try {
104-
return JMXConnectorFactory.connect(serviceURL, null);
106+
// Find GlassFish process by looking for our jar
107+
for (VirtualMachineDescriptor vmd : VirtualMachine.list()) {
108+
if (vmd.displayName().contains("glassfish-embedded")) {
109+
vm = VirtualMachine.attach(vmd.id());
110+
111+
// Get or create JMX connector address
112+
Properties props = vm.getAgentProperties();
113+
vm.detach();
114+
String connectorAddressProperty = "com.sun.management.jmxremote.localConnectorAddress";
115+
connectorAddress = props.getProperty(connectorAddressProperty);
116+
117+
if (connectorAddress != null) {
118+
LOG.info("Got connector address: " + connectorAddress);
119+
JMXServiceURL serviceURL = new JMXServiceURL(connectorAddress);
120+
return JMXConnectorFactory.connect(serviceURL, null);
121+
} else {
122+
throw new UnsupportedOperationException("Connector address not available!");
123+
}
124+
}
125+
}
105126
} catch (Exception e) {
106-
Thread.sleep(500);
127+
LOG.log(WARNING, e.getMessage(), e);
128+
if (vm != null) try { vm.detach(); } catch (Exception ignored) {}
107129
}
108-
}
109-
throw new IllegalStateException("Could not connect to JMX in " + WAIT_SECONDS + " seconds");
130+
return (JMXConnector)null;
131+
});
110132
}
111133

112-
private void bootAmxAndWait(MBeanServerConnection mbsc) throws Exception {
134+
private void bootAmx(MBeanServerConnection mbsc) throws Exception {
113135
ObjectName bootAMXObjectName = new ObjectName("amx-support:type=boot-amx");
114136

115-
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
116-
if (mbsc.isRegistered(bootAMXObjectName)) {
117-
break;
118-
}
119-
Thread.sleep(500);
120-
}
121-
122-
123-
assertTrue(mbsc.isRegistered(bootAMXObjectName), "bootAMX is registered");
137+
waitFor("bootAMX", () -> mbsc.isRegistered(bootAMXObjectName) ? true : null);
124138

125139
mbsc.invoke(bootAMXObjectName, "bootAMX", null, null);
126-
127-
// Wait for AMX runtime to be available
128-
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
129-
Set<ObjectName> runtimeBeans = mbsc.queryNames(new ObjectName("amx:pp=/,type=runtime"), null);
130-
if (!runtimeBeans.isEmpty()) {
131-
return;
132-
}
133-
Thread.sleep(500);
134-
}
135-
throw new IllegalStateException("AMX runtime not available after " + WAIT_SECONDS + " seconds");
136140
}
137141

138-
private void verifyMonitoringMBeans(MBeanServerConnection mbsc) throws Exception {
139-
Set<ObjectName> requestBeans = mbsc.queryNames(new ObjectName("amx:type=request-mon,*"), null);
140-
assertTrue(!requestBeans.isEmpty(), "Request monitoring MBean should be present");
142+
private void verifyMonitoringMBeans(final MBeanServerConnection mbsc) throws InterruptedException, IOException, JMException {
143+
Set<ObjectName> requestBeans = waitFor("equest-mon bean", () -> {
144+
Set<ObjectName> result = mbsc.queryNames(new ObjectName("amx:type=request-mon,*"), null);
145+
return result.isEmpty() ? null : result;
146+
});
141147

142148
// Verify we can read monitoring data
143149
ObjectName requestBean = requestBeans.iterator().next();
144150
assertNotNull(mbsc.getAttribute(requestBean, "countrequests"), "Should be able to read request count");
145151
}
146152

147-
private void cleanup(JMXConnector jmxConnector, Process process) throws InterruptedException {
153+
private void cleanup(JMXConnector jmxConnector, Process process, File... files) throws InterruptedException {
148154
if (jmxConnector != null) try { jmxConnector.close(); } catch (Exception ignored) {}
149155
if (process != null && process.isAlive()) {
150-
process.destroyForcibly();
156+
process.destroy();
151157
process.waitFor(5, TimeUnit.SECONDS);
158+
if (process.isAlive()) {
159+
process.destroy();
160+
process.waitFor(5, TimeUnit.SECONDS);
161+
}
152162
}
163+
cleanupFiles(files);
153164
}
154165

155166
private void cleanupFiles(File... files) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
/**
19+
*
20+
* @author Ondro Mihalyi
21+
*/
22+
public abstract class TestUtils {
23+
24+
private static final int WAIT_SECONDS = 30;
25+
26+
private TestUtils() {
27+
}
28+
29+
public static <T> T waitFor(String what, org.junit.jupiter.api.function.ThrowingSupplier<T> supplier) throws InterruptedException {
30+
Throwable lastThrowable = null;
31+
for (int i = 0; i < WAIT_SECONDS * 2; i++) {
32+
try {
33+
T result = supplier.get();
34+
if (result != null) {
35+
return result;
36+
}
37+
} catch (UnsupportedOperationException unrecoverableError) {
38+
throw unrecoverableError;
39+
} catch (Throwable ignore) {
40+
lastThrowable = ignore;
41+
}
42+
Thread.sleep(500);
43+
}
44+
throw new RuntimeException(what + " not received within timeout", lastThrowable);
45+
}
46+
47+
}

docs/embedded-server-guide/src/main/asciidoc/embedded-server-guide.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,11 @@ java -Djava.util.logging.config.file=customlogging.properties MyEmbeddedGlassFis
20682068

20692069
Embedded {productName} supports monitoring through JMX MBeans, similar to regular {productName} Server. However, you must attache the flashlight agent on command line to enable monitoring functionality.
20702070

2071+
[IMPORTANT]
2072+
====
2073+
AMX (Application Management Extensions) monitoring is only available in `glassfish-embedded-all.jar`. The `glassfish-embedded-web.jar` does not include AMX support, which limits the available monitoring MBeans.
2074+
====
2075+
20712076
=== Prerequisites
20722077

20732078
To enable monitoring in Embedded {productName}, you need the flashlight agent JAR file:

0 commit comments

Comments
 (0)