/*
* 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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.event.test.AbstractIdentifiable;
import org.springframework.context.event.test.AnotherTestEvent;
import org.springframework.context.event.test.EventCollector;
import org.springframework.context.event.test.GenericEventPojo;
import org.springframework.context.event.test.Identifiable;
import org.springframework.context.event.test.TestEvent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
* @author Stephane Nicoll
* @author Juergen Hoeller
*/
public class AnnotationDrivenEventListenerTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private ConfigurableApplicationContext context;
private EventCollector eventCollector;
private CountDownLatch countDownLatch; // 1 call by default
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void simpleEventJavaConfig() {
load(TestEventListener.class);
TestEvent event = new TestEvent(this, "test");
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
this.eventCollector.clear();
this.context.publishEvent(event);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void simpleEventXmlConfig() {
this.context = new ClassPathXmlApplicationContext(
"org/springframework/context/event/simple-event-configuration.xml");
TestEvent event = new TestEvent(this, "test");
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector = getEventCollector(this.context);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void metaAnnotationIsDiscovered() {
load(MetaAnnotationListenerTestBean.class);
MetaAnnotationListenerTestBean bean = this.context.getBean(MetaAnnotationListenerTestBean.class);
this.eventCollector.assertNoEventReceived(bean);
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(bean, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void contextEventsAreReceived() {
load(ContextEventListener.class);
ContextEventListener listener = this.context.getBean(ContextEventListener.class);
List<Object> events = this.eventCollector.getEvents(listener);
assertEquals("Wrong number of initial context events", 1, events.size());
assertEquals(ContextRefreshedEvent.class, events.get(0).getClass());
this.context.stop();
List<Object> eventsAfterStop = this.eventCollector.getEvents(listener);
assertEquals("Wrong number of context events on shutdown", 2, eventsAfterStop.size());
assertEquals(ContextStoppedEvent.class, eventsAfterStop.get(1).getClass());
this.eventCollector.assertTotalEventsCount(2);
}
@Test
public void methodSignatureNoEvent() {
AnnotationConfigApplicationContext failingContext =
new AnnotationConfigApplicationContext();
failingContext.register(BasicConfiguration.class,
InvalidMethodSignatureEventListener.class);
this.thrown.expect(BeanInitializationException.class);
this.thrown.expectMessage(InvalidMethodSignatureEventListener.class.getName());
this.thrown.expectMessage("cannotBeCalled");
failingContext.refresh();
}
@Test
public void simpleReply() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, "dummy");
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), "dummy")); // reply
this.eventCollector.assertTotalEventsCount(2);
}
@Test
public void nullReplyIgnored() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, null); // No response
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void arrayReply() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, new String[]{"first", "second"});
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "first", "second"); // reply
this.eventCollector.assertTotalEventsCount(3);
}
@Test
public void collectionReply() {
load(TestEventListener.class, ReplyEventListener.class);
Set<Object> replies = new LinkedHashSet<>();
replies.add("first");
replies.add(4L);
replies.add("third");
AnotherTestEvent event = new AnotherTestEvent(this, replies);
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "first", "third"); // reply (no listener for 4L)
this.eventCollector.assertTotalEventsCount(3);
}
@Test
public void collectionReplyNullValue() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, Arrays.asList(null, "test"));
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "test");
this.eventCollector.assertTotalEventsCount(2);
}
@Test
public void eventListenerWorksWithSimpleInterfaceProxy() throws Exception {
load(ScopedProxyTestBean.class);
SimpleService proxy = this.context.getBean(SimpleService.class);
assertTrue("bean should be a proxy", proxy instanceof Advised);
this.eventCollector.assertNoEventReceived(proxy.getId());
this.context.publishEvent(new ContextRefreshedEvent(this.context));
this.eventCollector.assertNoEventReceived(proxy.getId());
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(proxy.getId(), event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void eventListenerWorksWithAnnotatedInterfaceProxy() throws Exception {
load(AnnotatedProxyTestBean.class);
AnnotatedSimpleService proxy = this.context.getBean(AnnotatedSimpleService.class);
assertTrue("bean should be a proxy", proxy instanceof Advised);
this.eventCollector.assertNoEventReceived(proxy.getId());
this.context.publishEvent(new ContextRefreshedEvent(this.context));
this.eventCollector.assertNoEventReceived(proxy.getId());
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(proxy.getId(), event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void eventListenerWorksWithCglibProxy() throws Exception {
load(CglibProxyTestBean.class);
CglibProxyTestBean proxy = this.context.getBean(CglibProxyTestBean.class);
assertTrue("bean should be a cglib proxy", AopUtils.isCglibProxy(proxy));
this.eventCollector.assertNoEventReceived(proxy.getId());
this.context.publishEvent(new ContextRefreshedEvent(this.context));
this.eventCollector.assertNoEventReceived(proxy.getId());
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(proxy.getId(), event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void privateMethodOnCglibProxyFails() throws Exception {
try {
load(CglibProxyWithPrivateMethod.class);
fail("Should have thrown BeanInitializationException");
}
catch (BeanInitializationException ex) {
assertTrue(ex.getCause() instanceof IllegalStateException);
}
}
@Test
public void eventListenerWorksWithCustomScope() throws Exception {
load(CustomScopeTestBean.class);
CustomScope customScope = new CustomScope();
this.context.getBeanFactory().registerScope("custom", customScope);
CustomScopeTestBean proxy = this.context.getBean(CustomScopeTestBean.class);
assertTrue("bean should be a cglib proxy", AopUtils.isCglibProxy(proxy));
this.eventCollector.assertNoEventReceived(proxy.getId());
this.context.publishEvent(new ContextRefreshedEvent(this.context));
this.eventCollector.assertNoEventReceived(proxy.getId());
customScope.active = false;
this.context.publishEvent(new ContextRefreshedEvent(this.context));
customScope.active = true;
this.eventCollector.assertNoEventReceived(proxy.getId());
TestEvent event = new TestEvent();
this.context.publishEvent(event);
this.eventCollector.assertEvent(proxy.getId(), event);
this.eventCollector.assertTotalEventsCount(1);
try {
customScope.active = false;
this.context.publishEvent(new TestEvent());
fail("Should have thrown IllegalStateException");
}
catch (BeanCreationException ex) {
// expected
assertTrue(ex.getCause() instanceof IllegalStateException);
}
}
@Test
public void asyncProcessingApplied() throws InterruptedException {
loadAsync(AsyncEventListener.class);
String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
AsyncEventListener listener = this.context.getBean(AsyncEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void asyncProcessingAppliedWithInterfaceProxy() throws InterruptedException {
doLoad(AsyncConfigurationWithInterfaces.class, SimpleProxyTestBean.class);
String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
SimpleService listener = this.context.getBean(SimpleService.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void asyncProcessingAppliedWithScopedProxy() throws InterruptedException {
doLoad(AsyncConfigurationWithInterfaces.class, ScopedProxyTestBean.class);
String threadName = Thread.currentThread().getName();
AnotherTestEvent event = new AnotherTestEvent(this, threadName);
SimpleService listener = this.context.getBean(SimpleService.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void exceptionPropagated() {
load(ExceptionEventListener.class);
TestEvent event = new TestEvent(this, "fail");
ExceptionEventListener listener = this.context.getBean(ExceptionEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
try {
this.context.publishEvent(event);
fail("An exception should have thrown");
}
catch (IllegalStateException e) {
assertEquals("Wrong exception", "Test exception", e.getMessage());
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
}
@Test
public void exceptionNotPropagatedWithAsync() throws InterruptedException {
loadAsync(ExceptionEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, "fail");
ExceptionEventListener listener = this.context.getBean(ExceptionEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.countDownLatch.await(2, TimeUnit.SECONDS);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void listenerWithSimplePayload() {
load(TestEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent("test");
this.eventCollector.assertEvent(listener, "test");
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void listenerWithNonMatchingPayload() {
load(TestEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(123L);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
}
@Test
public void replyWithPayload() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, "String");
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "String"); // reply
this.eventCollector.assertTotalEventsCount(2);
}
@Test
public void listenerWithGenericApplicationEvent() {
load(GenericEventListener.class);
GenericEventListener listener = this.context.getBean(GenericEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent("TEST");
this.eventCollector.assertEvent(listener, "TEST");
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void listenerWithResolvableTypeEvent() {
load(ResolvableTypeEventListener.class);
ResolvableTypeEventListener listener = this.context.getBean(ResolvableTypeEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
GenericEventPojo<String> event = new GenericEventPojo<>("TEST");
this.context.publishEvent(event);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void listenerWithResolvableTypeEventWrongGeneric() {
load(ResolvableTypeEventListener.class);
ResolvableTypeEventListener listener = this.context.getBean(ResolvableTypeEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
GenericEventPojo<Long> event = new GenericEventPojo<>(123L);
this.context.publishEvent(event);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
}
@Test
public void conditionMatch() {
long timestamp = System.currentTimeMillis();
load(ConditionalEventListener.class);
TestEvent event = new TestEvent(this, "OK");
TestEventListener listener = this.context.getBean(ConditionalEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(listener, event);
this.eventCollector.assertTotalEventsCount(1);
this.context.publishEvent("OK");
this.eventCollector.assertEvent(listener, event, "OK");
this.eventCollector.assertTotalEventsCount(2);
this.context.publishEvent(timestamp);
this.eventCollector.assertEvent(listener, event, "OK", timestamp);
this.eventCollector.assertTotalEventsCount(3);
this.context.publishEvent(42d);
this.eventCollector.assertEvent(listener, event, "OK", timestamp, 42d);
this.eventCollector.assertTotalEventsCount(4);
}
@Test
public void conditionDoesNotMatch() {
long maxLong = Long.MAX_VALUE;
load(ConditionalEventListener.class);
TestEvent event = new TestEvent(this, "KO");
TestEventListener listener = this.context.getBean(ConditionalEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.context.publishEvent(event);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
this.context.publishEvent("KO");
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
this.context.publishEvent(maxLong);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
this.context.publishEvent(24d);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertTotalEventsCount(0);
}
@Test
public void orderedListeners() {
load(OrderedTestListener.class);
OrderedTestListener listener = this.context.getBean(OrderedTestListener.class);
assertTrue(listener.order.isEmpty());
this.context.publishEvent("whatever");
assertThat(listener.order, contains("first", "second", "third"));
}
@Test @Ignore // SPR-15122
public void listenersReceiveEarlyEvents() {
load(EventOnPostConstruct.class, OrderedTestListener.class);
OrderedTestListener listener = this.context.getBean(OrderedTestListener.class);
assertThat(listener.order, contains("first", "second", "third"));
}
private void load(Class<?>... classes) {
List<Class<?>> allClasses = new ArrayList<>();
allClasses.add(BasicConfiguration.class);
allClasses.addAll(Arrays.asList(classes));
doLoad(allClasses.toArray(new Class<?>[allClasses.size()]));
}
private void loadAsync(Class<?>... classes) {
List<Class<?>> allClasses = new ArrayList<>();
allClasses.add(AsyncConfiguration.class);
allClasses.addAll(Arrays.asList(classes));
doLoad(allClasses.toArray(new Class<?>[allClasses.size()]));
}
private void doLoad(Class<?>... classes) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
this.eventCollector = ctx.getBean(EventCollector.class);
this.countDownLatch = ctx.getBean(CountDownLatch.class);
this.context = ctx;
}
private EventCollector getEventCollector(ConfigurableApplicationContext context) {
return context.getBean(EventCollector.class);
}
@Configuration
static class BasicConfiguration {
@Bean
public EventCollector eventCollector() {
return new EventCollector();
}
@Bean
public CountDownLatch testCountDownLatch() {
return new CountDownLatch(1);
}
@Bean
public TestConditionEvaluator conditionEvaluator() {
return new TestConditionEvaluator();
}
static class TestConditionEvaluator {
public boolean valid(Double ratio) {
return new Double(42).equals(ratio);
}
}
}
static abstract class AbstractTestEventListener extends AbstractIdentifiable {
@Autowired
private EventCollector eventCollector;
protected void collectEvent(Object content) {
this.eventCollector.addEvent(this, content);
}
}
@Component
static class TestEventListener extends AbstractTestEventListener {
@EventListener
public void handle(TestEvent event) {
collectEvent(event);
}
@EventListener
public void handleString(String content) {
collectEvent(content);
}
}
@EventListener
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface FooListener {
}
@Component
static class MetaAnnotationListenerTestBean extends AbstractTestEventListener {
@FooListener
public void handleIt(TestEvent event) {
collectEvent(event);
}
}
@Component
static class ContextEventListener extends AbstractTestEventListener {
@EventListener
public void handleContextEvent(ApplicationContextEvent event) {
collectEvent(event);
}
}
@Component
static class InvalidMethodSignatureEventListener {
@EventListener
public void cannotBeCalled(String s, Integer what) {
}
}
@Component
static class ReplyEventListener extends AbstractTestEventListener {
@EventListener
public Object handle(AnotherTestEvent event) {
collectEvent(event);
if (event.content == null) {
return null;
}
else if (event.content instanceof String) {
String s = (String) event.content;
if (s.equals("String")) {
return event.content;
}
else {
return new TestEvent(this, event.getId(), s);
}
}
return event.content;
}
}
@Component
static class ExceptionEventListener extends AbstractTestEventListener {
@Autowired
private CountDownLatch countDownLatch;
@EventListener
public void handle(TestEvent event) {
collectEvent(event);
if ("fail".equals(event.msg)) {
throw new IllegalStateException("Test exception");
}
}
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
collectEvent(event);
if ("fail".equals(event.content)) {
this.countDownLatch.countDown();
throw new IllegalStateException("Test exception");
}
}
}
@Component
static class AsyncEventListener extends AbstractTestEventListener {
@Autowired
private CountDownLatch countDownLatch;
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
collectEvent(event);
this.countDownLatch.countDown();
}
}
@Configuration
@Import(BasicConfiguration.class)
@EnableAsync(proxyTargetClass = true)
static class AsyncConfiguration {
}
@Configuration
@Import(BasicConfiguration.class)
@EnableAsync(proxyTargetClass = false)
static class AsyncConfigurationWithInterfaces {
}
interface SimpleService extends Identifiable {
void handleIt(TestEvent event);
void handleAsync(AnotherTestEvent event);
}
@Component
static class SimpleProxyTestBean extends AbstractIdentifiable implements SimpleService {
@Autowired
private EventCollector eventCollector;
@Autowired
private CountDownLatch countDownLatch;
@EventListener
@Override
public void handleIt(TestEvent event) {
this.eventCollector.addEvent(this, event);
}
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
this.eventCollector.addEvent(this, event);
this.countDownLatch.countDown();
}
}
@Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
static class ScopedProxyTestBean extends AbstractIdentifiable implements SimpleService {
@Autowired
private EventCollector eventCollector;
@Autowired
private CountDownLatch countDownLatch;
@EventListener
@Override
public void handleIt(TestEvent event) {
this.eventCollector.addEvent(this, event);
}
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
this.eventCollector.addEvent(this, event);
this.countDownLatch.countDown();
}
}
interface AnnotatedSimpleService extends Identifiable {
@EventListener
void handleIt(TestEvent event);
}
@Component
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
static class AnnotatedProxyTestBean extends AbstractIdentifiable implements AnnotatedSimpleService {
@Autowired
private EventCollector eventCollector;
@Override
public void handleIt(TestEvent event) {
this.eventCollector.addEvent(this, event);
}
}
@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
static class CglibProxyTestBean extends AbstractTestEventListener {
@EventListener
public void handleIt(TestEvent event) {
collectEvent(event);
}
}
@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
static class CglibProxyWithPrivateMethod extends AbstractTestEventListener {
@EventListener
private void handleIt(TestEvent event) {
collectEvent(event);
}
}
@Component
@Scope(scopeName = "custom", proxyMode = ScopedProxyMode.TARGET_CLASS)
static class CustomScopeTestBean extends AbstractTestEventListener {
@EventListener
public void handleIt(TestEvent event) {
collectEvent(event);
}
}
@Component
static class GenericEventListener extends AbstractTestEventListener {
@EventListener
public void handleString(PayloadApplicationEvent<String> event) {
collectEvent(event.getPayload());
}
}
@Component
static class ResolvableTypeEventListener extends AbstractTestEventListener {
@EventListener
public void handleString(GenericEventPojo<String> value) {
collectEvent(value);
}
}
@EventListener
@Retention(RetentionPolicy.RUNTIME)
public @interface ConditionalEvent {
@AliasFor(annotation = EventListener.class, attribute = "condition")
String value();
}
@Component
static class ConditionalEventListener extends TestEventListener {
@EventListener(condition = "'OK'.equals(#root.event.msg)")
@Override
public void handle(TestEvent event) {
super.handle(event);
}
@Override
@EventListener(condition = "#payload.startsWith('OK')")
public void handleString(String payload) {
super.handleString(payload);
}
@ConditionalEvent("#root.event.timestamp > #p0")
public void handleTimestamp(Long timestamp) {
collectEvent(timestamp);
}
@ConditionalEvent("@conditionEvaluator.valid(#p0)")
public void handleRatio(Double ratio) {
collectEvent(ratio);
}
}
@Component
static class OrderedTestListener extends TestEventListener {
public final List<String> order = new ArrayList<>();
@EventListener
@Order(50)
public void handleThird(String payload) {
this.order.add("third");
}
@EventListener
@Order(-50)
public void handleFirst(String payload) {
this.order.add("first");
}
@EventListener
public void handleSecond(String payload) {
this.order.add("second");
}
}
static class EventOnPostConstruct {
@Autowired
ApplicationEventPublisher publisher;
@PostConstruct
public void init() {
this.publisher.publishEvent("earlyEvent");
}
}
private static class CustomScope implements org.springframework.beans.factory.config.Scope {
public boolean active = true;
private Object instance = null;
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Assert.state(this.active, "Not active");
if (this.instance == null) {
this.instance = objectFactory.getObject();
}
return this.instance;
}
@Override
public Object remove(String name) {
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
}