1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 jakarta.faces.FactoryFinder;
33 import jakarta.faces.component.UIComponent;
34 import jakarta.faces.component.UIViewParameter;
35 import jakarta.faces.component.UIViewRoot;
36 import jakarta.faces.component.behavior.ClientBehaviorContext;
37 import jakarta.faces.component.visit.VisitCallback;
38 import jakarta.faces.component.visit.VisitContext;
39 import jakarta.faces.component.visit.VisitContextFactory;
40 import jakarta.faces.component.visit.VisitHint;
41 import jakarta.faces.component.visit.VisitResult;
42 import jakarta.faces.context.ExternalContext;
43 import jakarta.faces.context.FacesContext;
44 import jakarta.faces.context.PartialResponseWriter;
45 import jakarta.faces.context.PartialViewContext;
46 import jakarta.faces.context.ResponseWriter;
47 import jakarta.faces.event.PhaseId;
48 import jakarta.faces.lifecycle.ClientWindow;
49 import jakarta.faces.render.RenderKit;
50 import jakarta.faces.render.RenderKitFactory;
51 import jakarta.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 = "jakarta.faces.partial.ajax";
66 private static final String PARTIAL_PROCESS = "partial/process";
67
68
69
70
71
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
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
85
86
87 private Boolean _ajaxRequest = null;
88
89
90
91
92
93
94 private Boolean _iframeRequest = null;
95
96 private Collection<String> _executeClientIds = null;
97 private Collection<String> _renderClientIds = null;
98
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
130
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
195
196
197
198
199
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
240 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
241 {
242
243 String[] clientIds
244 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(executeMode), ' ');
245
246
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
256
257
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
313 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
314 {
315 String[] clientIds
316 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
317
318
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
353
354
355 try
356 {
357 RenderKit renderKit = _facesContext.getRenderKit();
358 if (renderKit == null)
359 {
360
361
362
363
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
380
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
401
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
441
442
443
444
445
446
447
448
449
450
451
452
453
454 PartialResponseWriter writer = _facesContext.getPartialViewContext().getPartialResponseWriter();
455 PartialViewContext pvc = _facesContext.getPartialViewContext();
456
457 ResponseWriter oldWriter = _facesContext.getResponseWriter();
458 boolean inDocument = false;
459
460
461
462 ExternalContext externalContext = _facesContext.getExternalContext();
463 externalContext.setResponseContentType("text/xml");
464 externalContext.addResponseHeader("Pragma", "no-cache");
465 externalContext.addResponseHeader("Cache-control", "no-cache");
466
467
468
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
496 if (renderIds != null && !renderIds.isEmpty())
497 {
498
499 if (renderIds.contains(PartialResponseWriter.RENDER_ALL_MARKER))
500 {
501 processRenderAll(viewRoot, writer);
502 }
503 else
504 {
505
506
507
508
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
544
545
546
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
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
568 writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
569 _facesContext));
570 writer.write("stateless");
571 writer.endUpdate();
572
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("jakarta.faces.Resource");
627 for (UIComponent component : list)
628 {
629 boolean resourceRendered = false;
630 if ("jakarta.faces.resource.Script".equals(component.getRendererType()) ||
631 "jakarta.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
641 component.encodeAll(facesContext);
642 continue;
643 }
644 if ("".equals(resourceName))
645 {
646
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
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
684
685
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
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
795 return VisitResult.REJECT;
796 }
797
798
799
800
801
802
803
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
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 }