Skip to content

Commit 123ffd7

Browse files
author
Dave Syer
committed
Exclude @ManagedResources from Endpoint MBeans
If an Endpoint is already @ManagedResource then it doesn't need an additional (probably wrong) MBEan registration based on the invoke() method.
1 parent c0305ec commit 123ffd7

File tree

3 files changed

+117
-14
lines changed

3 files changed

+117
-14
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporter.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
import org.springframework.context.ApplicationContextAware;
3939
import org.springframework.context.ApplicationListener;
4040
import org.springframework.context.SmartLifecycle;
41+
import org.springframework.core.annotation.AnnotationUtils;
4142
import org.springframework.jmx.export.MBeanExportException;
4243
import org.springframework.jmx.export.MBeanExporter;
4344
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
45+
import org.springframework.jmx.export.annotation.ManagedResource;
4446
import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
4547
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
4648
import org.springframework.jmx.export.naming.SelfNaming;
@@ -144,6 +146,18 @@ protected void locateAndRegisterEndpoints() {
144146
}
145147

146148
protected void registerEndpoint(String beanName, Endpoint<?> endpoint) {
149+
@SuppressWarnings("rawtypes")
150+
Class<? extends Endpoint> type = endpoint.getClass();
151+
if (AnnotationUtils.findAnnotation(type, ManagedResource.class) != null) {
152+
// Already managed
153+
return;
154+
}
155+
if (type.isMemberClass()
156+
&& AnnotationUtils.findAnnotation(type.getEnclosingClass(),
157+
ManagedResource.class) != null) {
158+
// Nested class with @ManagedResource in parent
159+
return;
160+
}
147161
try {
148162
registerBeanNameOrInstance(getEndpointMBean(beanName, endpoint), beanName);
149163
}

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/ShutdownEndpointMBean.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import org.springframework.boot.actuate.endpoint.Endpoint;
2020
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
2121
import org.springframework.jmx.export.annotation.ManagedOperation;
22+
import org.springframework.jmx.export.annotation.ManagedResource;
2223

2324
/**
2425
* Special endpoint wrapper for {@link ShutdownEndpoint}.
2526
*
2627
* @author Christian Dupuis
2728
*/
29+
@ManagedResource
2830
public class ShutdownEndpointMBean extends EndpointMBean {
2931

3032
public ShutdownEndpointMBean(String beanName, Endpoint<?> endpoint) {

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,26 @@
2525
import org.junit.After;
2626
import org.junit.Test;
2727
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
28+
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
29+
import org.springframework.boot.actuate.endpoint.Endpoint;
2830
import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter;
31+
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
2932
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
3033
import org.springframework.context.ApplicationContext;
3134
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
35+
import org.springframework.context.annotation.Bean;
3236
import org.springframework.context.annotation.Configuration;
3337
import org.springframework.context.annotation.EnableMBeanExport;
3438
import org.springframework.jmx.export.MBeanExporter;
39+
import org.springframework.jmx.export.annotation.ManagedResource;
3540
import org.springframework.jmx.support.ObjectNameManager;
3641
import org.springframework.mock.env.MockEnvironment;
42+
import org.springframework.stereotype.Component;
3743
import org.springframework.util.ObjectUtils;
3844

45+
import static org.junit.Assert.assertFalse;
3946
import static org.junit.Assert.assertNotNull;
47+
import static org.junit.Assert.assertTrue;
4048
import static org.junit.Assert.fail;
4149

4250
/**
@@ -55,13 +63,51 @@ public void close() {
5563
}
5664

5765
@Test
58-
public void testEndpointMBeanExporterIsInstalled() {
66+
public void testEndpointMBeanExporterIsInstalled() throws Exception {
5967
this.context = new AnnotationConfigApplicationContext();
6068
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class,
6169
EndpointAutoConfiguration.class,
62-
EndpointMBeanExportAutoConfiguration.class);
70+
EndpointMBeanExportAutoConfiguration.class,
71+
PropertyPlaceholderAutoConfiguration.class);
72+
this.context.refresh();
73+
assertNotNull(this.context.getBean(EndpointMBeanExporter.class));
74+
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
75+
76+
assertFalse(mbeanExporter.getServer()
77+
.queryNames(getObjectName("*", "*,*", this.context), null).isEmpty());
78+
}
79+
80+
@Test
81+
public void testEndpointMBeanExporterIsNotInstalledIfManagedResource()
82+
throws Exception {
83+
this.context = new AnnotationConfigApplicationContext();
84+
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class,
85+
ManagedEndpoint.class, EndpointMBeanExportAutoConfiguration.class,
86+
PropertyPlaceholderAutoConfiguration.class);
87+
this.context.refresh();
88+
assertNotNull(this.context.getBean(EndpointMBeanExporter.class));
89+
90+
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
91+
92+
assertTrue(mbeanExporter.getServer()
93+
.queryNames(getObjectName("*", "*,*", this.context), null).isEmpty());
94+
}
95+
96+
@Test
97+
public void testEndpointMBeanExporterIsNotInstalledIfNestedInManagedResource()
98+
throws Exception {
99+
this.context = new AnnotationConfigApplicationContext();
100+
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class,
101+
NestedInManagedEndpoint.class,
102+
EndpointMBeanExportAutoConfiguration.class,
103+
PropertyPlaceholderAutoConfiguration.class);
63104
this.context.refresh();
64105
assertNotNull(this.context.getBean(EndpointMBeanExporter.class));
106+
107+
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
108+
109+
assertTrue(mbeanExporter.getServer()
110+
.queryNames(getObjectName("*", "*,*", this.context), null).isEmpty());
65111
}
66112

67113
@Test(expected = NoSuchBeanDefinitionException.class)
@@ -125,21 +171,23 @@ public void testEndpointMBeanExporterInParentChild() throws IntrospectionExcepti
125171

126172
private ObjectName getObjectName(String domain, String beanKey,
127173
ApplicationContext applicationContext) throws MalformedObjectNameException {
174+
String name = "%s:type=Endpoint,name=%s";
175+
if (applicationContext.getParent() != null) {
176+
name = name + ",context=%s";
177+
}
178+
if (applicationContext.getEnvironment().getProperty("endpoints.jmx.unique_names",
179+
Boolean.class, false)) {
180+
name = name
181+
+ ",identity="
182+
+ ObjectUtils.getIdentityHexString(applicationContext
183+
.getBean(beanKey));
184+
}
128185
if (applicationContext.getParent() != null) {
129-
return ObjectNameManager
130-
.getInstance(String.format(
131-
"%s:type=Endpoint,name=%s,context=%s,identity=%s", domain,
132-
beanKey,
133-
ObjectUtils.getIdentityHexString(applicationContext),
134-
ObjectUtils.getIdentityHexString(applicationContext
135-
.getBean(beanKey))));
186+
return ObjectNameManager.getInstance(String.format(name, domain, beanKey,
187+
ObjectUtils.getIdentityHexString(applicationContext)));
136188
}
137189
else {
138-
return ObjectNameManager
139-
.getInstance(String.format("%s:type=Endpoint,name=%s,identity=%s",
140-
domain, beanKey, ObjectUtils
141-
.getIdentityHexString(applicationContext
142-
.getBean(beanKey))));
190+
return ObjectNameManager.getInstance(String.format(name, domain, beanKey));
143191
}
144192
}
145193

@@ -148,4 +196,43 @@ private ObjectName getObjectName(String domain, String beanKey,
148196
public static class TestConfiguration {
149197

150198
}
199+
200+
@Component
201+
@ManagedResource
202+
protected static class ManagedEndpoint extends AbstractEndpoint<Boolean> {
203+
204+
public ManagedEndpoint() {
205+
super("managed", true, true);
206+
}
207+
208+
@Override
209+
public Boolean invoke() {
210+
return true;
211+
}
212+
213+
}
214+
215+
@Configuration
216+
@ManagedResource
217+
protected static class NestedInManagedEndpoint {
218+
219+
@Bean
220+
public Endpoint<Boolean> nested() {
221+
return new Nested();
222+
}
223+
224+
class Nested extends AbstractEndpoint<Boolean> {
225+
226+
public Nested() {
227+
super("managed", true, true);
228+
}
229+
230+
@Override
231+
public Boolean invoke() {
232+
return true;
233+
}
234+
}
235+
236+
}
237+
151238
}

0 commit comments

Comments
 (0)