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