diff --git a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java index 35a3ed3288e..7ea2b89e5b9 100644 --- a/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java @@ -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"); * 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.ApplicationListener; import org.springframework.core.OrderComparator; +import org.springframework.util.ObjectUtils; /** * Abstract implementation of the {@link ApplicationEventMulticaster} interface, @@ -134,7 +135,8 @@ public abstract class AbstractApplicationEventMulticaster implements Application */ protected Collection getApplicationListeners(ApplicationEvent event) { Class 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); ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { @@ -212,12 +214,14 @@ public abstract class AbstractApplicationEventMulticaster implements Application return true; } 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 public int hashCode() { - return this.eventType.hashCode() * 29 + this.sourceType.hashCode(); + return ObjectUtils.nullSafeHashCode(this.eventType) * 29 + + ObjectUtils.nullSafeHashCode(this.sourceType); } } diff --git a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java index ff1f4bb2d1a..7bb0f874b68 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java +++ b/spring-context/src/main/java/org/springframework/context/event/SourceFilteringListener.java @@ -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"); * you may not use this file except in compliance with the License. @@ -77,7 +77,7 @@ public class SourceFilteringListener implements SmartApplicationListener { @Override public boolean supportsSourceType(Class sourceType) { - return sourceType.isInstance(this.source); + return (sourceType != null && sourceType.isInstance(this.source)); } @Override diff --git a/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java index 6f407fb24e0..d92309186e2 100644 --- a/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/AbstractApplicationContextTests.java @@ -16,8 +16,13 @@ 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 org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.AbstractListableBeanFactoryTests; import org.springframework.tests.sample.beans.LifecycleBean; @@ -129,11 +134,29 @@ public abstract class AbstractApplicationContextTests extends AbstractListableBe } 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(); 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)); + this.applicationContext.publishEvent(event); assertTrue("1 events after publication, not " + listener.getEventCount(), listener.getEventCount() == 1); assertTrue("1 parent events after publication", parentListener.getEventCount() == 1); } diff --git a/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/context/AbstractXmlWebApplicationContextTests.java b/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/context/AbstractXmlWebApplicationContextTests.java index bec09daa5e2..21d6e905323 100644 --- a/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/context/AbstractXmlWebApplicationContextTests.java +++ b/spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/context/AbstractXmlWebApplicationContextTests.java @@ -45,19 +45,13 @@ public abstract class AbstractXmlWebApplicationContextTests extends AbstractAppl * @see org.springframework.context.AbstractApplicationContextTests#testEvents() */ @Override - public void testEvents() throws Exception { - TestListener listener = (TestListener) this.applicationContext.getBean("testListener"); - listener.zeroCounter(); - TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener"); - parentListener.zeroCounter(); - - 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); - } + protected void doTestEvents(TestListener listener, TestListener parentListener, + MyEvent event) { + TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener"); + TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener"); + super.doTestEvents(listenerBean, parentListenerBean, event); + + }; @Override public void testCount() { diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java index 4dce1e9fac3..3d1d0b203eb 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/XmlWebApplicationContextTests.java @@ -96,18 +96,11 @@ public class XmlWebApplicationContextTests extends AbstractApplicationContextTes * @see org.springframework.context.AbstractApplicationContextTests#testEvents() */ @Override - public void testEvents() throws Exception { - TestListener listener = (TestListener) this.applicationContext.getBean("testListener"); - listener.zeroCounter(); - TestListener parentListener = (TestListener) this.applicationContext.getParent().getBean("parentListener"); - parentListener.zeroCounter(); - - 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); + protected void doTestEvents(TestListener listener, TestListener parentListener, + MyEvent event) { + TestListener listenerBean = (TestListener) this.applicationContext.getBean("testListener"); + TestListener parentListenerBean = (TestListener) this.applicationContext.getParent().getBean("parentListener"); + super.doTestEvents(listenerBean, parentListenerBean, event); } @Override