/* * Copyright 2002-2015 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.channel; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.logging.Log; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy; import org.springframework.integration.dispatcher.UnicastingDispatcher; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.support.GenericMessage; import org.springframework.util.ReflectionUtils; /** * @author Mark Fisher * @author Oleg Zhurakousky * @author Gary Russell */ public class DirectChannelTests { @Test public void testSend() { DirectChannel channel = new DirectChannel(); Log logger = spy(TestUtils.getPropertyValue(channel, "logger", Log.class)); when(logger.isDebugEnabled()).thenReturn(true); new DirectFieldAccessor(channel).setPropertyValue("logger", logger); ThreadNameExtractingTestTarget target = new ThreadNameExtractingTestTarget(); channel.subscribe(target); GenericMessage<String> message = new GenericMessage<String>("test"); assertTrue(channel.send(message)); assertEquals(Thread.currentThread().getName(), target.threadName); DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel); UnicastingDispatcher dispatcher = (UnicastingDispatcher) channelAccessor.getPropertyValue("dispatcher"); DirectFieldAccessor dispatcherAccessor = new DirectFieldAccessor(dispatcher); Object loadBalancingStrategy = dispatcherAccessor.getPropertyValue("loadBalancingStrategy"); assertTrue(loadBalancingStrategy instanceof RoundRobinLoadBalancingStrategy); ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); verify(logger, times(2)).debug(captor.capture()); List<String> logs = captor.getAllValues(); assertEquals(2, logs.size()); assertThat(logs.get(0), startsWith("preSend")); assertThat(logs.get(1), startsWith("postSend")); } @Test public void testSendPerfOneHandler() { /* * INT-3308 - used to run 12 million/sec * 1. optimize for single handler 20 million/sec * 2. Don't iterate over empty datatypes 23 million/sec * 3. Don't iterate over empty interceptors 31 million/sec * 4. Move single handler optimization to dispatcher 34 million/sec * * 29 million per second with increment counter in the handler */ DirectChannel channel = new DirectChannel(); final AtomicInteger count = new AtomicInteger(); channel.subscribe(message -> count.incrementAndGet()); GenericMessage<String> message = new GenericMessage<String>("test"); assertTrue(channel.send(message)); for (int i = 0; i < 10000000; i++) { channel.send(message); } } @Test public void testSendPerfTwoHandlers() { /* * INT-3308 - used to run 6.4 million/sec * 1. Skip empty iterators as above 7.2 million/sec * 2. optimize for single handler 6.7 million/sec (small overhead added) * 3. remove LB rwlock from UnicastingDispatcher 7.2 million/sec * 4. Move single handler optimization to dispatcher 7.3 million/sec */ DirectChannel channel = new DirectChannel(); final AtomicInteger count1 = new AtomicInteger(); final AtomicInteger count2 = new AtomicInteger(); channel.subscribe(message -> count1.incrementAndGet()); channel.subscribe(message -> count2.getAndIncrement()); GenericMessage<String> message = new GenericMessage<String>("test"); assertTrue(channel.send(message)); for (int i = 0; i < 10000000; i++) { channel.send(message); } assertEquals(5000001, count1.get()); assertEquals(5000000, count2.get()); } @Test public void testSendPerfFixedSubscriberChannel() { /* * INT-3308 - 96 million/sec * NOTE: in order to get a measurable time, I had to add some code to the handler - * presumably the JIT compiler short circuited the call becaues it's a final field * and he knows the method does nothing. * Added the same code to the other tests for comparison. */ final AtomicInteger count = new AtomicInteger(); FixedSubscriberChannel channel = new FixedSubscriberChannel(message -> count.incrementAndGet()); GenericMessage<String> message = new GenericMessage<String>("test"); assertTrue(channel.send(message)); for (int i = 0; i < 100000000; i++) { channel.send(message, 0); } } @Test public void testSendInSeparateThread() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); final DirectChannel channel = new DirectChannel(); ThreadNameExtractingTestTarget target = new ThreadNameExtractingTestTarget(latch); channel.subscribe(target); final GenericMessage<String> message = new GenericMessage<String>("test"); new Thread((Runnable) () -> channel.send(message), "test-thread").start(); latch.await(1000, TimeUnit.MILLISECONDS); assertEquals("test-thread", target.threadName); } @Test // See INT-2434 public void testChannelCreationWithBeanDefinitionOverrideTrue() throws Exception { ClassPathXmlApplicationContext parentContext = new ClassPathXmlApplicationContext("parent-config.xml", this.getClass()); MessageChannel parentChannelA = parentContext.getBean("parentChannelA", MessageChannel.class); MessageChannel parentChannelB = parentContext.getBean("parentChannelB", MessageChannel.class); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); context.setAllowBeanDefinitionOverriding(false); context.setConfigLocations(new String[]{"classpath:org/springframework/integration/channel/channel-override-config.xml"}); context.setParent(parentContext); Method method = ReflectionUtils.findMethod(ClassPathXmlApplicationContext.class, "obtainFreshBeanFactory"); method.setAccessible(true); method.invoke(context); assertFalse(context.containsBean("channelA")); assertFalse(context.containsBean("channelB")); assertTrue(context.containsBean("channelC")); assertTrue(context.containsBean("channelD")); context.refresh(); PublishSubscribeChannel channelEarly = context.getBean("channelEarly", PublishSubscribeChannel.class); assertTrue(context.containsBean("channelA")); assertTrue(context.containsBean("channelB")); assertTrue(context.containsBean("channelC")); assertTrue(context.containsBean("channelD")); EventDrivenConsumer consumerA = context.getBean("serviceA", EventDrivenConsumer.class); assertEquals(context.getBean("channelA"), TestUtils.getPropertyValue(consumerA, "inputChannel")); assertEquals(context.getBean("channelB"), TestUtils.getPropertyValue(consumerA, "handler.outputChannel")); EventDrivenConsumer consumerB = context.getBean("serviceB", EventDrivenConsumer.class); assertEquals(context.getBean("channelB"), TestUtils.getPropertyValue(consumerB, "inputChannel")); assertEquals(context.getBean("channelC"), TestUtils.getPropertyValue(consumerB, "handler.outputChannel")); EventDrivenConsumer consumerC = context.getBean("serviceC", EventDrivenConsumer.class); assertEquals(context.getBean("channelC"), TestUtils.getPropertyValue(consumerC, "inputChannel")); assertEquals(context.getBean("channelD"), TestUtils.getPropertyValue(consumerC, "handler.outputChannel")); EventDrivenConsumer consumerD = context.getBean("serviceD", EventDrivenConsumer.class); assertEquals(parentChannelA, TestUtils.getPropertyValue(consumerD, "inputChannel")); assertEquals(parentChannelB, TestUtils.getPropertyValue(consumerD, "handler.outputChannel")); EventDrivenConsumer consumerE = context.getBean("serviceE", EventDrivenConsumer.class); assertEquals(parentChannelB, TestUtils.getPropertyValue(consumerE, "inputChannel")); EventDrivenConsumer consumerF = context.getBean("serviceF", EventDrivenConsumer.class); assertEquals(channelEarly, TestUtils.getPropertyValue(consumerF, "inputChannel")); } private static class ThreadNameExtractingTestTarget implements MessageHandler { private String threadName; private final CountDownLatch latch; ThreadNameExtractingTestTarget() { this(null); } ThreadNameExtractingTestTarget(CountDownLatch latch) { this.latch = latch; } @Override public void handleMessage(Message<?> message) { this.threadName = Thread.currentThread().getName(); if (this.latch != null) { this.latch.countDown(); } } } }