blob: 6170fc53f313ec780eab369c576ebfc2647d7420 [file] [log] [blame]
/*
* 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>> globalListeners
= 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 = globalListeners.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 = globalListeners.get(systemEventClass);
if (eventInfos == null)
{
eventInfos = new CopyOnWriteArrayList<>();
globalListeners.put(systemEventClass, eventInfos);
}
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 = globalListeners.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.get(systemEventClass);
if (constructor == null)
{
constructor = getConstructor(systemEventClass);
constructorCache.put(systemEventClass, constructor);
}
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;
}
}