blob: b9e5c8e1973585108dcfd51977a125c70bc83f99 [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.context.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.faces.FactoryFinder;
import jakarta.faces.component.EditableValueHolder;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIViewParameter;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.component.behavior.ClientBehaviorContext;
import jakarta.faces.component.visit.VisitCallback;
import jakarta.faces.component.visit.VisitContext;
import jakarta.faces.component.visit.VisitContextFactory;
import jakarta.faces.component.visit.VisitHint;
import jakarta.faces.component.visit.VisitResult;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.PartialResponseWriter;
import jakarta.faces.context.PartialViewContext;
import jakarta.faces.context.ResponseWriter;
import jakarta.faces.event.PhaseId;
import jakarta.faces.lifecycle.ClientWindow;
import jakarta.faces.render.RenderKit;
import jakarta.faces.render.RenderKitFactory;
import jakarta.faces.view.ViewMetadata;
import org.apache.myfaces.application.ResourceHandlerImpl;
import org.apache.myfaces.context.PartialResponseWriterImpl;
import org.apache.myfaces.context.RequestViewContext;
import org.apache.myfaces.renderkit.html.HtmlResponseStateManager;
import org.apache.myfaces.shared.renderkit.JSFAttr;
import org.apache.myfaces.shared.util.StringUtils;
public class PartialViewContextImpl extends PartialViewContext
{
private static final String FACES_REQUEST = "Faces-Request";
private static final String PARTIAL_AJAX = "partial/ajax";
private static final String PARTIAL_AJAX_REQ = "jakarta.faces.partial.ajax";
private static final String PARTIAL_PROCESS = "partial/process";
/**
* Internal extension for
* https://issues.apache.org/jira/browse/MYFACES-2841
* will be changed for 2.1 to the official marker
*/
private static final String PARTIAL_IFRAME = "org.apache.myfaces.partial.iframe";
private static final Set<VisitHint> PARTIAL_EXECUTE_HINTS = Collections.unmodifiableSet(
EnumSet.of(VisitHint.EXECUTE_LIFECYCLE, VisitHint.SKIP_UNRENDERED));
// unrendered have to be skipped, transient definitely must be added to our list!
private static final Set<VisitHint> PARTIAL_RENDER_HINTS =
Collections.unmodifiableSet(EnumSet.of(VisitHint.SKIP_UNRENDERED));
private static final VisitCallback RESET_VALUES_CALLBACK = new ResetValuesCallback();
private static final Set<VisitHint> RESET_VALUES_HINTS =
Collections.unmodifiableSet(EnumSet.of(VisitHint.SKIP_UNRENDERED));
private FacesContext _facesContext = null;
private boolean _released = false;
// Cached values, since their parent methods could be called
// many times and the result does not change during the life time
// of this object.
private Boolean _ajaxRequest = null;
/**
* Internal extension for
* https://issues.apache.org/jira/browse/MYFACES-2841
* will be changed for 2.1 to the official marker
*/
private Boolean _iframeRequest = null;
private Collection<String> _executeClientIds = null;
private Collection<String> _renderClientIds = null;
// Values that need to be saved because exists a setXX method
private Boolean _partialRequest = null;
private Boolean _renderAll = null;
private PartialResponseWriter _partialResponseWriter = null;
private VisitContextFactory _visitContextFactory = null;
private Boolean _resetValues = null;
private List<String> _evalScripts = new ArrayList<String>();
public PartialViewContextImpl(FacesContext context)
{
_facesContext = context;
}
public PartialViewContextImpl(FacesContext context,
VisitContextFactory visitContextFactory)
{
_facesContext = context;
_visitContextFactory = visitContextFactory;
}
@Override
public boolean isAjaxRequest()
{
assertNotReleased();
if (_ajaxRequest == null)
{
String requestType = _facesContext.getExternalContext().
getRequestHeaderMap().get(FACES_REQUEST);
_ajaxRequest = (requestType != null && PARTIAL_AJAX.equals(requestType));
String reqParmamterPartialAjax = _facesContext.getExternalContext().
getRequestParameterMap().get(PARTIAL_AJAX_REQ);
//jsdoc reference in an ajax request the jakarta.faces.partial.ajax must be set as ajax parameter
//the other one is Faces-Request == partial/ajax which is basically the same
_ajaxRequest = _ajaxRequest || reqParmamterPartialAjax != null;
}
return _ajaxRequest;
}
@Override
public boolean isExecuteAll()
{
assertNotReleased();
if (isAjaxRequest())
{
String executeMode = _facesContext.getExternalContext().
getRequestParameterMap().get(
PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
{
return true;
}
}
return false;
}
@Override
public boolean isPartialRequest()
{
assertNotReleased();
if (_partialRequest == null)
{
String requestType = _facesContext.getExternalContext().
getRequestHeaderMap().get(FACES_REQUEST);
_partialRequest = (requestType != null && PARTIAL_PROCESS.equals(requestType));
}
return _partialRequest || isAjaxRequest();
}
@Override
public boolean isRenderAll()
{
assertNotReleased();
if (_renderAll == null)
{
if (isAjaxRequest())
{
String executeMode = _facesContext.getExternalContext().
getRequestParameterMap().get(
PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
{
_renderAll = true;
}
}
if (_renderAll == null)
{
_renderAll = false;
}
}
return _renderAll;
}
/**
* Extension for
* https://issues.apache.org/jira/browse/MYFACES-2841
* internal extension which detects that the submit is an iframe request
* will be changed for the official version which will come in 2.1
*
* @return true if the current request is an iframe based ajax request
*/
public boolean isIFrameRequest()
{
if (_iframeRequest == null)
{
_iframeRequest = _facesContext.getExternalContext().getRequestParameterMap().containsKey(PARTIAL_IFRAME);
}
return _iframeRequest;
}
@Override
public void setPartialRequest(boolean isPartialRequest)
{
assertNotReleased();
_partialRequest = isPartialRequest;
}
@Override
public void setRenderAll(boolean renderAll)
{
assertNotReleased();
_renderAll = renderAll;
}
@Override
public Collection<String> getExecuteIds()
{
assertNotReleased();
if (_executeClientIds == null)
{
String executeMode = _facesContext.getExternalContext().
getRequestParameterMap().get(
PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
if (executeMode != null && !"".equals(executeMode) &&
//!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode) &&
!PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
{
String[] clientIds
= StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(executeMode), ' ');
//The collection must be mutable
List<String> tempList = new ArrayList<String>();
for (String clientId : clientIds)
{
if (clientId.length() > 0)
{
tempList.add(clientId);
}
}
// The "jakarta.faces.source" parameter needs to be added to the list of
// execute ids if missing (otherwise, we'd never execute an action associated
// with, e.g., a button).
String source = _facesContext.getExternalContext().getRequestParameterMap().get
(ClientBehaviorContext.BEHAVIOR_SOURCE_PARAM_NAME);
if (source != null)
{
source = source.trim();
if (!tempList.contains(source))
{
tempList.add(source);
}
}
_executeClientIds = tempList;
}
else
{
_executeClientIds = new ArrayList<String>();
}
}
return _executeClientIds;
}
private String _replaceTabOrEnterCharactersWithSpaces(String mode)
{
StringBuilder builder = new StringBuilder(mode.length());
for (int i = 0; i < mode.length(); i++)
{
if (mode.charAt(i) == '\t' ||
mode.charAt(i) == '\n')
{
builder.append(' ');
}
else
{
builder.append(mode.charAt(i));
}
}
return builder.toString();
}
@Override
public Collection<String> getRenderIds()
{
assertNotReleased();
if (_renderClientIds == null)
{
String renderMode = _facesContext.getExternalContext().
getRequestParameterMap().get(
PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
if (renderMode != null && !"".equals(renderMode) &&
//!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode) &&
!PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
{
String[] clientIds
= StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
//The collection must be mutable
List<String> tempList = new ArrayList<String>();
for (String clientId : clientIds)
{
if (clientId.length() > 0)
{
tempList.add(clientId);
}
}
_renderClientIds = tempList;
}
else
{
_renderClientIds = new ArrayList<String>();
if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
{
_renderClientIds.add(PartialResponseWriter.RENDER_ALL_MARKER);
}
}
}
return _renderClientIds;
}
@Override
public PartialResponseWriter getPartialResponseWriter()
{
assertNotReleased();
if (_partialResponseWriter == null)
{
ResponseWriter responseWriter = _facesContext.getResponseWriter();
if (responseWriter == null)
{
// This case happens when getPartialResponseWriter() is called before
// render phase, like in ExternalContext.redirect(). We have to create a
// ResponseWriter from the RenderKit and then wrap if necessary.
try
{
RenderKit renderKit = _facesContext.getRenderKit();
if (renderKit == null)
{
// If the viewRoot was set to null by some reason, or there is no
// renderKitId on that view, this could be still an ajax redirect,
// so we have to try to calculate the renderKitId and return a
// RenderKit instance, to send the response.
String renderKitId
= _facesContext.getApplication().getViewHandler().calculateRenderKitId(_facesContext);
RenderKitFactory rkf
= (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
renderKit = rkf.getRenderKit(_facesContext, renderKitId);
}
responseWriter = renderKit.createResponseWriter(
_facesContext.getExternalContext().getResponseOutputWriter(), "text/xml",
_facesContext.getExternalContext().getRequestCharacterEncoding());
}
catch (IOException e)
{
throw new IllegalStateException("Cannot create Partial Response Writer", e);
}
}
// It is possible that the RenderKit return a PartialResponseWriter instance when
// createResponseWriter, so we should cast here for it and prevent double wrapping.
if (responseWriter instanceof PartialResponseWriter)
{
_partialResponseWriter = (PartialResponseWriter) responseWriter;
}
else
{
_partialResponseWriter = new PartialResponseWriterImpl(responseWriter);
}
}
return _partialResponseWriter;
}
@Override
public List<String> getEvalScripts()
{
return _evalScripts;
}
/**
* process the partial response
* allowed phase ids according to the spec
*
*
*/
@Override
public void processPartial(PhaseId phaseId)
{
assertNotReleased();
UIViewRoot viewRoot = _facesContext.getViewRoot();
if (phaseId == PhaseId.APPLY_REQUEST_VALUES
|| phaseId == PhaseId.PROCESS_VALIDATIONS
|| phaseId == PhaseId.UPDATE_MODEL_VALUES)
{
processPartialExecute(viewRoot, phaseId);
}
else if (phaseId == PhaseId.RENDER_RESPONSE)
{
processPartialRendering(viewRoot, phaseId);
}
}
private void processPartialExecute(UIViewRoot viewRoot, PhaseId phaseId)
{
PartialViewContext pvc = _facesContext.getPartialViewContext();
Collection<String> executeIds = pvc.getExecuteIds();
if (executeIds == null || executeIds.isEmpty())
{
return;
}
VisitContext visitCtx = getVisitContextFactory().getVisitContext(_facesContext, executeIds,
PARTIAL_EXECUTE_HINTS);
viewRoot.visitTree(visitCtx, new PhaseAwareVisitCallback(_facesContext, phaseId));
}
private void processPartialRendering(UIViewRoot viewRoot, PhaseId phaseId)
{
//TODO process partial rendering
//https://issues.apache.org/jira/browse/MYFACES-2118
//Collection<String> renderIds = getRenderIds();
// We need to always update the view state marker when processing partial
// rendering, because there is no way to check when the state has been changed
// or not. Anyway, if we return empty response, according to the spec a javascript
// message displayed, so we need to return something.
//if (renderIds == null || renderIds.isEmpty()) {
// return;
//}
// note that we cannot use this.getPartialResponseWriter(), because
// this could cause problems if PartialResponseWriter is wrapped
PartialResponseWriter writer = _facesContext.getPartialViewContext().getPartialResponseWriter();
PartialViewContext pvc = _facesContext.getPartialViewContext();
ResponseWriter oldWriter = _facesContext.getResponseWriter();
boolean inDocument = false;
//response type = text/xml
//no caching and no timeout if possible!
ExternalContext externalContext = _facesContext.getExternalContext();
externalContext.setResponseContentType("text/xml");
externalContext.addResponseHeader("Pragma", "no-cache");
externalContext.addResponseHeader("Cache-control", "no-cache");
//under normal circumstances pragma should be enough, IE needs
//a special treatment!
//http://support.microsoft.com/kb/234067
externalContext.addResponseHeader("Expires", "-1");
try
{
String currentEncoding = writer.getCharacterEncoding();
writer.startDocument();
writer.writeAttribute("id", viewRoot.getContainerClientId(_facesContext),"id");
inDocument = true;
_facesContext.setResponseWriter(writer);
if (isResetValues())
{
VisitContext visitContext = VisitContext.createVisitContext(_facesContext, getRenderIds(),
RESET_VALUES_HINTS);
viewRoot.visitTree(visitContext, RESET_VALUES_CALLBACK);
}
if (pvc.isRenderAll())
{
processRenderAll(viewRoot, writer);
}
else
{
Collection<String> renderIds = pvc.getRenderIds();
//Only apply partial visit if we have ids to traverse
if (renderIds != null && !renderIds.isEmpty())
{
// render=@all, so output the body.
if (renderIds.contains(PartialResponseWriter.RENDER_ALL_MARKER))
{
processRenderAll(viewRoot, writer);
}
else
{
// In JSF 2.3 it was added jakarta.faces.Resource as an update target to add scripts or
// stylesheets inside <head> tag. In that sense
// org.apache.myfaces.STRICT_JSF_2_REFRESH_TARGET_AJAX web config param, which was a
// workaround for dynamic refresh can be deprecated.
List<UIComponent> updatedComponents = new ArrayList<UIComponent>();
RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
processRenderResource(_facesContext, writer, rvc, updatedComponents, "head");
processRenderResource(_facesContext, writer, rvc, updatedComponents, "body");
processRenderResource(_facesContext, writer, rvc, updatedComponents, "form");
VisitContext visitCtx = getVisitContextFactory().getVisitContext(
_facesContext, renderIds, PARTIAL_RENDER_HINTS);
viewRoot.visitTree(visitCtx,
new PhaseAwareVisitCallback(_facesContext, phaseId, updatedComponents));
}
}
else
{
List<UIComponent> updatedComponents = new ArrayList<UIComponent>();
RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
processRenderResource(_facesContext, writer, rvc, updatedComponents, "head");
processRenderResource(_facesContext, writer, rvc, updatedComponents, "body");
processRenderResource(_facesContext, writer, rvc, updatedComponents, "form");
}
List<String> evalScripts = pvc.getEvalScripts();
if (evalScripts != null && evalScripts.size() > 0)
{
for (String script : evalScripts)
{
writer.startEval();
writer.write(script);
writer.endEval();
}
}
}
// invoke encodeAll() on every UIViewParameter in the view to
// enable every UIViewParameter to save its value in the state
// just like UIViewRoot.encodeEnd() does on a normal request
// (see MYFACES-2645 for details)
Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(viewRoot);
if (!viewParams.isEmpty())
{
for (UIViewParameter param : viewParams)
{
param.encodeAll(_facesContext);
}
}
//Retrieve the state and apply it if it is not null.
String viewState = _facesContext.getApplication().getStateManager().getViewState(_facesContext);
if (viewState != null)
{
writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
_facesContext));
writer.write(viewState);
writer.endUpdate();
}
else if (viewRoot.isTransient())
{
//TODO: fix javascript side, so the field is not removed on ajax form update
writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
_facesContext));
writer.write("stateless");
writer.endUpdate();
//END TODO
}
ClientWindow cw = _facesContext.getExternalContext().getClientWindow();
if (cw != null)
{
writer.startUpdate(HtmlResponseStateManager.generateUpdateClientWindowId(
_facesContext));
writer.write(cw.getId());
writer.endUpdate();
}
}
catch (IOException ex)
{
Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
if (log.isLoggable(Level.SEVERE))
{
log.log(Level.SEVERE, "", ex);
}
}
finally
{
try
{
if (inDocument)
{
writer.endDocument();
}
writer.flush();
}
catch (IOException ex)
{
Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
if (log.isLoggable(Level.SEVERE))
{
log.log(Level.SEVERE, "", ex);
}
}
_facesContext.setResponseWriter(oldWriter);
}
}
// same like UIViewRoot#ResetValuesCallback
private static class ResetValuesCallback implements VisitCallback
{
public VisitResult visit(VisitContext context, UIComponent target)
{
if (target instanceof EditableValueHolder)
{
((EditableValueHolder)target).resetValue();
}
return VisitResult.ACCEPT;
}
}
private void processRenderResource(FacesContext facesContext, PartialResponseWriter writer, RequestViewContext rvc,
List<UIComponent> updatedComponents, String target) throws IOException
{
if (rvc.isRenderTarget(target))
{
List<UIComponent> list = rvc.getRenderTargetComponentList(target);
if (list != null && !list.isEmpty())
{
writer.startUpdate("jakarta.faces.Resource");
for (UIComponent component : list)
{
boolean resourceRendered = false;
if ("jakarta.faces.resource.Script".equals(component.getRendererType()) ||
"jakarta.faces.resource.Stylesheet".equals(component.getRendererType()))
{
String resourceName = (String)
component.getAttributes().get(JSFAttr.NAME_ATTR);
String libraryName = (String)
component.getAttributes().get(JSFAttr.LIBRARY_ATTR);
if (resourceName == null)
{
// No resource, render all
component.encodeAll(facesContext);
continue;
}
if ("".equals(resourceName))
{
// No resource, render all
component.encodeAll(facesContext);
continue;
}
int index = resourceName.indexOf('?');
if (index >= 0)
{
resourceName = resourceName.substring(0, index);
}
// Is resource, render only if it has not been rendered before.
if (!_facesContext.getApplication().getResourceHandler().isResourceRendered(
_facesContext, resourceName, libraryName))
{
component.encodeAll(facesContext);
}
}
else
{
component.encodeAll(facesContext);
}
if (!resourceRendered)
{
if (updatedComponents == null)
{
updatedComponents = new ArrayList<UIComponent>();
}
updatedComponents.add(component);
}
}
writer.endUpdate();
}
}
}
private void processRenderAll(UIViewRoot viewRoot, PartialResponseWriter writer) throws IOException
{
// Before render all we need to clear rendered resources set to be sure every component resource is
// rendered. Remember renderAll means the whole page is replaced, so everything inside <head> is replaced.
// and there is no way to diff between the old and the new content of <head>.
Map<String, Boolean> map = (Map) viewRoot.getTransientStateHelper().getTransient(
ResourceHandlerImpl.RENDERED_RESOURCES_SET);
if (map != null)
{
map.clear();
}
writer.startUpdate(PartialResponseWriter.RENDER_ALL_MARKER);
for (int i = 0, childCount = viewRoot.getChildCount(); i < childCount; i++)
{
UIComponent comp = viewRoot.getChildren().get(i);
comp.encodeAll(_facesContext);
}
writer.endUpdate();
}
/**
* has to be thrown in many of the methods if the method is called after the instance has been released!
*/
private void assertNotReleased()
{
if (_released)
{
throw new IllegalStateException("Error the FacesContext is already released!");
}
}
@Override
public void release()
{
assertNotReleased();
_visitContextFactory = null;
_executeClientIds = null;
_renderClientIds = null;
_ajaxRequest = null;
_partialRequest = null;
_renderAll = null;
_facesContext = null;
_released = true;
}
private VisitContextFactory getVisitContextFactory()
{
if (_visitContextFactory == null)
{
_visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
}
return _visitContextFactory;
}
@Override
public boolean isResetValues()
{
if (_resetValues == null)
{
String value = _facesContext.getExternalContext().getRequestParameterMap().
get(RESET_VALUES_PARAM_NAME);
_resetValues = "true".equals(value);
}
return _resetValues;
}
private class PhaseAwareVisitCallback implements VisitCallback
{
private PhaseId _phaseId;
private FacesContext _facesContext;
private List<UIComponent> _alreadyUpdatedComponents;
public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId)
{
this._phaseId = phaseId;
this._facesContext = facesContext;
this._alreadyUpdatedComponents = null;
}
public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId,
List<UIComponent> alreadyUpdatedComponents)
{
this._phaseId = phaseId;
this._facesContext = facesContext;
this._alreadyUpdatedComponents = alreadyUpdatedComponents;
}
@Override
public VisitResult visit(VisitContext context, UIComponent target)
{
if (_phaseId == PhaseId.APPLY_REQUEST_VALUES)
{
target.processDecodes(_facesContext);
}
else if (_phaseId == PhaseId.PROCESS_VALIDATIONS)
{
target.processValidators(_facesContext);
}
else if (_phaseId == PhaseId.UPDATE_MODEL_VALUES)
{
target.processUpdates(_facesContext);
}
else if (_phaseId == PhaseId.RENDER_RESPONSE)
{
processRenderComponent(target);
}
else
{
throw new IllegalStateException("PPR Response, illegale phase called");
}
// Return VisitResult.REJECT as processDecodes/Validators/Updates already traverse sub tree
return VisitResult.REJECT;
}
/**
* the rendering subpart of the tree walker
* every component id which is passed down via render must be handled
* here!
*
* @param target the target component to be handled!
*/
private void processRenderComponent(UIComponent target)
{
boolean inUpdate = false;
PartialResponseWriter writer = (PartialResponseWriter) _facesContext.getResponseWriter();
if (this._alreadyUpdatedComponents != null)
{
//Check if the parent was already updated.
UIComponent parent = target;
while (parent != null)
{
if (this._alreadyUpdatedComponents.contains(parent))
{
return;
}
parent = parent.getParent();
}
}
try
{
writer.startUpdate(target.getClientId(_facesContext));
inUpdate = true;
target.encodeAll(_facesContext);
}
catch (IOException ex)
{
Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
if (log.isLoggable(Level.SEVERE))
{
log.log(Level.SEVERE, "IOException for rendering component", ex);
}
}
finally
{
if (inUpdate)
{
try
{
writer.endUpdate();
}
catch (IOException ex)
{
Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
if (log.isLoggable(Level.SEVERE))
{
log.log(Level.SEVERE, "IOException for rendering component, stopping update rendering", ex);
}
}
}
}
}
}
}