Skip to content

Commit 1ea7f74

Browse files
author
Phillip Webb
committed
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 (cherry picked from commit b0ff834)
1 parent 8c6fe2c commit 1ea7f74

File tree

5 files changed

+46
-32
lines changed

5 files changed

+46
-32
lines changed

spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
2828
import org.springframework.context.ApplicationEvent;
2929
import org.springframework.context.ApplicationListener;
3030
import org.springframework.core.OrderComparator;
31+
import org.springframework.util.ObjectUtils;
3132

3233
/**
3334
* Abstract implementation of the {@link ApplicationEventMulticaster} interface,
@@ -128,7 +129,8 @@ protected Collection<ApplicationListener> getApplicationListeners() {
128129
*/
129130
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
130131
Class<? extends ApplicationEvent> eventType = event.getClass();
131-
Class sourceType = event.getSource().getClass();
132+
Object source = event.getSource();
133+
Class sourceType = (source == null ? null : source.getClass());
132134
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
133135
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
134136
if (retriever != null) {
@@ -206,12 +208,14 @@ public boolean equals(Object other) {
206208
return true;
207209
}
208210
ListenerCacheKey otherKey = (ListenerCacheKey) other;
209-
return (this.eventType.equals(otherKey.eventType) && this.sourceType.equals(otherKey.sourceType));
211+
return ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType)
212+
&& ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
210213
}
211214

212215
@Override
213216
public int hashCode() {
214-
return this.eventType.hashCode() * 29 + this.sourceType.hashCode();
217+
return ObjectUtils.nullSafeHashCode(this.eventType) * 29
218+
+ ObjectUtils.nullSafeHashCode(this.sourceType);
215219
}
216220
}
217221

spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -74,7 +74,7 @@ public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
7474
}
7575

7676
public boolean supportsSourceType(Class<?> sourceType) {
77-
return sourceType.isInstance(this.source);
77+
return (sourceType != null && sourceType.isInstance(this.source));
7878
}
7979

8080
public int getOrder() {

spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
package org.springframework.context;
1818

19+
import java.io.ByteArrayInputStream;
20+
import java.io.ByteArrayOutputStream;
21+
import java.io.ObjectInputStream;
22+
import java.io.ObjectOutputStream;
1923
import java.util.Locale;
2024

25+
import org.junit.Test;
2126
import org.springframework.beans.factory.BeanFactory;
2227
import org.springframework.beans.factory.xml.AbstractListableBeanFactoryTests;
2328
import org.springframework.tests.sample.beans.LifecycleBean;
@@ -129,11 +134,29 @@ public void testMessageSource() throws NoSuchMessageException {
129134
}
130135

131136
public void testEvents() throws Exception {
137+
doTestEvents(this.listener, this.parentListener, new MyEvent(this));
138+
}
139+
140+
@Test
141+
public void testEventsWithNoSource() throws Exception {
142+
// See SPR-10945 Serialized events result in a null source
143+
MyEvent event = new MyEvent(this);
144+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
145+
ObjectOutputStream oos = new ObjectOutputStream(bos);
146+
oos.writeObject(event);
147+
oos.close();
148+
event = (MyEvent) new ObjectInputStream(new ByteArrayInputStream(
149+
bos.toByteArray())).readObject();
150+
doTestEvents(this.listener, this.parentListener, event);
151+
}
152+
153+
protected void doTestEvents(TestListener listener, TestListener parentListener,
154+
MyEvent event) {
132155
listener.zeroCounter();
133156
parentListener.zeroCounter();
134157
assertTrue("0 events before publication", listener.getEventCount() == 0);
135158
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
136-
this.applicationContext.publishEvent(new MyEvent(this));
159+
this.applicationContext.publishEvent(event);
137160
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
138161
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
139162
}

spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/context/AbstractXmlWebApplicationContextTests.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,13 @@ public abstract class AbstractXmlWebApplicationContextTests extends AbstractAppl
4545
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
4646
*/
4747
@Override
48-
public void testEvents() throws Exception {
49-
TestListener listener = (TestListener) this.applicationContext.getBean("testListener");
50-
listener.zeroCounter();
51-
TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener");
52-
parentListener.zeroCounter();
53-
54-
parentListener.zeroCounter();
55-
assertTrue("0 events before publication", listener.getEventCount() == 0);
56-
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
57-
this.applicationContext.publishEvent(new MyEvent(this));
58-
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
59-
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
60-
}
48+
protected void doTestEvents(TestListener listener, TestListener parentListener,
49+
MyEvent event) {
50+
TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener");
51+
TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener");
52+
super.doTestEvents(listenerBean, parentListenerBean, event);
53+
54+
};
6155

6256
@Override
6357
public void testCount() {

spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,11 @@ public void testEnvironmentMerge() {
9696
* @see org.springframework.context.AbstractApplicationContextTests#testEvents()
9797
*/
9898
@Override
99-
public void testEvents() throws Exception {
100-
TestListener listener = (TestListener) this.applicationContext.getBean("testListener");
101-
listener.zeroCounter();
102-
TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener");
103-
parentListener.zeroCounter();
104-
105-
parentListener.zeroCounter();
106-
assertTrue("0 events before publication", listener.getEventCount() == 0);
107-
assertTrue("0 parent events before publication", parentListener.getEventCount() == 0);
108-
this.applicationContext.publishEvent(new MyEvent(this));
109-
assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1);
110-
assertTrue("1 parent events after publication", parentListener.getEventCount() == 1);
99+
protected void doTestEvents(TestListener listener, TestListener parentListener,
100+
MyEvent event) {
101+
TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener");
102+
TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener");
103+
super.doTestEvents(listenerBean, parentListenerBean, event);
111104
}
112105

113106
@Override

0 commit comments

Comments
 (0)