You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2020/04/14 13:15:17 UTC
[myfaces] branch master updated: MYFACES-4328 SystemEventListeners
registered for a superclass is not invoked for childclass
This is an automated email from the ASF dual-hosted git repository.
tandraschko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/master by this push:
new e0fdeac MYFACES-4328 SystemEventListeners registered for a superclass is not invoked for childclass
e0fdeac is described below
commit e0fdeac0c552c7ae83c63fe6bf470b882f4afa1d
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Tue Apr 14 15:15:10 2020 +0200
MYFACES-4328 SystemEventListeners registered for a superclass is not invoked for childclass
---
.../myfaces/application/ApplicationImpl.java | 214 +-----------
.../application/ApplicationImplEventManager.java | 363 +++++++++++++++++++++
.../myfaces/application/_ApplicationUtils.java | 222 -------------
.../myfaces/application/ApplicationImplTest.java | 34 +-
.../event/GlobalPostAddToViewEventTestCase.java | 51 ++-
5 files changed, 432 insertions(+), 452 deletions(-)
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.context.FacesContext;
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.Property;
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 @@ public class ApplicationImpl extends Application
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 @@ public class ApplicationImpl extends Application
private Lazy<ELResolver> elResolver;
- private ELResolverBuilder resolverBuilderForFaces;
-
private ProjectStage _projectStage;
private volatile boolean _firstRequestProcessed = false;
@@ -262,12 +256,13 @@ public class ApplicationImpl extends Application
_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 class ApplicationImpl extends Application
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 @@ public class ApplicationImpl extends Application
@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 @@ public class ApplicationImpl extends Application
}
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 @@ public class ApplicationImplTest extends TestCase
//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 class ApplicationImplTest extends TestCase
{
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 class ApplicationImplTest extends TestCase
{
public void run()
{
- myApp.getResourceBundle(context, "xxx");
+ myApp.getResourceBundle(facesContext, "xxx");
}
});
}
@@ -130,7 +130,7 @@ public class ApplicationImplTest extends TestCase
{
Locale locale = new Locale("xx");
UIViewRoot viewRoot = new UIViewRoot();
- context.setViewRoot(viewRoot);
+ facesContext.setViewRoot(viewRoot);
viewRoot.setLocale(locale);
assertGetResourceBundleWithLocale(locale);
}
@@ -143,10 +143,10 @@ public class ApplicationImplTest extends TestCase
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 @@ public class ApplicationImplTest extends TestCase
replay(expr);
try
{
- app.createComponent(expr, context, "testComponent");
+ application.createComponent(expr, context, "testComponent");
}
catch (FacesException e)
{
@@ -202,7 +202,7 @@ public class ApplicationImplTest extends TestCase
return bundle;
}
};
- assertSame(bundle, myapp.getResourceBundle(context, var));
+ assertSame(bundle, myapp.getResourceBundle(facesContext, var));
}
private enum MyEnum {VALUE1, VALUE2};
@@ -213,9 +213,9 @@ public class ApplicationImplTest extends TestCase
*/
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 class ApplicationImplTest extends TestCase
*/
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 @@ public class GlobalPostAddToViewEventTestCase extends AbstractJsfConfigurableMoc
}
@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());
+
+ eventManager.publishEvent(facesContext, PostAddToViewEvent.class, HtmlHead.class, new HtmlHead());
- application.addComponent(HtmlHead.COMPONENT_TYPE,
- HtmlHead.class.getName());
+ Assert.assertTrue(facesContext.getAttributes().containsKey("SystemEventListenerInvokedForHead"));
+ }
+
+ @Test
+ public void postAddToViewSourceSuperClass() throws Exception
+ {
+ ApplicationImplEventManager eventManager = new ApplicationImplEventManager();
- HtmlHead comp = (HtmlHead) application.createComponent(facesContext,
- HtmlHead.COMPONENT_TYPE,
- "javax.faces.Head");
+ 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();
- // Invoke PostAddToViewEvent
- facesContext.getViewRoot().getChildren().add(comp);
+ 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"));
+ }
}