Create one SpringApplicationAdminMXBeanRegistrar per context hierarchy

Previously, one SpringApplicationAdminMXBeanRegistrar was created
per context. When there was more then one context this would result
in a javax.management.InstanceAlreadyExistsException being thrown
as an attempt was made to register the MBean more than once.

This commit updates SpringApplicationAdminJmxAutoConfiguration so
that the registrar is only created when there's no such existing bean
in the context hierarchy.

Closes gh-6378
This commit is contained in:
Andy Wilkinson 2016-07-13 09:49:09 +01:00
parent 49302b3449
commit 68fb5789ca
2 changed files with 40 additions and 2 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 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.
@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.admin.SpringApplicationAdminMXBean; import org.springframework.boot.admin.SpringApplicationAdminMXBean;
import org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar; import org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -34,6 +35,7 @@ import org.springframework.jmx.export.MBeanExporter;
* for internal use only. * for internal use only.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
* @since 1.3.0 * @since 1.3.0
* @see SpringApplicationAdminMXBean * @see SpringApplicationAdminMXBean
*/ */
@ -60,6 +62,7 @@ public class SpringApplicationAdminJmxAutoConfiguration {
private Environment environment; private Environment environment;
@Bean @Bean
@ConditionalOnMissingBean
public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar() public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar()
throws MalformedObjectNameException { throws MalformedObjectNameException {
String jmxName = this.environment.getProperty(JMX_NAME_PROPERTY, String jmxName = this.environment.getProperty(JMX_NAME_PROPERTY,

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 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.
@ -30,6 +30,9 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
@ -49,6 +52,7 @@ import static org.junit.Assert.fail;
* Tests for {@link SpringApplicationAdminJmxAutoConfiguration}. * Tests for {@link SpringApplicationAdminJmxAutoConfiguration}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
*/ */
public class SpringApplicationAdminJmxAutoConfigurationTests { public class SpringApplicationAdminJmxAutoConfigurationTests {
@ -131,6 +135,37 @@ public class SpringApplicationAdminJmxAutoConfigurationTests {
assertEquals(String.valueOf(expected), actual); assertEquals(String.valueOf(expected), actual);
} }
@Test
public void onlyRegisteredOnceWhenThereIsAChildContext() throws Exception {
SpringApplicationBuilder parentBuilder = new SpringApplicationBuilder().web(false)
.sources(JmxAutoConfiguration.class,
SpringApplicationAdminJmxAutoConfiguration.class);
SpringApplicationBuilder childBuilder = parentBuilder
.child(JmxAutoConfiguration.class,
SpringApplicationAdminJmxAutoConfiguration.class)
.web(false);
ConfigurableApplicationContext parent = null;
ConfigurableApplicationContext child = null;
try {
parent = parentBuilder.run("--" + ENABLE_ADMIN_PROP);
child = childBuilder.run("--" + ENABLE_ADMIN_PROP);
BeanFactoryUtils.beanOfType(parent.getBeanFactory(),
SpringApplicationAdminMXBeanRegistrar.class);
this.thrown.expect(NoSuchBeanDefinitionException.class);
BeanFactoryUtils.beanOfType(child.getBeanFactory(),
SpringApplicationAdminMXBeanRegistrar.class);
}
finally {
if (parent != null) {
parent.close();
}
if (child != null) {
child.close();
}
}
}
private ObjectName createDefaultObjectName() { private ObjectName createDefaultObjectName() {
return createObjectName(DEFAULT_JMX_NAME); return createObjectName(DEFAULT_JMX_NAME);
} }