Allow JMX endpoint ObjectNames to be customized
Closes gh-25317
This commit is contained in:
@@ -23,6 +23,7 @@ import javax.management.MBeanServer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
@@ -58,7 +59,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(JmxAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ JmxAutoConfiguration.class, EndpointAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(JmxEndpointProperties.class)
|
||||
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true")
|
||||
public class JmxEndpointAutoConfiguration {
|
||||
@@ -83,15 +84,21 @@ public class JmxEndpointAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(MBeanServer.class)
|
||||
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer, Environment environment,
|
||||
ObjectProvider<ObjectMapper> objectMapper, JmxEndpointsSupplier jmxEndpointsSupplier) {
|
||||
@ConditionalOnMissingBean(EndpointObjectNameFactory.class)
|
||||
public DefaultEndpointObjectNameFactory endpointObjectNameFactory(MBeanServer mBeanServer,
|
||||
Environment environment) {
|
||||
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
|
||||
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(this.properties, environment,
|
||||
mBeanServer, contextId);
|
||||
return new DefaultEndpointObjectNameFactory(this.properties, environment, mBeanServer, contextId);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(MBeanServer.class)
|
||||
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
|
||||
EndpointObjectNameFactory endpointObjectNameFactory, ObjectProvider<ObjectMapper> objectMapper,
|
||||
JmxEndpointsSupplier jmxEndpointsSupplier) {
|
||||
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
|
||||
objectMapper.getIfAvailable());
|
||||
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
|
||||
return new JmxEndpointExporter(mBeanServer, endpointObjectNameFactory, responseMapper,
|
||||
jmxEndpointsSupplier.getEndpoints());
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.EndpointObjectNameFactory;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointExporter;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointDiscoverer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link JmxEndpointAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class JmxEndpointAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, JmxAutoConfiguration.class,
|
||||
JmxEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(TestEndpoint.class);
|
||||
|
||||
private final MBeanServer mBeanServer = mock(MBeanServer.class);
|
||||
|
||||
@Test
|
||||
void jmxEndpointWithoutJmxSupportNotAutoConfigured() {
|
||||
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(MBeanServer.class)
|
||||
.doesNotHaveBean(JmxEndpointDiscoverer.class).doesNotHaveBean(JmxEndpointExporter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jmxEndpointWithJmxSupportAutoConfigured() {
|
||||
this.contextRunner.withPropertyValues("spring.jmx.enabled=true").with(mockMBeanServer())
|
||||
.run((context) -> assertThat(context).hasSingleBean(JmxEndpointDiscoverer.class)
|
||||
.hasSingleBean(JmxEndpointExporter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jmxEndpointWithCustomEndpointObjectNameFactory() {
|
||||
EndpointObjectNameFactory factory = mock(EndpointObjectNameFactory.class);
|
||||
this.contextRunner.withPropertyValues("spring.jmx.enabled=true").with(mockMBeanServer())
|
||||
.withBean(EndpointObjectNameFactory.class, () -> factory).run((context) -> {
|
||||
ArgumentCaptor<ExposableJmxEndpoint> argumentCaptor = ArgumentCaptor
|
||||
.forClass(ExposableJmxEndpoint.class);
|
||||
verify(factory).getObjectName(argumentCaptor.capture());
|
||||
ExposableJmxEndpoint jmxEndpoint = argumentCaptor.getValue();
|
||||
assertThat(jmxEndpoint.getEndpointId().toLowerCaseString()).isEqualTo("test");
|
||||
});
|
||||
}
|
||||
|
||||
private Function<ApplicationContextRunner, ApplicationContextRunner> mockMBeanServer() {
|
||||
return (ctxRunner) -> ctxRunner.withBean("mbeanServer", MBeanServer.class, () -> this.mBeanServer);
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
String hello() {
|
||||
return "hello world";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1426,6 +1426,7 @@ This can be achieved using the configprop:management.endpoints.web.exposure.excl
|
||||
Java Management Extensions (JMX) provide a standard mechanism to monitor and manage applications.
|
||||
By default, this feature is not enabled and can be turned on by setting the configuration property configprop:spring.jmx.enabled[] to `true`.
|
||||
Spring Boot exposes management endpoints as JMX MBeans under the `org.springframework.boot` domain by default.
|
||||
To Take full control over endpoints registration in the JMX domain, consider registering your own `EndpointObjectNameFactory` implementation.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user