Commit bb0f43cf authored by Stephane Nicoll's avatar Stephane Nicoll

Allow JMX endpoint ObjectNames to be customized

Closes gh-25317
parent f81921c0
......@@ -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());
}
......
/*
* 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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment