MYFACES-4328 SystemEventListeners registered for a superclass is not invoked for childclass
diff --git a/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java b/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java
index a62b405..faad42f 100755
--- a/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java
@@ -74,14 +74,12 @@
 import javax.faces.convert.Converter;
 import javax.faces.convert.DateTimeConverter;
 import javax.faces.convert.FacesConverter;
-import javax.faces.event.AbortProcessingException;
 import javax.faces.event.ActionListener;
 import javax.faces.event.ComponentSystemEventListener;
 import javax.faces.event.ListenerFor;
 import javax.faces.event.ListenersFor;
 import javax.faces.event.SystemEvent;
 import javax.faces.event.SystemEventListener;
-import javax.faces.event.SystemEventListenerHolder;
 import javax.faces.flow.FlowHandler;
 import javax.faces.render.ClientBehaviorRenderer;
 import javax.faces.render.RenderKit;
@@ -117,7 +115,6 @@
 import org.apache.myfaces.config.element.ResourceBundle;
 import org.apache.myfaces.context.RequestViewContext;
 import org.apache.myfaces.context.RequestViewMetadata;
-import org.apache.myfaces.el.ELResolverBuilder;
 import org.apache.myfaces.el.ELResolverBuilderForFaces;
 import org.apache.myfaces.el.resolver.FacesCompositeELResolver;
 import org.apache.myfaces.el.resolver.FacesCompositeELResolver.Scope;
@@ -183,9 +180,8 @@
 
     private final Map<String, Object> _validatorClassMap = new ConcurrentHashMap<>();
 
-    private final Map<Class<? extends SystemEvent>, SystemListenerEntry> _systemEventListenerClassMap
-            = new ConcurrentHashMap<>();
-
+    private ApplicationImplEventManager _eventManager;
+    
     private final Map<String, String> _defaultValidatorsIds = new HashMap<>();
     
     private volatile Map<String, String> _cachedDefaultValidatorsIds = null;
@@ -197,8 +193,6 @@
 
     private Lazy<ELResolver> elResolver;
 
-    private ELResolverBuilder resolverBuilderForFaces;
-
     private ProjectStage _projectStage;
 
     private volatile boolean _firstRequestProcessed = false;
@@ -262,12 +256,13 @@
         _actionListener = new ActionListenerImpl();
         _defaultRenderKitId = "HTML_BASIC";
         _stateManager = new StateManagerImpl();
-        _elContextListeners = new ArrayList<ELContextListener>();
+        _elContextListeners = new ArrayList<>();
         _resourceHandler = new ResourceHandlerImpl();
         _flowHandler = new FlowHandlerImpl();
         _searchExpressionHandler = new SearchExpressionHandlerImpl();
         _runtimeConfig = runtimeConfig;
         _myfacesConfig = MyfacesConfig.getCurrentInstance(getFacesContext());
+        _eventManager = new ApplicationImplEventManager();
 
         if (log.isLoggable(Level.FINEST))
         {
@@ -516,68 +511,13 @@
     public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass,
                              Class<?> sourceBaseType, Object source)
     {
-        Assert.notNull(systemEventClass, "systemEventClass");
-        Assert.notNull(source, "source");
-        
-        //Call events only if event processing is enabled.
-        if (!facesContext.isProcessingEvents())
-        {
-            return;
-        }
-        
-        // spec: If this argument is null the return from source.getClass() must be used as the sourceBaseType. 
-        if (sourceBaseType == null)
-        {
-            sourceBaseType = source.getClass();
-        }
-        
-        try
-        {
-            SystemEvent event = null;
-            
-            // component attached listeners
-            if (source instanceof SystemEventListenerHolder)
-            {
-                SystemEventListenerHolder holder = (SystemEventListenerHolder) source;
-
-                // If the source argument implements SystemEventListenerHolder, call
-                // SystemEventListenerHolder.getListenersForEventClass(java.lang.Class) on it, passing the
-                // systemEventClass
-                // argument. If the list is not empty, perform algorithm traverseListenerList on the list.
-                event = _ApplicationUtils._traverseListenerList(
-                        facesContext, holder.getListenersForEventClass(systemEventClass),
-                        systemEventClass, source, event);
-            }
-
-            // view attached listeners
-            UIViewRoot viewRoot = facesContext.getViewRoot();
-            if (viewRoot != null)
-            {
-                event = _ApplicationUtils._traverseListenerListWithCopy(
-                        facesContext, viewRoot.getViewListenersForEventClass(systemEventClass), 
-                        systemEventClass, source, event);
-            }
-
-            // global listeners
-            SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap.get(systemEventClass);
-            if (systemListenerEntry != null)
-            {
-                systemListenerEntry.publish(facesContext, systemEventClass, sourceBaseType, source, event);
-            }
-        }
-        catch (AbortProcessingException e)
-        {
-            // If the act of invoking the processListener method causes an AbortProcessingException to be thrown,
-            // processing of the listeners must be aborted, no further processing of the listeners for this event must
-            // take place, and the exception must be logged with Level.SEVERE.
-            log.log(Level.SEVERE, "Event processing was aborted", e);
-        }
+        _eventManager.publishEvent(facesContext, systemEventClass, sourceBaseType, source);
     }
 
     @Override
     public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass, Object source)
     {
-        publishEvent(facesContext, systemEventClass, source.getClass(), source);
+        _eventManager.publishEvent(facesContext, systemEventClass, source);
     }
 
     @Override
@@ -856,48 +796,27 @@
     @Override
     public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener)
     {
-        subscribeToEvent(systemEventClass, null, listener);
+        _eventManager.subscribeToEvent(systemEventClass, listener);
     }
 
     @Override
     public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,
                                  SystemEventListener listener)
     {
-        Assert.notNull(systemEventClass, "systemEventClass");
-        Assert.notNull(listener, "listener");
-
-        SystemListenerEntry systemListenerEntry;
-        synchronized (_systemEventListenerClassMap)
-        {
-            systemListenerEntry = _systemEventListenerClassMap.get(systemEventClass);
-            if (systemListenerEntry == null)
-            {
-                systemListenerEntry = new SystemListenerEntry();
-                _systemEventListenerClassMap.put(systemEventClass, systemListenerEntry);
-            }
-        }
-
-        systemListenerEntry.addListener(listener, sourceClass);
+        _eventManager.subscribeToEvent(systemEventClass, sourceClass, listener);
     }
     
     @Override
     public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener)
     {
-        unsubscribeFromEvent(systemEventClass, null, listener);
+        _eventManager.unsubscribeFromEvent(systemEventClass, listener);
     }
 
     @Override
     public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,
                                      SystemEventListener listener)
     {
-        Assert.notNull(systemEventClass, "systemEventClass");
-        Assert.notNull(listener, "listener");
-
-        SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap.get(systemEventClass);
-        if (systemListenerEntry != null)
-        {
-            systemListenerEntry.removeListener(listener, sourceClass);
-        }
+        _eventManager.unsubscribeFromEvent(systemEventClass, sourceClass, listener);
     }
 
     @Override
@@ -2432,120 +2351,7 @@
         }
         return _firstRequestProcessed;
     }
-    
-    private static class SystemListenerEntry
-    {
-        private List<SystemEventListener> _lstSystemEventListener;
-        private Map<Class<?>, List<SystemEventListener>> _sourceClassMap;
 
-        public SystemListenerEntry()
-        {
-        }
-
-        public void addListener(SystemEventListener listener)
-        {
-            assert listener != null;
-
-            addListenerNoDuplicate(getAnySourceListenersNotNull(), listener);
-        }
-
-        public void addListener(SystemEventListener listener, Class<?> source)
-        {
-            assert listener != null;
-
-            if (source == null)
-            {
-                addListener(listener);
-            }
-            else
-            {
-                addListenerNoDuplicate(getSpecificSourceListenersNotNull(source), listener);
-            }
-        }
-
-        public void removeListener(SystemEventListener listener)
-        {
-            assert listener != null;
-
-            if (_lstSystemEventListener != null)
-            {
-                _lstSystemEventListener.remove(listener);
-            }
-        }
-
-        public void removeListener(SystemEventListener listener, Class<?> sourceClass)
-        {
-            assert listener != null;
-
-            if (sourceClass == null)
-            {
-                removeListener(listener);
-            }
-            else
-            {
-                if (_sourceClassMap != null)
-                {
-                    List<SystemEventListener> listeners = _sourceClassMap.get(sourceClass);
-                    if (listeners != null)
-                    {
-                        listeners.remove(listener);
-                    }
-                }
-            }
-        }
-
-        public void publish(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass,
-                Class<?> classSource, Object source, SystemEvent event)
-        {
-            if (source != null && _sourceClassMap != null)
-            {
-                event = _ApplicationUtils._traverseListenerList(facesContext, _sourceClassMap.get(classSource),
-                        systemEventClass, source, event);
-            }
-
-            _ApplicationUtils._traverseListenerList(facesContext, _lstSystemEventListener,
-                    systemEventClass, source, event);
-        }
-
-        private void addListenerNoDuplicate(List<SystemEventListener> listeners, SystemEventListener listener)
-        {
-            if (!listeners.contains(listener))
-            {
-                listeners.add(listener);
-            }
-        }
-
-        private synchronized List<SystemEventListener> getAnySourceListenersNotNull()
-        {
-            if (_lstSystemEventListener == null)
-            {
-                /*
-                 * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
-                 * 
-                 * Registrations found:
-                 */
-                _lstSystemEventListener = new CopyOnWriteArrayList<>();
-            }
-
-            return _lstSystemEventListener;
-        }
-
-        private synchronized List<SystemEventListener> getSpecificSourceListenersNotNull(Class<?> sourceClass)
-        {
-            if (_sourceClassMap == null)
-            {
-                _sourceClassMap = new ConcurrentHashMap<>();
-            }
-
-            /*
-             * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
-             * 
-             * Registrations found:
-             */
-            return _sourceClassMap.computeIfAbsent(sourceClass, k -> new CopyOnWriteArrayList<>());
-        }
-    }
-    
     /*
      * private method to look for config objects on a classmap.  The objects can be either a type string
      * or a Class<?> object.  This is done to facilitate lazy loading of config objects.   
diff --git a/impl/src/main/java/org/apache/myfaces/application/ApplicationImplEventManager.java b/impl/src/main/java/org/apache/myfaces/application/ApplicationImplEventManager.java
new file mode 100644
index 0000000..7734fc6
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/application/ApplicationImplEventManager.java
@@ -0,0 +1,363 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you 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.apache.myfaces.application;

+

+import java.lang.reflect.Constructor;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.CopyOnWriteArrayList;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+import javax.faces.FacesException;

+import javax.faces.component.UIViewRoot;

+import javax.faces.context.FacesContext;

+import javax.faces.event.AbortProcessingException;

+import javax.faces.event.SystemEvent;

+import javax.faces.event.SystemEventListener;

+import javax.faces.event.SystemEventListenerHolder;

+

+public class ApplicationImplEventManager

+{

+    private static final Logger log = Logger.getLogger(ApplicationImplEventManager.class.getName());

+    

+    protected static class EventInfo

+    {

+        private Class<? extends SystemEvent> systemEventClass;

+        private Class<?> sourceClass;

+        private SystemEventListener listener;

+    }

+    

+    private ConcurrentHashMap<Class<? extends SystemEvent>, List<EventInfo>> cache

+            = new ConcurrentHashMap<>();

+    private ConcurrentHashMap<Class<? extends SystemEvent>, Constructor<? extends SystemEvent>> constructorCache

+            = new ConcurrentHashMap<>();

+    

+    public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass, Object source)

+    {

+        publishEvent(facesContext, systemEventClass, source.getClass(), source);

+    }

+

+    public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass,

+                             Class<?> sourceBaseType, Object source)

+    {        

+        //Call events only if event processing is enabled.

+        if (!facesContext.isProcessingEvents())

+        {

+            return;

+        }

+        

+        // spec: If this argument is null the return from source.getClass() must be used as the sourceBaseType. 

+        if (sourceBaseType == null)

+        {

+            sourceBaseType = source.getClass();

+        }

+        

+        try

+        {

+            SystemEvent event = null;

+            

+            // component attached listeners

+            if (source instanceof SystemEventListenerHolder)

+            {

+                List<SystemEventListener> listeners =

+                        ((SystemEventListenerHolder) source).getListenersForEventClass(systemEventClass);

+                event = processComponentAttachedListeners(facesContext, listeners, systemEventClass, source, event);

+            }

+

+            

+            // view attached listeners

+            UIViewRoot viewRoot = facesContext.getViewRoot();

+            if (viewRoot != null)

+            {

+                List<SystemEventListener> listeners = viewRoot.getViewListenersForEventClass(systemEventClass);

+                event = processViewAttachedListeners(facesContext, listeners, systemEventClass, source, event);

+            }

+

+            

+            // global listeners

+            List<EventInfo> eventInfos = cache.get(systemEventClass);

+            event = processGlobalListeners(facesContext, eventInfos, systemEventClass, source, event, sourceBaseType);

+        }

+        catch (AbortProcessingException e)

+        {

+            // If the act of invoking the processListener method causes an AbortProcessingException to be thrown,

+            // processing of the listeners must be aborted, no further processing of the listeners for this event must

+            // take place, and the exception must be logged with Level.SEVERE.

+            log.log(Level.SEVERE, "Event processing was aborted", e);

+        }

+    }

+    

+    

+    public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener)

+    {

+        subscribeToEvent(systemEventClass, null, listener);

+    }

+

+    public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,

+                                 SystemEventListener listener)

+    {

+        List<EventInfo> eventInfos = cache.computeIfAbsent(systemEventClass, k -> new CopyOnWriteArrayList<>());

+        

+        EventInfo eventInfo = new EventInfo();

+        eventInfo.systemEventClass = systemEventClass;

+        eventInfo.sourceClass = sourceClass;

+        eventInfo.listener = listener;

+        

+        eventInfos.add(eventInfo);

+    }

+    

+    public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener)

+    {

+        unsubscribeFromEvent(systemEventClass, null, listener);

+    }

+

+    public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,

+                                     SystemEventListener listener)

+    {

+        List<EventInfo> eventInfos = cache.get(systemEventClass);

+        if (eventInfos == null || eventInfos.isEmpty())

+        {

+            return;

+        }

+

+        if (sourceClass == null)

+        {

+            eventInfos.removeIf(e -> e.listener.equals(listener));

+        }

+        else

+        {

+            eventInfos.removeIf(e -> e.sourceClass == sourceClass && e.listener.equals(listener));

+        }

+    }

+    

+  

+    

+    

+    protected SystemEvent createEvent(Class<? extends SystemEvent> systemEventClass, FacesContext facesContext,

+            Object source)

+    {

+        Constructor<? extends SystemEvent> constructor = constructorCache.computeIfAbsent(systemEventClass,

+                k -> getConstructor(k));

+        if (constructor == null)

+        {

+            return null;

+        }

+        

+        try

+        {

+            if (constructor.getParameterTypes().length == 2)

+            {

+                return constructor.newInstance(facesContext, source);

+            }

+

+            return constructor.newInstance(source);

+        }

+        catch (Exception e)

+        {

+            throw new FacesException("Couldn't instanciate system event of type " + 

+                    systemEventClass.getName(), e);

+        }

+    }

+    

+    protected Constructor<? extends SystemEvent> getConstructor(Class<? extends SystemEvent> systemEventClass)

+    {

+        Constructor<?>[] constructors = systemEventClass.getConstructors();

+        Constructor<? extends SystemEvent> constructor = null;

+

+        // try to lookup the new 2 parameter constructor

+        for (Constructor<?> c : constructors)

+        {

+            if (c.getParameterTypes().length == 2)

+            {

+                // Safe cast, since the constructor belongs

+                // to a class of type SystemEvent

+                constructor = (Constructor<? extends SystemEvent>) c;

+                break;

+            }

+        }

+

+        // try to lookup the old 1 parameter constructor

+        if (constructor == null)

+        {

+            for (Constructor<?> c : constructors)

+            {

+                if (c.getParameterTypes().length == 1)

+                {

+                    // Safe cast, since the constructor belongs

+                    // to a class of type SystemEvent

+                    constructor = (Constructor<? extends SystemEvent>) c;

+                    break;

+                }

+            }

+        }

+

+        return constructor;

+    }

+    

+

+

+    protected SystemEvent processComponentAttachedListeners(FacesContext facesContext,

+            List<? extends SystemEventListener> listeners, Class<? extends SystemEvent> systemEventClass,

+            Object source, SystemEvent event)

+    {

+        if (listeners == null || listeners.isEmpty())

+        {

+            return event;

+        }

+

+        for (int i  = 0, size = listeners.size(); i < size; i++)

+        {

+            SystemEventListener listener = listeners.get(i);

+            if (listener.isListenerForSource(source))

+            {

+                // Lazy construct the event; zhis same event instance must be passed to all listener instances.

+                if (event == null)

+                {

+                    event = createEvent(systemEventClass, facesContext, source);

+                }

+

+                if (event.isAppropriateListener(listener))

+                {

+                    event.processListener(listener);

+                }

+            }

+        }

+

+        return event;

+    }

+    

+    protected SystemEvent processViewAttachedListeners(FacesContext facesContext,

+            List<? extends SystemEventListener> listeners,

+            Class<? extends SystemEvent> systemEventClass, Object source,

+            SystemEvent event)

+    {

+        if (listeners == null || listeners.isEmpty())

+        {

+            return event;

+        }

+

+        int processedListenerIndex = 0;

+

+        // Do it with a copy because the list could be changed during a event see MYFACES-2935

+        List<SystemEventListener> listenersCopy = new ArrayList<>(listeners);

+

+        // If the inner for is succesful, processedListenerIndex == listenersCopy.size()

+        // and the loop will be complete.

+        while (processedListenerIndex < listenersCopy.size())

+        {                

+            for (; processedListenerIndex < listenersCopy.size(); processedListenerIndex++ )

+            {

+                SystemEventListener listener = listenersCopy.get(processedListenerIndex);

+                if (listener.isListenerForSource(source))

+                {

+                    // Lazy construct the event; zhis same event instance must be passed to all listener instances.

+                    if (event == null)

+                    {

+                        event = createEvent(systemEventClass, facesContext, source);

+                    }

+

+                    if (event.isAppropriateListener(listener))

+                    {

+                        event.processListener(listener);

+                    }

+                }

+            }

+

+            boolean listChanged = false;

+            if (listeners.size() == listenersCopy.size())

+            {

+                for (int i = 0; i < listenersCopy.size(); i++)

+                {

+                    if (listenersCopy.get(i) != listeners.get(i))

+                    {

+                        listChanged = true;

+                        break;

+                    }

+                }

+            }

+            else

+            {

+                listChanged = true;

+            }

+

+            if (listChanged)

+            {

+                for (int i = 0; i < listeners.size(); i++)

+                {

+                    SystemEventListener listener = listeners.get(i);

+

+                    // check if listenersCopy.get(i) is valid

+                    if (i < listenersCopy.size())

+                    {

+                        // The normal case is a listener was added, 

+                        // so as heuristic, check first if we can find it at the same location

+                        if (!listener.equals(listenersCopy.get(i)) &&

+                            !listenersCopy.contains(listener))

+                        {

+                            listenersCopy.add(listener);

+                        }

+                    }

+                    else

+                    {

+                        if (!listenersCopy.contains(listener))

+                        {

+                            listenersCopy.add(listener);

+                        }

+                    }

+                }

+            }

+        }

+

+        return event;

+    }

+    

+    protected SystemEvent processGlobalListeners(FacesContext facesContext, List<EventInfo> eventInfos,

+            Class<? extends SystemEvent> systemEventClass, Object source, SystemEvent event, Class<?> sourceBaseType)

+    {

+        if (eventInfos == null || eventInfos.isEmpty())

+        {

+            return event;

+        }

+        

+        for (int i  = 0, size = eventInfos.size(); i < size; i++)

+        {

+            EventInfo eventInfo = eventInfos.get(i);

+            if (eventInfo.sourceClass != null && !eventInfo.sourceClass.isAssignableFrom(sourceBaseType))

+            {

+                continue;

+            }

+

+            if (eventInfo.listener.isListenerForSource(source))

+            {

+                if (event == null)

+                {

+                    event = createEvent(systemEventClass, facesContext, source);

+                }

+

+                if (event.isAppropriateListener(eventInfo.listener))

+                {

+                    event.processListener(eventInfo.listener);

+                }

+            }

+        }

+        

+        return event;

+    }

+}

diff --git a/impl/src/main/java/org/apache/myfaces/application/_ApplicationUtils.java b/impl/src/main/java/org/apache/myfaces/application/_ApplicationUtils.java
deleted file mode 100644
index f16ee07..0000000
--- a/impl/src/main/java/org/apache/myfaces/application/_ApplicationUtils.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you 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.apache.myfaces.application;

-

-import java.lang.reflect.Constructor;

-import java.util.ArrayList;

-import java.util.List;

-import javax.faces.FacesException;

-import javax.faces.context.FacesContext;

-import javax.faces.event.SystemEvent;

-import javax.faces.event.SystemEventListener;

-

-/**

- *

- * lu4242

- */

-class _ApplicationUtils

-{

-    // TODO we could cache the constructor, it's called very often.

-    static SystemEvent _createEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass,

-            Object source, SystemEvent event)

-    {

-        if (event == null)

-        {

-            try

-            {

-                Constructor<?>[] constructors = systemEventClass.getConstructors();

-                Constructor<? extends SystemEvent> constructor = null;

-             

-                // try to lookup the new 2 parameter constructor

-                for (Constructor<?> c : constructors)

-                {

-                    if (c.getParameterTypes().length == 2)

-                    {

-                        // Safe cast, since the constructor belongs

-                        // to a class of type SystemEvent

-                        constructor = (Constructor<? extends SystemEvent>) c;

-                        break;

-                    }

-                }

-                if (constructor != null)

-                {

-                    event = constructor.newInstance(facesContext, source);

-                }

-                

-                // try to lookup the old 1 parameter constructor

-                if (constructor == null)

-                {

-                    for (Constructor<?> c : constructors)

-                    {

-                        if (c.getParameterTypes().length == 1)

-                        {

-                            // Safe cast, since the constructor belongs

-                            // to a class of type SystemEvent

-                            constructor = (Constructor<? extends SystemEvent>) c;

-                            break;

-                        }

-                    }

-                    if (constructor != null)

-                    {

-                        event = constructor.newInstance(source);

-                    }

-                }

-            }

-            catch (Exception e)

-            {

-                throw new FacesException("Couldn't instanciate system event of type " + 

-                        systemEventClass.getName(), e);

-            }

-        }

-

-        return event;

-    }

-

-    static SystemEvent _traverseListenerList(FacesContext facesContext, List<? extends SystemEventListener> listeners,

-                                                     Class<? extends SystemEvent> systemEventClass, Object source,

-                                                     SystemEvent event)

-    {

-        if (listeners != null && !listeners.isEmpty())

-        {

-            // perf: org.apache.myfaces.application.ApplicationImpl.

-            //    SystemListenerEntry.getSpecificSourceListenersNotNull(Class<?>)

-            // or javax.faces.component.UIComponent.subscribeToEvent(

-            //      Class<? extends SystemEvent>, ComponentSystemEventListener)

-            // creates a ArrayList:

-            for (int i  = 0, size = listeners.size(); i < size; i++)

-            {

-                SystemEventListener listener = listeners.get(i);

-                // Call SystemEventListener.isListenerForSource(java.lang.Object), passing the source argument.

-                // If this returns false, take no action on the listener.

-                if (listener.isListenerForSource(source))

-                {

-                    // Otherwise, if the event to be passed to the listener instances has not yet been constructed,

-                    // construct the event, passing source as the argument to the one-argument constructor that takes

-                    // an Object. This same event instance must be passed to all listener instances.

-                    event = _createEvent(facesContext, systemEventClass, source, event);

-

-                    // Call SystemEvent.isAppropriateListener(javax.faces.event.FacesListener), passing the listener

-                    // instance as the argument. If this returns false, take no action on the listener.

-                    if (event.isAppropriateListener(listener))

-                    {

-                        // Call SystemEvent.processListener(javax.faces.event.FacesListener), passing the listener

-                        // instance.

-                        event.processListener(listener);

-                    }

-                }

-            }

-        }

-

-        return event;

-    }

-    

-    // Do it with a copy because the list could be changed during a event

-    // see MYFACES-2935

-    static SystemEvent _traverseListenerListWithCopy(FacesContext facesContext,

-            List<? extends SystemEventListener> listeners,

-            Class<? extends SystemEvent> systemEventClass, Object source,

-            SystemEvent event)

-    {

-        if (listeners != null && !listeners.isEmpty())

-        {

-            int processedListenerIndex = 0;

-

-            List<SystemEventListener> listenersCopy = new ArrayList<SystemEventListener>(listeners);

-            

-            // If the inner for is succesful, processedListenerIndex == listenersCopy.size()

-            // and the loop will be complete.

-            while (processedListenerIndex < listenersCopy.size())

-            {                

-                for (; processedListenerIndex < listenersCopy.size(); processedListenerIndex++ )

-                {

-                    SystemEventListener listener = listenersCopy.get(processedListenerIndex);

-                    // Call SystemEventListener.isListenerForSource(java.lang.Object), passing the source argument.

-                    // If this returns false, take no action on the listener.

-                    if (listener.isListenerForSource(source))

-                    {

-                        // Otherwise, if the event to be passed to the listener instances has not yet been constructed,

-                        // construct the event, passing source as the argument

-                        // to the one-argument constructor that takes

-                        // an Object. This same event instance must be passed to all listener instances.

-                        event = _createEvent(facesContext, systemEventClass, source, event);

-    

-                        // Call SystemEvent.isAppropriateListener(javax.faces.event.FacesListener), passing the listener

-                        // instance as the argument. If this returns false, take no action on the listener.

-                        if (event.isAppropriateListener(listener))

-                        {

-                            // Call SystemEvent.processListener(javax.faces.event.FacesListener), passing the listener

-                            // instance.

-                            event.processListener(listener);

-                        }

-                    }

-                }

-                

-                boolean listChanged = false;

-                if (listeners.size() == listenersCopy.size())

-                {

-                    for (int i = 0; i < listenersCopy.size(); i++)

-                    {

-                        if (listenersCopy.get(i) != listeners.get(i))

-                        {

-                            listChanged = true;

-                            break;

-                        }

-                    }

-                }

-                else

-                {

-                    listChanged = true;

-                }

-                

-                if (listChanged)

-                {

-                    for (int i = 0; i < listeners.size(); i++)

-                    {

-                        SystemEventListener listener = listeners.get(i);

-                        

-                        // check if listenersCopy.get(i) is valid

-                        if (i < listenersCopy.size())

-                        {

-                            // The normal case is a listener was added, 

-                            // so as heuristic, check first

-                            // if we can find it at the same location

-                            if (!listener.equals(listenersCopy.get(i)) &&

-                                !listenersCopy.contains(listener))

-                            {

-                                listenersCopy.add(listener);

-                            }

-                        }

-                        else

-                        {

-                            if (!listenersCopy.contains(listener))

-                            {

-                                listenersCopy.add(listener);

-                            }

-                        }

-                    }

-                }

-            }

-        }

-

-        return event;

-    }

-

-

-}

diff --git a/impl/src/test/java/org/apache/myfaces/application/ApplicationImplTest.java b/impl/src/test/java/org/apache/myfaces/application/ApplicationImplTest.java
index 29fe03a..8af55af 100644
--- a/impl/src/test/java/org/apache/myfaces/application/ApplicationImplTest.java
+++ b/impl/src/test/java/org/apache/myfaces/application/ApplicationImplTest.java
@@ -56,13 +56,13 @@
     //TODO: need mock objects for VDL/VDLFactory
     //remove from excludes list in pom.xml after complete
     
-    private ApplicationImpl app;
-    private MockFacesContext context;
+    protected ApplicationImpl application;
+    protected MockFacesContext facesContext;
 
     protected void setUp() throws Exception
     {
-        app = new ApplicationImpl(new RuntimeConfig());
-        context = new MockFacesContext();
+        application = new ApplicationImpl(new RuntimeConfig());
+        facesContext = new MockFacesContext();
     }
 
     /**
@@ -75,14 +75,14 @@
         {
             public void run()
             {
-                app.getResourceBundle(null, "xxx");
+                application.getResourceBundle(null, "xxx");
             }
         });
         MyFacesAsserts.assertException(NullPointerException.class, new TestRunner()
         {
             public void run()
             {
-                app.getResourceBundle(context, null);
+                application.getResourceBundle(facesContext, null);
             }
         });
     }
@@ -108,7 +108,7 @@
         {
             public void run()
             {
-                myApp.getResourceBundle(context, "xxx");
+                myApp.getResourceBundle(facesContext, "xxx");
             }
         });
     }
@@ -130,7 +130,7 @@
     {
         Locale locale = new Locale("xx");
         UIViewRoot viewRoot = new UIViewRoot();
-        context.setViewRoot(viewRoot);
+        facesContext.setViewRoot(viewRoot);
         viewRoot.setLocale(locale);
         assertGetResourceBundleWithLocale(locale);
     }
@@ -143,10 +143,10 @@
         expect(context.getELContext()).andReturn(elcontext);
         expect(expr.getValue(elcontext)).andReturn(null);
         expr.setValue(eq(elcontext), isA(UIOutput.class));
-        app.addComponent("testComponent", UIOutput.class.getName());
+        application.addComponent("testComponent", UIOutput.class.getName());
         replay(context);
         replay(expr);
-        assertTrue(UIOutput.class.isAssignableFrom(app.createComponent(expr, context, "testComponent").getClass()));
+        assertTrue(UIOutput.class.isAssignableFrom(application.createComponent(expr, context, "testComponent").getClass()));
     }
 
     public void testCreateComponentExpressionFacesExceptionTest() throws Exception
@@ -160,7 +160,7 @@
         replay(expr);
         try
         {
-            app.createComponent(expr, context, "testComponent");
+            application.createComponent(expr, context, "testComponent");
         }
         catch (FacesException e)
         {
@@ -202,7 +202,7 @@
                 return bundle;
             }
         };
-        assertSame(bundle, myapp.getResourceBundle(context, var));
+        assertSame(bundle, myapp.getResourceBundle(facesContext, var));
     }
 
     private enum MyEnum {VALUE1, VALUE2}; 
@@ -213,9 +213,9 @@
      */
     public void testCreateEnumConverter() throws Exception
     {
-        app.addConverter(Enum.class, EnumConverter.class.getName());
+        application.addConverter(Enum.class, EnumConverter.class.getName());
 
-        Converter converter = app.createConverter(MyEnum.class);
+        Converter converter = application.createConverter(MyEnum.class);
         assertNotNull(converter);
         assertEquals(converter.getClass(), EnumConverter.class);
     }    
@@ -258,10 +258,10 @@
      */
     public void testCreateConverterForInterface() throws Exception 
     {
-        app.addConverter(Enum.class, EnumConverter.class.getName());
-    	app.addConverter(EnumCoded.class, EnumCodedTestConverter.class.getName());
+        application.addConverter(Enum.class, EnumConverter.class.getName());
+    	application.addConverter(EnumCoded.class, EnumCodedTestConverter.class.getName());
     	
-    	Converter converter = app.createConverter(AnotherEnum.class);
+    	Converter converter = application.createConverter(AnotherEnum.class);
     	assertNotNull(converter);
         assertEquals(converter.getClass(), EnumCodedTestConverter.class);
     }
diff --git a/impl/src/test/java/org/apache/myfaces/event/GlobalPostAddToViewEventTestCase.java b/impl/src/test/java/org/apache/myfaces/event/GlobalPostAddToViewEventTestCase.java
index 4568290..aef65d0 100644
--- a/impl/src/test/java/org/apache/myfaces/event/GlobalPostAddToViewEventTestCase.java
+++ b/impl/src/test/java/org/apache/myfaces/event/GlobalPostAddToViewEventTestCase.java
@@ -19,10 +19,13 @@
 package org.apache.myfaces.event;
 
 import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
 import javax.faces.component.html.HtmlHead;
+import javax.faces.component.html.HtmlInputText;
 import javax.faces.event.PostAddToViewEvent;
 import javax.faces.event.SystemEvent;
 import javax.faces.event.SystemEventListener;
+import org.apache.myfaces.application.ApplicationImplEventManager;
 import org.apache.myfaces.test.base.junit4.AbstractJsfConfigurableMockTestCase;
 
 import org.junit.Assert;
@@ -46,20 +49,50 @@
     }
 
     @Test
-    public void testPostAddToViewForHead() throws Exception
+    public void postAddToViewSourceNull() throws Exception
     {
-        application.subscribeToEvent(PostAddToViewEvent.class, new HeadResourceListener());
+        ApplicationImplEventManager eventManager = new ApplicationImplEventManager();
+        
+        eventManager.subscribeToEvent(PostAddToViewEvent.class, new HeadResourceListener());
 
-        application.addComponent(HtmlHead.COMPONENT_TYPE,
-                HtmlHead.class.getName());
-        
-        HtmlHead comp = (HtmlHead) application.createComponent(facesContext, 
-                HtmlHead.COMPONENT_TYPE,
-                "javax.faces.Head");
-        
-        // Invoke PostAddToViewEvent
-        facesContext.getViewRoot().getChildren().add(comp);
+        eventManager.publishEvent(facesContext, PostAddToViewEvent.class, HtmlHead.class, new HtmlHead());
 
         Assert.assertTrue(facesContext.getAttributes().containsKey("SystemEventListenerInvokedForHead"));
     }
+    
+    @Test
+    public void postAddToViewSourceSuperClass() throws Exception
+    {
+        ApplicationImplEventManager eventManager = new ApplicationImplEventManager();
+        
+        eventManager.subscribeToEvent(PostAddToViewEvent.class, UIOutput.class, new HeadResourceListener());
+
+        eventManager.publishEvent(facesContext, PostAddToViewEvent.class, HtmlHead.class, new HtmlHead());
+
+        Assert.assertTrue(facesContext.getAttributes().containsKey("SystemEventListenerInvokedForHead"));
+    }
+    
+    @Test
+    public void postAddToViewSourceDirect() throws Exception
+    {
+        ApplicationImplEventManager eventManager = new ApplicationImplEventManager();
+        
+        eventManager.subscribeToEvent(PostAddToViewEvent.class, HtmlHead.class, new HeadResourceListener());
+
+        eventManager.publishEvent(facesContext, PostAddToViewEvent.class, HtmlHead.class, new HtmlHead());
+
+        Assert.assertTrue(facesContext.getAttributes().containsKey("SystemEventListenerInvokedForHead"));
+    }
+    
+    @Test
+    public void postAddToViewSourceUnmatching() throws Exception
+    {
+        ApplicationImplEventManager eventManager = new ApplicationImplEventManager();
+        
+        eventManager.subscribeToEvent(PostAddToViewEvent.class, HtmlInputText.class, new HeadResourceListener());
+
+        eventManager.publishEvent(facesContext, PostAddToViewEvent.class, HtmlHead.class, new HtmlHead());
+
+        Assert.assertFalse(facesContext.getAttributes().containsKey("SystemEventListenerInvokedForHead"));
+    }
 }