blob: 1f2daa0327c23cc858f9f000b3055da617e3d019 [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 org.apache.myfaces.trinidadinternal.context;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialResponseWriter;
import javax.faces.context.PartialViewContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.PhaseId;
import javax.faces.render.RenderKit;
import org.apache.myfaces.trinidad.context.PartialPageContext;
import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.render.CoreRenderer;
import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderKit;
import org.apache.myfaces.trinidadinternal.renderkit.core.ppr.PPRResponseWriter;
import org.apache.myfaces.trinidadinternal.renderkit.core.ppr.XmlResponseWriter;
import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.PartialPageUtils;
import org.apache.myfaces.trinidadinternal.util.StateUtils;
public class PartialViewContextImpl
extends PartialViewContext
{
public PartialViewContextImpl(FacesContext context)
{
_context = context;
ExternalContext extContext = context.getExternalContext();
_requestType = ReqType.FULL;
if (_PARTIAL_AJAX.equals(extContext.getRequestHeaderMap().get(_FACES_REQUEST)))
{
// This request was sent with jsf.ajax.request()
if (extContext.getRequestParameterMap().get(_TRINIDAD_PPR) != null)
_requestType = ReqType.AJAX_LEGACY;
else
_requestType = ReqType.AJAX;
}
else if (CoreRenderKit.isLegacyPartialRequest(extContext))
{
_requestType = ReqType.LEGACY;
}
}
/**
* To force a full execute we always return an empty execute list.
* @return
*/
public Collection<String> getExecuteIds()
{
_assertNotReleased();
if (_executeIds == null)
{
_executeIds = _getIds(PARTIAL_EXECUTE_PARAM_NAME);
// force view parameter facet ID to be executed if we are executing anything else
if (!_executeIds.isEmpty())
{
UIViewRoot root = _context.getViewRoot();
if (root.getFacetCount() > 0)
{
UIComponent facet =
root.getFacet(UIViewRoot.METADATA_FACET_NAME);
if (facet != null)
{
_executeIds.add(facet.getClientId(_context));
}
}
}
}
return _executeIds;
}
public Collection<String> getRenderIds()
{
_assertNotReleased();
if (_renderIds == null)
{
_renderIds = _getIds(PARTIAL_RENDER_PARAM_NAME);
}
return _renderIds;
}
public boolean isAjaxRequest()
{
_assertNotReleased();
return (_requestType != ReqType.FULL);
}
public boolean isPartialRequest()
{
_assertNotReleased();
if (_partialRequest == null)
{
_partialRequest =
isAjaxRequest() || _PARTIAL_PROCESS.equals(_context.getExternalContext().getRequestHeaderMap().get(_FACES_REQUEST));
}
return _partialRequest;
}
@Override
public boolean isExecuteAll()
{
_assertNotReleased();
if (_executeAll == null)
{
if (_requestType == ReqType.AJAX_LEGACY ||
_requestType == ReqType.LEGACY)
{
// We are preserving old behavior (execute everything) for the legacy 'partialSubmit=true'
// Trinidad requests
_executeAll = true;
}
else
{
String execute =
_context.getExternalContext().getRequestParameterMap().get(PARTIAL_EXECUTE_PARAM_NAME);
_executeAll = execute.equals(ALL_PARTIAL_PHASE_CLIENT_IDS);
}
}
return _executeAll;
}
public boolean isRenderAll()
{
_assertNotReleased();
if (_renderAll == null)
{
String render =
_context.getExternalContext().getRequestParameterMap().get(PARTIAL_RENDER_PARAM_NAME);
_renderAll = ALL_PARTIAL_PHASE_CLIENT_IDS.equals(render);
}
return _renderAll;
}
@Override
public void processPartial(PhaseId phaseId)
{
UIViewRoot viewRoot = _context.getViewRoot();
if (phaseId == PhaseId.APPLY_REQUEST_VALUES ||
phaseId == PhaseId.PROCESS_VALIDATIONS ||
phaseId == PhaseId.UPDATE_MODEL_VALUES)
{
_processExecute(viewRoot, phaseId);
}
else if (phaseId == PhaseId.RENDER_RESPONSE)
{
_processRender(viewRoot);
}
}
@Override
public PartialResponseWriter getPartialResponseWriter()
{
ResponseWriter current = _context.getResponseWriter();
if (current != null && current instanceof PartialResponseWriter)
{
return (PartialResponseWriter)current;
}
if (current != null)
{
if (_context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE)
{
_LOG.warning("getPartialResponseWriter() called during render_reponse. " +
"The returned writer is not integrated with PPRResponseWriter");
}
return new PartialResponseWriter(current);
}
ExternalContext extContext = _context.getExternalContext();
String encoding = "UTF-8";
extContext.setResponseCharacterEncoding(encoding);
Writer out = null;
try
{
out = extContext.getResponseOutputWriter();
}
catch (IOException ioe)
{
_LOG.severe(ioe.toString(), ioe);
}
if (out != null)
{
ResponseWriter responseWriter = null;
RenderKit kit = _context.getRenderKit();
if (kit != null)
{
responseWriter = kit.createResponseWriter(out, "text/xml", encoding);
}
else
{
responseWriter = new XmlResponseWriter(out, encoding);
}
return new PartialResponseWriter(responseWriter);
}
return null;
}
public void setRenderAll(boolean renderAll)
{
// Ignore setRenderAll() from Mojarra's NavigationHandler
// to preserve old (redirect) behavior
//_renderAll = renderAll;
}
public void setPartialRequest(boolean isPartialRequest)
{
_partialRequest = isPartialRequest;
}
public void release()
{
_executeIds = null;
_context = null;
_partialRequest = null;
_released = true;
_renderAll = null;
_renderIds = null;
}
private void _processExecute(UIViewRoot component, PhaseId phaseId)
{
// We are only handling visit-based (partial) execution here.
// Full execution (isExecuteAll() == true) is handled by the UIViewRoot
// Note that this is different from the render phase
Collection<String> executeIds = getExecuteIds();
if (executeIds == null || executeIds.isEmpty())
{
_LOG.warning("No execute Ids were supplied for the Ajax request");
return;
}
VisitContext visitContext = VisitContext.createVisitContext(_context, executeIds,
EnumSet.of(VisitHint.SKIP_UNRENDERED, VisitHint.EXECUTE_LIFECYCLE));
VisitCallback visitCallback = new ProcessPhaseCallback(_context, phaseId);
component.visitTree(visitContext, visitCallback);
}
private void _processRender(UIComponent viewRoot)
{
ExternalContext extContext = _context.getExternalContext();
extContext.setResponseContentType(_RESPONSE_CONTENT_TYPE);
extContext.addResponseHeader("Pragma", "no-cache");
extContext.addResponseHeader("Cache-control", "no-cache");
if (isRenderAll())
{
_renderAll(_context, viewRoot);
return;
}
ResponseWriter origResponseWriter = _context.getResponseWriter();
RenderingContext rc = RenderingContext.getCurrentInstance();
assert (rc != null);
boolean bufferScripts = _requestType == ReqType.LEGACY || _requestType == ReqType.AJAX_LEGACY;
PPRResponseWriter pprWriter =
new PPRResponseWriter(origResponseWriter, rc, bufferScripts);
_context.setResponseWriter(pprWriter);
if (_requestType == ReqType.AJAX)
{
// Add render Ids as partial targets for the request from <f:ajax>
_addRenderIdsAsPartialTargets(rc);
// Force visit-based rendering for the <f:ajax> requests
PartialPageUtils.forceOptimizedPPR(_context);
}
try
{
pprWriter.startDocument();
// Note that PanelPartialRootRenderer will perform partial visit for the optimized PPR
// if it is enabled
_renderChildren(_context, viewRoot);
// PDA's JavaScript DOM is not capable of updating the ViewState just by
// using ViewState's value, so for PDAs, FormRenderer will again render
// the ViewState as a hidden element during its postscript element rendering
if (!CoreRenderer.isPDA(rc))
{
// Always write out ViewState as a separate update element.
String state =
_context.getApplication().getStateManager().getViewState(_context);
pprWriter.writeViewState(state);
}
pprWriter.endDocument();
}
catch (IOException e)
{
// launder the IOException as a FacesException, we'll unwrap this later
throw new FacesException(e);
}
finally
{
_context.setResponseWriter(origResponseWriter);
}
}
private void _renderAll(FacesContext context, UIComponent viewRoot)
{
ResponseWriter origResponseWriter = context.getResponseWriter();
// Use JSF-supplied PartialResponseWriter, since we are rendering the full page
PartialResponseWriter rw = new PartialResponseWriter(origResponseWriter);
context.setResponseWriter(rw);
try
{
rw.startDocument();
rw.startUpdate(PartialResponseWriter.RENDER_ALL_MARKER);
_renderChildren(context, viewRoot);
rw.endUpdate();
//write out JSF state
rw.startUpdate(StateUtils.getViewStateId(FacesContext.getCurrentInstance()));
String state = context.getApplication().getStateManager().getViewState(context);
rw.write(state);
rw.endUpdate();
rw.endDocument();
}
catch(IOException e)
{
throw new FacesException(e);
}
finally
{
context.setResponseWriter(origResponseWriter);
}
}
private void _addRenderIdsAsPartialTargets(RenderingContext rc)
{
Collection<String> renderIds = getRenderIds();
PartialPageContext pc = rc.getPartialPageContext();
for (String id: renderIds)
{
pc.addPartialTarget(id);
}
}
private static void _renderChildren(FacesContext context, UIComponent root) throws IOException
{
Iterator<UIComponent> iterator = root.getFacetsAndChildren();
while (iterator.hasNext())
{
UIComponent child = iterator.next();
if (child.isRendered())
child.encodeAll(context);
}
}
private void _assertNotReleased()
{
if (_released)
{
throw new IllegalStateException();
}
}
private Set<String> _getIds(String parameter)
{
String param =
_context.getExternalContext().getRequestParameterMap().get(parameter);
if (param != null)
{
String ids[] = param.split("[ \t]+");
if (ids != null && ids.length > 0)
{
HashSet<String> idSet = new HashSet<String>(ids.length);
for (int i = 0; i < ids.length; i++)
{
idSet.add(ids[i]);
}
return idSet;
}
}
return new HashSet<String>();
}
private static final class ProcessPhaseCallback
implements VisitCallback
{
ProcessPhaseCallback(FacesContext context, PhaseId phaseId)
{
_context = context;
_phaseId = phaseId;
}
public VisitResult visit(VisitContext context, UIComponent target)
{
if (_phaseId == PhaseId.APPLY_REQUEST_VALUES)
{
target.processDecodes(_context);
}
else if (_phaseId == PhaseId.PROCESS_VALIDATIONS)
{
target.processValidators(_context);
}
else if (_phaseId == PhaseId.UPDATE_MODEL_VALUES)
{
target.processUpdates(_context);
}
// No need to visit children, since they will be executed/rendred by their parents
return VisitResult.REJECT;
}
private FacesContext _context;
private PhaseId _phaseId;
}
private ReqType _requestType;
private Set<String> _executeIds;
private Set<String> _renderIds;
private FacesContext _context = null;
private Boolean _partialRequest;
private boolean _released;
private Boolean _renderAll = null;
private Boolean _executeAll = null;
private static final String _RESPONSE_CONTENT_TYPE = "text/xml";
private static final String _FACES_REQUEST = "Faces-Request";
private static final String _PARTIAL_AJAX = "partial/ajax";
private static final String _PARTIAL_PROCESS = "partial/process";
private static final String _TRINIDAD_PPR = "Tr-PPR-Message";
private static enum ReqType
{
FULL,
AJAX,
AJAX_LEGACY,
LEGACY;
}
private static final TrinidadLogger _LOG =
TrinidadLogger.createTrinidadLogger(PartialViewContextImpl.class);
}