/*
* Copyright 2002-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.event.inbound;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.event.core.MessagingEvent;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
public class ApplicationEventListeningMessageProducerTests {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
@Test
public void anyApplicationEventSentByDefault() {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class)));
adapter.onApplicationEvent(new TestApplicationEvent1());
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class)));
adapter.onApplicationEvent(new TestApplicationEvent2());
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("event1", ((ApplicationEvent) message2.getPayload()).getSource());
Message<?> message3 = channel.receive(20);
assertNotNull(message3);
assertEquals("event2", ((ApplicationEvent) message3.getPayload()).getSource());
}
@Test
public void onlyConfiguredEventTypesAreSent() {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
adapter.setEventTypes(TestApplicationEvent1.class);
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class)));
adapter.onApplicationEvent(new TestApplicationEvent1());
assertFalse(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class)));
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("event1", ((ApplicationEvent) message2.getPayload()).getSource());
assertNull(channel.receive(0));
adapter.setEventTypes((Class<? extends ApplicationEvent>) null);
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class)));
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class)));
adapter.setEventTypes(null, TestApplicationEvent2.class, null);
assertFalse(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class)));
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class)));
adapter.setEventTypes(null, null);
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class)));
assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class)));
}
@Test
public void applicationContextEvents() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"applicationEventInboundChannelAdapterTests.xml", this.getClass());
PollableChannel channel = (PollableChannel) context.getBean("channel");
Message<?> contextRefreshedEventMessage = channel.receive(0);
assertNotNull(contextRefreshedEventMessage);
assertEquals(ContextRefreshedEvent.class, contextRefreshedEventMessage.getPayload().getClass());
context.start();
Message<?> startedEventMessage = channel.receive(0);
assertNotNull(startedEventMessage);
assertEquals(ContextStartedEvent.class, startedEventMessage.getPayload().getClass());
context.stop();
Message<?> contextStoppedEventMessage = channel.receive(0);
assertNotNull(contextStoppedEventMessage);
assertEquals(ContextStoppedEvent.class, contextStoppedEventMessage.getPayload().getClass());
context.close();
Message<?> closedEventMessage = channel.receive(0);
assertNotNull(closedEventMessage);
assertEquals(ContextClosedEvent.class, closedEventMessage.getPayload().getClass());
}
@Test
public void payloadExpressionEvaluatedAgainstApplicationEvent() {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setPayloadExpression(PARSER.parseExpression("'received: ' + source"));
adapter.setOutputChannel(channel);
GenericApplicationContext ctx = TestUtils.createTestApplicationContext();
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
beanFactory.registerSingleton(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
new SimpleApplicationEventMulticaster(beanFactory));
adapter.setBeanFactory(beanFactory);
beanFactory.registerSingleton("testListenerMessageProducer", adapter);
adapter.afterPropertiesSet();
ctx.refresh();
Message<?> message1 = channel.receive(0);
// ContextRefreshedEvent
assertNotNull(message1);
assertTrue(message1.getPayload().toString()
.contains("org.springframework.integration.test.util.TestUtils$TestApplicationContext"));
adapter.onApplicationEvent(new TestApplicationEvent1());
adapter.onApplicationEvent(new TestApplicationEvent2());
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("received: event1", message2.getPayload());
Message<?> message3 = channel.receive(20);
assertNotNull(message3);
assertEquals("received: event2", message3.getPayload());
}
@Test
public void messagingEventReceived() {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
adapter.onApplicationEvent(new MessagingEvent(new GenericMessage<String>("test")));
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("test", message2.getPayload());
}
@Test
public void messageAsSourceOrCustomEventType() {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
adapter.onApplicationEvent(new TestMessagingEvent(new GenericMessage<String>("test")));
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("test", message2.getPayload());
}
@Test(expected = MessageHandlingException.class)
public void anyApplicationEventCausesExceptionWithErrorHandling() {
DirectChannel channel = new DirectChannel();
channel.subscribe(new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
throw new RuntimeException("Failed");
}
});
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
QueueChannel errorChannel = new QueueChannel();
adapter.setErrorChannel(errorChannel);
adapter.start();
adapter.onApplicationEvent(new TestApplicationEvent1());
Message<?> message = errorChannel.receive(10000);
assertNotNull(message);
assertEquals("Failed", ((Exception) message.getPayload()).getCause().getMessage());
adapter.setErrorChannel(null);
adapter.onApplicationEvent(new TestApplicationEvent1());
}
@Test
@SuppressWarnings({"unchecked", "serial"})
public void testInt2935CheckRetrieverCache() {
GenericApplicationContext ctx = TestUtils.createTestApplicationContext();
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer listenerMessageProducer =
new ApplicationEventListeningMessageProducer();
listenerMessageProducer.setOutputChannel(channel);
listenerMessageProducer.setEventTypes(TestApplicationEvent2.class);
beanFactory.registerSingleton("testListenerMessageProducer", listenerMessageProducer);
AtomicInteger listenerCounter = new AtomicInteger();
beanFactory.registerSingleton("testListener", new TestApplicationListener(listenerCounter));
ctx.refresh();
ApplicationEventMulticaster multicaster =
ctx.getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
ApplicationEventMulticaster.class);
Map<?, ?> retrieverCache = TestUtils.getPropertyValue(multicaster, "retrieverCache", Map.class);
ctx.publishEvent(new TestApplicationEvent1());
/*
* Previously, the retrieverCache grew unnecessarily; the adapter was added to the cache for each event type,
* event if not supported.
*/
assertEquals(2, retrieverCache.size());
for (Map.Entry<?, ?> entry : retrieverCache.entrySet()) {
Class<? extends ApplicationEvent> event = TestUtils.getPropertyValue(entry.getKey(), "eventType.resolved",
Class.class);
assertThat(event, Matchers.is(Matchers.isOneOf(ContextRefreshedEvent.class, TestApplicationEvent1.class)));
Set<?> listeners = TestUtils.getPropertyValue(entry.getValue(), "applicationListenerBeans", Set.class);
assertEquals(1, listeners.size());
assertEquals("testListener", listeners.iterator().next());
}
TestApplicationEvent2 event2 = new TestApplicationEvent2();
ctx.publishEvent(event2);
assertEquals(3, retrieverCache.size());
for (Map.Entry<?, ?> entry : retrieverCache.entrySet()) {
Class<?> event = TestUtils.getPropertyValue(entry.getKey(), "eventType.resolved", Class.class);
if (TestApplicationEvent2.class.isAssignableFrom(event)) {
Set<?> listeners = TestUtils.getPropertyValue(entry.getValue(), "applicationListenerBeans", Set.class);
assertEquals(2, listeners.size());
for (Object listener : listeners) {
assertThat((String) listener,
Matchers.is(Matchers.isOneOf("testListenerMessageProducer", "testListener")));
}
break;
}
}
ctx.publishEvent(new ApplicationEvent("Some event") {
});
assertEquals(4, listenerCounter.get());
final Message<?> receive = channel.receive(10);
assertNotNull(receive);
assertSame(event2, receive.getPayload());
assertNull(channel.receive(1));
ctx.close();
}
@Test
public void testPayloadEvents() {
GenericApplicationContext ctx = TestUtils.createTestApplicationContext();
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer listenerMessageProducer =
new ApplicationEventListeningMessageProducer();
listenerMessageProducer.setOutputChannel(channel);
listenerMessageProducer.setEventTypes(String.class);
beanFactory.registerSingleton("testListenerMessageProducer", listenerMessageProducer);
ctx.refresh();
ctx.publishEvent("foo");
Message<?> receive = channel.receive(10000);
assertNotNull(receive);
assertEquals("foo", receive.getPayload());
ctx.close();
}
@SuppressWarnings("serial")
private static class TestApplicationEvent1 extends ApplicationEvent {
TestApplicationEvent1() {
super("event1");
}
}
@SuppressWarnings("serial")
private static class TestApplicationEvent2 extends ApplicationEvent {
TestApplicationEvent2() {
super("event2");
}
}
@SuppressWarnings("serial")
private static class TestMessagingEvent extends ApplicationEvent {
TestMessagingEvent(Message<?> message) {
super(message);
}
}
private static class TestApplicationListener implements ApplicationListener<ApplicationEvent> {
private final AtomicInteger counter;
private TestApplicationListener(AtomicInteger counter) {
this.counter = counter;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.counter.incrementAndGet();
}
}
}