AbstractApplicationEventMulticaster "lost" application listener [SPR-12545] #17148
Labels
in: core
Issues in core modules (aop, beans, core, context, expression)
status: backported
An issue that has been backported to maintenance branches
type: bug
A general bug
Milestone
Jaroslav Beran opened SPR-12545 and commented
All versions younger then 3.2.8 are affected.
There is synchronization bug.
The cache "retrieverCache" is being cleared within synchronized blocks at methods:
public void addApplicationListener(ApplicationListener listener)
public void addApplicationListenerBean(String listenerBeanName)
public void removeApplicationListener(ApplicationListener listener)
public void removeApplicationListenerBean(String listenerBeanName)
public void removeAllListeners()
The application listeners (defaultRetriever.applicationListeners and defaultRetriever.applicationListenerBeans) are modified there as well.
But at the method:
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event)
the access is different.
There is only one synchronized block where the copy of listeners is created.
After the synchronized block the algorithm works with the copy of list of listeners.
If the thread context is switched after the synchronized block and new listener is added than this one is never added to "retrieverCache"
and of course the new listener is not fired because the new listener is added only to list of listeners of defaultRetriever.
The situation description (in detail):
and the component receives the event (the "retrieverCache" contains at least the record for this listenerA,
at "synchronized block" the copy of current listeners is created (there are at least two listeners for our components - listenerA and listenerB),
and the method addApplicationListener(ApplicationListener listener) is blocked because of "synchonized",
at the end of the algorithm the "retriever" contains only two listeners listenerA and listenerB, BUT NOT listenerC.
The "retriever" is put to "retrieverCache" and if the source component multicasts the event the listenerC IS NOT fired.
Solution (or guick fix):
remaining algorithm must be included to synchronized block (the getApplicationListeners(ApplicationEvent event) method snippet):
if (retriever != null) {
return retriever.getApplicationListeners();
}
else {
retriever = new ListenerRetriever(true);
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
Set<ApplicationListener> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
}
Affects: 3.2.9, 4.1.3
Issue Links:
Backported to: 4.0.9, 3.2.13
The text was updated successfully, but these errors were encountered: