/* * Copyright 2009-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.monitor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Date; import java.util.Set; import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import org.junit.After; import org.junit.Test; import org.springframework.aop.framework.Advised; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.Lifecycle; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.context.OrderlyShutdownCapable; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; import org.springframework.integration.test.util.TestUtils; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.util.Assert; public class MBeanExporterIntegrationTests { private IntegrationMBeanExporter messageChannelsMonitor; private GenericXmlApplicationContext context; @After public void close() { if (context != null) { context.close(); } } @Test public void testCircularReferenceNoChannel() throws Exception { context = new GenericXmlApplicationContext(getClass(), "oref-nonchannel.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); } @Test public void testCircularReferenceNoChannelInFactoryBean() throws Exception { context = new GenericXmlApplicationContext(getClass(), "oref-factory-nonchannel.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); } @Test public void testCircularReferenceWithChannel() throws Exception { context = new GenericXmlApplicationContext(getClass(), "oref-channel.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); } @Test public void testCircularReferenceWithChannelInFactoryBean() throws Exception { context = new GenericXmlApplicationContext(getClass(), "oref-factory-channel.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); assertTrue(Arrays.asList(messageChannelsMonitor.getChannelNames()).contains("anonymous")); MBeanServer server = context.getBean(MBeanServer.class); assertEquals(1, server.queryNames(ObjectName.getInstance("com.foo:*"), null).size()); assertEquals(1, server.queryNames(ObjectName.getInstance("org.springframework.integration:name=anonymous,*"), null).size()); } @Test public void testCircularReferenceWithChannelInFactoryBeanAutodetected() throws Exception { context = new GenericXmlApplicationContext(getClass(), "oref-factory-channel-autodetect.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); } @Test public void testLifecycleInEndpointWithMessageSource() throws Exception { context = new GenericXmlApplicationContext(getClass(), "lifecycle-source.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); MBeanServer server = context.getBean(MBeanServer.class); Set<ObjectName> names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=ManagedEndpoint,*"), null); assertEquals(2, names.size()); names = server.queryNames(ObjectName.getInstance("org.springframework.integration:name=explicit,*"), null); assertEquals(1, names.size()); MBeanOperationInfo[] operations = server.getMBeanInfo(names.iterator().next()).getOperations(); String startName = null; for (MBeanOperationInfo info : operations) { String name = info.getName(); if (name.startsWith("start")) { startName = name; } } // Lifecycle method name assertEquals("start", startName); assertTrue((Boolean) server.invoke(names.iterator().next(), "isRunning", null, null)); messageChannelsMonitor.stopActiveComponents(3000); assertFalse((Boolean) server.invoke(names.iterator().next(), "isRunning", null, null)); ActiveChannel activeChannel = context.getBean("activeChannel", ActiveChannel.class); assertTrue(activeChannel.isStopCalled()); OtherActiveComponent otherActiveComponent = context.getBean(OtherActiveComponent.class); assertTrue(otherActiveComponent.isBeforeCalled()); assertTrue(otherActiveComponent.isAfterCalled()); assertTrue(otherActiveComponent.isRunning()); assertFalse(context.getBean(AMessageProducer.class).isRunning()); // check pollers are still running QueueChannel input = (QueueChannel) extractTarget(context.getBean("input")); QueueChannel input2 = (QueueChannel) extractTarget(context.getBean("input2")); input.purge(null); input2.purge(null); input.send(new GenericMessage<String>("foo")); assertNotNull(input2.receive(10000)); } private Object extractTarget(Object bean) { if (!(bean instanceof Advised)) { return bean; } Advised advised = (Advised) bean; if (advised.getTargetSource() == null) { return null; } try { return extractTarget(advised.getTargetSource().getTarget()); } catch (Exception e) { return null; } } @Test public void testSelfDestruction() throws Exception { context = new GenericXmlApplicationContext(getClass(), "self-destruction-context.xml"); SourcePollingChannelAdapter adapter = context.getBean(SourcePollingChannelAdapter.class); adapter.start(); int n = 0; while (adapter.isRunning()) { n += 10; if (n > 10000) { fail("Adapter failed to stop"); } Thread.sleep(10); } } @Test public void testLifecycleInEndpointWithoutMessageSource() throws Exception { context = new GenericXmlApplicationContext(getClass(), "lifecycle-no-source.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); MBeanServer server = context.getBean(MBeanServer.class); Set<ObjectName> names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=ManagedEndpoint,*"), null); assertEquals(1, names.size()); names = server.queryNames(ObjectName.getInstance("org.springframework.integration:name=gateway,*"), null); assertEquals(1, names.size()); MBeanOperationInfo[] operations = server.getMBeanInfo(names.iterator().next()).getOperations(); String startName = null; for (MBeanOperationInfo info : operations) { String name = info.getName(); if (name.startsWith("start")) { startName = name; } } // Lifecycle method name assertEquals("start", startName); context.close(); context = new GenericXmlApplicationContext(getClass(), "lifecycle-no-source.xml"); server = context.getBean(MBeanServer.class); names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=ManagedEndpoint,*"), null); assertEquals(1, names.size()); names = server.queryNames(ObjectName.getInstance("org.springframework.integration:name=gateway,*"), null); assertEquals(1, names.size()); } @Test public void testComponentNames() throws Exception { context = new GenericXmlApplicationContext(getClass(), "excluded-components.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); MBeanServer server = context.getBean(MBeanServer.class); Set<ObjectName> names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=MessageChannel,*"), null); // Only one registered (out of >2 available) assertEquals(1, names.size()); names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=MessageHandler,*"), null); assertEquals(0, names.size()); } @Test public void testDuplicateComponentNames() throws Exception { context = new GenericXmlApplicationContext(getClass(), "duplicate-components.xml"); messageChannelsMonitor = context.getBean(IntegrationMBeanExporter.class); assertNotNull(messageChannelsMonitor); MBeanServer server = context.getBean(MBeanServer.class); Set<ObjectName> names = server.queryNames(ObjectName.getInstance("org.springframework.integration:type=ManagedEndpoint,*"), null); assertEquals(2, names.size()); } @Test public void testSingleMBeanServer() { AnnotationConfigApplicationContext ctx1 = new AnnotationConfigApplicationContext(Config1.class); AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(Config2.class); assertSame(TestUtils.getPropertyValue(ctx1.getBean(IntegrationMBeanExporter.class), "server"), TestUtils.getPropertyValue(ctx2.getBean(IntegrationMBeanExporter.class), "server")); ctx1.close(); ctx2.close(); } @EnableIntegration @EnableIntegrationMBeanExport(defaultDomain = "config1") public static class Config1 { } @EnableIntegration @EnableIntegrationMBeanExport(defaultDomain = "config2") public static class Config2 { } public static class BogusEndpoint extends AbstractEndpoint { @SuppressWarnings("unused") private IntegrationObjectSupport parent; public void setParent(IntegrationObjectSupport parent) { this.parent = parent; setComponentName(parent.getComponentName()); } @Override protected void doStart() { } @Override protected void doStop() { } } public static class DateFactoryBean implements FactoryBean<Date> { private Date date; public void setDate(Date date) { this.date = date; } // This has the potential to blow up if called before setDate(). // Depends on bean instantiation order and IntegrationMBeanExporter // can influence that by aggressively instantiating other MBeanExporters @Override public Date getObject() throws Exception { Assert.state(date != null, "A date must be provided"); return date; } @Override public Class<?> getObjectType() { return Date.class; } @Override public boolean isSingleton() { return true; } } public static class DateHolder implements InitializingBean { private Date date; public void setDate(Date date) { this.date = date; } @Override public void afterPropertiesSet() throws Exception { Assert.state(date != null, "A date must be provided"); } } public static class MetricFactoryBean implements FactoryBean<Metric> { private MessageChannel channel; private final Metric date = new Metric(); public void setChannel(MessageChannel channel) { this.channel = channel; } @Override public Metric getObject() throws Exception { Assert.state(channel != null, "A channel must be provided"); return date; } @Override public Class<?> getObjectType() { return Metric.class; } @Override public boolean isSingleton() { return true; } } @ManagedResource public static class Metric { } public static class MetricHolder implements InitializingBean { private MessageChannel channel; public void setChannel(MessageChannel channel) { this.channel = channel; } @Override public void afterPropertiesSet() throws Exception { Assert.state(channel != null, "A channel must be provided"); } } public interface Service { String execute() throws Exception; int getCounter(); } public static class SimpleService implements Service { private int counter; @Override public String execute() throws Exception { Thread.sleep(10L); // make the duration non-zero counter++; return "count=" + counter; } @Override public int getCounter() { return counter; } } public interface ActiveChannel { boolean isStopCalled(); } public static class ActiveChannelImpl extends AbstractMessageChannel implements Lifecycle, ActiveChannel { private boolean stopCalled; @Override protected boolean doSend(Message<?> message, long timeout) { return false; } @Override public void start() { } @Override public void stop() { this.stopCalled = true; } @Override public boolean isRunning() { return false; } @Override public boolean isStopCalled() { return this.stopCalled; } } public static class OtherActiveComponent extends MessageProducerSupport implements OrderlyShutdownCapable { private boolean beforeCalled; private boolean afterCalled; public boolean isBeforeCalled() { return this.beforeCalled; } protected boolean isAfterCalled() { return afterCalled; } @Override public int beforeShutdown() { this.beforeCalled = true; return 0; } @Override public int afterShutdown() { this.afterCalled = true; return 0; } } public static class AMessageProducer extends MessageProducerSupport { } }