diff --git a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java index 8aef23614c..2cca9ffc78 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java @@ -46,7 +46,6 @@ import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.CannotLoadBeanClassException; -import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -68,7 +67,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; - /** * JMX exporter that allows for exposing any Spring-managed bean to a * JMX {@link javax.management.MBeanServer}, without the need to define any @@ -91,6 +89,8 @@ import org.springframework.util.ObjectUtils; * @author Juergen Hoeller * @author Rick Evans * @author Mark Fisher + * @author Marten Deinum + * @author Stephane Nicoll * @since 1.2 * @see #setBeans * @see #setAutodetect @@ -100,7 +100,7 @@ import org.springframework.util.ObjectUtils; * @see MBeanExporterListener */ public class MBeanExporter extends MBeanRegistrationSupport - implements MBeanExportOperations, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean, SmartLifecycle { + implements MBeanExportOperations, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, SmartLifecycle { /** * Autodetection mode indicating that no autodetection should be used. @@ -405,16 +405,31 @@ public class MBeanExporter extends MBeanRegistrationSupport } } + /** + * Specify the phase in which the MBeans should be exported to the + * JMX domain. The startup order proceeds from lowest to highest, and + * the shutdown order is the reverse of that. By default this value + * is {@code Integer.MAX_VALUE} meaning that MBeans are exported + * as late as possible and removed from the domain as soon as possible. + */ + public void setPhase(int phase) { + this.phase = phase; + } + + /** + * Set whether to automatically export MBeans after initialization. + *

Default is "true"; set this to "false" to allow for manual startup + * through the {@link #start()} method. + */ + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + //--------------------------------------------------------------------- // Lifecycle in bean factory: automatically register/unregister beans //--------------------------------------------------------------------- - /** - * Start bean registration automatically when deployed in an - * {@code ApplicationContext}. - * @see #registerBeans() - */ @Override public void afterPropertiesSet() { // If no server was provided then try to find one. This is useful in an environment @@ -424,15 +439,56 @@ public class MBeanExporter extends MBeanRegistrationSupport } } - /** - * Unregisters all beans that this exported has exposed via JMX - * when the enclosing {@code ApplicationContext} is destroyed. - */ @Override - public void destroy() { - logger.info("Unregistering JMX-exposed beans on shutdown"); - unregisterNotificationListeners(); - unregisterBeans(); + public void start() { + logger.info("Registering beans for JMX exposure"); + synchronized (this.lifecycleMonitor) { + try { + registerBeans(); + registerNotificationListeners(); + } catch (RuntimeException ex) { + // Unregister beans already registered by this exporter. + unregisterNotificationListeners(); + unregisterBeans(); + throw ex; + } + } + running = true; + } + + @Override + public void stop() { + logger.info("Unregistering JMX-exposed beans on stop"); + synchronized (this.lifecycleMonitor) { + unregisterNotificationListeners(); + unregisterBeans(); + running = false; + } + } + + @Override + public void stop(Runnable callback) { + synchronized (this.lifecycleMonitor) { + stop(); + callback.run(); + } + } + + @Override + public boolean isRunning() { + synchronized (this.lifecycleMonitor) { + return this.running; + } + } + + @Override + public boolean isAutoStartup() { + return this.autoStartup; + } + + @Override + public int getPhase() { + return this.phase; } @@ -1053,79 +1109,6 @@ public class MBeanExporter extends MBeanRegistrationSupport } } - /** - * Set whether to automatically start the container after initialization. - *

Default is "true"; set this to "false" to allow for manual startup - * through the {@link #start()} method. - */ - public void setAutoStartup(boolean autoStartup) { - this.autoStartup = autoStartup; - } - - @Override - public boolean isAutoStartup() { - return this.autoStartup; - } - - @Override - public void stop(Runnable callback) { - synchronized (this.lifecycleMonitor) { - stop(); - callback.run(); - } - } - - @Override - public void start() { - logger.info("Registering beans for JMX exposure"); - synchronized (this.lifecycleMonitor) { - try { - registerBeans(); - registerNotificationListeners(); - } catch (RuntimeException ex) { - // Unregister beans already registered by this exporter. - unregisterNotificationListeners(); - unregisterBeans(); - throw ex; - } - } - running = true; - } - - @Override - public void stop() { - logger.info("Unregistering JMX-exposed beans on stop"); - synchronized (this.lifecycleMonitor) { - unregisterNotificationListeners(); - unregisterBeans(); - running = false; - } - } - - @Override - public boolean isRunning() { - synchronized (this.lifecycleMonitor) { - return this.running; - } - } - - /** - * Specify the phase in which this container should be started and - * stopped. The startup order proceeds from lowest to highest, and - * the shutdown order is the reverse of that. By default this value - * is Integer.MAX_VALUE meaning that this container starts as late - * as possible and stops as soon as possible. - */ - public void setPhase(int phase) { - this.phase = phase; - } - - @Override - public int getPhase() { - return this.phase; - } - - //--------------------------------------------------------------------- // Inner classes for internal use //--------------------------------------------------------------------- diff --git a/spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java b/spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java index ab05a3ea6e..9676a69adf 100644 --- a/spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.jmx.export.MBeanExporter; import org.springframework.tests.TestGroup; import org.springframework.util.MBeanTestUtils; @@ -91,6 +92,17 @@ public abstract class AbstractMBeanServerTests { return this.server; } + /** + * Start the specified {@link MBeanExporter}. + * + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + * @see org.springframework.context.Lifecycle#start() + */ + protected void start(MBeanExporter exporter) { + exporter.afterPropertiesSet(); + exporter.start(); + } + protected void assertIsRegistered(String message, ObjectName objectName) { assertTrue(message, getServer().isRegistered(objectName)); } diff --git a/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java b/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java index e1bd521a1d..a249f6a315 100644 --- a/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java @@ -67,7 +67,7 @@ public class MBeanClientInterceptorTests extends AbstractMBeanServerTests { adapter.setServer(getServer()); adapter.setBeans(beans); adapter.setAssembler(new ProxyTestAssembler()); - adapter.afterPropertiesSet(); + start(adapter); } protected MBeanServerConnection getServerConnection() throws Exception { diff --git a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java index 808eedba81..532034b64c 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java @@ -33,11 +33,16 @@ import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.modelmbean.ModelMBeanInfo; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.jmx.AbstractMBeanServerTests; @@ -63,10 +68,14 @@ import static org.junit.Assert.*; * @author Mark Fisher * @author Chris Beams * @author Sam Brannen + * @author Stephane Nicoll */ @SuppressWarnings("deprecation") public final class MBeanExporterTests extends AbstractMBeanServerTests { + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private static final String OBJECT_NAME = "spring:test=jmxMBeanAdaptor"; @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -76,12 +85,9 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { // put a non-NotificationListener instance in as a value... listeners.put("*", this); MBeanExporter exporter = new MBeanExporter(); - try { - exporter.setNotificationListenerMappings(listeners); - fail("Must have thrown a ClassCastException when registering a non-NotificationListener instance as a NotificationListener."); - } - catch (ClassCastException expected) { - } + + thrown.expect(ClassCastException.class); + exporter.setNotificationListenerMappings(listeners); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -91,12 +97,9 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { // put null in as a value... listeners.put("*", null); MBeanExporter exporter = new MBeanExporter(); - try { - exporter.setNotificationListenerMappings(listeners); - fail("Must have thrown an IllegalArgumentException when registering a null instance as a NotificationListener."); - } - catch (IllegalArgumentException expected) { - } + + thrown.expect(IllegalArgumentException.class); + exporter.setNotificationListenerMappings(listeners); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -116,9 +119,9 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setNotificationListenerMappings(listeners); try { - exporter.afterPropertiesSet(); - exporter.start(); - fail("Must have thrown an MBeanExportException when registering a NotificationListener on a non-existent MBean."); + start(exporter); + fail("Must have thrown an MBeanExportException when registering a " + + "NotificationListener on a non-existent MBean."); } catch (MBeanExportException expected) { assertTrue(expected.contains(InstanceNotFoundException.class)); @@ -130,23 +133,16 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { MBeanExporter exporter = new MBeanExporter(); exporter.setBeans(getBeanMap()); exporter.setServer(server); - exporter.afterPropertiesSet(); - exporter.start(); - assertIsRegistered("The bean was not registered with the MBeanServer", - ObjectNameManager.getInstance(OBJECT_NAME)); + try { + start(exporter); + assertIsRegistered("The bean was not registered with the MBeanServer", + ObjectNameManager.getInstance(OBJECT_NAME)); + } + finally { + exporter.stop(); + } } - /** Fails if JVM platform MBean server has been started already - @Test - public void testWithLocatedMBeanServer() throws Exception { - MBeanExporter adaptor = new MBeanExporter(); - adaptor.setBeans(getBeanMap()); - adaptor.afterPropertiesSet(); - assertIsRegistered("The bean was not registered with the MBeanServer", ObjectNameManager.getInstance(OBJECT_NAME)); - server.unregisterMBean(new ObjectName(OBJECT_NAME)); - } - */ - @Test public void testUserCreatedMBeanRegWithDynamicMBean() throws Exception { Map map = new HashMap(); @@ -158,22 +154,24 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(map); exporter.setAssembler(asm); - exporter.afterPropertiesSet(); - exporter.start(); - Object name = server.getAttribute(ObjectNameManager.getInstance("spring:name=dynBean"), "Name"); - assertEquals("The name attribute is incorrect", "Rob Harrop", name); - assertFalse("Assembler should not have been invoked", asm.invoked); + try { + start(exporter); + Object name = server.getAttribute(ObjectNameManager.getInstance("spring:name=dynBean"), "Name"); + assertEquals("The name attribute is incorrect", "Rob Harrop", name); + assertFalse("Assembler should not have been invoked", asm.invoked); + } + finally { + exporter.stop(); + } } @Test public void testAutodetectMBeans() throws Exception { - GenericApplicationContext ctx = new GenericApplicationContext(); - new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(new ClassPathResource("autodetectMBeans.xml", getClass())); - ctx.refresh(); + ConfigurableApplicationContext ctx = load("autodetectMBeans.xml"); try { ctx.getBean("exporter"); - MBeanServer server = (MBeanServer) ctx.getBean("server"); + MBeanServer server = ctx.getBean("server", MBeanServer.class); ObjectInstance instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=true")); assertNotNull(instance); instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean2=true")); @@ -181,39 +179,32 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean3=true")); assertNotNull(instance); } finally { - ctx.destroy(); + ctx.close(); } } @Test public void testAutodetectWithExclude() throws Exception { - GenericApplicationContext ctx = new GenericApplicationContext(); - new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(new ClassPathResource("autodetectMBeans.xml", getClass())); - ctx.refresh(); + ConfigurableApplicationContext ctx = load("autodetectMBeans.xml"); try { ctx.getBean("exporter"); - MBeanServer server = (MBeanServer) ctx.getBean("server"); + MBeanServer server = ctx.getBean("server", MBeanServer.class); ObjectInstance instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=true")); assertNotNull(instance); - try { - server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=false")); - fail("MBean with name spring:mbean=false should have been excluded"); - } catch (InstanceNotFoundException expected) { - } + thrown.expect(InstanceNotFoundException.class); + server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=false")); } finally { - ctx.destroy(); + ctx.close(); } } @Test public void testAutodetectLazyMBeans() throws Exception { - GenericApplicationContext ctx = new GenericApplicationContext(); - new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(new ClassPathResource("autodetectLazyMBeans.xml", getClass())); - ctx.refresh(); + ConfigurableApplicationContext ctx = load("autodetectLazyMBeans.xml"); try { ctx.getBean("exporter"); - MBeanServer server = (MBeanServer) ctx.getBean("server"); + MBeanServer server = ctx.getBean("server", MBeanServer.class); ObjectName oname = ObjectNameManager.getInstance("spring:mbean=true"); assertNotNull(server.getObjectInstance(oname)); @@ -225,19 +216,41 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { name = (String) server.getAttribute(oname, "Name"); assertEquals("Invalid name returned", "Juergen Hoeller", name); } finally { - ctx.destroy(); + ctx.close(); } } @Test public void testAutodetectNoMBeans() throws Exception { - GenericApplicationContext ctx = new GenericApplicationContext(); - new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(new ClassPathResource("autodetectNoMBeans.xml", getClass())); - ctx.refresh(); + ConfigurableApplicationContext ctx = load("autodetectNoMBeans.xml"); try { ctx.getBean("exporter"); } finally { - ctx.destroy(); + ctx.close(); + } + } + + @Test + public void testAutoStartupToFalse() throws Exception { + ConfigurableApplicationContext ctx = load("autodetectNoAutoStartup.xml"); + try { + MBeanExporter exporter = ctx.getBean("exporter", MBeanExporter.class); + MBeanServer server = ctx.getBean("server", MBeanServer.class); + + ObjectName on = ObjectNameManager.getInstance("spring:mbean=true"); + try { + server.getObjectInstance(on); + fail("MBeans should not have been exported with autoStartup set to false"); + } + catch (InstanceNotFoundException e) { + // expected + } + + // Export manually + exporter.start(); + assertNotNull(server.getObjectInstance(on)); // Should be exposed now. + } finally { + ctx.close(); } } @@ -250,14 +263,14 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeans(getBeanMap()); exporter.setServer(server); exporter.setListeners(new MBeanExporterListener[] { listener1, listener2 }); - exporter.afterPropertiesSet(); - exporter.start(); - exporter.destroy(); + start(exporter); + exporter.stop(); assertListener(listener1); assertListener(listener2); } + @Test public void testExportJdkProxy() throws Exception { JmxTestBean bean = new JmxTestBean(); @@ -297,8 +310,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); ObjectInstance instance = server.getObjectInstance(objectName); assertNotNull(instance); @@ -327,8 +339,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setRegistrationBehavior(MBeanExporter.REGISTRATION_IGNORE_EXISTING); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); ObjectInstance instance = server.getObjectInstance(objectName); assertNotNull(instance); @@ -359,8 +370,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setRegistrationPolicy(RegistrationPolicy.REPLACE_EXISTING); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); ObjectInstance instance = server.getObjectInstance(objectName); assertNotNull(instance); @@ -385,13 +395,12 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(getServer()); exporter.setBeans(beans); exporter.setExposeManagedResourceClassLoader(true); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsRegistered("Bean instance not registered", objectName); - Object result = server.invoke(objectName, "add", new Object[] { new Integer(2), new Integer(3) }, new String[] { - int.class.getName(), int.class.getName() }); + Object result = server.invoke(objectName, "add", new Object[] {new Integer(2), new Integer(3)}, new String[] { + int.class.getName(), int.class.getName()}); assertEquals("Incorrect result return from add", result, new Integer(5)); assertEquals("Incorrect attribute value", name, server.getAttribute(objectName, "Name")); @@ -416,8 +425,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeanFactory(factory); exporter.setAutodetectMode(MBeanExporter.AUTODETECT_NONE); // MBean has a bad ObjectName, so if said MBean is autodetected, an exception will be thrown... - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); } @@ -434,8 +442,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(exportedBeanName)); exporter.setBeanFactory(factory); exporter.setAutodetectMode(MBeanExporter.AUTODETECT_MBEAN); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsRegistered("Bona fide MBean not autodetected in AUTODETECT_MBEAN mode", ObjectNameManager.getInstance(OBJECT_NAME)); @@ -458,8 +465,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(exportedBeanName)); exporter.setBeanFactory(factory); exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsRegistered("Bona fide MBean not autodetected in (AUTODETECT_ALL) mode", ObjectNameManager.getInstance(OBJECT_NAME)); assertIsRegistered("Bean not autodetected in (AUTODETECT_ALL) mode", @@ -481,8 +487,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(exportedBeanName)); exporter.setBeanFactory(factory); exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ASSEMBLER); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsNotRegistered("Bona fide MBean was autodetected in AUTODETECT_ASSEMBLER mode - must not have been", ObjectNameManager.getInstance(OBJECT_NAME)); assertIsRegistered("Bean not autodetected in AUTODETECT_ASSEMBLER mode", @@ -506,104 +511,69 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(OBJECT_NAME)); exporter.setBeanFactory(factory); exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ASSEMBLER); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsRegistered("Explicitly exported bona fide MBean obviously not exported.", ObjectNameManager.getInstance(OBJECT_NAME)); } @Test - public void testSetAutodetectModeToOutOfRangeNegativeValue() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectMode(-1); - fail("Must have failed when supplying an invalid negative out-of-range autodetect mode"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeToOutOfRangeNegativeValue() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectMode(-1); } @Test - public void testSetAutodetectModeToOutOfRangePositiveValue() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectMode(5); - fail("Must have failed when supplying an invalid positive out-of-range autodetect mode"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeToOutOfRangePositiveValue() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectMode(5); } @Test - public void testSetAutodetectModeNameToNull() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectModeName(null); - fail("Must have failed when supplying a null autodetect mode name"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeNameToNull() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectModeName(null); } @Test - public void testSetAutodetectModeNameToAnEmptyString() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectModeName(""); - fail("Must have failed when supplying an empty autodetect mode name"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeNameToAnEmptyString() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectModeName(""); } @Test - public void testSetAutodetectModeNameToAWhitespacedString() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectModeName(" \t"); - fail("Must have failed when supplying a whitespace-only autodetect mode name"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeNameToAWhitespacedString() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectModeName(" \t"); } @Test - public void testSetAutodetectModeNameToARubbishValue() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectModeName("That Hansel is... *sssooo* hot right now!"); - fail("Must have failed when supplying a whitespace-only autodetect mode name"); - } - catch (IllegalArgumentException expected) { - } + public void testSetAutodetectModeNameToARubbishValue() { + MBeanExporter exporter = new MBeanExporter(); + thrown.expect(IllegalArgumentException.class); + exporter.setAutodetectModeName("That Hansel is... *sssooo* hot right now!"); } @Test public void testNotRunningInBeanFactoryAndPassedBeanNameToExport() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - Map beans = new HashMap(); - beans.put(OBJECT_NAME, "beanName"); - exporter.setBeans(beans); - exporter.afterPropertiesSet(); - exporter.start(); - fail("Expecting exception because MBeanExporter is not running in a BeanFactory and was passed bean name to (lookup and then) export"); - } - catch (MBeanExportException expected) { - } + MBeanExporter exporter = new MBeanExporter(); + Map beans = new HashMap(); + beans.put(OBJECT_NAME, "beanName"); + exporter.setBeans(beans); + thrown.expect(MBeanExportException.class); + start(exporter); } @Test public void testNotRunningInBeanFactoryAndAutodetectionIsOn() throws Exception { - try { - MBeanExporter exporter = new MBeanExporter(); - exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); - exporter.afterPropertiesSet(); - exporter.start(); - fail("Expecting exception because MBeanExporter is not running in a BeanFactory and was configured to autodetect beans"); - } - catch (MBeanExportException expected) { - } + MBeanExporter exporter = new MBeanExporter(); + exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + thrown.expect(MBeanExportException.class); + start(exporter); } /** @@ -616,13 +586,12 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setServer(this.server); MockMBeanExporterListener listener = new MockMBeanExporterListener(); exporter.setListeners(new MBeanExporterListener[] { listener }); - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); assertIsRegistered("The bean was not registered with the MBeanServer", ObjectNameManager.getInstance(OBJECT_NAME)); this.server.unregisterMBean(new ObjectName(OBJECT_NAME)); - exporter.destroy(); + exporter.stop(); assertEquals("Listener should not have been invoked (MBean previously unregistered by external agent)", 0, listener.getUnregistered().size()); } @@ -649,7 +618,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { StubNotificationListener listener = new StubNotificationListener(); exporter.setNotificationListenerMappings(Collections.singletonMap(beanName, listener)); - exporter.afterPropertiesSet(); + start(exporter); } @Test @@ -671,7 +640,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { StubNotificationListener listener = new StubNotificationListener(); exporter.setNotificationListenerMappings(Collections.singletonMap("*", listener)); - exporter.afterPropertiesSet(); + start(exporter); } /* @@ -699,8 +668,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { exporter.setBeanFactory(factory); try { - exporter.afterPropertiesSet(); - exporter.start(); + start(exporter); fail("Must have failed during creation of RuntimeExceptionThrowingConstructorBean"); } catch (RuntimeException expected) { @@ -712,6 +680,10 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { ObjectNameManager.getInstance(objectName2)); } + private ConfigurableApplicationContext load(String context) { + return new ClassPathXmlApplicationContext(context, getClass()); + } + private Map getBeanMap() { Map map = new HashMap(); map.put(OBJECT_NAME, new JmxTestBean()); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/NotificationListenerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/NotificationListenerTests.java index a6423dcee8..86edb16bf0 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/NotificationListenerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/NotificationListenerTests.java @@ -61,7 +61,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); exporter.setNotificationListenerMappings(notificationListeners); - exporter.afterPropertiesSet(); + start(exporter); // update the attribute String attributeName = "Name"; @@ -87,7 +87,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); exporter.setNotificationListenerMappings(notificationListeners); - exporter.afterPropertiesSet(); + start(exporter); // update the attribute String attributeName = "Name"; @@ -115,7 +115,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); exporter.setNotificationListeners(new NotificationListenerBean[] { listenerBean }); - exporter.afterPropertiesSet(); + start(exporter); // update the attribute String attributeName = "Name"; @@ -143,7 +143,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); exporter.setNotificationListeners(new NotificationListenerBean[] { listenerBean }); - exporter.afterPropertiesSet(); + start(exporter); // update the attribute String attributeName = "Name"; @@ -181,7 +181,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setServer(server); exporter.setBeans(beans); exporter.setNotificationListeners(new NotificationListenerBean[] { listenerBean }); - exporter.afterPropertiesSet(); + start(exporter); // update the attributes String nameAttribute = "Name"; @@ -227,7 +227,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setNotificationListenerMappings(listenerMappings); exporter.setBeanFactory(factory); - exporter.afterPropertiesSet(); + start(exporter); assertIsRegistered("Should have registered MBean", objectName); server.setAttribute(objectName, new Attribute("Age", new Integer(77))); @@ -258,7 +258,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setNotificationListenerMappings(listenerMappings); exporter.setBeanFactory(factory); - exporter.afterPropertiesSet(); + start(exporter); assertIsRegistered("Should have registered MBean", objectName); server.setAttribute(objectName, new Attribute("Age", new Integer(77))); @@ -290,7 +290,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setNotificationListenerMappings(listenerMappings); exporter.setBeanFactory(factory); - exporter.afterPropertiesSet(); + start(exporter); assertIsRegistered("Should have registered MBean", objectName); server.setAttribute(objectName, new Attribute("Age", new Integer(77))); @@ -322,7 +322,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setNotificationListenerMappings(listenerMappings); exporter.setBeanFactory(factory); - exporter.afterPropertiesSet(); + start(exporter); assertIsRegistered("Should have registered MBean", objectName); server.setAttribute(objectName, new Attribute("Age", new Integer(77))); @@ -362,7 +362,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { exporter.setBeans(beans); exporter.setNotificationListenerMappings(listenerMappings); exporter.setBeanFactory(factory); - exporter.afterPropertiesSet(); + start(exporter); assertIsRegistered("Should have registered MBean", objectName1); assertIsRegistered("Should have registered MBean", objectName2); @@ -384,7 +384,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { MBeanExporter exporter = new MBeanExporter(); exporter.setServer(server); exporter.setBeans(beans); - exporter.afterPropertiesSet(); + start(exporter); CountingAttributeChangeNotificationListener listener = new CountingAttributeChangeNotificationListener(); @@ -420,7 +420,7 @@ public class NotificationListenerTests extends AbstractMBeanServerTests { MBeanExporter exporter = new MBeanExporter(); exporter.setServer(server); exporter.setBeans(beans); - exporter.afterPropertiesSet(); + start(exporter); CountingAttributeChangeNotificationListener listener = new CountingAttributeChangeNotificationListener(); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java index 0c3ece7839..06424ac7b3 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java @@ -175,7 +175,7 @@ public abstract class AbstractMetadataAssemblerTests extends AbstractJmxAssemble Map beans = new HashMap(); beans.put(objectName, proxy); exporter.setBeans(beans); - exporter.afterPropertiesSet(); + start(exporter); MBeanInfo inf = getServer().getMBeanInfo(ObjectNameManager.getInstance(objectName)); assertEquals("Incorrect number of operations", getExpectedOperationCount(), inf.getOperations().length); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/autodetectLazyMBeans.xml b/spring-context/src/test/java/org/springframework/jmx/export/autodetectLazyMBeans.xml index 960a57b12c..009109490d 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/autodetectLazyMBeans.xml +++ b/spring-context/src/test/java/org/springframework/jmx/export/autodetectLazyMBeans.xml @@ -17,10 +17,4 @@ - - - - - - diff --git a/spring-context/src/test/java/org/springframework/jmx/export/autodetectMBeans.xml b/spring-context/src/test/java/org/springframework/jmx/export/autodetectMBeans.xml index d028b5274c..ccda368e1b 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/autodetectMBeans.xml +++ b/spring-context/src/test/java/org/springframework/jmx/export/autodetectMBeans.xml @@ -17,12 +17,6 @@ - - - - - - diff --git a/spring-context/src/test/resources/org/springframework/jmx/export/autodetectNoAutoStartup.xml b/spring-context/src/test/resources/org/springframework/jmx/export/autodetectNoAutoStartup.xml new file mode 100644 index 0000000000..d385c07c48 --- /dev/null +++ b/spring-context/src/test/resources/org/springframework/jmx/export/autodetectNoAutoStartup.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 84eeb27e88..afcdf5eced 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -41809,6 +41809,13 @@ With this configuration the `testBean` bean is exposed as an MBean under the are exposed as attributes and all __public__ methods (bar those inherited from the `Object` class) are exposed as operations. +[NOTE] +==== +`MBeanExporter` is a `Lifecycle` bean (see <>) +and MBeans are exported as late as possible during the application lifecycle by default. It +is possible to configure the `phase` at which the export happens or disable automatic +registration by setting the `autoStartup` flag. +==== [[jmx-exporting-mbeanserver]]