Ensure JMX endpoints are uniquely named in a context hierarchy

Closes gh-31718
pull/32521/head
Andy Wilkinson 2 years ago
parent 043354f93b
commit 628c2cd0b1

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -43,6 +43,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -86,7 +87,7 @@ public class JmxEndpointAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnMissingBean(EndpointObjectNameFactory.class) @ConditionalOnMissingBean(value = EndpointObjectNameFactory.class, search = SearchStrategy.CURRENT)
public DefaultEndpointObjectNameFactory endpointObjectNameFactory(MBeanServer mBeanServer, public DefaultEndpointObjectNameFactory endpointObjectNameFactory(MBeanServer mBeanServer,
Environment environment) { Environment environment) {
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext); String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);

@ -16,9 +16,13 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx; package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -35,8 +39,11 @@ import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
/** /**
* Tests for {@link JmxEndpointAutoConfiguration}. * Tests for {@link JmxEndpointAutoConfiguration}.
@ -78,6 +85,26 @@ class JmxEndpointAutoConfigurationTests {
}); });
} }
@Test
void jmxEndpointWithContextHierarchyGeneratesUniqueNamesForEachEndpoint() throws Exception {
given(this.mBeanServer.queryNames(any(), any()))
.willReturn(new HashSet<>(Arrays.asList(new ObjectName("test:test=test"))));
ArgumentCaptor<ObjectName> objectName = ArgumentCaptor.forClass(ObjectName.class);
this.contextRunner.withPropertyValues("spring.jmx.enabled=true").with(mockMBeanServer()).run((parent) -> {
this.contextRunner.withPropertyValues("spring.jmx.enabled=true").withParent(parent).run((child) -> {
});
this.contextRunner.withPropertyValues("spring.jmx.enabled=true").withParent(parent).run((child) -> {
});
});
then(this.mBeanServer).should(times(3)).registerMBean(any(Object.class), objectName.capture());
Set<ObjectName> uniqueValues = new HashSet<>(objectName.getAllValues());
assertThat(uniqueValues).hasSize(3);
assertThat(uniqueValues).allMatch((name) -> name.getDomain().equals("org.springframework.boot"));
assertThat(uniqueValues).allMatch((name) -> name.getKeyProperty("type").equals("Endpoint"));
assertThat(uniqueValues).allMatch((name) -> name.getKeyProperty("name").equals("Test"));
assertThat(uniqueValues).allMatch((name) -> name.getKeyProperty("context") != null);
}
private Function<ApplicationContextRunner, ApplicationContextRunner> mockMBeanServer() { private Function<ApplicationContextRunner, ApplicationContextRunner> mockMBeanServer() {
return (ctxRunner) -> ctxRunner.withBean("mbeanServer", MBeanServer.class, () -> this.mBeanServer); return (ctxRunner) -> ctxRunner.withBean("mbeanServer", MBeanServer.class, () -> this.mBeanServer);
} }

Loading…
Cancel
Save