View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.context.servlet;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.EnumSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  
32  import javax.faces.FactoryFinder;
33  import javax.faces.component.UIComponent;
34  import javax.faces.component.UIViewParameter;
35  import javax.faces.component.UIViewRoot;
36  import javax.faces.component.behavior.ClientBehaviorContext;
37  import javax.faces.component.visit.VisitCallback;
38  import javax.faces.component.visit.VisitContext;
39  import javax.faces.component.visit.VisitContextFactory;
40  import javax.faces.component.visit.VisitHint;
41  import javax.faces.component.visit.VisitResult;
42  import javax.faces.context.ExternalContext;
43  import javax.faces.context.FacesContext;
44  import javax.faces.context.PartialResponseWriter;
45  import javax.faces.context.PartialViewContext;
46  import javax.faces.context.ResponseWriter;
47  import javax.faces.event.PhaseId;
48  import javax.faces.lifecycle.ClientWindow;
49  import javax.faces.render.RenderKit;
50  import javax.faces.render.RenderKitFactory;
51  import javax.faces.view.ViewMetadata;
52  import org.apache.myfaces.application.ResourceHandlerImpl;
53  
54  import org.apache.myfaces.context.PartialResponseWriterImpl;
55  import org.apache.myfaces.context.RequestViewContext;
56  import org.apache.myfaces.renderkit.html.HtmlResponseStateManager;
57  import org.apache.myfaces.shared.renderkit.JSFAttr;
58  import org.apache.myfaces.shared.util.StringUtils;
59  
60  public class PartialViewContextImpl extends PartialViewContext
61  {
62  
63      private static final String FACES_REQUEST = "Faces-Request";
64      private static final String PARTIAL_AJAX = "partial/ajax";
65      private static final String PARTIAL_AJAX_REQ = "javax.faces.partial.ajax";
66      private static final String PARTIAL_PROCESS = "partial/process";
67  
68      /**
69       * Internal extension for
70       * https://issues.apache.org/jira/browse/MYFACES-2841
71       * will be changed for 2.1 to the official marker
72       */
73      private static final String PARTIAL_IFRAME = "org.apache.myfaces.partial.iframe";
74      
75      private static final  Set<VisitHint> PARTIAL_EXECUTE_HINTS = Collections.unmodifiableSet( 
76              EnumSet.of(VisitHint.EXECUTE_LIFECYCLE, VisitHint.SKIP_UNRENDERED));
77      
78      // unrendered have to be skipped, transient definitely must be added to our list!
79      private static final  Set<VisitHint> PARTIAL_RENDER_HINTS = 
80              Collections.unmodifiableSet(EnumSet.of(VisitHint.SKIP_UNRENDERED));
81  
82      private FacesContext _facesContext = null;
83      private boolean _released = false;
84      // Cached values, since their parent methods could be called
85      // many times and the result does not change during the life time
86      // of this object.
87      private Boolean _ajaxRequest = null;
88  
89      /**
90       * Internal extension for
91       * https://issues.apache.org/jira/browse/MYFACES-2841
92       * will be changed for 2.1 to the official marker
93       */
94      private Boolean _iframeRequest = null;
95  
96      private Collection<String> _executeClientIds = null;
97      private Collection<String> _renderClientIds = null;
98      // Values that need to be saved because exists a setXX method 
99      private Boolean _partialRequest = null;
100     private Boolean _renderAll = null;
101     private PartialResponseWriter _partialResponseWriter = null;
102     private VisitContextFactory _visitContextFactory = null;
103     private Boolean _resetValues = null;
104     private List<String> _evalScripts = new ArrayList<String>();
105 
106     public PartialViewContextImpl(FacesContext context)
107     {
108         _facesContext = context;
109     }
110     
111     public PartialViewContextImpl(FacesContext context, 
112             VisitContextFactory visitContextFactory)
113     {
114         _facesContext = context;
115         _visitContextFactory = visitContextFactory;
116     }
117 
118     @Override
119     public boolean isAjaxRequest()
120     {
121         assertNotReleased();
122         if (_ajaxRequest == null)
123         {
124             String requestType = _facesContext.getExternalContext().
125                    getRequestHeaderMap().get(FACES_REQUEST);
126             _ajaxRequest = (requestType != null && PARTIAL_AJAX.equals(requestType));
127             String reqParmamterPartialAjax = _facesContext.getExternalContext().
128                     getRequestParameterMap().get(PARTIAL_AJAX_REQ);
129             //jsdoc reference in an ajax request the javax.faces.partial.ajax must be set as ajax parameter
130             //the other one is Faces-Request == partial/ajax which is basically the same
131             _ajaxRequest = _ajaxRequest || reqParmamterPartialAjax != null;
132         }
133         return _ajaxRequest;
134     }
135 
136     @Override
137     public boolean isExecuteAll()
138     {
139         assertNotReleased();
140 
141         if (isAjaxRequest())
142         {
143             String executeMode = _facesContext.getExternalContext().
144                     getRequestParameterMap().get(
145                     PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
146             if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
147             {
148                 return true;
149             }
150         }
151         return false;
152     }
153 
154     @Override
155     public boolean isPartialRequest()
156     {
157         assertNotReleased();
158 
159         if (_partialRequest == null)
160         {
161             String requestType = _facesContext.getExternalContext().
162                     getRequestHeaderMap().get(FACES_REQUEST);
163             _partialRequest = (requestType != null && PARTIAL_PROCESS.equals(requestType));
164         }
165         return _partialRequest || isAjaxRequest();
166     }
167 
168     @Override
169     public boolean isRenderAll()
170     {
171         assertNotReleased();
172 
173         if (_renderAll == null)
174         {
175             if (isAjaxRequest())
176             {
177                 String executeMode = _facesContext.getExternalContext().
178                         getRequestParameterMap().get(
179                         PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
180                 if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
181                 {
182                     _renderAll = true;
183                 }
184             }
185             if (_renderAll == null)
186             {
187                 _renderAll = false;
188             }
189         }
190         return _renderAll;
191     }
192 
193     /**
194      * Extension for
195      * https://issues.apache.org/jira/browse/MYFACES-2841
196      * internal extension which detects that the submit is an iframe request
197      * will be changed for the official version which will come in 2.1
198      *
199      * @return true if the current request is an iframe based ajax request
200      */
201     public boolean isIFrameRequest()
202     {
203         if (_iframeRequest == null)
204         {
205             _iframeRequest = _facesContext.getExternalContext().getRequestParameterMap().containsKey(PARTIAL_IFRAME);
206         }
207         return _iframeRequest;
208     }
209 
210     @Override
211     public void setPartialRequest(boolean isPartialRequest)
212     {
213         assertNotReleased();
214 
215         _partialRequest = isPartialRequest;
216 
217     }
218 
219     @Override
220     public void setRenderAll(boolean renderAll)
221     {
222         assertNotReleased();
223 
224         _renderAll = renderAll;
225     }
226 
227     @Override
228     public Collection<String> getExecuteIds()
229     {
230         assertNotReleased();
231 
232         if (_executeClientIds == null)
233         {
234             String executeMode = _facesContext.getExternalContext().
235                     getRequestParameterMap().get(
236                     PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
237 
238             if (executeMode != null && !"".equals(executeMode) &&
239                     //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode) &&
240                     !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
241             {
242 
243                 String[] clientIds
244                         = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(executeMode), ' ');
245 
246                 //The collection must be mutable
247                 List<String> tempList = new ArrayList<String>();
248                 for (String clientId : clientIds)
249                 {
250                     if (clientId.length() > 0)
251                     {
252                         tempList.add(clientId);
253                     }
254                 }
255                 // The "javax.faces.source" parameter needs to be added to the list of
256                 // execute ids if missing (otherwise, we'd never execute an action associated
257                 // with, e.g., a button).
258 
259                 String source = _facesContext.getExternalContext().getRequestParameterMap().get
260                         (ClientBehaviorContext.BEHAVIOR_SOURCE_PARAM_NAME);
261 
262                 if (source != null)
263                 {
264                     source = source.trim();
265 
266                     if (!tempList.contains(source))
267                     {
268                         tempList.add(source);
269                     }
270                 }
271 
272                 _executeClientIds = tempList;
273             }
274             else
275             {
276                 _executeClientIds = new ArrayList<String>();
277             }
278         }
279         return _executeClientIds;
280     }
281 
282     private String _replaceTabOrEnterCharactersWithSpaces(String mode)
283     {
284         StringBuilder builder = new StringBuilder(mode.length());
285         for (int i = 0; i < mode.length(); i++)
286         {
287             if (mode.charAt(i) == '\t' ||
288                     mode.charAt(i) == '\n')
289             {
290                 builder.append(' ');
291             }
292             else
293             {
294                 builder.append(mode.charAt(i));
295             }
296         }
297         return builder.toString();
298     }
299 
300     @Override
301     public Collection<String> getRenderIds()
302     {
303         assertNotReleased();
304 
305         if (_renderClientIds == null)
306         {
307             String renderMode = _facesContext.getExternalContext().
308                     getRequestParameterMap().get(
309                     PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
310 
311             if (renderMode != null && !"".equals(renderMode) &&
312                     //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode) &&
313                     !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
314             {
315                 String[] clientIds
316                         = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
317 
318                 //The collection must be mutable
319                 List<String> tempList = new ArrayList<String>();
320                 for (String clientId : clientIds)
321                 {
322                     if (clientId.length() > 0)
323                     {
324                         tempList.add(clientId);
325                     }
326                 }
327                 _renderClientIds = tempList;
328             }
329             else
330             {
331                 _renderClientIds = new ArrayList<String>();
332 
333                 if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
334                 {
335                     _renderClientIds.add(PartialResponseWriter.RENDER_ALL_MARKER);
336                 }
337             }
338         }
339         return _renderClientIds;
340     }
341 
342     @Override
343     public PartialResponseWriter getPartialResponseWriter()
344     {
345         assertNotReleased();
346 
347         if (_partialResponseWriter == null)
348         {
349             ResponseWriter responseWriter = _facesContext.getResponseWriter();
350             if (responseWriter == null)
351             {
352                 // This case happens when getPartialResponseWriter() is called before
353                 // render phase, like in ExternalContext.redirect(). We have to create a
354                 // ResponseWriter from the RenderKit and then wrap if necessary. 
355                 try
356                 {
357                     RenderKit renderKit = _facesContext.getRenderKit();
358                     if (renderKit == null)
359                     {
360                         // If the viewRoot was set to null by some reason, or there is no 
361                         // renderKitId on that view, this could be still an ajax redirect,
362                         // so we have to try to calculate the renderKitId and return a 
363                         // RenderKit instance, to send the response.
364                         String renderKitId
365                                 = _facesContext.getApplication().getViewHandler().calculateRenderKitId(_facesContext);
366                         RenderKitFactory rkf
367                                 = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
368                         renderKit = rkf.getRenderKit(_facesContext, renderKitId);
369                     }
370                     responseWriter = renderKit.createResponseWriter(
371                             _facesContext.getExternalContext().getResponseOutputWriter(), "text/xml",
372                             _facesContext.getExternalContext().getRequestCharacterEncoding());
373                 }
374                 catch (IOException e)
375                 {
376                     throw new IllegalStateException("Cannot create Partial Response Writer", e);
377                 }
378             }
379             // It is possible that the RenderKit return a PartialResponseWriter instance when 
380             // createResponseWriter,  so we should cast here for it and prevent double wrapping.
381             if (responseWriter instanceof PartialResponseWriter)
382             {
383                 _partialResponseWriter = (PartialResponseWriter) responseWriter;
384             }
385             else
386             {
387                 _partialResponseWriter = new PartialResponseWriterImpl(responseWriter);
388             }
389         }
390         return _partialResponseWriter;
391     }
392 
393     @Override
394     public List<String> getEvalScripts()
395     {
396         return _evalScripts;
397     }
398 
399     /**
400      * process the partial response
401      * allowed phase ids according to the spec
402      *
403      *
404      */
405     @Override
406     public void processPartial(PhaseId phaseId)
407     {
408         assertNotReleased();
409 
410         UIViewRoot viewRoot = _facesContext.getViewRoot();
411 
412         if (phaseId == PhaseId.APPLY_REQUEST_VALUES
413                 || phaseId == PhaseId.PROCESS_VALIDATIONS
414                 || phaseId == PhaseId.UPDATE_MODEL_VALUES)
415         {
416             processPartialExecute(viewRoot, phaseId);
417         }
418         else if (phaseId == PhaseId.RENDER_RESPONSE)
419         {
420             processPartialRendering(viewRoot, phaseId);
421         }
422     }
423 
424     private void processPartialExecute(UIViewRoot viewRoot, PhaseId phaseId)
425     {
426         PartialViewContext pvc = _facesContext.getPartialViewContext();
427         Collection<String> executeIds = pvc.getExecuteIds();
428         if (executeIds == null || executeIds.isEmpty())
429         {
430             return;
431         }
432         
433         VisitContext visitCtx = getVisitContextFactory().getVisitContext(_facesContext, executeIds, 
434                 PARTIAL_EXECUTE_HINTS);
435         viewRoot.visitTree(visitCtx, new PhaseAwareVisitCallback(_facesContext, phaseId));
436     }
437 
438     private void processPartialRendering(UIViewRoot viewRoot, PhaseId phaseId)
439     {
440         //TODO process partial rendering
441         //https://issues.apache.org/jira/browse/MYFACES-2118
442         //Collection<String> renderIds = getRenderIds();
443 
444         // We need to always update the view state marker when processing partial
445         // rendering, because there is no way to check when the state has been changed
446         // or not. Anyway, if we return empty response, according to the spec a javascript
447         // message displayed, so we need to return something.
448         //if (renderIds == null || renderIds.isEmpty()) {
449         //    return;
450         //}
451 
452         // note that we cannot use this.getPartialResponseWriter(), because
453         // this could cause problems if PartialResponseWriter is wrapped
454         PartialResponseWriter writer = _facesContext.getPartialViewContext().getPartialResponseWriter();
455         PartialViewContext pvc = _facesContext.getPartialViewContext();
456 
457         ResponseWriter oldWriter = _facesContext.getResponseWriter();
458         boolean inDocument = false;
459 
460         //response type = text/xml
461         //no caching and no timeout if possible!
462         ExternalContext externalContext = _facesContext.getExternalContext();
463         externalContext.setResponseContentType("text/xml");
464         externalContext.addResponseHeader("Pragma", "no-cache");
465         externalContext.addResponseHeader("Cache-control", "no-cache");
466         //under normal circumstances pragma should be enough, IE needs
467         //a special treatment!
468         //http://support.microsoft.com/kb/234067
469         externalContext.addResponseHeader("Expires", "-1");
470 
471         try
472         {
473             String currentEncoding = writer.getCharacterEncoding();
474             writer.writePreamble("<?xml version=\"1.0\" encoding=\""+
475                 (currentEncoding == null ? "UTF-8" : currentEncoding) +"\"?>");
476             writer.startDocument();
477             
478             writer.writeAttribute("id", viewRoot.getContainerClientId(_facesContext),"id");
479             
480             inDocument = true;
481             _facesContext.setResponseWriter(writer);
482             
483             if (isResetValues())
484             {
485                 viewRoot.resetValues(_facesContext, getRenderIds());
486             }
487 
488             if (pvc.isRenderAll())
489             {
490                 processRenderAll(viewRoot, writer);
491             }
492             else
493             {
494                 Collection<String> renderIds = pvc.getRenderIds();
495                 //Only apply partial visit if we have ids to traverse
496                 if (renderIds != null && !renderIds.isEmpty())
497                 {
498                     // render=@all, so output the body.
499                     if (renderIds.contains(PartialResponseWriter.RENDER_ALL_MARKER))
500                     {
501                         processRenderAll(viewRoot, writer);
502                     }
503                     else
504                     {
505                         // In JSF 2.3 it was added javax.faces.Resource as an update target to add scripts or
506                         // stylesheets inside <head> tag. In that sense 
507                         // org.apache.myfaces.STRICT_JSF_2_REFRESH_TARGET_AJAX web config param, which was a 
508                         // workaround for dynamic refresh can be deprecated.
509                         
510                         List<UIComponent> updatedComponents = new ArrayList<UIComponent>();
511                         RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
512                         processRenderResource(_facesContext, writer, rvc, updatedComponents, "head");
513                         processRenderResource(_facesContext, writer, rvc, updatedComponents, "body");
514                         processRenderResource(_facesContext, writer, rvc, updatedComponents, "form");
515 
516                         VisitContext visitCtx = getVisitContextFactory().getVisitContext(
517                                 _facesContext, renderIds, PARTIAL_RENDER_HINTS);
518                         viewRoot.visitTree(visitCtx,
519                                            new PhaseAwareVisitCallback(_facesContext, phaseId, updatedComponents));
520                     }
521                 }
522                 else
523                 {
524                     List<UIComponent> updatedComponents = new ArrayList<UIComponent>();
525                     RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
526                     processRenderResource(_facesContext, writer, rvc, updatedComponents, "head");
527                     processRenderResource(_facesContext, writer, rvc, updatedComponents, "body");
528                     processRenderResource(_facesContext, writer, rvc, updatedComponents, "form");
529                 }
530                 
531                 List<String> evalScripts = pvc.getEvalScripts();
532                 if (evalScripts != null && evalScripts.size() > 0)
533                 {
534                     for (String script : evalScripts)
535                     {
536                         writer.startEval();
537                         writer.write(script);
538                         writer.endEval();
539                     }
540                 }
541             }
542 
543             // invoke encodeAll() on every UIViewParameter in the view to 
544             // enable every UIViewParameter to save its value in the state
545             // just like UIViewRoot.encodeEnd() does on a normal request
546             // (see MYFACES-2645 for details)
547             Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(viewRoot);
548             if (!viewParams.isEmpty())
549             {
550                 for (UIViewParameter param : viewParams)
551                 {
552                     param.encodeAll(_facesContext);
553                 }
554             }
555 
556             //Retrieve the state and apply it if it is not null.
557             String viewState = _facesContext.getApplication().getStateManager().getViewState(_facesContext);
558             if (viewState != null)
559             {
560                 writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
561                     _facesContext));
562                 writer.write(viewState);
563                 writer.endUpdate();
564             }
565             else if (viewRoot.isTransient())
566             {
567                 //TODO: fix javascript side, so the field is not removed on ajax form update
568                 writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
569                     _facesContext));
570                 writer.write("stateless");
571                 writer.endUpdate();
572                 //END TODO
573             }
574             
575             
576             ClientWindow cw = _facesContext.getExternalContext().getClientWindow();
577             if (cw != null)
578             {
579                 writer.startUpdate(HtmlResponseStateManager.generateUpdateClientWindowId(
580                     _facesContext));
581                 writer.write(cw.getId());
582                 writer.endUpdate();
583             }
584         }
585         catch (IOException ex)
586         {
587             Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
588             if (log.isLoggable(Level.SEVERE))
589             {
590                 log.log(Level.SEVERE, "", ex);
591             }
592 
593         }
594         finally
595         {
596             try
597             {
598                 if (inDocument)
599                 {
600                     writer.endDocument();
601                 }
602                 writer.flush();
603             }
604             catch (IOException ex)
605             {
606                 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
607                 if (log.isLoggable(Level.SEVERE))
608                 {
609                     log.log(Level.SEVERE, "", ex);
610                 }
611             }
612 
613             _facesContext.setResponseWriter(oldWriter);
614         }
615 
616     }
617     
618     private void processRenderResource(FacesContext facesContext, PartialResponseWriter writer, RequestViewContext rvc, 
619             List<UIComponent> updatedComponents, String target) throws IOException
620     {
621         if (rvc.isRenderTarget(target))
622         {
623             List<UIComponent> list = rvc.getRenderTargetComponentList(target);
624             if (list != null && !list.isEmpty())
625             {
626                 writer.startUpdate("javax.faces.Resource");
627                 for (UIComponent component : list)
628                 {
629                     boolean resourceRendered = false;
630                     if ("javax.faces.resource.Script".equals(component.getRendererType()) ||
631                         "javax.faces.resource.Stylesheet".equals(component.getRendererType()))
632                     {
633                         String resourceName = (String) 
634                                 component.getAttributes().get(JSFAttr.NAME_ATTR);
635                         String libraryName = (String) 
636                                 component.getAttributes().get(JSFAttr.LIBRARY_ATTR);
637 
638                         if (resourceName == null)
639                         {
640                             // No resource, render all
641                             component.encodeAll(facesContext);
642                             continue;
643                         }
644                         if ("".equals(resourceName))
645                         {
646                             // No resource, render all
647                             component.encodeAll(facesContext);
648                             continue;
649                         }
650 
651                         int index = resourceName.indexOf('?');
652                         if (index >= 0)
653                         {
654                             resourceName = resourceName.substring(0, index);
655                         }
656                         // Is resource, render only if it has not been rendered before.
657                         if (!_facesContext.getApplication().getResourceHandler().isResourceRendered(
658                                 _facesContext, resourceName, libraryName))
659                         {
660                             component.encodeAll(facesContext);
661                         }
662                     }
663                     else
664                     {
665                         component.encodeAll(facesContext);
666                     }
667                     if (!resourceRendered)
668                     {
669                         if (updatedComponents == null)
670                         {
671                             updatedComponents = new ArrayList<UIComponent>();
672                         }
673                         updatedComponents.add(component);
674                     }
675                 }
676                 writer.endUpdate();
677             }
678         }
679     }
680 
681     private void processRenderAll(UIViewRoot viewRoot, PartialResponseWriter writer) throws IOException
682     {
683         // Before render all we need to clear rendered resources set to be sure every component resource is
684         // rendered. Remember renderAll means the whole page is replaced, so everything inside <head> is replaced.
685         // and there is no way to diff between the old and the new content of <head>.
686         Map<String, Boolean> map = (Map) viewRoot.getTransientStateHelper().getTransient(
687                 ResourceHandlerImpl.RENDERED_RESOURCES_SET);
688         if (map != null)
689         {
690             map.clear();
691         }
692         
693         writer.startUpdate(PartialResponseWriter.RENDER_ALL_MARKER);
694         for (int i = 0, childCount = viewRoot.getChildCount(); i < childCount; i++)
695         {
696             UIComponent comp = viewRoot.getChildren().get(i);
697             comp.encodeAll(_facesContext);
698         }
699         writer.endUpdate();
700     }
701 
702     /**
703      * has to be thrown in many of the methods if the method is called after the instance has been released!
704      */
705     private void assertNotReleased()
706     {
707         if (_released)
708         {
709             throw new IllegalStateException("Error the FacesContext is already released!");
710         }
711     }
712 
713     @Override
714     public void release()
715     {
716         assertNotReleased();
717         _visitContextFactory = null;
718         _executeClientIds = null;
719         _renderClientIds = null;
720         _ajaxRequest = null;
721         _partialRequest = null;
722         _renderAll = null;
723         _facesContext = null;
724         _released = true;
725     }
726     
727     private VisitContextFactory getVisitContextFactory()
728     {
729         if (_visitContextFactory == null)
730         {
731             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
732         }
733         return _visitContextFactory;
734     }
735 
736     @Override
737     public boolean isResetValues()
738     {
739         if (_resetValues == null)
740         {
741             String value = _facesContext.getExternalContext().getRequestParameterMap().
742                 get(RESET_VALUES_PARAM_NAME);
743             _resetValues = "true".equals(value);
744         }
745         return _resetValues;
746     }
747 
748     private class PhaseAwareVisitCallback implements VisitCallback
749     {
750 
751         private PhaseId _phaseId;
752         private FacesContext _facesContext;
753         private List<UIComponent> _alreadyUpdatedComponents;
754 
755         public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId)
756         {
757             this._phaseId = phaseId;
758             this._facesContext = facesContext;
759             this._alreadyUpdatedComponents = null;
760         }
761 
762         public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId,
763                                        List<UIComponent> alreadyUpdatedComponents)
764         {
765             this._phaseId = phaseId;
766             this._facesContext = facesContext;
767             this._alreadyUpdatedComponents = alreadyUpdatedComponents;
768         }
769 
770         @Override
771         public VisitResult visit(VisitContext context, UIComponent target)
772         {
773             if (_phaseId == PhaseId.APPLY_REQUEST_VALUES)
774             {
775                 target.processDecodes(_facesContext);
776             }
777             else if (_phaseId == PhaseId.PROCESS_VALIDATIONS)
778             {
779                 target.processValidators(_facesContext);
780             }
781             else if (_phaseId == PhaseId.UPDATE_MODEL_VALUES)
782             {
783                 target.processUpdates(_facesContext);
784             }
785             else if (_phaseId == PhaseId.RENDER_RESPONSE)
786             {
787                 processRenderComponent(target);
788             }
789             else
790             {
791                 throw new IllegalStateException("PPR Response, illegale phase called");
792             }
793 
794             // Return VisitResult.REJECT as processDecodes/Validators/Updates already traverse sub tree
795             return VisitResult.REJECT;
796         }
797 
798         /**
799          * the rendering subpart of the tree walker
800          * every component id which is passed down via render must be handled
801          * here!
802          *
803          * @param target the target component to be handled!
804          */
805         private void processRenderComponent(UIComponent target)
806         {
807             boolean inUpdate = false;
808             PartialResponseWriter writer = (PartialResponseWriter) _facesContext.getResponseWriter();
809             if (this._alreadyUpdatedComponents != null)
810             {
811                 //Check if the parent was already updated.
812                 UIComponent parent = target;
813                 while (parent != null)
814                 {
815                     if (this._alreadyUpdatedComponents.contains(parent))
816                     {
817                         return;
818                     }
819                     parent = parent.getParent();
820                 }
821             }
822             try
823             {
824                 writer.startUpdate(target.getClientId(_facesContext));
825                 inUpdate = true;
826                 target.encodeAll(_facesContext);
827             }
828             catch (IOException ex)
829             {
830                 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
831                 if (log.isLoggable(Level.SEVERE))
832                 {
833                     log.log(Level.SEVERE, "IOException for rendering component", ex);
834                 }
835             }
836             finally
837             {
838                 if (inUpdate)
839                 {
840                     try
841                     {
842                         writer.endUpdate();
843                     }
844                     catch (IOException ex)
845                     {
846                         Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
847                         if (log.isLoggable(Level.SEVERE))
848                         {
849                             log.log(Level.SEVERE, "IOException for rendering component, stopping update rendering", ex);
850                         }
851                     }
852                 }
853             }
854         }
855     }
856 }