/** * Copyright 2013 by dueni.ch * * Licensed 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 ch.dueni.faces.event; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.PreRenderViewEvent; import javax.faces.event.SystemEvent; import javax.faces.event.SystemEventListener; /** * PreRenderViewEventListener acts as a mediator for components * that need to be notified before view rendering starts. *

* Since {@link PreRenderViewEvent} is application scoped, UIComponents should * not act as listener for such event directly. This mediator manages the * listeners on ViewRequestMap - therefore the listeners are registered only * during a request and as long as the ViewRoot does not change. *

* * @author hampidu@gmail.com * */ // TODO: system event listener registration does not work (verify if JSF 2.2 fixes this) // - use configuration to register this listener // @ListenerFor(systemEventClass = PreRenderViewEvent.class) public class PreRenderViewEventMediator implements SystemEventListener { public static final String SUBSCRIBED_LIST_KEY = PreRenderViewEventMediator.class.getName(); public static final String SAVED_VIEW_ROOT_KEY = SUBSCRIBED_LIST_KEY + ".savedViewRoot"; @Override public void processEvent(SystemEvent event) throws AbortProcessingException { FacesContext jsf = FacesContext.getCurrentInstance(); List listeners = getListeners(jsf, false); for (PreRenderViewEventListener listener : listeners) { listener.processPreRenderViewEvent(jsf); } } @Override public boolean isListenerForSource(Object source) { return (source instanceof UIViewRoot); } /** * Subscribe to be notified on PreRenderViewEvent. * * @param listener * the PreRenderViewEventListener instance to notify. */ public static void subscribe(PreRenderViewEventListener listener) { List listeners = getListeners(null, true); listeners.add(listener); } /** * Unsubscribe from being notified on PreRenderViewEvent - this is only needed * if a component wants to unsubscribe during a request. The listeners are * lost after the request anyway. * * @param listener * the PreRenderViewEventListener instance to notify. */ public static void unsubscribe(PreRenderViewEventListener listener) { List listeners = getListeners(null, false); listeners.remove(listener); } /** * Handle the subscriber list on FacesContext attributes Map - the list is * dropped when UIViewRoot instance changes. * * @param jsf * current FacesContext * @param create * true to create the listeners List * @return List or Collections.emptyList(). */ @SuppressWarnings("unchecked") private static List getListeners(FacesContext jsf, boolean create) { Map attMap = jsf.getAttributes(); // savedRoot is needed to detect changed view UIViewRoot savedRoot = (UIViewRoot) attMap.get(SAVED_VIEW_ROOT_KEY); List listeners = null; if (savedRoot == jsf.getViewRoot()) { // we have unchanged view root, retrieve the listeners List listeners = (List) attMap.get(SUBSCRIBED_LIST_KEY); } if (listeners == null) { // create new listeners List or return Collections.emptyList() if (create) { listeners = new ArrayList(); attMap.put(SUBSCRIBED_LIST_KEY, listeners); attMap.put(SAVED_VIEW_ROOT_KEY, jsf.getViewRoot()); } else { listeners = Collections.emptyList(); } } return listeners; } }