INT-2313 Avoid Naming Conflicts With Mutliple Ctx

If multiple application contexts exist in the same JVM, where
managed beans exist with the same name, InstanceAlreadyExistsExceptions
could result.

1. Change int-jmx MBean exporter parser to ensure the exporter itself
has a unique name within the JVM.
2. When the IMBE exports Integration components for monitoring, where
the component itself is already an MBean, add domain and object name
static properties configured in the MBean exporter.
3. Add tests to load the same ac a second time to ensure no naming
conflicts arise.
This commit is contained in:
Gary Russell
2011-12-16 22:03:11 -05:00
committed by Mark Fisher
parent 1a56747a8a
commit cfc37616e5
5 changed files with 79 additions and 14 deletions

View File

@@ -13,6 +13,8 @@
package org.springframework.integration.jmx.config;
import java.util.UUID;
import javax.management.MBeanServerFactory;
import org.w3c.dom.Element;
@@ -26,12 +28,14 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.util.StringUtils;
/**
* Parser for the 'mbean-export' element of the integration JMX namespace.
*
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class MBeanExporterParser extends AbstractSingleBeanDefinitionParser {
@@ -46,7 +50,7 @@ public class MBeanExporterParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.monitor.IntegrationMBeanExporter";
return IntegrationMBeanExporter.class.getName();
}
@Override
@@ -85,6 +89,10 @@ public class MBeanExporterParser extends AbstractSingleBeanDefinitionParser {
" (clashes with <context:mbean-export/> default). Please choose another bean id.",
definition);
}
if (id.matches(IntegrationMBeanExporter.class.getName() + "#[0-9]+")) {
// Randomize the name in case there are multiple contexts in the same JVM
id += "#" + UUID.randomUUID();
}
return id;
}

View File

@@ -90,6 +90,7 @@ import org.springframework.util.ReflectionUtils;
* @author Dave Syer
* @author Helena Edelson
* @author Oleg Zhurakousky
* @author Gary Russell
*/
@ManagedResource
public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostProcessor, BeanFactoryAware,
@@ -516,7 +517,8 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP
// Expose the raw bean if it is managed
MessageChannel bean = monitor.getMessageChannel();
if (assembler.includeBean(bean.getClass(), monitor.getName())) {
registerBeanInstance(bean, monitor.getName());
registerBeanInstance(bean,
this.getMonitoredIntegrationObjectBeanKey(bean, name));
}
}
}
@@ -539,7 +541,8 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP
// Expose the raw bean if it is managed
MessageHandler bean = source.getMessageHandler();
if (assembler.includeBean(bean.getClass(), source.getName())) {
registerBeanInstance(bean, monitor.getName());
registerBeanInstance(bean,
this.getMonitoredIntegrationObjectBeanKey(bean, name));
}
}
}
@@ -562,7 +565,8 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP
// Expose the raw bean if it is managed
MessageSource<?> bean = source.getMessageSource();
if (assembler.includeBean(bean.getClass(), source.getName())) {
registerBeanInstance(bean, monitor.getName());
registerBeanInstance(bean,
this.getMonitoredIntegrationObjectBeanKey(bean, name));
}
}
}
@@ -688,6 +692,11 @@ public class IntegrationMBeanExporter extends MBeanExporter implements BeanPostP
return String.format(domain + ":type=ManagedEndpoint,name=%s,bean=%s" + getStaticNames(), name, source);
}
private String getMonitoredIntegrationObjectBeanKey(Object object, String name) {
// This ordering of keys seems to work with default settings of JConsole
return String.format(domain + ":type=" + object.getClass().getSimpleName() + ",name=%s" + getStaticNames(), name);
}
private String getStaticNames() {
if (objectNameStaticProperties.isEmpty()) {
return "";

View File

@@ -12,27 +12,57 @@
*/
package org.springframework.integration_.mbeanexporterhelper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectInstance;
import org.springframework.context.ApplicationContext;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.jmx.export.MBeanExporter;
import static org.junit.Assert.assertTrue;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
*
*/
public class Int2307Tests {
@Test
public void testInt2307_DefaultMBeanExporter() throws Exception{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
context.destroy();
new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
assertEquals(1, servers.size());
MBeanServer server = servers.get(0);
Set<ObjectInstance> mbeans = server.queryMBeans(null, null);
int bits = 0;
int count = 0;
for (ObjectInstance mbean : mbeans) {
if (mbean.toString().startsWith("org.springframework.integration.router.RecipientListRouter[test.domain:type=RecipientListRouter,name=rlr,random=")) {
bits |= 1;
count++;
} else if (mbean.toString().startsWith("org.springframework.integration.monitor.LifecycleMessageHandlerMetrics[test.domain:type=MessageHandler,name=rlr,bean=endpoint,random=")) {
bits |= 2;
count++;
} else if (mbean.toString().startsWith("org.springframework.integration.router.HeaderValueRouter[test.domain:type=HeaderValueRouter,name=hvr,random=")) {
bits |= 4;
count++;
} else if (mbean.toString().startsWith("org.springframework.integration.monitor.LifecycleMessageHandlerMetrics[test.domain:type=MessageHandler,name=hvr,bean=endpoint,random=")) {
bits |= 8;
count++;
}
}
assertEquals(0xf, bits);
assertEquals(4, count);
// make sure there are no duplicate MBean ObjectNames if 2 contexts loaded from same config
new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
}
@SuppressWarnings("unchecked")
@@ -46,7 +76,6 @@ public class Int2307Tests {
assertTrue(excludedBeanNames.contains("x"));
assertTrue(excludedBeanNames.contains("y"));
assertTrue(excludedBeanNames.contains("foo")); // non SI bean
context.destroy();
}
public static class Foo{}

View File

@@ -3,15 +3,24 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-jmx="http://www.springframework.org/schema/integration/jmx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/jmx http://www.springframework.org/schema/integration/jmx/spring-integration-jmx-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:mbean-server/>
<int-jmx:mbean-export/>
<int-jmx:mbean-export
default-domain="test.domain"
object-name-static-properties="intJmxProps"/>
<util:properties id="intJmxProps">
<prop key="random">#{new java.util.Random().nextInt()}</prop>
<prop key="test">test</prop>
</util:properties>
<bean id="myExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="excludedBeans" value="foo"/>
</bean>

View File

@@ -3,14 +3,24 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-jmx="http://www.springframework.org/schema/integration/jmx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/jmx http://www.springframework.org/schema/integration/jmx/spring-integration-jmx-2.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:mbean-server />
<int-jmx:mbean-export />
<int-jmx:mbean-export
default-domain="test.domain"
object-name-static-properties="intJmxProps"/>
<util:properties id="intJmxProps">
<prop key="random">#{new java.util.Random().nextInt()}</prop>
<prop key="test">test</prop>
</util:properties>
<context:mbean-export/>
<int:recipient-list-router id="rlr" input-channel="x">