Multicaster support for events with null source
Update `AbstractApplicationEventMulticaster` to support `EventObjects` with a null source (which can happen if they have been serialized). Issue: SPR-10945
This commit is contained in:
parent
6d882b149d
commit
b0ff834ee3
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2013 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -28,6 +28,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
|
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
|
||||||
|
@ -134,7 +135,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||||
*/
|
*/
|
||||||
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
|
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
|
||||||
Class<? extends ApplicationEvent> eventType = event.getClass();
|
Class<? extends ApplicationEvent> eventType = event.getClass();
|
||||||
Class sourceType = event.getSource().getClass();
|
Object source = event.getSource();
|
||||||
|
Class sourceType = (source == null ? null : source.getClass());
|
||||||
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
|
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
|
||||||
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
||||||
if (retriever != null) {
|
if (retriever != null) {
|
||||||
|
@ -212,12 +214,14 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ListenerCacheKey otherKey = (ListenerCacheKey) other;
|
ListenerCacheKey otherKey = (ListenerCacheKey) other;
|
||||||
return (this.eventType.equals(otherKey.eventType) && this.sourceType.equals(otherKey.sourceType));
|
return ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType)
|
||||||
|
&& ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.eventType.hashCode() * 29 + this.sourceType.hashCode();
|
return ObjectUtils.nullSafeHashCode(this.eventType) * 29
|
||||||
|
+ ObjectUtils.nullSafeHashCode(this.sourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2013 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -77,7 +77,7 @@ public class SourceFilteringListener implements SmartApplicationListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsSourceType(Class<?> sourceType) {
|
public boolean supportsSourceType(Class<?> sourceType) {
|
||||||
return sourceType.isInstance(this.source);
|
return (sourceType != null && sourceType.isInstance(this.source));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,8 +16,13 @@
|
||||||
|
|
||||||
package org.springframework.context;
|
package org.springframework.context;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.xml.AbstractListableBeanFactoryTests;
|
import org.springframework.beans.factory.xml.AbstractListableBeanFactoryTests;
|
||||||
import org.springframework.tests.sample.beans.LifecycleBean;
|
import org.springframework.tests.sample.beans.LifecycleBean;
|
||||||
|
@ -129,11 +134,29 @@ public abstract class AbstractApplicationContextTests extends AbstractListableBe
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEvents() throws Exception {
|
public void testEvents() throws Exception {
|
||||||
|
doTestEvents(this.listener, this.parentListener, new MyEvent(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEventsWithNoSource() throws Exception {
|
||||||
|
// See SPR-10945 Serialized events result in a null source
|
||||||
|
MyEvent event = new MyEvent(this);
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||||
|
oos.writeObject(event);
|
||||||
|
oos.close();
|
||||||
|
event = (MyEvent) new ObjectInputStream(new ByteArrayInputStream(
|
||||||
|
bos.toByteArray())).readObject();
|
||||||
|
doTestEvents(this.listener, this.parentListener, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doTestEvents(TestListener listener, TestListener parentListener,
|
||||||
|
MyEvent event) {
|
||||||
listener.zeroCounter();
|
listener.zeroCounter();
|
||||||
parentListener.zeroCounter();
|
parentListener.zeroCounter();
|
||||||
assertTrue("0 events before publication", listener.getEventCount() == 0);
|
assertTrue("0 events before publication", listener.getEventCount() == 0);
|
||||||
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
|
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
|
||||||
this.applicationContext.publishEvent(new MyEvent(this));
|
this.applicationContext.publishEvent(event);
|
||||||
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
|
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
|
||||||
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
|
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,19 +45,13 @@ public abstract class AbstractXmlWebApplicationContextTests extends AbstractAppl
|
||||||
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
|
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void testEvents() throws Exception {
|
protected void doTestEvents(TestListener listener, TestListener parentListener,
|
||||||
TestListener listener = (TestListener) this.applicationContext.getBean("testListener");
|
MyEvent event) {
|
||||||
listener.zeroCounter();
|
TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener");
|
||||||
TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener");
|
TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener");
|
||||||
parentListener.zeroCounter();
|
super.doTestEvents(listenerBean, parentListenerBean, event);
|
||||||
|
|
||||||
parentListener.zeroCounter();
|
};
|
||||||
assertTrue("0 events before publication", listener.getEventCount() == 0);
|
|
||||||
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
|
|
||||||
this.applicationContext.publishEvent(new MyEvent(this));
|
|
||||||
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
|
|
||||||
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void testCount() {
|
public void testCount() {
|
||||||
|
|
|
@ -96,18 +96,11 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes
|
||||||
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
|
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void testEvents() throws Exception {
|
protected void doTestEvents(TestListener listener, TestListener parentListener,
|
||||||
TestListener listener = (TestListener) this.applicationContext.getBean("testListener");
|
MyEvent event) {
|
||||||
listener.zeroCounter();
|
TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener");
|
||||||
TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener");
|
TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener");
|
||||||
parentListener.zeroCounter();
|
super.doTestEvents(listenerBean, parentListenerBean, event);
|
||||||
|
|
||||||
parentListener.zeroCounter();
|
|
||||||
assertTrue("0 events before publication", listener.getEventCount() == 0);
|
|
||||||
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
|
|
||||||
this.applicationContext.publishEvent(new MyEvent(this));
|
|
||||||
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
|
|
||||||
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue