Skip to content

Commit 74b7b55

Browse files
committed
Make MBeanServer tests more robust
This commit refactors several tests to use SocketUtils to find an available port, compose a custom JMX service URL using that port, and start an MBeanServer for the particular test using that port. This commit also makes other changes to MBeanServer related tests in an effort to make them more robust when executed concurrently. Closes gh-23699
1 parent 57b4b74 commit 74b7b55

12 files changed

+172
-189
lines changed

spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
2424

2525
import org.junit.jupiter.api.AfterEach;
2626
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.extension.ExtendWith;
2728
import org.junit.jupiter.api.extension.ExtensionContext;
2829
import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;
29-
import org.junit.jupiter.api.extension.RegisterExtension;
3030
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
3131
import org.opentest4j.TestAbortedException;
3232

3333
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
3434
import org.springframework.context.ConfigurableApplicationContext;
3535
import org.springframework.context.support.GenericApplicationContext;
36+
import org.springframework.jmx.AbstractMBeanServerTests.BindExceptionHandler;
3637
import org.springframework.jmx.export.MBeanExporter;
3738
import org.springframework.util.MBeanTestUtils;
3839

@@ -57,11 +58,9 @@
5758
* @author Chris Beams
5859
* @author Stephane Nicoll
5960
*/
61+
@ExtendWith(BindExceptionHandler.class)
6062
public abstract class AbstractMBeanServerTests {
6163

62-
@RegisterExtension
63-
BindExceptionHandler bindExceptionHandler = new BindExceptionHandler();
64-
6564
protected MBeanServer server;
6665

6766

@@ -77,32 +76,39 @@ public final void setUp() throws Exception {
7776
}
7877
}
7978

80-
protected ConfigurableApplicationContext loadContext(String configLocation) {
81-
GenericApplicationContext ctx = new GenericApplicationContext();
82-
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(configLocation);
83-
ctx.getDefaultListableBeanFactory().registerSingleton("server", this.server);
84-
ctx.refresh();
85-
return ctx;
86-
}
87-
8879
@AfterEach
8980
public void tearDown() throws Exception {
9081
releaseServer();
9182
onTearDown();
9283
}
9384

9485
private void releaseServer() throws Exception {
95-
MBeanServerFactory.releaseMBeanServer(getServer());
86+
try {
87+
MBeanServerFactory.releaseMBeanServer(getServer());
88+
}
89+
catch (IllegalArgumentException ex) {
90+
if (!ex.getMessage().contains("not in list")) {
91+
throw ex;
92+
}
93+
}
9694
MBeanTestUtils.resetMBeanServers();
9795
}
9896

99-
protected void onTearDown() throws Exception {
97+
protected final ConfigurableApplicationContext loadContext(String configLocation) {
98+
GenericApplicationContext ctx = new GenericApplicationContext();
99+
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(configLocation);
100+
ctx.getDefaultListableBeanFactory().registerSingleton("server", getServer());
101+
ctx.refresh();
102+
return ctx;
100103
}
101104

102105
protected void onSetUp() throws Exception {
103106
}
104107

105-
public MBeanServer getServer() {
108+
protected void onTearDown() throws Exception {
109+
}
110+
111+
protected final MBeanServer getServer() {
106112
return this.server;
107113
}
108114

@@ -123,7 +129,7 @@ protected void assertIsNotRegistered(String message, ObjectName objectName) {
123129
}
124130

125131

126-
private static class BindExceptionHandler implements TestExecutionExceptionHandler, LifecycleMethodExecutionExceptionHandler {
132+
static class BindExceptionHandler implements TestExecutionExceptionHandler, LifecycleMethodExecutionExceptionHandler {
127133

128134
@Override
129135
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {

spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package org.springframework.jmx.access;
1818

1919
import java.beans.PropertyDescriptor;
20+
import java.lang.management.ManagementFactory;
21+
import java.lang.management.MemoryMXBean;
22+
import java.lang.management.ThreadMXBean;
2023
import java.lang.reflect.Method;
2124
import java.net.BindException;
2225
import java.util.HashMap;
@@ -30,6 +33,7 @@
3033

3134
import org.junit.jupiter.api.Test;
3235

36+
import org.springframework.aop.framework.ProxyFactory;
3337
import org.springframework.jmx.AbstractMBeanServerTests;
3438
import org.springframework.jmx.IJmxTestBean;
3539
import org.springframework.jmx.JmxException;
@@ -50,7 +54,7 @@
5054
* @author Sam Brannen
5155
* @author Chris Beams
5256
*/
53-
public class MBeanClientInterceptorTests extends AbstractMBeanServerTests {
57+
class MBeanClientInterceptorTests extends AbstractMBeanServerTests {
5458

5559
protected static final String OBJECT_NAME = "spring:test=proxy";
5660

@@ -87,14 +91,14 @@ protected IJmxTestBean getProxy() throws Exception {
8791
}
8892

8993
@Test
90-
public void testProxyClassIsDifferent() throws Exception {
94+
void testProxyClassIsDifferent() throws Exception {
9195
assumeTrue(runTests);
9296
IJmxTestBean proxy = getProxy();
93-
assertThat((proxy.getClass() != IJmxTestBean.class)).as("The proxy class should be different than the base class").isTrue();
97+
assertThat(proxy.getClass()).as("The proxy class should be different than the base class").isNotSameAs(IJmxTestBean.class);
9498
}
9599

96100
@Test
97-
public void testDifferentProxiesSameClass() throws Exception {
101+
void testDifferentProxiesSameClass() throws Exception {
98102
assumeTrue(runTests);
99103
IJmxTestBean proxy1 = getProxy();
100104
IJmxTestBean proxy2 = getProxy();
@@ -104,79 +108,79 @@ public void testDifferentProxiesSameClass() throws Exception {
104108
}
105109

106110
@Test
107-
public void testGetAttributeValue() throws Exception {
111+
void testGetAttributeValue() throws Exception {
108112
assumeTrue(runTests);
109113
IJmxTestBean proxy1 = getProxy();
110114
int age = proxy1.getAge();
111115
assertThat(age).as("The age should be 100").isEqualTo(100);
112116
}
113117

114118
@Test
115-
public void testSetAttributeValue() throws Exception {
119+
void testSetAttributeValue() throws Exception {
116120
assumeTrue(runTests);
117121
IJmxTestBean proxy = getProxy();
118122
proxy.setName("Rob Harrop");
119123
assertThat(target.getName()).as("The name of the bean should have been updated").isEqualTo("Rob Harrop");
120124
}
121125

122126
@Test
123-
public void testSetAttributeValueWithRuntimeException() throws Exception {
127+
void testSetAttributeValueWithRuntimeException() throws Exception {
124128
assumeTrue(runTests);
125129
IJmxTestBean proxy = getProxy();
126130
assertThatIllegalArgumentException().isThrownBy(() ->
127131
proxy.setName("Juergen"));
128132
}
129133

130134
@Test
131-
public void testSetAttributeValueWithCheckedException() throws Exception {
135+
void testSetAttributeValueWithCheckedException() throws Exception {
132136
assumeTrue(runTests);
133137
IJmxTestBean proxy = getProxy();
134138
assertThatExceptionOfType(ClassNotFoundException.class).isThrownBy(() ->
135139
proxy.setName("Juergen Class"));
136140
}
137141

138142
@Test
139-
public void testSetAttributeValueWithIOException() throws Exception {
143+
void testSetAttributeValueWithIOException() throws Exception {
140144
assumeTrue(runTests);
141145
IJmxTestBean proxy = getProxy();
142146
assertThatIOException().isThrownBy(() ->
143147
proxy.setName("Juergen IO"));
144148
}
145149

146150
@Test
147-
public void testSetReadOnlyAttribute() throws Exception {
151+
void testSetReadOnlyAttribute() throws Exception {
148152
assumeTrue(runTests);
149153
IJmxTestBean proxy = getProxy();
150154
assertThatExceptionOfType(InvalidInvocationException.class).isThrownBy(() ->
151155
proxy.setAge(900));
152156
}
153157

154158
@Test
155-
public void testInvokeNoArgs() throws Exception {
159+
void testInvokeNoArgs() throws Exception {
156160
assumeTrue(runTests);
157161
IJmxTestBean proxy = getProxy();
158162
long result = proxy.myOperation();
159163
assertThat(result).as("The operation should return 1").isEqualTo(1);
160164
}
161165

162166
@Test
163-
public void testInvokeArgs() throws Exception {
167+
void testInvokeArgs() throws Exception {
164168
assumeTrue(runTests);
165169
IJmxTestBean proxy = getProxy();
166170
int result = proxy.add(1, 2);
167171
assertThat(result).as("The operation should return 3").isEqualTo(3);
168172
}
169173

170174
@Test
171-
public void testInvokeUnexposedMethodWithException() throws Exception {
175+
void testInvokeUnexposedMethodWithException() throws Exception {
172176
assumeTrue(runTests);
173177
IJmxTestBean bean = getProxy();
174178
assertThatExceptionOfType(InvalidInvocationException.class).isThrownBy(() ->
175179
bean.dontExposeMe());
176180
}
177181

178182
@Test
179-
public void testTestLazyConnectionToRemote() throws Exception {
183+
void testTestLazyConnectionToRemote() throws Exception {
180184
assumeTrue(runTests);
181185

182186
final int port = SocketUtils.findAvailableTcpPort();
@@ -219,46 +223,25 @@ public void testTestLazyConnectionToRemote() throws Exception {
219223
catch (JmxException ex) {
220224
// expected
221225
}
222-
223-
connector = JMXConnectorServerFactory.newJMXConnectorServer(url, null, getServer());
224-
connector.start();
225-
226-
// should now be able to access data via the lazy proxy
227-
try {
228-
assertThat(bean.getName()).isEqualTo("Rob Harrop");
229-
assertThat(bean.getAge()).isEqualTo(100);
230-
}
231-
finally {
232-
connector.stop();
233-
}
234226
}
235227

236-
/*
237228
public void testMXBeanAttributeAccess() throws Exception {
238229
MBeanClientInterceptor interceptor = new MBeanClientInterceptor();
239230
interceptor.setServer(ManagementFactory.getPlatformMBeanServer());
240231
interceptor.setObjectName("java.lang:type=Memory");
241232
interceptor.setManagementInterface(MemoryMXBean.class);
242233
MemoryMXBean proxy = ProxyFactory.getProxy(MemoryMXBean.class, interceptor);
243-
assertTrue(proxy.getHeapMemoryUsage().getMax() > 0);
234+
assertThat(proxy.getHeapMemoryUsage().getMax()).isGreaterThan(0);
244235
}
245236

246237
public void testMXBeanOperationAccess() throws Exception {
247238
MBeanClientInterceptor interceptor = new MBeanClientInterceptor();
248239
interceptor.setServer(ManagementFactory.getPlatformMBeanServer());
249240
interceptor.setObjectName("java.lang:type=Threading");
250241
ThreadMXBean proxy = ProxyFactory.getProxy(ThreadMXBean.class, interceptor);
251-
assertTrue(proxy.getThreadInfo(Thread.currentThread().getId()).getStackTrace() != null);
242+
assertThat(proxy.getThreadInfo(Thread.currentThread().getId()).getStackTrace()).isNotNull();
252243
}
253244

254-
public void testMXBeanAttributeListAccess() throws Exception {
255-
MBeanClientInterceptor interceptor = new MBeanClientInterceptor();
256-
interceptor.setServer(ManagementFactory.getPlatformMBeanServer());
257-
interceptor.setObjectName("com.sun.management:type=HotSpotDiagnostic");
258-
HotSpotDiagnosticMXBean proxy = ProxyFactory.getProxy(HotSpotDiagnosticMXBean.class, interceptor);
259-
assertFalse(proxy.getDiagnosticOptions().isEmpty());
260-
}
261-
*/
262245

263246
private static class ProxyTestAssembler extends AbstractReflectiveMBeanInfoAssembler {
264247

spring-context/src/test/java/org/springframework/jmx/access/RemoteMBeanClientInterceptorTests.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,11 @@
3535
* @author Chris Beams
3636
* @author Sam Brannen
3737
*/
38-
public class RemoteMBeanClientInterceptorTests extends MBeanClientInterceptorTests {
38+
class RemoteMBeanClientInterceptorTests extends MBeanClientInterceptorTests {
3939

40-
private static final int SERVICE_PORT;
40+
private final int servicePort = SocketUtils.findAvailableTcpPort();
4141

42-
private static final String SERVICE_URL;
43-
44-
static {
45-
SERVICE_PORT = SocketUtils.findAvailableTcpPort();
46-
SERVICE_URL = "service:jmx:jmxmp://localhost:" + SERVICE_PORT;
47-
}
42+
private final String serviceUrl = "service:jmx:jmxmp://localhost:" + servicePort;
4843

4944

5045
private JMXConnectorServer connectorServer;
@@ -61,13 +56,13 @@ public void onSetUp() throws Exception {
6156
}
6257
catch (BindException ex) {
6358
System.out.println("Skipping remote JMX tests because binding to local port ["
64-
+ SERVICE_PORT + "] failed: " + ex.getMessage());
59+
+ this.servicePort + "] failed: " + ex.getMessage());
6560
runTests = false;
6661
}
6762
}
6863

6964
private JMXServiceURL getServiceUrl() throws MalformedURLException {
70-
return new JMXServiceURL(SERVICE_URL);
65+
return new JMXServiceURL(this.serviceUrl);
7166
}
7267

7368
@Override

spring-context/src/test/java/org/springframework/jmx/export/CustomEditorConfigurerTests.java

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
/**
3232
* @author Rob Harrop
3333
*/
34-
public class CustomEditorConfigurerTests extends AbstractJmxTests {
34+
class CustomEditorConfigurerTests extends AbstractJmxTests {
3535

3636
private final SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
3737

@@ -41,33 +41,30 @@ protected String getApplicationContextPath() {
4141
}
4242

4343
@Test
44-
public void testDatesInJmx() throws Exception {
45-
// System.out.println(getServer().getClass().getName());
46-
ObjectName oname = new ObjectName("bean:name=dateRange");
47-
48-
Date startJmx = (Date) getServer().getAttribute(oname, "StartDate");
49-
Date endJmx = (Date) getServer().getAttribute(oname, "EndDate");
44+
void datesInApplicationContext() throws Exception {
45+
DateRange dr = getContext().getBean("dateRange", DateRange.class);
5046

51-
assertThat(startJmx).as("startDate ").isEqualTo(getStartDate());
52-
assertThat(endJmx).as("endDate ").isEqualTo(getEndDate());
47+
assertThat(dr.getStartDate()).as("startDate").isEqualTo(getStartDate());
48+
assertThat(dr.getEndDate()).as("endDate").isEqualTo(getEndDate());
5349
}
5450

5551
@Test
56-
public void testGetDates() throws Exception {
57-
DateRange dr = (DateRange) getContext().getBean("dateRange");
52+
void datesInJmx() throws Exception {
53+
ObjectName oname = new ObjectName("bean:name=dateRange");
54+
55+
Date startJmx = (Date) getServer().getAttribute(oname, "StartDate");
56+
Date endJmx = (Date) getServer().getAttribute(oname, "EndDate");
5857

59-
assertThat(dr.getStartDate()).as("startDate ").isEqualTo(getStartDate());
60-
assertThat(dr.getEndDate()).as("endDate ").isEqualTo(getEndDate());
58+
assertThat(startJmx).as("startDate").isEqualTo(getStartDate());
59+
assertThat(endJmx).as("endDate").isEqualTo(getEndDate());
6160
}
6261

6362
private Date getStartDate() throws ParseException {
64-
Date start = df.parse("2004/10/12");
65-
return start;
63+
return df.parse("2004/10/12");
6664
}
6765

6866
private Date getEndDate() throws ParseException {
69-
Date end = df.parse("2004/11/13");
70-
return end;
67+
return df.parse("2004/11/13");
7168
}
7269

7370
}

0 commit comments

Comments
 (0)