/* * Copyright 2017 OmniFaces * * 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 org.omnifaces.eventlistener; import static javax.faces.event.PhaseId.ANY_PHASE; import static org.omnifaces.util.Faces.getContext; import static org.omnifaces.util.FacesLocal.getRequestAttribute; import static org.omnifaces.util.FacesLocal.setRequestAttribute; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; /** * <p> * This phase listener picks up phase listener instances and phase event callbacks from the request scope subscribed via * <code>subscribeToRequestXxxPhase()</code> methods of the {@link org.omnifaces.util.Events} utility class and calls * them back for each matching phase. * <p> * This differs in a few subtle ways from <code>subscribeToViewXxxPhase()</code> methods of the * {@link org.omnifaces.util.Events} class which subscribes to the view scope. Namely, this phase listener will execute * slightly earlier for its before phase and slightly later for its after phase as compared to the view scoped ones. * Additionally, the phase listener instances and phase event callbacks registered via this phase listener will not * become part of the view state, but will execute only once during the current request instead of during every * (postback) request on the same view. * * @author Arjan Tijms * @author Bauke Scholtz * @since 1.2 * @see org.omnifaces.util.Events */ public class CallbackPhaseListener implements PhaseListener { // Constants ------------------------------------------------------------------------------------------------------ private static final long serialVersionUID = 3611407485061585042L; // Actions -------------------------------------------------------------------------------------------------------- @Override public PhaseId getPhaseId() { return ANY_PHASE; } @Override public void beforePhase(final PhaseEvent event) { for (PhaseListener phaseListener : getCallbackPhaseListenersForEvent(event)) { phaseListener.beforePhase(event); } } @Override public void afterPhase(PhaseEvent event) { for (PhaseListener phaseListener : getCallbackPhaseListenersForEvent(event)) { phaseListener.afterPhase(event); } } // Utility -------------------------------------------------------------------------------------------------------- /** * Adds the given phase listener to the current request scope. * @param phaseListener The phase listener to be added to the current request scope. */ public static void add(PhaseListener phaseListener) { getCallbackPhaseListeners(getContext(), true).add(phaseListener); } /** * Removes the given phase listener from the current request scope. * @param phaseListener The phase listener to be removed from the current request scope. * @return <code>true</code> if the current request scope indeed contained the given phase listener. */ public static boolean remove(PhaseListener phaseListener) { Set<PhaseListener> phaseListeners = getCallbackPhaseListeners(getContext(), false); return phaseListeners == null ? false : phaseListeners.remove(phaseListener); } // Helpers -------------------------------------------------------------------------------------------------------- private static Set<PhaseListener> getCallbackPhaseListeners(FacesContext context, boolean create) { Set<PhaseListener> set = getRequestAttribute(context, CallbackPhaseListener.class.getName()); if (set == null && create) { set = new HashSet<>(1); setRequestAttribute(context, CallbackPhaseListener.class.getName(), set); } return set; } private static Set<PhaseListener> getCallbackPhaseListenersForEvent(PhaseEvent event) { Set<PhaseListener> phaseListeners = getCallbackPhaseListeners(event.getFacesContext(), false); if (phaseListeners == null) { return Collections.emptySet(); } Set<PhaseListener> phaseListenersForEvent = new HashSet<>(); for (PhaseListener phaseListener : phaseListeners) { if (isPhaseMatch(event, phaseListener.getPhaseId())) { phaseListenersForEvent.add(phaseListener); } } return Collections.unmodifiableSet(phaseListenersForEvent); } private static boolean isPhaseMatch(PhaseEvent event, PhaseId phaseId) { return ANY_PHASE.equals(phaseId) || event.getPhaseId().equals(phaseId); } }