From cfc37616e591330bfb06c4e3f29a2a7f88b52dbb Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Fri, 16 Dec 2011 22:03:11 -0500 Subject: [PATCH] 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. --- .../jmx/config/MBeanExporterParser.java | 10 ++++- .../monitor/IntegrationMBeanExporter.java | 15 +++++-- .../mbeanexporterhelper/Int2307Tests.java | 43 ++++++++++++++++--- .../single-config-custom-exporter.xml | 13 +++++- .../mbeanexporterhelper/single-config.xml | 12 +++++- 5 files changed, 79 insertions(+), 14 deletions(-) diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterParser.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterParser.java index 1337250856..75c9889fa0 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterParser.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterParser.java @@ -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 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; } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java index e9d9e079f4..f2621dda66 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java @@ -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 ""; diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java index 397bc40b8c..de1a6e5b80 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java @@ -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 servers = MBeanServerFactory.findMBeanServer(null); + assertEquals(1, servers.size()); + MBeanServer server = servers.get(0); + Set 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{} diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml index 670e34a37a..a3b926d6b3 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml @@ -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"> - - + + + + #{new java.util.Random().nextInt()} + test + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml index fffedc1c31..4dd3005e3a 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml @@ -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"> - + + + + #{new java.util.Random().nextInt()} + test + +