INT-2935: Improve Event Inbound Adapter

Previously, the `ApplicationEventListeningMessageProducer` accepted
all `ApplicationEvent`'s and than filtered them.
This caused some `ApplicationEventMulticaster.retrieverCache` overhead.

* Improve `ApplicationEventListeningMessageProducer` to `implements SmartApplicationListener`.

    This allows filtering earlier on first the appropriate `ApplicationEvent`
    using `ApplicationEventListeningMessageProducer#supportsEventType`
    and caching the `ApplicationListener` only for that `ApplicationEvent`.

* Re-register `ApplicationEventListeningMessageProducer` in the `ApplicationEventMulticaster`
    when  `ApplicationEventListeningMessageProducer#setEventTypes` is invoked
    to clear the `ApplicationEventMulticaster.retrieverCache`.

* Move `org.springframework.integration.gemfire.inbound.SpelMessageProducerSupport` to core `ExpressionMessageProducerSupport`.

* Add test for the new logic int `ApplicationEventListeningMessageProducer` and its behavior with respect to the `ApplicationEventMulticaster.retrieverCache`.

JIRA: https://jira.springsource.org/browse/INT-2935

INT-2935: EMPS JavaDoc & AELMP ReadWriteLock

INT-2935: AELMP#eventTypes changing 'barrier'

INT-2935: avoid 'eventTypes' mutation afterwards

INT-2935: Polishing according PR comments

INT-2935: Fix NPE in the `setEventTypes`

Minor Polishing

   Polish java docs/comments
   Remove compiler warnings
This commit is contained in:
Artem Bilan
2013-02-18 10:22:50 +02:00
committed by Gary Russell
parent 515d78f8ee
commit 116f4934c1
6 changed files with 216 additions and 99 deletions

View File

@@ -2,7 +2,7 @@ log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%c{1}: %m%n
log4j.appender.stdout.layout.ConversionPattern=%c{1}: (%t) %m%n
log4j.category.org.springframework.integration=WARN
log4j.category.org.springframework.integration.file=WARN
log4j.category.org.springframework.integration.event=INFO

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 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.
@@ -17,16 +17,31 @@
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.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.channel.DirectChannel;
@@ -35,10 +50,12 @@ import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.event.core.MessagingEvent;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.test.util.TestUtils;
/**
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
public class ApplicationEventListeningMessageProducerTests {
@@ -50,7 +67,9 @@ public class ApplicationEventListeningMessageProducerTests {
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
assertTrue(adapter.supportsEventType(TestApplicationEvent1.class));
adapter.onApplicationEvent(new TestApplicationEvent1());
assertTrue(adapter.supportsEventType(TestApplicationEvent2.class));
adapter.onApplicationEvent(new TestApplicationEvent2());
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
@@ -66,17 +85,29 @@ public class ApplicationEventListeningMessageProducerTests {
QueueChannel channel = new QueueChannel();
ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer();
adapter.setOutputChannel(channel);
adapter.setEventTypes(new Class[]{TestApplicationEvent1.class});
adapter.setEventTypes(TestApplicationEvent1.class);
adapter.start();
Message<?> message1 = channel.receive(0);
assertNull(message1);
assertTrue(adapter.supportsEventType(TestApplicationEvent1.class));
adapter.onApplicationEvent(new TestApplicationEvent1());
adapter.onApplicationEvent(new TestApplicationEvent2());
assertFalse(adapter.supportsEventType(TestApplicationEvent2.class));
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("event1", ((ApplicationEvent) message2.getPayload()).getSource());
Message<?> message3 = channel.receive(0);
assertNull(message3);
assertNull(channel.receive(0));
adapter.setEventTypes((Class<? extends ApplicationEvent>) null);
assertTrue(adapter.supportsEventType(TestApplicationEvent1.class));
assertTrue(adapter.supportsEventType(TestApplicationEvent2.class));
adapter.setEventTypes(null, TestApplicationEvent2.class, null);
assertFalse(adapter.supportsEventType(TestApplicationEvent1.class));
assertTrue(adapter.supportsEventType(TestApplicationEvent2.class));
adapter.setEventTypes(null, null);
assertTrue(adapter.supportsEventType(TestApplicationEvent1.class));
assertTrue(adapter.supportsEventType(TestApplicationEvent2.class));
}
@Test
@@ -148,7 +179,7 @@ public class ApplicationEventListeningMessageProducerTests {
assertEquals("test", message2.getPayload());
}
@Test(expected=MessageHandlingException.class)
@Test(expected = MessageHandlingException.class)
public void anyApplicationEventCausesExceptionWithErrorHandling() {
DirectChannel channel = new DirectChannel();
channel.subscribe(new AbstractReplyProducingMessageHandler() {
@@ -170,6 +201,67 @@ public class ApplicationEventListeningMessageProducerTests {
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 (Object key : retrieverCache.keySet()) {
Class<? extends ApplicationEvent> event = TestUtils.getPropertyValue(key, "eventType", Class.class);
assertThat(event, Matchers.is(Matchers.isOneOf(ContextRefreshedEvent.class, TestApplicationEvent1.class)));
Set<?> listeners = TestUtils.getPropertyValue(retrieverCache.get(key), "applicationListenerBeans", Set.class);
assertEquals(1, listeners.size());
assertEquals("testListener", listeners.iterator().next());
}
TestApplicationEvent2 event2 = new TestApplicationEvent2();
ctx.publishEvent(event2);
assertEquals(3, retrieverCache.size());
for (Object key : retrieverCache.keySet()) {
Class<?> event = TestUtils.getPropertyValue(key, "eventType", Class.class);
if (TestApplicationEvent2.class.isAssignableFrom(event)) {
Set<?> listeners = TestUtils.getPropertyValue(retrieverCache.get(key), "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));
}
@SuppressWarnings("serial")
private static class TestApplicationEvent1 extends ApplicationEvent {
@@ -179,7 +271,6 @@ public class ApplicationEventListeningMessageProducerTests {
}
}
@SuppressWarnings("serial")
private static class TestApplicationEvent2 extends ApplicationEvent {
@@ -188,7 +279,6 @@ public class ApplicationEventListeningMessageProducerTests {
}
}
@SuppressWarnings("serial")
private static class TestMessagingEvent extends ApplicationEvent {
@@ -197,4 +287,17 @@ public class ApplicationEventListeningMessageProducerTests {
}
}
private static class TestApplicationListener implements ApplicationListener<ApplicationEvent> {
private final AtomicInteger counter;
private TestApplicationListener(AtomicInteger counter) {
this.counter = counter;
}
public void onApplicationEvent(ApplicationEvent event) {
this.counter.incrementAndGet();
}
}
}