com.sun.faces.application.view.FaceletViewHandlingStrategy Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.faces.application.view;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.context.StateContext;
import javax.faces.view.facelets.Facelet;
import javax.faces.view.facelets.FaceletFactory;
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.tag.composite.CompositeComponentBeanInfo;
import com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler;
import com.sun.faces.facelets.tag.ui.UIDebug;
import com.sun.faces.scripting.groovy.GroovyHelper;
import com.sun.faces.util.Cache.Factory;
import com.sun.faces.util.Cache;
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 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.logging.Level;
import java.util.logging.Logger;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.el.VariableMapper;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.application.ViewHandler;
import javax.faces.component.ActionSource2;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIPanel;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import javax.faces.event.MethodExpressionActionListener;
import javax.faces.event.MethodExpressionValueChangeListener;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.render.RenderKit;
import javax.faces.validator.MethodExpressionValidator;
import javax.faces.view.ActionSource2AttachedObjectHandler;
import javax.faces.view.ActionSource2AttachedObjectTarget;
import javax.faces.view.AttachedObjectHandler;
import javax.faces.view.AttachedObjectTarget;
import javax.faces.view.BehaviorHolderAttachedObjectHandler;
import javax.faces.view.BehaviorHolderAttachedObjectTarget;
import javax.faces.view.EditableValueHolderAttachedObjectHandler;
import javax.faces.view.EditableValueHolderAttachedObjectTarget;
import javax.faces.view.StateManagementStrategy;
import javax.faces.view.ValueHolderAttachedObjectHandler;
import javax.faces.view.ValueHolderAttachedObjectTarget;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewDeclarationLanguageFactory;
import javax.faces.view.ViewMetadata;
import javax.faces.view.facelets.FaceletContext;
import javax.servlet.http.HttpServletResponse;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsBufferSize;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.FaceletsViewMappings;
import static javax.faces.application.StateManager.IS_BUILDING_INITIAL_STATE;
/**
* This {@link ViewHandlingStrategy} handles Facelets/PDL-based views.
*/
public class FaceletViewHandlingStrategy extends ViewHandlingStrategy {
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
private ViewDeclarationLanguageFactory vdlFactory;
// FaceletFactory singleton for this application
private FaceletFactory 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";
private volatile StateManagementStrategyImpl stateManagementStrategy;
private MethodRetargetHandlerManager retargetHandlerManager =
new MethodRetargetHandlerManager();
private boolean groovyAvailable;
private int responseBufferSize;
private boolean responseBufferSizeSet;
private Cache metadataCache;
// ------------------------------------------------------------ Constructors
public FaceletViewHandlingStrategy() {
initialize();
}
// ------------------------------------------------------------ Constructors
public static boolean isBuildingMetadata(FacesContext context) {
return context.getAttributes().containsKey(FaceletViewHandlingStrategy.IS_BUILDING_METADATA);
}
// ------------------------------------ Methods from ViewDeclarationLanguage
@Override
public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId) {
StateContext stateCtx = StateContext.getStateContext(context);
if (stateCtx.partialStateSaving(context, viewId)) {
if (stateManagementStrategy == null) {
synchronized (this) {
if (stateManagementStrategy == null) {
stateManagementStrategy = new StateManagementStrategyImpl();
}
}
}
return stateManagementStrategy;
}
return null; // a null return means use full state saving
}
/*
* 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 javax.faces.NamingContainer to serve as the temporary
* top level component
* 2. facetComponent: a javax.faces.Panel to serve as the parent
* UIComponent that is passed to Facelets so that the
* 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) {
FaceletFactory factory = (FaceletFactory)
RequestStateManager.get(context, RequestStateManager.FACELET_FACTORY);
assert(factory instanceof DefaultFaceletFactory);
DefaultFaceletFactory ourFactory = (DefaultFaceletFactory) factory;
if (ourFactory.needsToBeRefreshed(ccResource.getURL())) {
metadataCache.remove(ccResource);
}
return metadataCache.get(ccResource);
}
public BeanInfo createComponentMetadata(FacesContext context,
Resource ccResource) {
// PENDING this implementation is terribly wasteful.
// Must find a better way.
CompositeComponentBeanInfo result;
FaceletContext ctx = (FaceletContext)
context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
FaceletFactory factory = (FaceletFactory)
RequestStateManager.get(context, RequestStateManager.FACELET_FACTORY);
VariableMapper orig = ctx.getVariableMapper();
// create tmp and facetComponent
UIComponent tmp = context.getApplication().createComponent("javax.faces.NamingContainer");
UIPanel facetComponent = (UIPanel)
context.getApplication().createComponent("javax.faces.Panel");
// PENDING I think this can be skipped because we don't render
// this component instance.
facetComponent.setRendererType("javax.faces.Group");
// PENDING This could possibly be skipped too. However, I think
// this is important because other tag handlers, within
// expect it will be there.
tmp.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, facetComponent);
// We have to put the resource in here just so the classes that eventually
// get called by facelets have access to it.
tmp.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY,
ccResource);
Facelet f;
try {
f = factory.getFacelet(ccResource.getURL());
VariableMapper wrapper = new VariableMapperWrapper(orig) {
@Override
public ValueExpression resolveVariable(String variable) {
return super.resolveVariable(variable);
}
};
ctx.setVariableMapper(wrapper);
context.getAttributes().put(IS_BUILDING_METADATA, Boolean.TRUE);
// Because mojarra currently requires a
// element within the compcomp markup, we can rely on the
// fact that its tag handler, InterfaceHandler.apply(), is
// called. In this method, we first imbue facetComponent
// with any config information present on the
// element.
// Then we do the normal facelet thing:
// this.nextHandler.apply(). This causes any child tag
// handlers of the to be called. The
// compcomp spec says each such tag handler is responsible
// for adding to the compcomp metadata, referenced from the
// facetComponent parent.
f.apply(context, facetComponent);
// When f.apply() returns (and therefore
// InterfaceHandler.apply() returns), the compcomp metadata
// pointed to by facetComponent is fully populated.
} catch (Exception e) {
if (e instanceof FacesException) {
throw (FacesException) e;
} else {
throw new FacesException(e);
}
}
finally {
context.getAttributes().remove(IS_BUILDING_METADATA);
ctx.setVariableMapper(orig);
}
// we extract the compcomp metadata and return it, making sure
// to discard tmp and facetComponent. The compcomp metadata
// should be cacheable and shareable across threads, but this is
// not yet implemented.
result = (CompositeComponentBeanInfo)
tmp.getAttributes().get(UIComponent.BEANINFO_KEY);
return result;
}
@Override
public ViewMetadata getViewMetadata(FacesContext context, String viewId) {
Util.notNull("context", context);
Util.notNull("viewId", viewId);
return new ViewMetadataImpl(viewId);
}
/**
* @see javax.faces.view.ViewDeclarationLanguage#getScriptComponentResource(javax.faces.context.FacesContext, javax.faces.application.Resource)
*/
public Resource getScriptComponentResource(FacesContext context,
Resource componentResource) {
Util.notNull("context", context);
Util.notNull("componentResource", componentResource);
if (!groovyAvailable) {
return null;
}
Resource result = null;
String resourceName = componentResource.getResourceName();
if (resourceName.endsWith(".xhtml")) {
resourceName = resourceName.substring(0,
resourceName.length() - 6) + ".groovy";
ResourceHandler resourceHandler = context.getApplication().getResourceHandler();
result = resourceHandler.createResource(resourceName,
componentResource.getLibraryName());
}
return result;
}
/**
* @see javax.faces.view.ViewDeclarationLanguage#renderView(javax.faces.context.FacesContext, javax.faces.component.UIViewRoot)
*/
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(Level.FINE)) {
LOGGER.fine("Rendering View: " + viewToRender.getViewId());
}
WriteBehindStateWriter stateWriter = null;
try {
// Only build the view if this view has not yet been built.
if (!Util.isViewPopulated(ctx, viewToRender)) {
ViewDeclarationLanguage vdl = vdlFactory.getViewDeclarationLanguage(viewToRender.getViewId());
vdl.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();
Writer outputWriter = extContext.getResponseOutputWriter();
stateWriter = new WriteBehindStateWriter(outputWriter,
ctx,
responseBufferSize);
ResponseWriter writer = origWriter.cloneWithWriter(stateWriter);
ctx.setResponseWriter(writer);
// Don't call startDoc and endDoc on a partial response
if (ctx.getPartialViewContext().isPartialRequest()) {
viewToRender.encodeAll(ctx);
try {
ctx.getExternalContext().getFlash().doPostPhaseActions(ctx);
} catch (UnsupportedOperationException uoe) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("ExternalContext.getFlash() throw UnsupportedOperationException -> Flash unavailable");
}
}
} else {
// render the view to the response
writer.startDocument();
viewToRender.encodeAll(ctx);
try {
ctx.getExternalContext().getFlash().doPostPhaseActions(ctx);
} catch (UnsupportedOperationException uoe) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("ExternalContext.getFlash() throw UnsupportedOperationException -> Flash unavailable");
}
}
writer.endDocument();
}
// finish writing
writer.close();
boolean writtenState = stateWriter.stateWritten();
// flush to origWriter
if (writtenState) {
stateWriter.flushToWriter();
}
} catch (FileNotFoundException fnfe) {
this.handleFaceletNotFound(ctx,
viewToRender.getViewId(),
fnfe.getMessage());
} catch (Exception e) {
this.handleRenderException(ctx, e);
} finally {
if (stateWriter != null)
stateWriter.release();
}
}
/**
*
* If {@link UIDebug#debugRequest(javax.faces.context.FacesContext)}} is true
,
* simply return a new UIViewRoot(), otherwise, call the default logic.
*
* @see {@link javax.faces.view.ViewDeclarationLanguage#restoreView(javax.faces.context.FacesContext, String)}
*/
@Override
public UIViewRoot restoreView(FacesContext ctx,
String viewId) {
Util.notNull("context", ctx);
Util.notNull("viewId", viewId);
if (UIDebug.debugRequest(ctx)) {
ctx.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE);
}
UIViewRoot root = super.restoreView(ctx, viewId);
StateContext stateCtx = StateContext.getStateContext(ctx);
stateCtx.startTrackViewModifications(ctx, root);
return root;
}
/**
* @see ViewHandlingStrategy#retargetAttachedObjects(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.util.List)
*/
@SuppressWarnings({"unchecked"})
@Override
public void retargetAttachedObjects(FacesContext context,
UIComponent topLevelComponent,
List handlers) {
Util.notNull("context", context);
Util.notNull("topLevelComponent", topLevelComponent);
Util.notNull("handlers", handlers);
//List handlers =
// getAttachedObjectHandlers(topLevelComponent, false);
if (handlers == null || handlers.isEmpty()) {
return;
}
BeanInfo componentBeanInfo = (BeanInfo)
topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
// PENDING(edburns): log error message if componentBeanInfo is null;
if (null == componentBeanInfo) {
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(AttachedObjectTarget.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 ActionSource2AttachedObjectHandler &&
curTarget instanceof ActionSource2AttachedObjectTarget) {
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);
}
}
}
}
}
}
/**
* @see ViewHandlingStrategy#retargetMethodExpressions(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
*/
@Override
public void retargetMethodExpressions(FacesContext context,
UIComponent topLevelComponent) {
Util.notNull("context", context);
Util.notNull("topLevelComponent", topLevelComponent);
BeanInfo componentBeanInfo = (BeanInfo)
topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY);
// PENDING(edburns): log error message if componentBeanInfo is null;
if (null == componentBeanInfo) {
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 (null == attrValue) {
Map attrs = topLevelComponent.getAttributes();
attrValue = (attrs.containsKey(attrName)) ?
attrs.get(attrName) : metadata.getDefault();
if (attrValue == null) {
if (metadata.isRequired(context)) {
Object location = attrs.get(UIComponent.VIEW_LOCATION_KEY);
if (location == null) {
location = "";
}
throw new FacesException(
// RELEASE_PENDING need a better message
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 (null != targetAttributeName) {
attrName = targetAttributeName;
}
if (targets != null) {
MethodRetargetHandler handler = retargetHandlerManager.getRetargetHandler(attrName);
if (handler != null) {
for (String curTarget : targets) {
targetComp = topLevelComponent.findComponent(curTarget);
if (null == targetComp) {
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(Level.WARNING)) {
LOGGER.log(Level.WARNING,
"jsf.compcomp.unecessary.targets.attribute",
new Object[] { getCompositeComponentName(topLevelComponent),
attrName });
}
handler = retargetHandlerManager.getDefaultHandler();
handler.retarget(context, metadata, attrValue, topLevelComponent);
}
} else {
MethodRetargetHandler handler = null;
if (null != targetAttributeName) {
targetComp = topLevelComponent.findComponent(metadata.getName());
handler = retargetHandlerManager.getRetargetHandler(attrName);
}
if (null == handler) {
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);
}
}
/**
* @see javax.faces.view.ViewDeclarationLanguage#createView(javax.faces.context.FacesContext, String)
* @return
*/
@Override
public UIViewRoot createView(FacesContext ctx,
String viewId) {
Util.notNull("context", ctx);
Util.notNull("viewId", viewId);
if (UIDebug.debugRequest(ctx)) {
UIViewRoot root = (UIViewRoot)
ctx.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE);
root.setViewId(viewId);
return root;
}
return super.createView(ctx, viewId);
}
// --------------------------------------- Methods from ViewHandlingStrategy
/**
* @param viewId the view ID to check
* @return true
if assuming a default configuration and the
* view ID's extension is .xhtml
Otherwise try to match
* the view ID based on the configured extendsion and prefixes.
*
* @see com.sun.faces.config.WebConfiguration.WebContextInitParameter#FaceletsViewMappings
*/
@Override
public boolean handlesViewId(String viewId) {
if (viewId != null) {
// If there's no extensions array or prefixes array, then
// assume defaults. .xhtml extension is handled by
// the FaceletViewHandler and .jsp will be handled by
// the JSP view handler
if ((extensionsArray == null) && (prefixesArray == null)) {
return (viewId.endsWith(ViewHandler.DEFAULT_FACELETS_SUFFIX));
}
if (extensionsArray != null) {
for (String extension : extensionsArray) {
if (viewId.endsWith(extension)) {
return true;
}
}
}
if (prefixesArray != null) {
for (String prefix : prefixesArray) {
if (viewId.startsWith(prefix)) {
return true;
}
}
}
}
return false;
}
/**
* Build the view.
* @param ctx the {@link FacesContext} for the current request
* @param view the {@link UIViewRoot} to populate based
* of the Facelet template
* @throws IOException if an error occurs building the view.
*/
@Override
public void buildView(FacesContext ctx, UIViewRoot view)
throws IOException {
StateContext stateCtx = StateContext.getStateContext(ctx);
if (Util.isViewPopulated(ctx, view)) {
Facelet f = faceletFactory.getFacelet(view.getViewId());
// Disable events from being intercepted by the StateContext by
// virute of re-applying the handlers.
try {
stateCtx.setTrackViewModifications(false);
f.apply(ctx, view);
} finally {
stateCtx.setTrackViewModifications(true);
}
return;
}
view.setViewId(view.getViewId());
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Building View: " + view.getViewId());
}
if (faceletFactory == null) {
ApplicationAssociate associate = ApplicationAssociate.getInstance(ctx.getExternalContext());
faceletFactory = associate.getFaceletFactory();
assert (faceletFactory != null);
}
RequestStateManager.set(ctx,
RequestStateManager.FACELET_FACTORY,
faceletFactory);
Facelet f = faceletFactory.getFacelet(view.getViewId());
// populate UIViewRoot
try {
ctx.getAttributes().put(IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
stateCtx.setTrackViewModifications(false);
f.apply(ctx, view);
doPostBuildActions(ctx, view);
} finally {
ctx.getAttributes().remove(IS_BUILDING_INITIAL_STATE);
}
ctx.getApplication().publishEvent(ctx,
PostAddToViewEvent.class,
UIViewRoot.class,
view);
markInitialState(ctx, view);
Util.setViewPopulated(ctx, view);
}
@Override
public boolean viewExists(FacesContext context,
String viewId) {
if (handlesViewId(viewId)) {
if (faceletFactory == null) {
ApplicationAssociate associate = ApplicationAssociate.getInstance(context.getExternalContext());
faceletFactory = associate.getFaceletFactory();
assert (faceletFactory != null);
}
return faceletFactory.getResourceResolver().resolveUrl(viewId) != null;
}
return false;
}
@Override
public String getId() {
return FACELETS_VIEW_DECLARATION_LANGUAGE_ID;
}
// ------------------------------------------------------- Protected Methods
/**
* Initialize the core Facelets runtime.
*/
protected void initialize() {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Initializing FaceletViewHandlingStrategy");
}
this.initializeMappings();
groovyAvailable = GroovyHelper.isGroovyAvailable(FacesContext.getCurrentInstance());
metadataCache = new Cache(new Factory() {
public BeanInfo newInstance(Resource ccResource) throws InterruptedException {
FacesContext context = FacesContext.getCurrentInstance();
return FaceletViewHandlingStrategy.this.createComponentMetadata(context, ccResource);
}
});
try {
responseBufferSizeSet = webConfig.isSet(FaceletsBufferSize);
responseBufferSize =
Integer.parseInt(webConfig.getOptionValue(FaceletsBufferSize));
} catch (NumberFormatException nfe) {
responseBufferSize = Integer.parseInt(FaceletsBufferSize.getDefaultValue());
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Initialization Successful");
}
vdlFactory = (ViewDeclarationLanguageFactory) FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
}
/**
* Initialize mappings, during the first request.
*/
protected void initializeMappings() {
String viewMappings = webConfig.getOptionValue(FaceletsViewMappings);
if ((viewMappings != null) && (viewMappings.length() > 0)) {
String[] mappingsArray = Util.split(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 + "\"");
}
if (responseBufferSizeSet) {
// set the buffer for content
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");
// Create a dummy ResponseWriter with a bogus writer,
// so we can figure out what content type the ReponseWriter
// is really going to ask for
ResponseWriter writer = renderKit.createResponseWriter(NullWriter.Instance,
contentType,
encoding);
contentType = getResponseContentType(context, writer.getContentType());
encoding = getResponseEncoding(context, writer.getCharacterEncoding());
// apply them to the response
char[] buffer = new char[1028];
HtmlUtils.writeTextForXML(writer, contentType, buffer);
String str = String.valueOf(buffer).trim();
extContext.setResponseContentType(str);
extContext.setResponseCharacterEncoding(encoding);
// Now, clone with the real writer
writer = writer.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 {
// always log
if (LOGGER.isLoggable(Level.SEVERE)) {
UIViewRoot root = context.getViewRoot();
StringBuffer sb = new StringBuffer(64);
sb.append("Error Rendering View");
if (root != null) {
sb.append('[');
sb.append(root.getViewId());
sb.append(']');
}
LOGGER.log(Level.SEVERE, 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(HttpServletResponse.SC_NOT_FOUND, ((message != null)
? (viewId + ": " + message)
: viewId));
context.responseComplete();
}
/**
* @param context the {@link FacesContext} for the current request
* @param orig the original encoding
* @return the encoding to be used for this response
*/
protected String getResponseEncoding(FacesContext context, String orig) {
String encoding = orig;
// 1. get it from request
encoding = context.getExternalContext().getRequestCharacterEncoding();
// 2. get it from the session
if (encoding == null) {
if (null != context.getExternalContext().getSession(false)) {
Map sessionMap = context.getExternalContext().getSessionMap();
encoding = (String) sessionMap.get(ViewHandler.CHARACTER_ENCODING_KEY);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST,
"Session specified alternate encoding {0}",
encoding);
}
}
}
// see if we need to override the encoding
Map