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
This commit is contained in:
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.EndpointObjectNameFactory;
|
||||||
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.jmx.support.ObjectNameManager;
|
import org.springframework.jmx.support.ObjectNameManager;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -40,11 +41,30 @@ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
|
||||||
|
|
||||||
private final String contextId;
|
private final String contextId;
|
||||||
|
|
||||||
|
private final boolean uniqueNames;
|
||||||
|
|
||||||
DefaultEndpointObjectNameFactory(JmxEndpointProperties properties,
|
DefaultEndpointObjectNameFactory(JmxEndpointProperties properties,
|
||||||
MBeanServer mBeanServer, String contextId) {
|
Environment environment, MBeanServer mBeanServer, String contextId) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.mBeanServer = mBeanServer;
|
this.mBeanServer = mBeanServer;
|
||||||
this.contextId = contextId;
|
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
|
@Override
|
||||||
|
@ -57,7 +77,7 @@ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
|
||||||
if (this.mBeanServer != null && hasMBean(baseName)) {
|
if (this.mBeanServer != null && hasMBean(baseName)) {
|
||||||
builder.append(",context=" + this.contextId);
|
builder.append(",context=" + this.contextId);
|
||||||
}
|
}
|
||||||
if (this.properties.isUniqueNames()) {
|
if (this.uniqueNames) {
|
||||||
String identity = ObjectUtils.getIdentityHexString(endpoint);
|
String identity = ObjectUtils.getIdentityHexString(endpoint);
|
||||||
builder.append(",identity=" + identity);
|
builder.append(",identity=" + identity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,11 +85,11 @@ public class JmxEndpointAutoConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnSingleCandidate(MBeanServer.class)
|
@ConditionalOnSingleCandidate(MBeanServer.class)
|
||||||
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
|
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
|
||||||
ObjectProvider<ObjectMapper> objectMapper,
|
Environment environment, ObjectProvider<ObjectMapper> objectMapper,
|
||||||
JmxEndpointsSupplier jmxEndpointsSupplier) {
|
JmxEndpointsSupplier jmxEndpointsSupplier) {
|
||||||
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
|
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
|
||||||
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(
|
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(
|
||||||
this.properties, mBeanServer, contextId);
|
this.properties, environment, mBeanServer, contextId);
|
||||||
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
|
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
|
||||||
objectMapper.getIfAvailable());
|
objectMapper.getIfAvailable());
|
||||||
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
|
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@ -41,9 +42,9 @@ public class JmxEndpointProperties {
|
||||||
private String domain = "org.springframework.boot";
|
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
|
* Additional static properties to append to all ObjectNames of MBeans representing
|
||||||
|
@ -70,11 +71,14 @@ public class JmxEndpointProperties {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUniqueNames() {
|
@Deprecated
|
||||||
|
@DeprecatedConfigurationProperty(replacement = "spring.jmx.unique-names")
|
||||||
|
public Boolean getUniqueNames() {
|
||||||
return this.uniqueNames;
|
return this.uniqueNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUniqueNames(boolean uniqueNames) {
|
@Deprecated
|
||||||
|
public void setUniqueNames(Boolean uniqueNames) {
|
||||||
this.uniqueNames = uniqueNames;
|
this.uniqueNames = uniqueNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@ import javax.management.MBeanServer;
|
||||||
import javax.management.MalformedObjectNameException;
|
import javax.management.MalformedObjectNameException;
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
@ -39,6 +41,9 @@ import static org.mockito.Mockito.mock;
|
||||||
*/
|
*/
|
||||||
public class DefaultEndpointObjectNameFactoryTests {
|
public class DefaultEndpointObjectNameFactoryTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
private final MockEnvironment environment = new MockEnvironment();
|
private final MockEnvironment environment = new MockEnvironment();
|
||||||
|
|
||||||
private final JmxEndpointProperties properties = new JmxEndpointProperties(
|
private final JmxEndpointProperties properties = new JmxEndpointProperties(
|
||||||
|
@ -72,7 +77,18 @@ public class DefaultEndpointObjectNameFactoryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void generateObjectNameWithUniqueNames() {
|
public void generateObjectNameWithUniqueNames() {
|
||||||
|
this.environment.setProperty("spring.jmx.unique-names", "true");
|
||||||
|
assertUniqueObjectName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Deprecated
|
||||||
|
public void generateObjectNameWithUniqueNamesDeprecatedProperty() {
|
||||||
this.properties.setUniqueNames(true);
|
this.properties.setUniqueNames(true);
|
||||||
|
assertUniqueObjectName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertUniqueObjectName() {
|
||||||
ExposableJmxEndpoint endpoint = endpoint("test");
|
ExposableJmxEndpoint endpoint = endpoint("test");
|
||||||
String id = ObjectUtils.getIdentityHexString(endpoint);
|
String id = ObjectUtils.getIdentityHexString(endpoint);
|
||||||
ObjectName objectName = generateObjectName(endpoint);
|
ObjectName objectName = generateObjectName(endpoint);
|
||||||
|
@ -80,6 +96,18 @@ public class DefaultEndpointObjectNameFactoryTests {
|
||||||
"org.springframework.boot:type=Endpoint,name=Test,identity=" + id);
|
"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
|
@Test
|
||||||
public void generateObjectNameWithStaticNames() {
|
public void generateObjectNameWithStaticNames() {
|
||||||
this.properties.getStaticNames().setProperty("counter", "42");
|
this.properties.getStaticNames().setProperty("counter", "42");
|
||||||
|
@ -107,8 +135,8 @@ public class DefaultEndpointObjectNameFactoryTests {
|
||||||
|
|
||||||
private ObjectName generateObjectName(ExposableJmxEndpoint endpoint) {
|
private ObjectName generateObjectName(ExposableJmxEndpoint endpoint) {
|
||||||
try {
|
try {
|
||||||
return new DefaultEndpointObjectNameFactory(this.properties, this.mBeanServer,
|
return new DefaultEndpointObjectNameFactory(this.properties, this.environment,
|
||||||
this.contextId).getObjectName(endpoint);
|
this.mBeanServer, this.contextId).getObjectName(endpoint);
|
||||||
}
|
}
|
||||||
catch (MalformedObjectNameException ex) {
|
catch (MalformedObjectNameException ex) {
|
||||||
throw new AssertionError("Invalid object name", ex);
|
throw new AssertionError("Invalid object name", ex);
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.springframework.util.StringUtils;
|
||||||
*
|
*
|
||||||
* @author Christian Dupuis
|
* @author Christian Dupuis
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
|
* @author Artsiom Yudovin
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass({ MBeanExporter.class })
|
@ConditionalOnClass({ MBeanExporter.class })
|
||||||
|
@ -93,6 +94,9 @@ public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware
|
||||||
if (StringUtils.hasLength(defaultDomain)) {
|
if (StringUtils.hasLength(defaultDomain)) {
|
||||||
namingStrategy.setDefaultDomain(defaultDomain);
|
namingStrategy.setDefaultDomain(defaultDomain);
|
||||||
}
|
}
|
||||||
|
boolean uniqueName = this.environment.getProperty("spring.jmx.unique-names",
|
||||||
|
Boolean.class, false);
|
||||||
|
namingStrategy.setEnsureUniqueRuntimeObjectNames(uniqueName);
|
||||||
return namingStrategy;
|
return namingStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,12 @@
|
||||||
"description": "MBeanServer bean name.",
|
"description": "MBeanServer bean name.",
|
||||||
"defaultValue": "mbeanServer"
|
"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",
|
"name": "spring.jpa.open-in-view",
|
||||||
"defaultValue": true
|
"defaultValue": true
|
||||||
|
|
|
@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
* Tests for {@link JmxAutoConfiguration}.
|
* Tests for {@link JmxAutoConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Christian Dupuis
|
* @author Christian Dupuis
|
||||||
|
* @author Artsiom Yudovin
|
||||||
*/
|
*/
|
||||||
public class JmxAutoConfigurationTests {
|
public class JmxAutoConfigurationTests {
|
||||||
|
|
||||||
|
@ -92,6 +93,7 @@ public class JmxAutoConfigurationTests {
|
||||||
MockEnvironment env = new MockEnvironment();
|
MockEnvironment env = new MockEnvironment();
|
||||||
env.setProperty("spring.jmx.enabled", "true");
|
env.setProperty("spring.jmx.enabled", "true");
|
||||||
env.setProperty("spring.jmx.default-domain", "my-test-domain");
|
env.setProperty("spring.jmx.default-domain", "my-test-domain");
|
||||||
|
env.setProperty("spring.jmx.unique-names", "true");
|
||||||
this.context = new AnnotationConfigApplicationContext();
|
this.context = new AnnotationConfigApplicationContext();
|
||||||
this.context.setEnvironment(env);
|
this.context.setEnvironment(env);
|
||||||
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class);
|
this.context.register(TestConfiguration.class, JmxAutoConfiguration.class);
|
||||||
|
@ -102,6 +104,8 @@ public class JmxAutoConfigurationTests {
|
||||||
.getField(mBeanExporter, "namingStrategy");
|
.getField(mBeanExporter, "namingStrategy");
|
||||||
assertThat(ReflectionTestUtils.getField(naming, "defaultDomain"))
|
assertThat(ReflectionTestUtils.getField(naming, "defaultDomain"))
|
||||||
.isEqualTo("my-test-domain");
|
.isEqualTo("my-test-domain");
|
||||||
|
assertThat(ReflectionTestUtils.getField(naming, "ensureUniqueRuntimeObjectNames"))
|
||||||
|
.isEqualTo(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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.default-domain= # JMX domain name.
|
||||||
spring.jmx.enabled=true # Expose management beans to the JMX domain.
|
spring.jmx.enabled=true # Expose management beans to the JMX domain.
|
||||||
spring.jmx.server=mbeanServer # MBeanServer bean name.
|
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])
|
# Email ({sc-spring-boot-autoconfigure}/mail/MailProperties.{sc-ext}[MailProperties])
|
||||||
spring.mail.default-encoding=UTF-8 # Default MimeMessage encoding.
|
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.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.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.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])
|
# 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.
|
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`.
|
`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
|
If your application contains more than one Spring `ApplicationContext`, you may find that
|
||||||
names clash. To solve this problem, you can set the
|
names clash. To solve this problem, you can set the `spring.jmx.unique-names` property to
|
||||||
`management.endpoints.jmx.unique-names` property to `true` so that MBean names are always
|
`true` so that MBean names are always unique.
|
||||||
unique.
|
|
||||||
|
|
||||||
You can also customize the JMX domain under which endpoints are exposed. The following
|
You can also customize the JMX domain under which endpoints are exposed. The following
|
||||||
settings show an example of doing so in `application.properties`:
|
settings show an example of doing so in `application.properties`:
|
||||||
|
|
||||||
[source,properties,indent=0]
|
[source,properties,indent=0]
|
||||||
----
|
----
|
||||||
|
spring.jmx.unique-names=true
|
||||||
management.endpoints.jmx.domain=com.example.myapp
|
management.endpoints.jmx.domain=com.example.myapp
|
||||||
management.endpoints.jmx.unique-names=true
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue