Merge pull request #13990 from ayudovin:support-ensureUniqueRuntimeObjectNames-globally

* pr/13990:
  Polish "Add global support for JMX unique names"
  Add global support for JMX unique names
pull/14005/head
Stephane Nicoll 6 years ago
commit e4e8311a1a

@ -22,6 +22,7 @@ import javax.management.ObjectName;
import org.springframework.boot.actuate.endpoint.jmx.EndpointObjectNameFactory;
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
import org.springframework.core.env.Environment;
import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@ -40,11 +41,30 @@ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
private final String contextId;
private final boolean uniqueNames;
DefaultEndpointObjectNameFactory(JmxEndpointProperties properties,
MBeanServer mBeanServer, String contextId) {
Environment environment, MBeanServer mBeanServer, String contextId) {
this.properties = properties;
this.mBeanServer = mBeanServer;
this.contextId = contextId;
this.uniqueNames = determineUniqueNames(environment, properties);
}
@SuppressWarnings("deprecation")
private static boolean determineUniqueNames(Environment environment,
JmxEndpointProperties properties) {
Boolean uniqueName = environment.getProperty("spring.jmx.unique-names",
Boolean.class);
Boolean endpointUniqueNames = properties.getUniqueNames();
if (uniqueName == null) {
return (endpointUniqueNames != null) ? endpointUniqueNames : false;
}
else if (endpointUniqueNames != null & !uniqueName.equals(endpointUniqueNames)) {
throw new IllegalArgumentException(
"Configuration mismatch, 'management.endpoints.jmx.unique-names' is deprecated, use only 'spring.jmx.unique-names'");
}
return uniqueName;
}
@Override
@ -57,7 +77,7 @@ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
if (this.mBeanServer != null && hasMBean(baseName)) {
builder.append(",context=" + this.contextId);
}
if (this.properties.isUniqueNames()) {
if (this.uniqueNames) {
String identity = ObjectUtils.getIdentityHexString(endpoint);
builder.append(",identity=" + identity);
}

@ -45,6 +45,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;
/**
@ -84,11 +85,11 @@ public class JmxEndpointAutoConfiguration {
@Bean
@ConditionalOnSingleCandidate(MBeanServer.class)
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
ObjectProvider<ObjectMapper> objectMapper,
Environment environment, ObjectProvider<ObjectMapper> objectMapper,
JmxEndpointsSupplier jmxEndpointsSupplier) {
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(
this.properties, mBeanServer, contextId);
this.properties, environment, mBeanServer, contextId);
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
objectMapper.getIfAvailable());
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,

@ -21,6 +21,7 @@ import java.util.Properties;
import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
@ -41,9 +42,9 @@ public class JmxEndpointProperties {
private String domain = "org.springframework.boot";
/**
* Whether to ensure that ObjectNames are modified in case of conflict.
* Whether unique runtime object names should be ensured.
*/
private boolean uniqueNames = false;
private Boolean uniqueNames;
/**
* Additional static properties to append to all ObjectNames of MBeans representing
@ -70,11 +71,14 @@ public class JmxEndpointProperties {
this.domain = domain;
}
public boolean isUniqueNames() {
@Deprecated
@DeprecatedConfigurationProperty(replacement = "spring.jmx.unique-names")
public Boolean getUniqueNames() {
return this.uniqueNames;
}
public void setUniqueNames(boolean uniqueNames) {
@Deprecated
public void setUniqueNames(Boolean uniqueNames) {
this.uniqueNames = uniqueNames;
}

@ -22,7 +22,9 @@ import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
import org.springframework.mock.env.MockEnvironment;
@ -39,6 +41,9 @@ import static org.mockito.Mockito.mock;
*/
public class DefaultEndpointObjectNameFactoryTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final MockEnvironment environment = new MockEnvironment();
private final JmxEndpointProperties properties = new JmxEndpointProperties(
@ -72,7 +77,18 @@ public class DefaultEndpointObjectNameFactoryTests {
@Test
public void generateObjectNameWithUniqueNames() {
this.environment.setProperty("spring.jmx.unique-names", "true");
assertUniqueObjectName();
}
@Test
@Deprecated
public void generateObjectNameWithUniqueNamesDeprecatedProperty() {
this.properties.setUniqueNames(true);
assertUniqueObjectName();
}
private void assertUniqueObjectName() {
ExposableJmxEndpoint endpoint = endpoint("test");
String id = ObjectUtils.getIdentityHexString(endpoint);
ObjectName objectName = generateObjectName(endpoint);
@ -80,6 +96,18 @@ public class DefaultEndpointObjectNameFactoryTests {
"org.springframework.boot:type=Endpoint,name=Test,identity=" + id);
}
@Test
@Deprecated
public void generateObjectNameWithUniqueNamesDeprecatedPropertyMismatchMainProperty() {
this.environment.setProperty("spring.jmx.unique-names", "false");
this.properties.setUniqueNames(true);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("spring.jmx.unique-names");
this.thrown.expectMessage("management.endpoints.jmx.unique-names");
generateObjectName(endpoint("test"));
}
@Test
public void generateObjectNameWithStaticNames() {
this.properties.getStaticNames().setProperty("counter", "42");
@ -107,8 +135,8 @@ public class DefaultEndpointObjectNameFactoryTests {
private ObjectName generateObjectName(ExposableJmxEndpoint endpoint) {
try {
return new DefaultEndpointObjectNameFactory(this.properties, this.mBeanServer,
this.contextId).getObjectName(endpoint);
return new DefaultEndpointObjectNameFactory(this.properties, this.environment,
this.mBeanServer, this.contextId).getObjectName(endpoint);
}
catch (MalformedObjectNameException ex) {
throw new AssertionError("Invalid object name", ex);

@ -49,6 +49,7 @@ import org.springframework.util.StringUtils;
*
* @author Christian Dupuis
* @author Madhura Bhave
* @author Artsiom Yudovin
*/
@Configuration
@ConditionalOnClass({ MBeanExporter.class })
@ -93,6 +94,9 @@ public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware
if (StringUtils.hasLength(defaultDomain)) {
namingStrategy.setDefaultDomain(defaultDomain);
}
boolean uniqueName = this.environment.getProperty("spring.jmx.unique-names",
Boolean.class, false);
namingStrategy.setEnsureUniqueRuntimeObjectNames(uniqueName);
return namingStrategy;
}

@ -290,6 +290,12 @@
"description": "MBeanServer bean name.",
"defaultValue": "mbeanServer"
},
{
"name": "spring.jmx.unique-names",
"type": "java.lang.Boolean",
"description": "Whether unique runtime object names should be ensured.",
"defaultValue": false
},
{
"name": "spring.jpa.open-in-view",
"defaultValue": true

@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link JmxAutoConfiguration}.
*
* @author Christian Dupuis
* @author Artsiom Yudovin
*/
public class JmxAutoConfigurationTests {
@ -92,6 +93,7 @@ public class JmxAutoConfigurationTests {
MockEnvironment env = new MockEnvironment();
env.setProperty("spring.jmx.enabled", "true");
env.setProperty("spring.jmx.default-domain", "my-test-domain");
env.setProperty("spring.jmx.unique-names", "true");
this.context = new AnnotationConfigApplicationContext();
this.context.setEnvironment(env);
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class);
@ -102,6 +104,8 @@ public class JmxAutoConfigurationTests {
.getField(mBeanExporter, "namingStrategy");
assertThat(ReflectionTestUtils.getField(naming, "defaultDomain"))
.isEqualTo("my-test-domain");
assertThat(ReflectionTestUtils.getField(naming, "ensureUniqueRuntimeObjectNames"))
.isEqualTo(true);
}
@Test

@ -100,6 +100,7 @@ content into your application. Rather, pick only the properties that you need.
spring.jmx.default-domain= # JMX domain name.
spring.jmx.enabled=true # Expose management beans to the JMX domain.
spring.jmx.server=mbeanServer # MBeanServer bean name.
spring.jmx.unique-names=false # Whether unique runtime object names should be ensured.
# Email ({sc-spring-boot-autoconfigure}/mail/MailProperties.{sc-ext}[MailProperties])
spring.mail.default-encoding=UTF-8 # Default MimeMessage encoding.
@ -1213,7 +1214,6 @@ content into your application. Rather, pick only the properties that you need.
management.endpoints.jmx.exposure.include=* # Endpoint IDs that should be included or '*' for all.
management.endpoints.jmx.exposure.exclude= # Endpoint IDs that should be excluded or '*' for all.
management.endpoints.jmx.static-names= # Additional static properties to append to all ObjectNames of MBeans representing Endpoints.
management.endpoints.jmx.unique-names=false # Whether to ensure that ObjectNames are modified in case of conflict.
# ENDPOINTS WEB CONFIGURATION ({sc-spring-boot-actuator-autoconfigure}/endpoint/web/WebEndpointProperties.{sc-ext}[WebEndpointProperties])
management.endpoints.web.exposure.include=health,info # Endpoint IDs that should be included or '*' for all.

@ -1200,17 +1200,16 @@ The name of the MBean is usually generated from the `id` of the endpoint. For ex
`health` endpoint is exposed as `org.springframework.boot:type=Endpoint,name=Health`.
If your application contains more than one Spring `ApplicationContext`, you may find that
names clash. To solve this problem, you can set the
`management.endpoints.jmx.unique-names` property to `true` so that MBean names are always
unique.
names clash. To solve this problem, you can set the `spring.jmx.unique-names` property to
`true` so that MBean names are always unique.
You can also customize the JMX domain under which endpoints are exposed. The following
settings show an example of doing so in `application.properties`:
[source,properties,indent=0]
----
spring.jmx.unique-names=true
management.endpoints.jmx.domain=com.example.myapp
management.endpoints.jmx.unique-names=true
----

Loading…
Cancel
Save