blob: 41f474b6833ada0334bf5e4f6f10e3d39b3f29a0 [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 javax.faces.component;
import org.apache.myfaces.core.api.shared.ClassUtils;
import org.apache.myfaces.core.api.shared.ComponentUtils;
import org.apache.myfaces.core.api.shared.LocaleUtils;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FactoryFinder;
import javax.faces.application.ProjectStage;
import javax.faces.application.StateManager;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialViewContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PostConstructViewMapEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewMetadata;
import javax.faces.webapp.FacesServlet;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
/**
* Creates a JSF View, which is a container that holds all of the components that are part of the view.
* <p>
* Unless otherwise specified, all attributes accept static values or EL expressions.
* </p>
* <p>
* See the javadoc for this class in the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF
* Specification</a> for further details.
* </p>
*/
@JSFComponent(name = "f:view", bodyContent = "JSP", tagClass = "org.apache.myfaces.taglib.core.ViewTag")
@JSFJspProperty(name = "binding", returnType = "java.lang.String", tagExcluded = true)
public class UIViewRoot extends UIComponentBase implements UniqueIdVendor
{
public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot";
public static final String COMPONENT_TYPE = "javax.faces.ViewRoot";
public static final String METADATA_FACET_NAME = "javax_faces_metadata";
public static final String UNIQUE_ID_PREFIX = "j_id";
public static final String VIEW_PARAMETERS_KEY = "javax.faces.component.VIEW_PARAMETERS_KEY";
/**
* @since 2.3
*/
@JSFWebConfigParam(defaultValue="false", expectedValues="true, false", since="2.3")
public static final String VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME
= "javax.faces.VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS";
private transient Logger logger = null;
private static final PhaseProcessor APPLY_REQUEST_VALUES_PROCESSOR = new ApplyRequestValuesPhaseProcessor();
private static final PhaseProcessor PROCESS_VALIDATORS_PROCESSOR = new ProcessValidatorPhaseProcessor();
private static final PhaseProcessor UPDATE_MODEL_PROCESSOR = new UpdateModelPhaseProcessor();
/**
* Class that is used to create the view scope map. This strategy
* allows change the implementation of view scope map to use cdi or
* whatever without change UIViewRoot implementation.
*/
private static Class<?> VIEW_SCOPE_PROXY_MAP_CLASS = null;
private static Class<?> REQUEST_VIEW_CONTEXT_CLASS = null;
private static Method REQUEST_VIEW_CONTEXT_GET_INSTANCE = null;
private static Method REQUEST_VIEW_CONTEXT_SET_RENDER_TARGET = null;
static
{
try
{
VIEW_SCOPE_PROXY_MAP_CLASS
= ClassUtils.classForName("org.apache.myfaces.view.ViewScopeProxyMap");
}
catch (Exception e)
{
// no op
}
try
{
REQUEST_VIEW_CONTEXT_CLASS
= ClassUtils.classForName("org.apache.myfaces.context.RequestViewContext");
REQUEST_VIEW_CONTEXT_GET_INSTANCE = REQUEST_VIEW_CONTEXT_CLASS.getMethod("getCurrentInstance",
new Class[] { FacesContext.class });
REQUEST_VIEW_CONTEXT_SET_RENDER_TARGET = REQUEST_VIEW_CONTEXT_CLASS.getMethod("setRenderTarget",
new Class[] { String.class, boolean.class, UIComponent.class });
}
catch (Exception e)
{
// no op
}
}
// todo: is it right to save the state of _events and _phaseListeners?
private List<FacesEvent> _events;
/**
* Map containing view scope objects.
*
* It is not expected this map hold PartialStateHolder instances,
* so we can use saveAttachedState and restoreAttachedState methods.
*/
private Map<String, Object> _viewScope;
private transient boolean _restoreViewScopeStateCalled = false;
private transient Lifecycle _lifecycle = null;
private HashMap<Class<? extends SystemEvent>, List<SystemEventListener>> _systemEventListeners;
// Tracks success in the beforePhase. Listeners that threw an exception
// in beforePhase or were never called, because a previous listener threw
// an exception, should not have their afterPhase method called
private transient Map<PhaseId, boolean[]> listenerSuccessMap;
private static final String JAVAX_FACES_LOCATION_PREFIX = "javax_faces_location_";
private static final String JAVAX_FACES_LOCATION_HEAD = "javax_faces_location_head";
private static final String JAVAX_FACES_LOCATION_BODY = "javax_faces_location_body";
private static final String JAVAX_FACES_LOCATION_FORM = "javax_faces_location_form";
private static final String SKIP_VIEW_MAP_SAVE_STATE = "oam.viewPool.SKIP_VIEW_MAP_SAVE_STATE";
private transient int _resetSaveStateMode = 0;
private transient boolean _resourceDependencyUniqueId;
private transient Map<String,Object> _attributesMap;
/**
* Construct an instance of the UIViewRoot.
*/
public UIViewRoot()
{
setRendererType(null);
}
/**
* @since 2.0
*/
public void addComponentResource(FacesContext context, UIComponent componentResource)
{
addComponentResource(context, componentResource, null);
}
/**
* @since 2.0
*/
public void addComponentResource(FacesContext context, UIComponent componentResource, String target)
{
// If the target argument is null
if (target == null)
{
// Look for a target attribute on the component
target = (String)componentResource.getAttributes().get("target");
// If there is no target attribute, set target to be the default value head
if (target == null)
{
target = "head";
}
}
// Call getComponentResources to obtain the child list for the given target
List<UIComponent> componentResources = _getComponentResources(context, target);
// If the component ID of componentResource matches the ID of a resource
// that has already been added, remove the old resource.
String componentId = componentResource.getId();
if (componentId == null)
{
// componentResource can have no id - calling createUniqueId makes us sure that component will have one
// https://issues.apache.org/jira/browse/MYFACES-2775
componentId = createUniqueId(context, null);
componentResource.setId(componentId);
}
// This var helps to handle the case when we try to add a component that already is
// on the resource list, because PostAddToViewEvent also is sent to components
// backing resources. The problem start when a component is already inside
// componentResources list and we try to relocate it again. This leads to a StackOverflowException
// so we need to check if a component is and prevent remove and add it again. Note
// that remove and then add a component trigger another PostAddToViewEvent. The right
// point to prevent this StackOverflowException is here, because this method is
// responsible to traverse the componentResources list and add when necessary.
boolean alreadyAdded = false;
//The check is only necessary if the component resource is part of the tree.
if (componentResource.isInView())
{
if (componentResource.getParent() != null &&
componentResource.getParent().getId() != null &&
componentResource.getParent().getId().equals(JAVAX_FACES_LOCATION_PREFIX + target))
{
// We can assume safely that the component is in place, because there is no way to
// put a component resource on a component resource container without call addComponentResource
// so relocation here will not happen.
alreadyAdded = true;
}
else if (componentId != null)
{
for(Iterator<UIComponent> it = componentResources.iterator(); it.hasNext();)
{
UIComponent component = it.next();
if(componentId.equals(component.getId()) && componentResource != component)
{
if (!component.isCachedFacesContext())
{
try
{
component.setCachedFacesContext(context);
it.remove();
}
finally
{
component.setCachedFacesContext(null);
}
}
else
{
it.remove();
}
}
else if (componentResource == component)
{
alreadyAdded = true;
}
}
}
}
else if (componentId != null)
{
for(Iterator<UIComponent> it = componentResources.iterator(); it.hasNext();)
{
UIComponent component = it.next();
if(componentId.equals(component.getId()) && componentResource != component)
{
if (!component.isCachedFacesContext())
{
try
{
component.setCachedFacesContext(context);
it.remove();
}
finally
{
component.setCachedFacesContext(null);
}
}
else
{
it.remove();
}
}
else if (componentResource == component)
{
alreadyAdded = true;
}
}
}
// Add the component resource to the list
if (!alreadyAdded)
{
if (!componentResource.isCachedFacesContext())
{
try
{
componentResource.setCachedFacesContext(context);
componentResources.add(componentResource);
}
finally
{
componentResource.setCachedFacesContext(context);
}
}
else
{
componentResources.add(componentResource);
}
// this is required to make dynamic resource loading possible since JSF 2.3
if (context.getPartialViewContext().isAjaxRequest())
{
boolean isBuildingInitialState
= context.getAttributes().containsKey(StateManager.IS_BUILDING_INITIAL_STATE);
// FaceletViewDeclarationLanguage.isRefreshingTransientBuild(context)
boolean isRefreshTransientBuild
= context.getAttributes().containsKey("org.apache.myfaces.REFRESHING_TRANSIENT_BUILD");
boolean isPostAddToViewEventAfterBuildInitialState =
!isBuildingInitialState || (isBuildingInitialState && isRefreshTransientBuild);
if (isPostAddToViewEventAfterBuildInitialState)
{
try
{
// RequestViewContext requestViewContext = RequestViewContext.getInstance(context);
// requestViewContext.setRenderTarget("head", true, componentResource);
Object requestViewContext = REQUEST_VIEW_CONTEXT_GET_INSTANCE.invoke(null, context);
REQUEST_VIEW_CONTEXT_SET_RENDER_TARGET.invoke(requestViewContext, "head", true, componentResource);
}
catch (Exception e)
{
_getLogger().log(Level.SEVERE, "Could not access RequestViewContext", e);
}
}
}
}
}
/**
* Adds a The phaseListeners attached to ViewRoot.
*/
public void addPhaseListener(PhaseListener phaseListener)
{
if (phaseListener == null)
{
throw new NullPointerException("phaseListener");
}
getStateHelper().add(PropertyKeys.phaseListeners, phaseListener);
}
/**
* @since 2.0
*/
public void broadcastEvents(FacesContext context, PhaseId phaseId)
{
if (_events == null)
{
return;
}
Events events = _getEvents(phaseId);
// Spec. 3.4.2.6 Event Broadcasting:
// Queue one or more additional events, from the same source
// component or a different one, for processing during the
// current lifecycle phase.
// Unfortunately with that requirement it is easy to create infinite loop in processing. One example can be:
//
// public processAction(ActionEvent actionEvent)
// {
// actionEvent = new ActionEvent(actionEvent.getComponent());
// actionEvent.queue();
// }
//
// Thus we iterate here only 15x. If iteration overreachs 15 we output a warning
int loops = 0;
int maxLoops = 15;
Collection<FacesEvent> eventsAborted = new LinkedList<FacesEvent>();
do
{
// First broadcast events that have been queued for PhaseId.ANY_PHASE.
boolean noUnexpectedException = _broadcastAll(context, events.getAnyPhase(), eventsAborted);
if (!noUnexpectedException)
{
return;
}
List<FacesEvent> eventsOnPhase = events.getOnPhase();
if (!eventsAborted.isEmpty())
{
eventsOnPhase.removeAll(eventsAborted);
eventsAborted.clear();
}
noUnexpectedException = _broadcastAll(context, eventsOnPhase, eventsAborted);
if (!noUnexpectedException)
{
return;
}
events = _getEvents(phaseId);
loops++;
} while (events.hasMoreEvents() && loops < maxLoops);
if (loops == maxLoops && events.hasMoreEvents())
{
// broadcast reach maxLoops - probably a infinitive recursion:
Level level = getFacesContext().isProjectStage(ProjectStage.Production)
? Level.FINE
: Level.WARNING;
if (_getLogger().isLoggable(level))
{
List<String> name = new ArrayList<>(events.getAnyPhase().size() + events.getOnPhase().size());
for (FacesEvent facesEvent : events.getAnyPhase())
{
String clientId = facesEvent.getComponent().getClientId(getFacesContext());
name.add(clientId);
}
for (FacesEvent facesEvent : events.getOnPhase())
{
String clientId = facesEvent.getComponent().getClientId(getFacesContext());
name.add(clientId);
}
_getLogger().log(level,
"Event broadcating for PhaseId {0} at UIViewRoot {1} reaches maximal limit, please check " +
"listeners for infinite recursion. Component id: {2}",
new Object [] {phaseId, getViewId(), name});
}
}
}
/**
* Provides a unique id for this component instance.
*/
public String createUniqueId()
{
return createUniqueId(getFacesContext(), null);
}
/**
*
* {@inheritDoc}
*
* @since 2.0
*/
@Override
public String createUniqueId(FacesContext context, String seed)
{
// Generate an identifier for a component. The identifier will be prefixed with
// UNIQUE_ID_PREFIX, and will be unique within this UIViewRoot.
if(seed==null)
{
if (isResourceDependencyUniqueId())
{
Integer uniqueIdCounter = (Integer) getStateHelper().get(
PropertyKeys.resourceDependencyUniqueIdCounter);
uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
getStateHelper().put(PropertyKeys.resourceDependencyUniqueIdCounter, (uniqueIdCounter+1));
if (uniqueIdCounter >= ComponentUtils.UNIQUE_COMPONENT_RD_IDS_SIZE)
{
StringBuilder bld = _getSharedStringBuilder(context);
return bld.append(UNIQUE_ID_PREFIX).append(ComponentUtils.RD_ID_PREFIX).append(uniqueIdCounter).toString();
}
else
{
return ComponentUtils.UNIQUE_COMPONENT_RD_IDS[uniqueIdCounter];
}
}
else
{
Integer uniqueIdCounter = (Integer) getStateHelper().get(PropertyKeys.uniqueIdCounter);
uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1));
if (uniqueIdCounter >= ComponentUtils.UNIQUE_COMPONENT_V_IDS_SIZE)
{
StringBuilder bld = _getSharedStringBuilder(context);
return bld.append(UNIQUE_ID_PREFIX).append(ComponentUtils.V_ID_PREFIX).append(uniqueIdCounter).toString();
}
else
{
return ComponentUtils.UNIQUE_COMPONENT_V_IDS[uniqueIdCounter];
}
}
}
// Optionally, a unique seed value can be supplied by component creators which
// should be included in the generated unique id.
else
{
StringBuilder bld = _getSharedStringBuilder(context);
return bld.append(UNIQUE_ID_PREFIX).append(seed).toString();
}
}
@Override
public void encodeBegin(FacesContext context) throws IOException
{
checkNull(context, "context");
boolean skipPhase = false;
try
{
skipPhase = notifyListeners(context, PhaseId.RENDER_RESPONSE, getBeforePhaseListener(), true);
}
catch (Exception e)
{
// following the spec we have to swallow the exception
_getLogger().log(Level.SEVERE, "Exception while processing phase listener: " + e.getMessage(), e);
}
if (!skipPhase)
{
//prerendering happens, we now publish the prerender view event
//the specs states that the viewroot as source is about to be rendered
//hence we issue the event immediately before publish, if the phase is not skipped
//context.getApplication().publishEvent(context, PreRenderViewEvent.class, this);
//then the view rendering is about to begin
super.encodeBegin(context);
}
else
{
pushComponentToEL(context, this);
}
}
/**
* @since 2.0
*/
@Override
public void encodeChildren(FacesContext context) throws IOException
{
if (context.getResponseComplete())
{
return;
}
PartialViewContext pContext = context.getPartialViewContext();
// If PartialViewContext.isAjaxRequest() returns true
if (pContext.isAjaxRequest())
{
// Perform partial rendering by calling PartialViewContext.processPartial() with PhaseId.RENDER_RESPONSE.
//sectin 13.4.3 of the jsf2 specification
pContext.processPartial(PhaseId.RENDER_RESPONSE);
}
else
{
// If PartialViewContext.isAjaxRequest() returns false
// delegate to super.encodeChildren(javax.faces.context.FacesContext) method.
super.encodeChildren(context);
}
}
@Override
public void encodeEnd(FacesContext context) throws IOException
{
checkNull(context, "context");
if (!context.getResponseComplete())
{
super.encodeEnd(context);
// the call to encodeAll() on every UIViewParameter here is only necessary
// if the current request is _not_ an AJAX request, because if it was an
// AJAX request, the call would already have happened in PartialViewContextImpl and
// would anyway be too late here, because the state would already have been generated
PartialViewContext partialContext = context.getPartialViewContext();
if (!partialContext.isAjaxRequest())
{
ViewDeclarationLanguage vdl
= context.getApplication().getViewHandler().getViewDeclarationLanguage(context, getViewId());
if (vdl != null)
{
// If the current view has view parameters, as indicated by a non-empty
// and non-UnsupportedOperationException throwing
// return from ViewDeclarationLanguage.getViewMetadata(javax.faces.context.FacesContext, String)
ViewMetadata metadata = null;
try
{
metadata = vdl.getViewMetadata(context, getViewId());
}
catch(UnsupportedOperationException e)
{
_getLogger().log(Level.SEVERE, "Exception while obtaining the view metadata: " +
e.getMessage(), e);
}
if (metadata != null)
{
try
{
Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(this);
if(!viewParams.isEmpty())
{
// call UIViewParameter.encodeAll(javax.faces.context.FacesContext) on each parameter.
for(UIViewParameter param : viewParams)
{
param.encodeAll(context);
}
}
}
catch(UnsupportedOperationException e)
{
// If calling getViewParameters() causes UnsupportedOperationException
// to be thrown, the exception must be silently swallowed.
}
}
}
}
}
try
{
notifyListeners(context, PhaseId.RENDER_RESPONSE, getAfterPhaseListener(), false);
}
catch (Exception e)
{
// following the spec we have to swallow the exception
_getLogger().log(Level.SEVERE, "Exception while processing phase listener: " + e.getMessage(), e);
}
}
/**
* MethodExpression pointing to a method that takes a javax.faces.event.PhaseEvent and returns void,
* called after every phase except for restore view.
*
* @return the new afterPhaseListener value
*/
@JSFProperty(returnSignature = "void", methodSignature = "javax.faces.event.PhaseEvent",
jspName = "afterPhase", stateHolder=true)
public MethodExpression getAfterPhaseListener()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.afterPhaseListener);
}
/**
* MethodExpression pointing to a method that takes a javax.faces.event.PhaseEvent and returns void,
* called before every phase except for restore view.
*
* @return the new beforePhaseListener value
*/
@JSFProperty(returnSignature = "void", methodSignature = "javax.faces.event.PhaseEvent",
jspName = "beforePhase", stateHolder=true)
public MethodExpression getBeforePhaseListener()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.beforePhaseListener);
}
/**
* @since 2.0
*/
public List<UIComponent> getComponentResources(FacesContext context, String target)
{
if (target == null)
{
throw new NullPointerException("target");
}
// Locate the facet for the component by calling getFacet() using target as the argument
UIComponent facet = getFacet(target);
/*
// If the facet is not found,
if (facet == null)
{
// create the facet by calling context.getApplication().createComponent()
// using javax.faces.Panel as the argument
facet = context.getApplication().createComponent("javax.faces.Panel");
// Set the id of the facet to be target
facet.setId(target);
// Add the facet to the facets Map using target as the key
getFacets().put(target, facet);
}
// Return the children of the facet
// The API doc indicates that this method should "Return an unmodifiable
// List of UIComponents for the provided target argument."
// and also that "If no children are found for the facet, return Collections.emptyList()."
List<UIComponent> children = facet.getChildren();
return ( children == null ? Collections.<UIComponent>emptyList() : Collections.unmodifiableList(children) );
*/
if (facet != null)
{
if (facet.getChildCount() > 0)
{
return Collections.unmodifiableList(facet.getChildren());
}
else
{
return Collections.<UIComponent>emptyList();
}
}
return Collections.<UIComponent>emptyList();
}
private List<UIComponent> _getComponentResources(FacesContext context, String target)
{
// Locate the facet for the component by calling getFacet() using target as the argument
UIComponent facet = getFacet(target);
// If the facet is not found,
if (facet == null)
{
// create the facet by calling context.getApplication().createComponent()
// using javax.faces.Panel as the argument
facet = context.getApplication().createComponent(context,
"javax.faces.ComponentResourceContainer", null);
// Set the id of the facet to be target
if (target.equals("head"))
{
facet.setId(JAVAX_FACES_LOCATION_HEAD);
}
else if (target.equals("body"))
{
facet.setId(JAVAX_FACES_LOCATION_BODY);
}
else if (target.equals("form"))
{
facet.setId(JAVAX_FACES_LOCATION_FORM);
}
else
{
facet.setId(JAVAX_FACES_LOCATION_PREFIX + target);
}
// From jsr-314-open list it was made clear this facet is transient,
// because all component resources does not change its inner state between
// requests
//
// MYFACES-3047 It was found that resources added using ResourceDependency annotation
// requires to be saved and restored, so it is not possible to mark this facets
// as transient. The previous statement is true only for PSS.
//facet.setTransient(true);
// Add the facet to the facets Map using target as the key
getFacets().put(target, facet);
}
return facet.getChildren();
}
/**
* @since 2.3
* @param context
* @return
*/
public List<UIComponent> getComponentResources(FacesContext context)
{
List<UIComponent> componentResources = new ArrayList<UIComponent>();
componentResources.addAll(_getComponentResources(context, "head"));
componentResources.addAll(_getComponentResources(context, "body"));
componentResources.addAll(_getComponentResources(context, "form"));
return Collections.unmodifiableList(componentResources);
}
@Override
public String getFamily()
{
return COMPONENT_FAMILY;
}
/**
* The locale for this view.
* <p>
* Defaults to the default locale specified in the faces configuration file.
* </p>
*/
@JSFProperty
public Locale getLocale()
{
Object locale = getStateHelper().get(PropertyKeys.locale);
if (locale != null)
{
return (Locale)locale;
}
ValueExpression expression = getValueExpression(PropertyKeys.locale.toString());
if (expression != null)
{
Object veLocale = expression.getValue(getFacesContext().getELContext());
if (veLocale instanceof Locale)
{
return (Locale) veLocale;
}
else
{
return (Locale) LocaleUtils.toLocale(veLocale.toString());
}
}
else
{
locale = getFacesContext().getApplication().getViewHandler().calculateLocale(getFacesContext());
if (locale instanceof Locale)
{
return (Locale)locale;
}
else if (locale instanceof String)
{
return LocaleUtils.toLocale((String)locale);
}
}
return getFacesContext().getApplication().getViewHandler().calculateLocale(getFacesContext());
}
/**
* @since 2.0
*/
public List<PhaseListener> getPhaseListeners()
{
List<PhaseListener> listeners = (List<PhaseListener>) getStateHelper().get(PropertyKeys.phaseListeners);
if (listeners == null)
{
listeners = Collections.emptyList();
}
else
{
listeners = Collections.unmodifiableList(listeners);
}
return listeners;
}
/**
* Defines what renderkit should be used to render this view.
*/
@JSFProperty
public String getRenderKitId()
{
return (String) getStateHelper().eval(PropertyKeys.renderKitId);
}
/**
* @since 2.0
*/
@Override
public boolean getRendersChildren()
{
// Call UIComponentBase.getRendersChildren()
// If PartialViewContext.isAjaxRequest() returns true this method must return true.
PartialViewContext context = getFacesContext().getPartialViewContext();
return (context.isAjaxRequest()) ? true : super.getRendersChildren();
}
/**
* A unique identifier for the "template" from which this view was generated.
* <p>
* Typically this is the filesystem path to the template file, but the exact details are the responsibility of the
* current ViewHandler implementation.
*/
@JSFProperty(tagExcluded = true)
public String getViewId()
{
return (String) getStateHelper().get(PropertyKeys.viewId);
}
/**
* @since 2.0
*/
public Map<String, Object> getViewMap()
{
return this.getViewMap(true);
}
/**
* @since 2.0
*/
public Map<String, Object> getViewMap(boolean create)
{
if (_viewScope == null && create)
{
_viewScope = (Map<String, Object>) ClassUtils.newInstance(VIEW_SCOPE_PROXY_MAP_CLASS);
FacesContext facesContext = getFacesContext();
if (facesContext != null)
{
facesContext.getApplication().publishEvent(facesContext, PostConstructViewMapEvent.class, UIViewRoot.class, this);
}
}
return _viewScope;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isInView()
{
return true;
}
public void processApplication(final FacesContext context)
{
checkNull(context, "context");
_process(context, PhaseId.INVOKE_APPLICATION, null);
}
@Override
public void processDecodes(FacesContext context)
{
checkNull(context, "context");
_process(context, PhaseId.APPLY_REQUEST_VALUES, APPLY_REQUEST_VALUES_PROCESSOR);
}
/**
* @since 2.0
*/
@Override
public void processRestoreState(FacesContext context, Object state)
{
// The default implementation must call UIComponentBase.processRestoreState(javax.faces.context.FacesContext,
// java.lang.Object) from within a try block.
try
{
super.processRestoreState(context, state);
}
finally
{
// The try block must have a finally block that ensures that no FacesEvents remain in the event queue
broadcastEvents(context, PhaseId.RESTORE_VIEW);
}
}
@Override
public void queueEvent(FacesEvent event)
{
checkNull(event, "event");
if (_events == null)
{
_events = new ArrayList<FacesEvent>();
}
_events.add(event);
}
@Override
public void processValidators(FacesContext context)
{
checkNull(context, "context");
_process(context, PhaseId.PROCESS_VALIDATIONS, PROCESS_VALIDATORS_PROCESSOR);
}
@Override
public void processUpdates(FacesContext context)
{
checkNull(context, "context");
_process(context, PhaseId.UPDATE_MODEL_VALUES, UPDATE_MODEL_PROCESSOR);
}
public void setLocale(Locale locale)
{
getStateHelper().put(PropertyKeys.locale, locale );
}
/**
* Invoke view-specific phase listeners, plus an optional EL MethodExpression.
* <p>
* JSF1.2 adds the ability for PhaseListener objects to be added to a UIViewRoot instance, and for
* "beforePhaseListener" and "afterPhaseListener" EL expressions to be defined on the viewroot. This method is
* expected to be called at appropriate times, and will then execute the relevant listener callbacks.
* <p>
* Parameter "listener" may be null. If not null, then it is an EL expression pointing to a user method that will be
* invoked.
* <p>
* Note that the global PhaseListeners are invoked via the Lifecycle implementation, not from this method here.
* <p>
* These PhaseListeners are processed with the same rules as the globally defined PhaseListeners, except
* that any Exceptions, which may occur during the execution of the PhaseListeners, will only be logged
* and not published to the ExceptionHandler.
*/
private boolean notifyListeners(FacesContext context, PhaseId phaseId, MethodExpression listener,
boolean beforePhase)
{
List<PhaseListener> phaseListeners = (List<PhaseListener>) getStateHelper().get(PropertyKeys.phaseListeners);
// Check if any listener was called
boolean listenerCalled = false;
if (listener != null || (phaseListeners != null && !phaseListeners.isEmpty()))
{
// how many listeners do we have? (the MethodExpression listener is counted in either way)
// NOTE: beforePhaseSuccess[0] always refers to the MethodExpression listener
int listenerCount = (phaseListeners != null ? phaseListeners.size() + 1 : 1);
boolean[] beforePhaseSuccess;
if (beforePhase)
{
beforePhaseSuccess = new boolean[listenerCount];
_getListenerSuccessMap().put(phaseId, beforePhaseSuccess);
}
else
{
// afterPhase - get beforePhaseSuccess from the Map
beforePhaseSuccess = _getListenerSuccessMap().get(phaseId);
if (beforePhaseSuccess == null)
{
// no Map available - assume that everything went well
beforePhaseSuccess = new boolean[listenerCount];
Arrays.fill(beforePhaseSuccess, true);
}
}
PhaseEvent event = createEvent(context, phaseId);
// only invoke the listener if we are in beforePhase
// or if the related before PhaseListener finished without an Exception
if (listener != null && (beforePhase || beforePhaseSuccess[0]))
{
listenerCalled = true;
try
{
listener.invoke(context.getELContext(), new Object[] { event });
beforePhaseSuccess[0] = true;
}
catch (Throwable t)
{
beforePhaseSuccess[0] = false; // redundant - for clarity
_getLogger().log(Level.SEVERE, "An Exception occured while processing " +
listener.getExpressionString() +
" in Phase " + phaseId, t);
if (beforePhase)
{
return context.getResponseComplete() ||
(context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
}
}
}
else if (beforePhase)
{
// there is no beforePhase MethodExpression listener
beforePhaseSuccess[0] = true;
}
if (phaseListeners != null && !phaseListeners.isEmpty())
{
if (beforePhase)
{
// process listeners in ascending order
for (int i = 0; i < beforePhaseSuccess.length - 1; i++)
{
PhaseListener phaseListener;
try
{
phaseListener = phaseListeners.get(i);
}
catch (IndexOutOfBoundsException e)
{
// happens when a PhaseListener removes another PhaseListener
// from UIViewRoot in its beforePhase method
throw new IllegalStateException("A PhaseListener must not remove " +
"PhaseListeners from UIViewRoot.");
}
PhaseId listenerPhaseId = phaseListener.getPhaseId();
if (phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
{
listenerCalled = true;
try
{
phaseListener.beforePhase(event);
beforePhaseSuccess[i + 1] = true;
}
catch (Throwable t)
{
beforePhaseSuccess[i + 1] = false; // redundant - for clarity
_getLogger().log(Level.SEVERE, "An Exception occured while processing the " +
"beforePhase method of PhaseListener " + phaseListener +
" in Phase " + phaseId, t);
if (shouldViewRootPhaseListenerQueuesExceptions(context))
{
publishException (context, t, phaseId,
ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY);
}
return context.getResponseComplete() ||
(context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
}
}
}
}
else
{
// afterPhase
// process listeners in descending order
for (int i = beforePhaseSuccess.length - 1; i > 0; i--)
{
PhaseListener phaseListener;
try
{
phaseListener = phaseListeners.get(i - 1);
}
catch (IndexOutOfBoundsException e)
{
// happens when a PhaseListener removes another PhaseListener
// from UIViewRoot in its beforePhase or afterPhase method
throw new IllegalStateException("A PhaseListener must not remove " +
"PhaseListeners from UIViewRoot.");
}
PhaseId listenerPhaseId = phaseListener.getPhaseId();
if ((phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
&& beforePhaseSuccess[i])
{
listenerCalled = true;
try
{
phaseListener.afterPhase(event);
}
catch (Throwable t)
{
logger.log(Level.SEVERE, "An Exception occured while processing the " +
"afterPhase method of PhaseListener " + phaseListener +
" in Phase " + phaseId, t);
if (shouldViewRootPhaseListenerQueuesExceptions(context))
{
publishException (context, t, phaseId,
ExceptionQueuedEventContext.IN_AFTER_PHASE_KEY);
}
}
}
}
}
}
}
// The spec javadoc says "... Upon return from the listener, call FacesContext.getResponseComplete()
// and FacesContext.getRenderResponse(). If either return true set the internal state flag to true. ..."
// and later it says:
// "... Execute any processing for this phase if the internal state flag was not set. ..."
// But after some testing it seems if the internal state flag is not set, the check is not done and the
// phase is not skipped. The only exception is in render response phase.
if (listenerCalled)
{
if (beforePhase)
{
return context.getResponseComplete() ||
(context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
}
else
{
return context.getResponseComplete() || context.getRenderResponse();
}
}
else
{
if (beforePhase)
{
if (PhaseId.RENDER_RESPONSE.equals(phaseId))
{
return context.getResponseComplete();
}
else
{
// Don't check and don't skip
return false;
}
}
else
{
// Note if is afterPhase the return value is not relevant.
return context.getResponseComplete() || context.getRenderResponse();
}
}
}
private PhaseEvent createEvent(FacesContext context, PhaseId phaseId)
{
if (_lifecycle == null)
{
LifecycleFactory factory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
String id = context.getExternalContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
if (id == null)
{
id = LifecycleFactory.DEFAULT_LIFECYCLE;
}
_lifecycle = factory.getLifecycle(id);
}
return new PhaseEvent(context, phaseId, _lifecycle);
}
/**
* Broadcast all events in the specified collection, stopping the at any time an AbortProcessingException
* is thrown.
*
* @param context the current JSF context
* @param events the events to broadcast
* @return
*
* @return <code>true</code> if the broadcast was completed without unexpected abortion/exception,
* <code>false</code> otherwise
*/
private boolean _broadcastAll(FacesContext context,
List<? extends FacesEvent> events,
Collection<FacesEvent> eventsAborted)
{
assert events != null;
for (int i = 0; i < events.size(); i++)
{
FacesEvent event = events.get(i);
UIComponent source = event.getComponent();
UIComponent compositeParent = UIComponent.getCompositeComponentParent(source);
if (compositeParent != null)
{
pushComponentToEL(context, compositeParent);
}
// Push the source as the current component
pushComponentToEL(context, source);
try
{
// Actual event broadcasting
if (!source.isCachedFacesContext())
{
try
{
source.setCachedFacesContext(context);
source.broadcast(event);
}
finally
{
source.setCachedFacesContext(null);
}
}
else
{
source.broadcast(event);
}
}
catch (Exception e)
{
Throwable cause = e;
AbortProcessingException ape = null;
do
{
if (cause != null && cause instanceof AbortProcessingException)
{
ape = (AbortProcessingException) cause;
break;
}
cause = cause.getCause();
}
while (cause != null);
// for any other exception publish ExceptionQueuedEvent
// publish the Exception to be handled by the ExceptionHandler
// to publish or to not publish APE? That is the question : MYFACES-3199. We publish it,
// because user can handle it in custom exception handler then.
if (ape != null)
{
e = ape;
}
ExceptionQueuedEventContext exceptionContext
= new ExceptionQueuedEventContext(context, e, source, context.getCurrentPhaseId());
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, exceptionContext);
if (ape != null)
{
// APE found, abortion for this event only
eventsAborted.add(event);
}
else
{
// We can't continue broadcast processing if other exception is thrown:
return false;
}
}
finally
{
// Restore the current component
source.popComponentFromEL(context);
if (compositeParent != null)
{
compositeParent.popComponentFromEL(context);
}
}
}
return true;
}
private void clearEvents()
{
_events = null;
}
private void checkNull(Object value, String valueLabel)
{
if (value == null)
{
throw new NullPointerException(valueLabel + " is null");
}
}
public void setRenderKitId(String renderKitId)
{
getStateHelper().put(PropertyKeys.renderKitId, renderKitId );
}
/**
* DO NOT USE.
* <p>
* This inherited property is disabled. Although this class extends a base-class that defines a read/write rendered
* property, this particular subclass does not support setting it. Yes, this is broken OO design: direct all
* complaints to the JSF spec group.
*/
@Override
@JSFProperty(tagExcluded = true)
public void setRendered(boolean state)
{
// Call parent method due to TCK problems
super.setRendered(state);
// throw new UnsupportedOperationException();
}
@JSFProperty(tagExcluded=true)
@Override
public void setId(String id)
{
super.setId(id);
}
/**
* {@inheritDoc}
*/
@Override
public void setInView(boolean isInView)
{
// no-op view root is always in view
}
public void removeComponentResource(FacesContext context, UIComponent componentResource)
{
removeComponentResource(context, componentResource, null);
}
public void removeComponentResource(FacesContext context, UIComponent componentResource, String target)
{
// If the target argument is null
if (target == null)
{
// Look for a target attribute on the component
target = (String)componentResource.getAttributes().get("target");
// If there is no target attribute
if (target == null)
{
// Set target to be the default value head
target = "head";
}
}
// Call getComponentResources to obtain the child list for the given target.
//List<UIComponent> componentResources = getComponentResources(context, target);
UIComponent facet = getFacet(target);
if (facet != null)
{
//Only if the facet is found it is possible to remove the resource,
//otherwise nothing should happen (call to getComponentResource trigger
//creation of facet)
// Remove the component resource from the child list
facet.getChildren().remove(componentResource);
}
}
public void setViewId(String viewId)
{
// It really doesn't make much sense to allow null here.
// However the TCK does not check for it, and sun's implementation
// allows it so here we allow it too.
getStateHelper().put(PropertyKeys.viewId, viewId );
}
/**
* Removes a The phaseListeners attached to ViewRoot.
*/
public void removePhaseListener(PhaseListener phaseListener)
{
if (phaseListener == null)
{
return;
}
getStateHelper().remove(PropertyKeys.phaseListeners, phaseListener);
}
/**
* Sets
*
* @param beforePhaseListener
* the new beforePhaseListener value
*/
public void setBeforePhaseListener(MethodExpression beforePhaseListener)
{
getStateHelper().put(PropertyKeys.beforePhaseListener, beforePhaseListener);
}
/**
* Sets
*
* @param afterPhaseListener
* the new afterPhaseListener value
*/
public void setAfterPhaseListener(MethodExpression afterPhaseListener)
{
getStateHelper().put(PropertyKeys.afterPhaseListener, afterPhaseListener);
}
/**
* @return the clearTransientMapOnSaveState
*/
int getResetSaveStateMode()
{
return _resetSaveStateMode;
}
/**
* @param clearTransientMapOnSaveState the clearTransientMapOnSaveState to set
*/
void setResetSaveStateMode(int clearTransientMapOnSaveState)
{
this._resetSaveStateMode = clearTransientMapOnSaveState;
}
@Override
public Map<String, Object> getAttributes()
{
if (_attributesMap == null)
{
_attributesMap = new _ViewAttributeMap(this, super.getAttributes());
}
return _attributesMap;
}
/**
* @since 2.2
* @param context
* @param clientIds
*/
public void resetValues(FacesContext context, java.util.Collection<java.lang.String> clientIds)
{
VisitContext visitContext = (VisitContext) VisitContext.createVisitContext(context, clientIds, null);
this.visitTree(visitContext, ResetValuesCallback.INSTANCE);
}
/**
* Indicates if the component is created when facelets builds the view and
* is caused by the presence of a ResourceDependency annotation.
*
* @return the _resourceDependencyUniqueId
*/
boolean isResourceDependencyUniqueId()
{
return _resourceDependencyUniqueId;
}
void setResourceDependencyUniqueId(boolean resourceDependencyUniqueId)
{
this._resourceDependencyUniqueId = resourceDependencyUniqueId;
}
enum PropertyKeys
{
afterPhaseListener
, beforePhaseListener
, phaseListeners
, locale
, renderKitId
, viewId
, uniqueIdCounter
, resourceDependencyUniqueIdCounter
}
@Override
public Object saveState(FacesContext facesContext)
{
if (getResetSaveStateMode() == RESET_MODE_SOFT)
{
// Clear view listeners.
if (_systemEventListeners != null)
{
_systemEventListeners.clear();
}
if (_events != null)
{
_events.clear();
}
if (listenerSuccessMap != null)
{
listenerSuccessMap.clear();
}
_restoreViewScopeStateCalled = false;
}
if (getResetSaveStateMode() == RESET_MODE_HARD)
{
// Clear view listeners.
if (_systemEventListeners != null)
{
_systemEventListeners.clear();
}
if (_events != null)
{
_events.clear();
}
if (listenerSuccessMap != null)
{
listenerSuccessMap.clear();
}
if (_viewScope != null)
{
if (VIEW_SCOPE_PROXY_MAP_CLASS.isInstance(_viewScope))
{
_viewScope = null;
}
else
{
_viewScope.clear();
}
}
_restoreViewScopeStateCalled = false;
}
if (initialStateMarked())
{
Object parentSaved = super.saveState(facesContext);
if (_viewScope != null &&
Boolean.TRUE.equals(facesContext.getAttributes().get(
SKIP_VIEW_MAP_SAVE_STATE)))
{
if (parentSaved == null)
{
return null;
}
return new Object[]{parentSaved, null};
}
if (parentSaved == null && _viewScope == null)
{
//No values
return null;
}
else if (parentSaved == null && _viewScope != null && _viewScope.isEmpty()
&& !(_viewScope instanceof StateHolder) )
{
//Empty view scope, no values
return null;
}
Object[] values = new Object[2];
values[0] = parentSaved;
values[1] = saveAttachedState(facesContext,_viewScope);
return values;
}
else
{
if (_viewScope != null &&
Boolean.TRUE.equals(facesContext.getAttributes().get(
SKIP_VIEW_MAP_SAVE_STATE)))
{
return new Object[]{super.saveState(facesContext), null};
}
Object[] values = new Object[2];
values[0] = super.saveState(facesContext);
values[1] = saveAttachedState(facesContext,_viewScope);
return values;
}
}
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext facesContext, Object state)
{
if (state == null)
{
return;
}
Object[] values = (Object[])state;
super.restoreState(facesContext,values[0]);
// JSF 2.2 spec says that restoreViewScopeState can be called but only if
// StateManagementStrategy is used. If that's not the case (JSF 1.2 state saving),
// restoreViewScopeState could not be called, so this code should avoid restore
// the state twice.
if (!_restoreViewScopeStateCalled)
{
_viewScope = (Map<String, Object>) restoreAttachedState(facesContext, values[1]);
}
else
{
_restoreViewScopeStateCalled = false;
}
}
/**
* @since 2.2
* @param facesContext
* @param state
*/
public void restoreViewScopeState(FacesContext facesContext, Object state)
{
if (state == null)
{
return;
}
//StateManagementStrategy says "... obtain the state of the UIViewRoot from the
// state Object returned from ResponseStateManager.getState(javax.faces.context.FacesContext,
// java.lang.String) and pass that to UIViewRoot.restoreViewScopeState(
// javax.faces.context.FacesContext, java.lang.Object).
// Note restoreState() will be called later, and it will restore the view. If
// we restore the component state here, later it could be a problem in the later
// restoreState() call, because the initial state will not be the same.
Object[] values = (Object[])state;
_viewScope = (Map<String, Object>) restoreAttachedState(facesContext, values[1]);
_restoreViewScopeStateCalled = true;
}
public List<SystemEventListener> getViewListenersForEventClass(Class<? extends SystemEvent> systemEvent)
{
checkNull(systemEvent, "systemEvent");
if (_systemEventListeners == null)
{
return null;
}
return _systemEventListeners.get(systemEvent);
}
public void subscribeToViewEvent(Class<? extends SystemEvent> systemEvent, SystemEventListener listener)
{
List<SystemEventListener> listeners;
checkNull(systemEvent, "systemEvent");
checkNull(listener, "listener");
if (_systemEventListeners == null)
{
_systemEventListeners = new HashMap<>(4, 1f);
}
listeners = _systemEventListeners.get(systemEvent);
if (listeners == null)
{
listeners = new ArrayList<SystemEventListener>();
_systemEventListeners.put(systemEvent, listeners);
}
listeners.add (listener);
}
public void unsubscribeFromViewEvent(Class<? extends SystemEvent> systemEvent, SystemEventListener listener)
{
List<SystemEventListener> listeners;
checkNull (systemEvent, "systemEvent");
checkNull (listener, "listener");
if (_systemEventListeners == null)
{
return;
}
listeners = _systemEventListeners.get(systemEvent);
if (listeners != null)
{
listeners.remove(listener);
}
}
/**
* Process the specified phase by calling PhaseListener.beforePhase for every phase listeners defined on this
* view root, then calling the process method of the processor, broadcasting relevant events and finally
* notifying the afterPhase method of every phase listeners registered on this view root.
*
* @param context
* @param phaseId
* @param processor
* @param broadcast
*
* @return
*/
private boolean _process(FacesContext context, PhaseId phaseId, PhaseProcessor processor)
{
RuntimeException processingException = null;
try
{
if (!notifyListeners(context, phaseId, getBeforePhaseListener(), true))
{
try
{
if (processor != null)
{
processor.process(context, this);
}
broadcastEvents(context, phaseId);
}
catch (RuntimeException re)
{
// catch any Exception that occures while processing the phase
// to ensure invocation of the afterPhase methods
processingException = re;
}
}
}
finally
{
if (context.getRenderResponse() || context.getResponseComplete())
{
clearEvents();
}
}
boolean retVal = notifyListeners(context, phaseId, getAfterPhaseListener(), false);
if (processingException == null)
{
return retVal;
}
else
{
throw processingException;
}
}
private void _processDecodesDefault(FacesContext context)
{
super.processDecodes(context);
}
private void _processUpdatesDefault(FacesContext context)
{
super.processUpdates(context);
}
private void _processValidatorsDefault(FacesContext context)
{
super.processValidators(context);
}
/**
* Gathers all event for current and ANY phase
* @param phaseId current phase id
*/
private Events _getEvents(PhaseId phaseId)
{
// Gather the events and purge the event list to prevent concurrent modification during broadcasting
int size = _events.size();
List<FacesEvent> anyPhase = new ArrayList<>(size);
List<FacesEvent> onPhase = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
FacesEvent event = _events.get(i);
if (event.getPhaseId().equals(PhaseId.ANY_PHASE))
{
anyPhase.add(event);
_events.remove(i);
size--;
i--;
}
else if (event.getPhaseId().equals(phaseId))
{
onPhase.add(event);
_events.remove(i);
size--;
i--;
}
}
return new Events(anyPhase, onPhase);
}
private Logger _getLogger()
{
if (logger == null)
{
logger = Logger.getLogger(UIViewRoot.class.getName());
}
return logger;
}
private Map<PhaseId, boolean[]> _getListenerSuccessMap()
{
if (listenerSuccessMap == null)
{
listenerSuccessMap = new HashMap<>(PhaseId.VALUES.size(), 1f);
}
return listenerSuccessMap;
}
private static interface PhaseProcessor
{
public void process(FacesContext context, UIViewRoot root);
}
private static class ApplyRequestValuesPhaseProcessor implements PhaseProcessor
{
@Override
public void process(FacesContext context, UIViewRoot root)
{
PartialViewContext pvc = context.getPartialViewContext();
// Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
// with PhaseId.UPDATE_MODEL_VALUES if:
// * PartialViewContext.isPartialRequest() returns true and we don't have a request to process all
// components in the view (PartialViewContext.isExecuteAll() returns false)
//section 13.4.2 from the JSF2 spec also see https://issues.apache.org/jira/browse/MYFACES-2119
if (pvc.isPartialRequest() && !pvc.isExecuteAll())
{
pvc.processPartial(PhaseId.APPLY_REQUEST_VALUES);
}
// Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
// if one of the following conditions are met:
// * PartialViewContext.isPartialRequest() returns true and we have a request to process all components
// in the view (PartialViewContext.isExecuteAll() returns true)
// * PartialViewContext.isPartialRequest() returns false
else
{
root._processDecodesDefault(context);
}
}
}
private static class ProcessValidatorPhaseProcessor implements PhaseProcessor
{
@Override
public void process(FacesContext context, UIViewRoot root)
{
PartialViewContext pvc = context.getPartialViewContext();
// Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
// with PhaseId.UPDATE_MODEL_VALUES if:
// PartialViewContext.isPartialRequest() returns true and we don't have a request to process all components
// in the view (PartialViewContext.isExecuteAll() returns false)
//section 13.4.2 from the JSF2 spec also see https://issues.apache.org/jira/browse/MYFACES-2119
if (pvc.isPartialRequest() && !pvc.isExecuteAll())
{
pvc.processPartial(PhaseId.PROCESS_VALIDATIONS);
}
// Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
// if one of the following conditions are met:
// * PartialViewContext.isPartialRequest() returns true and we have a request to process all components
// in the view (PartialViewContext.isExecuteAll() returns true)
// * PartialViewContext.isPartialRequest() returns false
else
{
root._processValidatorsDefault(context);
}
}
}
private static class UpdateModelPhaseProcessor implements PhaseProcessor
{
@Override
public void process(FacesContext context, UIViewRoot root)
{
PartialViewContext pvc = context.getPartialViewContext();
// Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
// with PhaseId.UPDATE_MODEL_VALUES if:
// * PartialViewContext.isPartialRequest() returns true and we don't have a request to process
// all components in the view (PartialViewContext.isExecuteAll() returns false)
//section 13.4.2 from the JSF2 spec also see https://issues.apache.org/jira/browse/MYFACES-2119
if (pvc.isPartialRequest() && !pvc.isExecuteAll())
{
pvc.processPartial(PhaseId.UPDATE_MODEL_VALUES);
}
// Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
// if one of the following conditions are met:
// * PartialViewContext.isPartialRequest() returns true and we have a request to process all components
// in the view (PartialViewContext.isExecuteAll() returns true)
// * PartialViewContext.isPartialRequest() returns false
else
{
root._processUpdatesDefault(context);
}
}
}
/**
* Agregates events for ANY_PHASE and current phase
*/
private static class Events
{
private final List<FacesEvent> _anyPhase;
private final List<FacesEvent> _onPhase;
public Events(List<FacesEvent> anyPhase, List<FacesEvent> onPhase)
{
super();
this._anyPhase = anyPhase;
this._onPhase = onPhase;
}
public boolean hasMoreEvents()
{
return (_anyPhase != null && _anyPhase.size() > 0) || (_onPhase != null && _onPhase.size() > 0);
}
public List<FacesEvent> getAnyPhase()
{
return _anyPhase;
}
public List<FacesEvent> getOnPhase()
{
return _onPhase;
}
}
private static class ResetValuesCallback implements VisitCallback
{
private static final ResetValuesCallback INSTANCE = new ResetValuesCallback();
@Override
public VisitResult visit(VisitContext context, UIComponent target)
{
if (target instanceof EditableValueHolder)
{
((EditableValueHolder)target).resetValue();
}
return VisitResult.ACCEPT;
}
}
private void publishException(FacesContext facesContext, Throwable e, PhaseId phaseId, String key)
{
ExceptionQueuedEventContext context = new ExceptionQueuedEventContext (facesContext, e, null, phaseId);
context.getAttributes().put (key, Boolean.TRUE);
facesContext.getApplication().publishEvent (facesContext, ExceptionQueuedEvent.class, context);
}
private boolean shouldViewRootPhaseListenerQueuesExceptions(FacesContext context)
{
ExternalContext ec = context.getExternalContext();
Boolean alwaysPerformValidationWhenRequiredTrue = (Boolean) ec.getApplicationMap().get(
VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME);
if (alwaysPerformValidationWhenRequiredTrue == null)
{
String param = ec.getInitParameter(VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME);
// null means the same as auto.
if (param == null)
{
param = "false";
}
else
{
// The environment variables are case insensitive.
param = param.toLowerCase();
}
if (param.equals("true"))
{
alwaysPerformValidationWhenRequiredTrue = true;
}
else
{
alwaysPerformValidationWhenRequiredTrue = false;
}
// cache the parsed value
ec.getApplicationMap().put(VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME,
alwaysPerformValidationWhenRequiredTrue);
}
return alwaysPerformValidationWhenRequiredTrue;
}
}