/*
* Copyright 2002-2017 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.context.event;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.BeanThatBroadcasts;
import org.springframework.context.BeanThatListens;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.support.TaskUtils;
import org.springframework.tests.sample.beans.TestBean;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Unit and integration tests for the ApplicationContext event support.
*
* @author Alef Arendsen
* @author Rick Evans
* @author Stephane Nicoll
* @author Juergen Hoeller
*/
public class ApplicationContextEventTests extends AbstractApplicationEventListenerTests {
@Test
public void multicastSimpleEvent() {
multicastEvent(true, ApplicationListener.class,
new ContextClosedEvent(new StaticApplicationContext()), null);
}
@Test
public void multicastGenericEvent() {
multicastEvent(true, StringEventListener.class, createGenericTestEvent("test"),
getGenericApplicationEventType("stringEvent"));
}
@Test
public void multicastGenericEventWrongType() {
multicastEvent(false, StringEventListener.class, createGenericTestEvent(123L),
getGenericApplicationEventType("longEvent"));
}
@Test
public void multicastGenericEventWildcardSubType() {
multicastEvent(false, StringEventListener.class, createGenericTestEvent("test"),
getGenericApplicationEventType("wildcardEvent"));
}
@Test
public void multicastConcreteTypeGenericListener() {
multicastEvent(true, StringEventListener.class, new StringEvent(this, "test"), null);
}
@Test
public void multicastConcreteWrongTypeGenericListener() {
multicastEvent(false, StringEventListener.class, new LongEvent(this, 123L), null);
}
@Test
public void multicastSmartGenericTypeGenericListener() {
multicastEvent(true, StringEventListener.class, new SmartGenericTestEvent<>(this, "test"), null);
}
@Test
public void multicastSmartGenericWrongTypeGenericListener() {
multicastEvent(false, StringEventListener.class, new SmartGenericTestEvent<>(this, 123L), null);
}
private void multicastEvent(boolean match, Class<?> listenerType, ApplicationEvent event, ResolvableType eventType) {
@SuppressWarnings("unchecked")
ApplicationListener<ApplicationEvent> listener =
(ApplicationListener<ApplicationEvent>) mock(listenerType);
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener);
if (eventType != null) {
smc.multicastEvent(event, eventType);
}
else {
smc.multicastEvent(event);
}
int invocation = match ? 1 : 0;
verify(listener, times(invocation)).onApplicationEvent(event);
}
@Test
public void simpleApplicationEventMulticasterWithTaskExecutor() {
@SuppressWarnings("unchecked")
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.setTaskExecutor(new Executor() {
@Override
public void execute(Runnable command) {
command.run();
command.run();
}
});
smc.addApplicationListener(listener);
smc.multicastEvent(evt);
verify(listener, times(2)).onApplicationEvent(evt);
}
@Test
public void simpleApplicationEventMulticasterWithException() {
@SuppressWarnings("unchecked")
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener);
RuntimeException thrown = new RuntimeException();
willThrow(thrown).given(listener).onApplicationEvent(evt);
try {
smc.multicastEvent(evt);
fail("Should have thrown RuntimeException");
}
catch (RuntimeException ex) {
assertSame(thrown, ex);
}
}
@Test
public void simpleApplicationEventMulticasterWithErrorHandler() {
@SuppressWarnings("unchecked")
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
ApplicationEvent evt = new ContextClosedEvent(new StaticApplicationContext());
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.setErrorHandler(TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER);
smc.addApplicationListener(listener);
willThrow(new RuntimeException()).given(listener).onApplicationEvent(evt);
smc.multicastEvent(evt);
}
@Test
public void orderedListeners() {
MyOrderedListener1 listener1 = new MyOrderedListener1();
MyOrderedListener2 listener2 = new MyOrderedListener2(listener1);
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener2);
smc.addApplicationListener(listener1);
smc.multicastEvent(new MyEvent(this));
smc.multicastEvent(new MyOtherEvent(this));
assertEquals(2, listener1.seenEvents.size());
}
@Test
public void orderedListenersWithAnnotation() {
MyOrderedListener3 listener1 = new MyOrderedListener3();
MyOrderedListener4 listener2 = new MyOrderedListener4(listener1);
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener2);
smc.addApplicationListener(listener1);
smc.multicastEvent(new MyEvent(this));
smc.multicastEvent(new MyOtherEvent(this));
assertEquals(2, listener1.seenEvents.size());
}
@Test
@SuppressWarnings("unchecked")
public void proxiedListeners() {
MyOrderedListener1 listener1 = new MyOrderedListener1();
MyOrderedListener2 listener2 = new MyOrderedListener2(listener1);
ApplicationListener<ApplicationEvent> proxy1 = (ApplicationListener<ApplicationEvent>) new ProxyFactory(listener1).getProxy();
ApplicationListener<ApplicationEvent> proxy2 = (ApplicationListener<ApplicationEvent>) new ProxyFactory(listener2).getProxy();
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(proxy1);
smc.addApplicationListener(proxy2);
smc.multicastEvent(new MyEvent(this));
smc.multicastEvent(new MyOtherEvent(this));
assertEquals(2, listener1.seenEvents.size());
}
@Test
@SuppressWarnings("unchecked")
public void proxiedListenersMixedWithTargetListeners() {
MyOrderedListener1 listener1 = new MyOrderedListener1();
MyOrderedListener2 listener2 = new MyOrderedListener2(listener1);
ApplicationListener<ApplicationEvent> proxy1 = (ApplicationListener<ApplicationEvent>) new ProxyFactory(listener1).getProxy();
ApplicationListener<ApplicationEvent> proxy2 = (ApplicationListener<ApplicationEvent>) new ProxyFactory(listener2).getProxy();
SimpleApplicationEventMulticaster smc = new SimpleApplicationEventMulticaster();
smc.addApplicationListener(listener1);
smc.addApplicationListener(listener2);
smc.addApplicationListener(proxy1);
smc.addApplicationListener(proxy2);
smc.multicastEvent(new MyEvent(this));
smc.multicastEvent(new MyOtherEvent(this));
assertEquals(2, listener1.seenEvents.size());
}
@Test
public void testEventPublicationInterceptor() throws Throwable {
MethodInvocation invocation = mock(MethodInvocation.class);
ApplicationContext ctx = mock(ApplicationContext.class);
EventPublicationInterceptor interceptor = new EventPublicationInterceptor();
interceptor.setApplicationEventClass(MyEvent.class);
interceptor.setApplicationEventPublisher(ctx);
interceptor.afterPropertiesSet();
given(invocation.proceed()).willReturn(new Object());
given(invocation.getThis()).willReturn(new Object());
interceptor.invoke(invocation);
verify(ctx).publishEvent(isA(MyEvent.class));
}
@Test
public void listenersInApplicationContext() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition("listener1", new RootBeanDefinition(MyOrderedListener1.class));
RootBeanDefinition listener2 = new RootBeanDefinition(MyOrderedListener2.class);
listener2.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("listener1"));
listener2.setLazyInit(true);
context.registerBeanDefinition("listener2", listener2);
context.refresh();
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
MyOtherEvent event1 = new MyOtherEvent(context);
context.publishEvent(event1);
assertFalse(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyEvent event2 = new MyEvent(context);
context.publishEvent(event2);
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("listener2"));
MyEvent event3 = new MyEvent(context);
context.publishEvent(event3);
MyOtherEvent event4 = new MyOtherEvent(context);
context.publishEvent(event4);
assertTrue(listener1.seenEvents.contains(event1));
assertTrue(listener1.seenEvents.contains(event2));
assertTrue(listener1.seenEvents.contains(event3));
assertTrue(listener1.seenEvents.contains(event4));
listener1.seenEvents.clear();
context.publishEvent(event1);
context.publishEvent(event2);
context.publishEvent(event3);
context.publishEvent(event4);
assertTrue(listener1.seenEvents.contains(event1));
assertTrue(listener1.seenEvents.contains(event2));
assertTrue(listener1.seenEvents.contains(event3));
assertTrue(listener1.seenEvents.contains(event4));
AbstractApplicationEventMulticaster multicaster = context.getBean(AbstractApplicationEventMulticaster.class);
assertEquals(2, multicaster.retrieverCache.size());
context.close();
}
@Test
public void listenersInApplicationContextWithPayloadEvents() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition("listener", new RootBeanDefinition(MyPayloadListener.class));
context.refresh();
MyPayloadListener listener = context.getBean("listener", MyPayloadListener.class);
context.publishEvent("event1");
context.publishEvent("event2");
context.publishEvent("event3");
context.publishEvent("event4");
assertTrue(listener.seenPayloads.contains("event1"));
assertTrue(listener.seenPayloads.contains("event2"));
assertTrue(listener.seenPayloads.contains("event3"));
assertTrue(listener.seenPayloads.contains("event4"));
AbstractApplicationEventMulticaster multicaster = context.getBean(AbstractApplicationEventMulticaster.class);
assertEquals(2, multicaster.retrieverCache.size());
context.close();
}
@Test
public void listenersInApplicationContextWithNestedChild() {
StaticApplicationContext context = new StaticApplicationContext();
RootBeanDefinition nestedChild = new RootBeanDefinition(StaticApplicationContext.class);
nestedChild.getPropertyValues().add("parent", context);
nestedChild.setInitMethodName("refresh");
context.registerBeanDefinition("nestedChild", nestedChild);
RootBeanDefinition listener1Def = new RootBeanDefinition(MyOrderedListener1.class);
listener1Def.setDependsOn("nestedChild");
context.registerBeanDefinition("listener1", listener1Def);
context.refresh();
MyOrderedListener1 listener1 = context.getBean("listener1", MyOrderedListener1.class);
MyEvent event1 = new MyEvent(context);
context.publishEvent(event1);
assertTrue(listener1.seenEvents.contains(event1));
SimpleApplicationEventMulticaster multicaster = context.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
SimpleApplicationEventMulticaster.class);
assertFalse(multicaster.getApplicationListeners().isEmpty());
context.close();
assertTrue(multicaster.getApplicationListeners().isEmpty());
}
@Test
public void nonSingletonListenerInApplicationContext() {
StaticApplicationContext context = new StaticApplicationContext();
RootBeanDefinition listener = new RootBeanDefinition(MyNonSingletonListener.class);
listener.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("listener", listener);
context.refresh();
MyEvent event1 = new MyEvent(context);
context.publishEvent(event1);
MyOtherEvent event2 = new MyOtherEvent(context);
context.publishEvent(event2);
MyEvent event3 = new MyEvent(context);
context.publishEvent(event3);
MyOtherEvent event4 = new MyOtherEvent(context);
context.publishEvent(event4);
assertTrue(MyNonSingletonListener.seenEvents.contains(event1));
assertTrue(MyNonSingletonListener.seenEvents.contains(event2));
assertTrue(MyNonSingletonListener.seenEvents.contains(event3));
assertTrue(MyNonSingletonListener.seenEvents.contains(event4));
MyNonSingletonListener.seenEvents.clear();
context.close();
}
@Test
public void listenerAndBroadcasterWithCircularReference() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBeanDefinition("broadcaster", new RootBeanDefinition(BeanThatBroadcasts.class));
RootBeanDefinition listenerDef = new RootBeanDefinition(BeanThatListens.class);
listenerDef.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("broadcaster"));
context.registerBeanDefinition("listener", listenerDef);
context.refresh();
BeanThatBroadcasts broadcaster = context.getBean("broadcaster", BeanThatBroadcasts.class);
context.publishEvent(new MyEvent(context));
assertEquals("The event was not received by the listener", 2, broadcaster.receivedCount);
context.close();
}
@Test
public void innerBeanAsListener() {
StaticApplicationContext context = new StaticApplicationContext();
RootBeanDefinition listenerDef = new RootBeanDefinition(TestBean.class);
listenerDef.getPropertyValues().add("friends", new RootBeanDefinition(BeanThatListens.class));
context.registerBeanDefinition("listener", listenerDef);
context.refresh();
context.publishEvent(new MyEvent(this));
context.publishEvent(new MyEvent(this));
TestBean listener = context.getBean(TestBean.class);
assertEquals(3, ((BeanThatListens) listener.getFriends().iterator().next()).getEventCount());
context.close();
}
@Test
public void anonymousClassAsListener() {
final Set<MyEvent> seenEvents = new HashSet<>();
StaticApplicationContext context = new StaticApplicationContext();
context.addApplicationListener(new ApplicationListener<MyEvent>() {
@Override
public void onApplicationEvent(MyEvent event) {
seenEvents.add(event);
}
});
context.refresh();
MyEvent event1 = new MyEvent(context);
context.publishEvent(event1);
context.publishEvent(new MyOtherEvent(context));
MyEvent event2 = new MyEvent(context);
context.publishEvent(event2);
assertSame(2, seenEvents.size());
assertTrue(seenEvents.contains(event1));
assertTrue(seenEvents.contains(event2));
context.close();
}
@Test
public void lambdaAsListener() {
final Set<MyEvent> seenEvents = new HashSet<>();
StaticApplicationContext context = new StaticApplicationContext();
ApplicationListener<MyEvent> listener = seenEvents::add;
context.addApplicationListener(listener);
context.refresh();
MyEvent event1 = new MyEvent(context);
context.publishEvent(event1);
context.publishEvent(new MyOtherEvent(context));
MyEvent event2 = new MyEvent(context);
context.publishEvent(event2);
assertSame(2, seenEvents.size());
assertTrue(seenEvents.contains(event1));
assertTrue(seenEvents.contains(event2));
context.close();
}
@Test
public void beanPostProcessorPublishesEvents() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("listener", new RootBeanDefinition(BeanThatListens.class));
context.registerBeanDefinition("messageSource", new RootBeanDefinition(StaticMessageSource.class));
context.registerBeanDefinition("postProcessor", new RootBeanDefinition(EventPublishingBeanPostProcessor.class));
context.refresh();
context.publishEvent(new MyEvent(this));
BeanThatListens listener = context.getBean(BeanThatListens.class);
assertEquals(4, listener.getEventCount());
context.close();
}
@SuppressWarnings("serial")
public static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
@SuppressWarnings("serial")
public static class MyOtherEvent extends ApplicationEvent {
public MyOtherEvent(Object source) {
super(source);
}
}
public static class MyOrderedListener1 implements ApplicationListener<ApplicationEvent>, Ordered {
public final List<ApplicationEvent> seenEvents = new LinkedList<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.seenEvents.add(event);
}
@Override
public int getOrder() {
return 0;
}
}
public interface MyOrderedListenerIfc<E extends ApplicationEvent> extends ApplicationListener<E>, Ordered {
}
public static abstract class MyOrderedListenerBase implements MyOrderedListenerIfc<MyEvent> {
@Override
public int getOrder() {
return 1;
}
}
public static class MyOrderedListener2 extends MyOrderedListenerBase {
private final MyOrderedListener1 otherListener;
public MyOrderedListener2(MyOrderedListener1 otherListener) {
this.otherListener = otherListener;
}
@Override
public void onApplicationEvent(MyEvent event) {
assertTrue(this.otherListener.seenEvents.contains(event));
}
}
public static class MyPayloadListener implements ApplicationListener<PayloadApplicationEvent> {
public final Set<Object> seenPayloads = new HashSet<>();
@Override
public void onApplicationEvent(PayloadApplicationEvent event) {
this.seenPayloads.add(event.getPayload());
}
}
public static class MyNonSingletonListener implements ApplicationListener<ApplicationEvent> {
public static final Set<ApplicationEvent> seenEvents = new HashSet<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
seenEvents.add(event);
}
}
@Order(5)
public static class MyOrderedListener3 implements ApplicationListener<ApplicationEvent> {
public final Set<ApplicationEvent> seenEvents = new HashSet<>();
@Override
public void onApplicationEvent(ApplicationEvent event) {
this.seenEvents.add(event);
}
}
@Order(50)
public static class MyOrderedListener4 implements ApplicationListener<MyEvent> {
private final MyOrderedListener3 otherListener;
public MyOrderedListener4(MyOrderedListener3 otherListener) {
this.otherListener = otherListener;
}
@Override
public void onApplicationEvent(MyEvent event) {
assertTrue(this.otherListener.seenEvents.contains(event));
}
}
public static class EventPublishingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
this.applicationContext.publishEvent(new MyEvent(this));
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
}