com.sun.faces.application.view.FaceletViewHandlingStrategy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jakarta.faces Show documentation
Show all versions of jakarta.faces Show documentation
EE4J Compatible Implementation for Jakarta Faces API
The newest version!
/*
* Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.faces.application.view;
import static com.sun.faces.RIConstants.DYNAMIC_COMPONENT;
import static com.sun.faces.RIConstants.FACELETS_ENCODING_KEY;
import static com.sun.faces.RIConstants.FLOW_DEFINITION_ID_SUFFIX;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsBufferSize;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsViewMappings;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.StateSavingMethod;
import static com.sun.faces.context.StateContext.getStateContext;
import static com.sun.faces.facelets.tag.ui.UIDebug.debugRequest;
import static com.sun.faces.renderkit.RenderKitUtils.getResponseStateManager;
import static com.sun.faces.util.ComponentStruct.ADD;
import static com.sun.faces.util.ComponentStruct.REMOVE;
import static com.sun.faces.util.RequestStateManager.FACELET_FACTORY;
import static com.sun.faces.util.Util.getDOCTYPEFromFacesContextAttributes;
import static com.sun.faces.util.Util.getXMLDECLFromFacesContextAttributes;
import static com.sun.faces.util.Util.isEmpty;
import static com.sun.faces.util.Util.isViewIdExactMappedToFacesServlet;
import static com.sun.faces.util.Util.isViewPopulated;
import static com.sun.faces.util.Util.notNull;
import static com.sun.faces.util.Util.saveDOCTYPEToFacesContextAttributes;
import static com.sun.faces.util.Util.saveXMLDECLToFacesContextAttributes;
import static com.sun.faces.util.Util.setViewPopulated;
import static com.sun.faces.util.Util.split;
import static jakarta.faces.FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY;
import static jakarta.faces.application.ProjectStage.Development;
import static jakarta.faces.application.Resource.COMPONENT_RESOURCE_KEY;
import static jakarta.faces.application.StateManager.IS_BUILDING_INITIAL_STATE;
import static jakarta.faces.application.StateManager.STATE_SAVING_METHOD_SERVER;
import static jakarta.faces.application.ViewHandler.DEFAULT_FACELETS_SUFFIX;
import static jakarta.faces.application.ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME;
import static jakarta.faces.component.UIComponent.BEANINFO_KEY;
import static jakarta.faces.component.UIComponent.COMPOSITE_FACET_NAME;
import static jakarta.faces.component.UIComponent.VIEW_LOCATION_KEY;
import static jakarta.faces.component.UIViewRoot.COMPONENT_TYPE;
import static jakarta.faces.view.AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY;
import static jakarta.faces.view.facelets.FaceletContext.FACELET_CONTEXT_KEY;
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static java.lang.Boolean.TRUE;
import static java.util.Arrays.stream;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.context.StateContext;
import com.sun.faces.facelets.compiler.FaceletDoctype;
import com.sun.faces.facelets.el.ContextualCompositeMethodExpression;
import com.sun.faces.facelets.el.VariableMapperWrapper;
import com.sun.faces.facelets.impl.DefaultFaceletFactory;
import com.sun.faces.facelets.impl.XMLFrontMatterSaver;
import com.sun.faces.facelets.tag.composite.CompositeComponentBeanInfo;
import com.sun.faces.facelets.tag.faces.CompositeComponentTagHandler;
import com.sun.faces.facelets.tag.ui.UIDebug;
import com.sun.faces.renderkit.RenderKitUtils;
import com.sun.faces.renderkit.html_basic.DoctypeRenderer;
import com.sun.faces.util.Cache;
import com.sun.faces.util.ComponentStruct;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.HtmlUtils;
import com.sun.faces.util.RequestStateManager;
import com.sun.faces.util.Util;
import jakarta.el.ELContext;
import jakarta.el.ExpressionFactory;
import jakarta.el.MethodExpression;
import jakarta.el.ValueExpression;
import jakarta.el.VariableMapper;
import jakarta.faces.FacesException;
import jakarta.faces.FactoryFinder;
import jakarta.faces.application.Resource;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.application.ViewVisitOption;
import jakarta.faces.component.ActionSource;
import jakarta.faces.component.Doctype;
import jakarta.faces.component.EditableValueHolder;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIPanel;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.component.html.HtmlDoctype;
import jakarta.faces.component.visit.VisitContext;
import jakarta.faces.component.visit.VisitResult;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.event.ActionEvent;
import jakarta.faces.event.MethodExpressionActionListener;
import jakarta.faces.event.MethodExpressionValueChangeListener;
import jakarta.faces.event.PostAddToViewEvent;
import jakarta.faces.event.ValueChangeEvent;
import jakarta.faces.render.RenderKit;
import jakarta.faces.validator.MethodExpressionValidator;
import jakarta.faces.view.ActionSourceAttachedObjectHandler;
import jakarta.faces.view.ActionSourceAttachedObjectTarget;
import jakarta.faces.view.AttachedObjectHandler;
import jakarta.faces.view.AttachedObjectTarget;
import jakarta.faces.view.BehaviorHolderAttachedObjectHandler;
import jakarta.faces.view.BehaviorHolderAttachedObjectTarget;
import jakarta.faces.view.EditableValueHolderAttachedObjectHandler;
import jakarta.faces.view.EditableValueHolderAttachedObjectTarget;
import jakarta.faces.view.StateManagementStrategy;
import jakarta.faces.view.ValueHolderAttachedObjectHandler;
import jakarta.faces.view.ValueHolderAttachedObjectTarget;
import jakarta.faces.view.ViewDeclarationLanguage;
import jakarta.faces.view.ViewDeclarationLanguageFactory;
import jakarta.faces.view.ViewMetadata;
import jakarta.faces.view.facelets.Facelet;
import jakarta.faces.view.facelets.FaceletContext;
import jakarta.servlet.http.HttpSession;
/**
* This {@link ViewHandlingStrategy} handles Facelets/PDL-based views.
*/
public class FaceletViewHandlingStrategy extends ViewHandlingStrategy {
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
private ViewDeclarationLanguageFactory vdlFactory;
private DefaultFaceletFactory faceletFactory;
// Array of viewId extensions that should be handled by Facelets
private String[] extensionsArray;
// Array of viewId prefixes that should be handled by Facelets
private String[] prefixesArray;
public static final String IS_BUILDING_METADATA = FaceletViewHandlingStrategy.class.getName() + ".IS_BUILDING_METADATA";
public static final String RESOURCE_LIBRARY_CONTRACT_DATA_STRUCTURE_KEY = FaceletViewHandlingStrategy.class.getName()
+ ".RESOURCE_LIBRARY_CONTRACT_DATA_STRUCTURE";
private final MethodRetargetHandlerManager retargetHandlerManager = new MethodRetargetHandlerManager();
private int responseBufferSize;
private Cache metadataCache;
private Map> contractMappings;
// ------------------------------------------------------------ Constructors
public FaceletViewHandlingStrategy() {
initialize();
}
// ------------------------------------------------------------ Constructors
public static boolean isBuildingMetadata(FacesContext context) {
return context.getAttributes().containsKey(IS_BUILDING_METADATA);
}
// ------------------------------------ Methods from ViewDeclarationLanguage
/**
*
* If {@link UIDebug#debugRequest(jakarta.faces.context.FacesContext)}} is true
, simply return a new
* UIViewRoot(), otherwise, call the default logic.
*
*
* @see ViewDeclarationLanguage#restoreView(jakarta.faces.context.FacesContext, java.lang.String)
*/
@Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
notNull("context", context);
notNull("viewId", viewId);
if (UIDebug.debugRequest(context)) {
context.getApplication().createComponent(COMPONENT_TYPE);
}
// Check if we are stateless
if (isStateless(context, viewId)) {
try {
context.setProcessingEvents(true);
ViewDeclarationLanguage vdl = vdlFactory.getViewDeclarationLanguage(viewId);
UIViewRoot viewRoot = vdl.createView(context, viewId);
context.setViewRoot(viewRoot);
vdl.buildView(context, viewRoot);
if (!viewRoot.isTransient()) {
throw new FacesException("Unable to restore view " + viewId);
}
return viewRoot;
} catch (IOException ioe) {
throw new FacesException(ioe);
}
}
if (getStateContext(context).isPartialStateSaving(context, viewId)) {
try {
context.setProcessingEvents(false);
ViewDeclarationLanguage vdl = vdlFactory.getViewDeclarationLanguage(viewId);
UIViewRoot viewRoot = vdl.getViewMetadata(context, viewId).createMetadataView(context);
context.setViewRoot(viewRoot);
Object[] rawState = (Object[]) RenderKitUtils.getResponseStateManager(context, context.getApplication().getViewHandler().calculateRenderKitId(context)).getState(context, viewId);
if (rawState != null) {
@SuppressWarnings("unchecked")
Map state = (Map) rawState[1];
if (state != null) {
String clientId = viewRoot.getClientId(context);
Object stateObj = state.get(clientId);
if (stateObj != null) {
context.getAttributes().put("com.sun.faces.application.view.restoreViewScopeOnly", true);
viewRoot.restoreState(context, stateObj);
context.getAttributes().remove("com.sun.faces.application.view.restoreViewScopeOnly");
}
}
}
context.setProcessingEvents(true);
vdl.buildView(context, viewRoot);
} catch (IOException ioe) {
throw new FacesException(ioe);
}
}
UIViewRoot root = super.restoreView(context, viewId);
setResourceLibraryContracts(context, viewId);
startTrackViewModifications(context, root);
return root;
}
@Override
public ViewMetadata getViewMetadata(FacesContext context, String viewId) {
notNull("context", context);
notNull("viewId", viewId);
return new ViewMetadataImpl(viewId);
}
@Override
public UIViewRoot createView(FacesContext ctx, String viewId) {
notNull("context", ctx);
notNull("viewId", viewId);
if (debugRequest(ctx)) {
UIViewRoot root = (UIViewRoot) ctx.getApplication().createComponent(COMPONENT_TYPE);
root.setViewId(viewId);
return root;
}
UIViewRoot result = super.createView(ctx, viewId);
setResourceLibraryContracts(ctx, viewId);
return result;
}
/**
* @see ViewDeclarationLanguage#buildView(FacesContext, UIViewRoot)
*/
@Override
public void buildView(FacesContext ctx, UIViewRoot view) throws IOException {
StateContext stateCtx = StateContext.getStateContext(ctx);
if (isViewPopulated(ctx, view)) {
Facelet facelet = faceletFactory.getFacelet(ctx, view.getViewId());
// Disable events from being intercepted by the StateContext by
// virute of re-applying the handlers.
try {
stateCtx.setTrackViewModifications(false);
facelet.apply(ctx, view);
reapplyDynamicActions(ctx);
if (stateCtx.isPartialStateSaving(ctx, view.getViewId())) {
markInitialStateIfNotMarked(view);
}
} finally {
stateCtx.setTrackViewModifications(true);
}
return;
}
view.setViewId(view.getViewId());
LOGGER.log(FINE, () -> "Building View: " + view.getViewId());
if (faceletFactory == null) {
faceletFactory = ApplicationAssociate.getInstance(ctx.getExternalContext()).getFaceletFactory();
}
RequestStateManager.set(ctx, FACELET_FACTORY, faceletFactory);
Facelet facelet = faceletFactory.getFacelet(ctx, view.getViewId());
// populate UIViewRoot
try {
ctx.getAttributes().put(IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
stateCtx.setTrackViewModifications(false);
facelet.apply(ctx, view);
if (facelet instanceof XMLFrontMatterSaver) {
XMLFrontMatterSaver frontMatterSaver = (XMLFrontMatterSaver) facelet;
Doctype doctype = frontMatterSaver.getSavedDoctype();
if (doctype != null) {
saveDOCTYPEToFacesContextAttributes(doctype);
}
String XMLDECL = frontMatterSaver.getSavedXMLDecl();
if (XMLDECL != null) {
saveXMLDECLToFacesContextAttributes(XMLDECL);
}
}
FaceletDoctype doctype = (FaceletDoctype) getDOCTYPEFromFacesContextAttributes(ctx);
if (doctype != null) {
HtmlDoctype htmlDoctype = new HtmlDoctype();
htmlDoctype.setRootElement(doctype.getRootElement());
htmlDoctype.setPublic(doctype.getPublic());
htmlDoctype.setSystem(doctype.getSystem());
view.setDoctype(htmlDoctype);
}
if (!stateCtx.isPartialStateSaving(ctx, view.getViewId())) {
reapplyDynamicActions(ctx);
}
startTrackViewModifications(ctx, view);
} finally {
ctx.getAttributes().remove(IS_BUILDING_INITIAL_STATE);
}
ctx.getApplication().publishEvent(ctx, PostAddToViewEvent.class, UIViewRoot.class, view);
markInitialState(ctx, view);
setViewPopulated(ctx, view);
}
/**
* @see jakarta.faces.view.ViewDeclarationLanguage#renderView(jakarta.faces.context.FacesContext,
* jakarta.faces.component.UIViewRoot)
*/
@Override
public void renderView(FacesContext ctx, UIViewRoot viewToRender) throws IOException {
// Suppress rendering if "rendered" property on the component is false
if (!viewToRender.isRendered()) {
return;
}
// Log request
if (LOGGER.isLoggable(FINE)) {
LOGGER.fine("Rendering View: " + viewToRender.getViewId());
}
WriteBehindStateWriter stateWriter = null;
try {
// Only build the view if this view has not yet been built.
if (!isViewPopulated(ctx, viewToRender)) {
vdlFactory.getViewDeclarationLanguage(viewToRender.getViewId())
.buildView(ctx, viewToRender);
}
// Setup writer and assign it to the ctx
ResponseWriter origWriter = ctx.getResponseWriter();
if (origWriter == null) {
origWriter = createResponseWriter(ctx);
}
ExternalContext extContext = ctx.getExternalContext();
/*
* Make sure we have a session here if we are using server state saving. The WriteBehindStateWriter needs an active
* session when it writes out state to a server session.
*
* Note if you flag a view as transient then we won't acquire the session as you are stating it does not need one.
*/
if (isServerStateSaving() && !viewToRender.isTransient()) {
getSession(ctx);
}
// If the buffer size is -1, use the default buffer size
final int bufferSize = responseBufferSize != -1 ? responseBufferSize : Integer.parseInt(FaceletsBufferSize.getDefaultValue());
stateWriter = new WriteBehindStateWriter(extContext.getResponseOutputWriter(), ctx, bufferSize);
ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
ctx.setResponseWriter(writer);
if (ctx.getPartialViewContext().isPartialRequest()) {
// Any pre/post processing logic such as startDocument(), doPostPhaseActions() and endDocument() must be done in PartialViewContextImpl, see also #4977
viewToRender.encodeAll(ctx);
} else {
if (ctx.isProjectStage(Development)) {
FormOmittedChecker.check(ctx);
}
// Render the XML declaration to the response
String xmlDecl = getXMLDECLFromFacesContextAttributes(ctx);
if (xmlDecl != null) {
// Do not escape.
writer.writePreamble(xmlDecl);
}
// Render the DOCTYPE declaration to the response
Doctype doctype = viewToRender.getDoctype();
if (doctype != null) {
if (doctype instanceof UIComponent) {
((UIComponent) doctype).encodeAll(ctx); // E.g. HtmlDoctype + DoctypeRenderer
} else {
// Do not escape.
writer.writeDoctype(DoctypeRenderer.toString(doctype));
}
writer.append('\n');
}
// Render the view to the response
writer.startDocument();
viewToRender.encodeAll(ctx);
try {
ctx.getExternalContext().getFlash().doPostPhaseActions(ctx);
} catch (UnsupportedOperationException uoe) {
LOGGER.fine("ExternalContext.getFlash() throw UnsupportedOperationException -> Flash unavailable");
}
writer.endDocument();
}
// Finish writing
writer.close();
// Flush to origWriter
if (stateWriter.stateWritten()) {
stateWriter.flushToWriter();
}
} catch (FileNotFoundException fnfe) {
handleFaceletNotFound(ctx, viewToRender.getViewId(), fnfe.getMessage());
} catch (Exception e) {
handleRenderException(ctx, e);
} finally {
if (stateWriter != null) {
stateWriter.release();
}
}
}
@Override
public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId) {
if (getStateContext(context).isPartialStateSaving(context, viewId)) {
return new FaceletPartialStateManagementStrategy(context);
}
return new FaceletFullStateManagementStrategy(context);
}
/**
* Called by Application._createComponent(Resource).
*
* This method creates two temporary UIComponent instances to aid in the creation of the compcomp metadata. These
* instances no longer needed after the method returns and can be safely garbage collected.
*
* PENDING(): memory analysis should be done to verify there are no memory leaks as a result of this implementation.
*
* The instances are
*
* 1. tmp: a jakarta.faces.NamingContainer to serve as the temporary top level component
*
* 2. facetComponent: a jakarta.faces.Panel to serve as the parent UIComponent that is passed to Facelets so that the
* <cc:interface>
section can be parsed and understood.
*
* Per the compcomp spec, tmp has the compcomp Resource stored in its attr set under the key
* Resource.COMPONENT_RESOURCE_KEY. tmp has the facetComponent added as its UIComponent.COMPOSITE_FACET_NAME facet.
*
*/
@Override
public BeanInfo getComponentMetadata(FacesContext context, Resource ccResource) {
DefaultFaceletFactory factory = (DefaultFaceletFactory) RequestStateManager.get(context, FACELET_FACTORY);
if (factory.needsToBeRefreshed(ccResource.getURL())) {
metadataCache.remove(ccResource);
}
return metadataCache.get(ccResource);
}
/**
* @see jakarta.faces.view.ViewDeclarationLanguage#getScriptComponentResource(jakarta.faces.context.FacesContext,
* jakarta.faces.application.Resource)
*/
@Override
public Resource getScriptComponentResource(FacesContext context, Resource componentResource) {
notNull("context", context);
notNull("componentResource", componentResource);
return null;
}
/**
* @see ViewHandlingStrategy#retargetAttachedObjects(jakarta.faces.context.FacesContext,
* jakarta.faces.component.UIComponent, java.util.List)
*/
@SuppressWarnings({ "unchecked" })
@Override
public void retargetAttachedObjects(FacesContext context, UIComponent topLevelComponent, List handlers) {
notNull("context", context);
notNull("topLevelComponent", topLevelComponent);
notNull("handlers", handlers);
if (handlers == null || handlers.isEmpty()) {
return;
}
BeanInfo componentBeanInfo = (BeanInfo) topLevelComponent.getAttributes().get(BEANINFO_KEY);
if (componentBeanInfo == null) {
return;
}
BeanDescriptor componentDescriptor = componentBeanInfo.getBeanDescriptor();
// There is an entry in targetList for each attached object in the
// section of the composite component.
List targetList = (List) componentDescriptor.getValue(ATTACHED_OBJECT_TARGETS_KEY);
// Each entry in targetList will vend one or more UIComponent instances
// that is to serve as the target of an attached object in the consuming
// page.
List targetComponents;
String forAttributeValue, curTargetName;
// For each of the attached object handlers...
for (AttachedObjectHandler curHandler : handlers) {
// Get the name given to this attached object by the page author
// in the consuming page.
forAttributeValue = curHandler.getFor();
// For each of the attached objects in the section
// of this composite component...
for (AttachedObjectTarget curTarget : targetList) {
// Get the name given to this attached object target by the
// composite component author
curTargetName = curTarget.getName();
targetComponents = curTarget.getTargets(topLevelComponent);
if (curHandler instanceof ActionSourceAttachedObjectHandler && curTarget instanceof ActionSourceAttachedObjectTarget) {
if (forAttributeValue.equals(curTargetName)) {
for (UIComponent curTargetComponent : targetComponents) {
retargetHandler(context, curHandler, curTargetComponent);
}
break;
}
} else if (curHandler instanceof EditableValueHolderAttachedObjectHandler && curTarget instanceof EditableValueHolderAttachedObjectTarget) {
if (forAttributeValue.equals(curTargetName)) {
for (UIComponent curTargetComponent : targetComponents) {
retargetHandler(context, curHandler, curTargetComponent);
}
break;
}
} else if (curHandler instanceof ValueHolderAttachedObjectHandler && curTarget instanceof ValueHolderAttachedObjectTarget) {
if (forAttributeValue.equals(curTargetName)) {
for (UIComponent curTargetComponent : targetComponents) {
retargetHandler(context, curHandler, curTargetComponent);
}
break;
}
} else if (curHandler instanceof BehaviorHolderAttachedObjectHandler && curTarget instanceof BehaviorHolderAttachedObjectTarget) {
BehaviorHolderAttachedObjectHandler behaviorHandler = (BehaviorHolderAttachedObjectHandler) curHandler;
BehaviorHolderAttachedObjectTarget behaviorTarget = (BehaviorHolderAttachedObjectTarget) curTarget;
String eventName = behaviorHandler.getEventName();
if (null != eventName && eventName.equals(curTargetName) || null == eventName && behaviorTarget.isDefaultEvent()) {
for (UIComponent curTargetComponent : targetComponents) {
retargetHandler(context, curHandler, curTargetComponent);
}
}
}
}
}
}
@Override
public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent) {
notNull("context", context);
notNull("topLevelComponent", topLevelComponent);
BeanInfo componentBeanInfo = (BeanInfo) topLevelComponent.getAttributes().get(BEANINFO_KEY);
// PENDING(edburns): log error message if componentBeanInfo is null;
if (componentBeanInfo == null) {
return;
}
PropertyDescriptor attributes[] = componentBeanInfo.getPropertyDescriptors();
MethodMetadataIterator allMetadata = new MethodMetadataIterator(context, attributes);
for (CompCompInterfaceMethodMetadata metadata : allMetadata) {
String attrName = metadata.getName();
String[] targets = metadata.getTargets(context);
Object attrValue = topLevelComponent.getValueExpression(attrName);
// In all cases but one, the attrValue will be a ValueExpression.
// The only case when it will not be a ValueExpression is
// the case when the attrName is an action, and even then, it'll be a
// ValueExpression in all cases except when it's a literal string.
if (attrValue == null) {
Map attrs = topLevelComponent.getAttributes();
attrValue = attrs.containsKey(attrName) ? attrs.get(attrName) : metadata.getDefault();
if (attrValue == null) {
if (metadata.isRequired(context)) {
Object location = attrs.get(VIEW_LOCATION_KEY);
if (location == null) {
location = "";
}
throw new FacesException(
location.toString() + ": Unable to find attribute with name \"" + attrName + "\" in top level component in consuming page, "
+ " or with default value in composite component. " + "Page author or composite component author error.");
} else {
continue;
}
}
}
String targetAttributeName = metadata.getTargetAttributeName(context);
UIComponent targetComp = null;
if (targetAttributeName != null) {
attrName = targetAttributeName;
}
if (targets != null) {
MethodRetargetHandler handler = retargetHandlerManager.getRetargetHandler(attrName);
if (handler != null) {
for (String curTarget : targets) {
targetComp = topLevelComponent.findComponent(curTarget);
if (targetComp == null) {
throw new FacesException(
attrValue.toString() + " : Unable to re-target MethodExpression as inner component referenced by target id '" + curTarget
+ "' cannot be found.");
}
handler.retarget(context, metadata, attrValue, targetComp);
}
} else {
// the developer has specified a target for a MethodExpression
// but the attribute name doesn't match one action, actionListener,
// validator, or valueChangeListener. We can ignore the
// target(s) in this case
if (LOGGER.isLoggable(WARNING)) {
LOGGER.log(WARNING, "faces.compcomp.unecessary.targets.attribute",
new Object[] { getCompositeComponentName(topLevelComponent), attrName });
}
handler = retargetHandlerManager.getDefaultHandler();
handler.retarget(context, metadata, attrValue, topLevelComponent);
}
} else {
MethodRetargetHandler handler = null;
if (targetAttributeName != null) {
targetComp = topLevelComponent.findComponent(metadata.getName());
handler = retargetHandlerManager.getRetargetHandler(attrName);
}
if (handler == null) {
targetComp = topLevelComponent;
handler = retargetHandlerManager.getDefaultHandler();
}
handler.retarget(context, metadata, attrValue, targetComp);
}
// clear out the ValueExpression that we've retargeted as a
// MethodExpression
topLevelComponent.setValueExpression(attrName, null);
}
}
@Override
public UIComponent createComponent(FacesContext context, String taglibURI, String tagName, Map attributes) {
notNull("context", context);
notNull("taglibURI", taglibURI);
notNull("tagName", tagName);
return associate.getFaceletFactory()._createComponent(context, taglibURI, tagName, attributes);
}
@Override
public List calculateResourceLibraryContracts(FacesContext context, String viewId) {
List result = null;
String longestPattern = null;
if (contractMappings == null) {
return emptyList();
}
String longestMatch = null;
for (Map.Entry> mappings : contractMappings.entrySet()) {
String urlPattern = mappings.getKey();
if (urlPattern.endsWith("*")) {
String prefix = urlPattern.substring(0, urlPattern.length() - 1);
if (viewId.startsWith(prefix)) {
if (longestPattern == null) {
longestPattern = urlPattern;
longestMatch = prefix;
} else if (longestMatch.length() < prefix.length()) {
longestPattern = urlPattern;
longestMatch = prefix;
}
}
} else if (viewId.equals(urlPattern)) {
longestPattern = urlPattern;
break;
}
}
if (longestPattern != null) {
result = contractMappings.get(longestPattern);
}
if (result == null) {
result = contractMappings.get("*");
}
return result;
}
@Override
public boolean viewExists(FacesContext context, String viewId) {
if (handlesViewId(viewId)) {
return getFaceletFactory().getResourceResolver().resolveUrl(viewId) != null;
}
return false;
}
@Override
public Stream getViews(FacesContext context, String path, ViewVisitOption... options) {
return mapIfNeeded(super.getViews(context, path).filter(viewId -> handlesViewId(viewId)), options);
}
@Override
public Stream getViews(FacesContext context, String path, int maxDepth, ViewVisitOption... options) {
return mapIfNeeded(super.getViews(context, path, maxDepth).filter(viewId -> handlesViewId(viewId)), options);
}
// --------------------------------------- Methods from ViewHandlingStrategy
/**
* @param viewId the view ID to check
* @return true
if assuming a default configuration and the view ID's extension in {@link ViewHandler#FACELETS_SUFFIX_PARAM_NAME}
* Otherwise try to match the view ID based on the configured extensions and prefixes in {@link ViewHandler#FACELETS_VIEW_MAPPINGS_PARAM_NAME}
*
* @see com.sun.faces.config.WebConfiguration.WebContextInitParameter#FaceletsSuffix
* @see com.sun.faces.config.WebConfiguration.WebContextInitParameter#FaceletsViewMappings
*/
@Override
public boolean handlesViewId(String viewId) {
if (viewId != null) {
if (handlesByPrefixOrSuffix(viewId)) {
return true;
}
if (isViewIdExactMappedToFacesServlet(viewId)) {
// If the Facelets VDL is reached, no other ViewDeclarationLanguage has declared
// to handle the view (via ViewExists()), so we handle it if the viewId happens to be exact
// mapped to the FacesServlet.
return true;
}
}
return false;
}
@Override
public String getId() {
return FACELETS_VIEW_DECLARATION_LANGUAGE_ID;
}
// ------------------------------------------------------- Protected Methods
/**
* Initialize the core Facelets runtime.
*/
protected void initialize() {
LOGGER.fine("Initializing FaceletViewHandlingStrategy");
initializeMappings();
metadataCache = new Cache<>(ccResource -> {
FacesContext context = FacesContext.getCurrentInstance();
return FaceletViewHandlingStrategy.this.createComponentMetadata(context, ccResource);
});
try {
responseBufferSize = Integer.parseInt(webConfig.getOptionValue(FaceletsBufferSize));
} catch (NumberFormatException nfe) {
responseBufferSize = Integer.parseInt(FaceletsBufferSize.getDefaultValue());
}
LOGGER.fine("Initialization Successful");
vdlFactory = (ViewDeclarationLanguageFactory) FactoryFinder.getFactory(VIEW_DECLARATION_LANGUAGE_FACTORY);
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext extContext = context.getExternalContext();
Map appMap = extContext.getApplicationMap();
@SuppressWarnings("unchecked")
Map> contractDataStructure = (Map>) appMap.remove(RESOURCE_LIBRARY_CONTRACT_DATA_STRUCTURE_KEY);
if (!isEmpty(contractDataStructure)) {
contractMappings = new ConcurrentHashMap<>();
for (Map.Entry> cur : contractDataStructure.entrySet()) {
contractMappings.put(cur.getKey(), new CopyOnWriteArrayList<>(cur.getValue()));
cur.getValue().clear();
}
contractDataStructure.clear();
}
}
/**
* Initialize mappings, during the first request.
*/
protected void initializeMappings() {
String viewMappings = webConfig.getOptionValue(FaceletsViewMappings);
if (viewMappings != null && viewMappings.length() > 0) {
Map appMap = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap();
String[] mappingsArray = split(appMap, viewMappings, ";");
List extensionsList = new ArrayList<>(mappingsArray.length);
List prefixesList = new ArrayList<>(mappingsArray.length);
for (String aMappingsArray : mappingsArray) {
String mapping = aMappingsArray.trim();
int mappingLength = mapping.length();
if (mappingLength <= 1) {
continue;
}
if (mapping.charAt(0) == '*') {
extensionsList.add(mapping.substring(1));
} else if (mapping.charAt(mappingLength - 1) == '*') {
prefixesList.add(mapping.substring(0, mappingLength - 1));
}
}
extensionsArray = new String[extensionsList.size()];
extensionsList.toArray(extensionsArray);
prefixesArray = new String[prefixesList.size()];
prefixesList.toArray(prefixesArray);
}
}
/**
* @param context the {@link FacesContext} for the current request
* @return a {@link ResponseWriter} for processing the request
* @throws IOException if the writer cannot be created
*/
protected ResponseWriter createResponseWriter(FacesContext context) throws IOException {
ExternalContext extContext = context.getExternalContext();
RenderKit renderKit = context.getRenderKit();
// Avoid a cryptic NullPointerException when the renderkit ID
// is incorrectly set
if (renderKit == null) {
String id = context.getViewRoot().getRenderKitId();
throw new IllegalStateException("No render kit was available for id \"" + id + "\"");
}
// set the buffer for content, -1 indicates nothing should be set.
if (responseBufferSize != -1) {
extContext.setResponseBufferSize(responseBufferSize);
}
// get our content type
String contentType = (String) context.getAttributes().get("facelets.ContentType");
// get the encoding
String encoding = (String) context.getAttributes().get(FACELETS_ENCODING_KEY);
// Create a dummy ResponseWriter with a bogus writer,
// so we can figure out what content type and encoding the ReponseWriter
// is really going to ask for
ResponseWriter initWriter = renderKit.createResponseWriter(NullWriter.INSTANCE, contentType, encoding);
contentType = getResponseContentType(context, initWriter.getContentType());
encoding = Util.getResponseEncoding(context, Optional.ofNullable(initWriter.getCharacterEncoding()));
// apply them to the response
char[] buffer = new char[1028];
HtmlUtils.writeTextForXML(initWriter, contentType, buffer);
String str = String.valueOf(buffer).trim();
extContext.setResponseContentType(str);
extContext.setResponseCharacterEncoding(encoding);
// Save encoding in UIViewRoot for faster consult when Util#getResponseEncoding() is invoked again elsewhere.
context.getViewRoot().getAttributes().put(FACELETS_ENCODING_KEY, encoding);
// Now, clone with the real writer
ResponseWriter writer = initWriter.cloneWithWriter(extContext.getResponseOutputWriter());
return writer;
}
/**
* Handles the case where rendering throws an Exception.
*
* @param context the {@link FacesContext} for the current request
* @param e the caught Exception
* @throws IOException if the custom debug content cannot be written
*/
protected void handleRenderException(FacesContext context, Exception e) throws IOException {
if (LOGGER.isLoggable(FINE)) {
UIViewRoot root = context.getViewRoot();
StringBuilder sb = new StringBuilder(64);
sb.append("Error Rendering View");
if (root != null) {
sb.append('[');
sb.append(root.getViewId());
sb.append(']');
}
LOGGER.log(FINE, sb.toString(), e);
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else if (e instanceof IOException) {
throw (IOException) e;
} else {
throw new FacesException(e.getMessage(), e);
}
}
/**
* Handles the case where a Facelet cannot be found.
*
* @param context the {@link FacesContext} for the current request
* @param viewId the view ID that was to be mapped to a Facelet
* @param message optional message to include in the 404
* @throws IOException if an error occurs sending the 404 to the client
*/
protected void handleFaceletNotFound(FacesContext context, String viewId, String message) throws IOException {
context.getExternalContext().responseSendError(SC_NOT_FOUND, message != null ? viewId + ": " + message : viewId);
context.responseComplete();
}
/**
* @param context the {@link FacesContext} for the current request
* @param orig the original contentType
* @return the content type to be used for this response
*/
protected String getResponseContentType(FacesContext context, String orig) {
String contentType = orig;
// See if we need to override the contentType
Map