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.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.lifecycle.ClientWindow;
49 import javax.faces.render.RenderKit;
50 import javax.faces.render.RenderKitFactory;
51 import javax.faces.view.ViewMetadata;
52
53 import org.apache.myfaces.context.PartialResponseWriterImpl;
54 import org.apache.myfaces.context.RequestViewContext;
55 import org.apache.myfaces.renderkit.html.HtmlResponseStateManager;
56 import org.apache.myfaces.shared.config.MyfacesConfig;
57 import org.apache.myfaces.shared.util.ExternalContextUtils;
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 private static final String SOURCE_PARAM_NAME = "javax.faces.source";
68 private static final String JAVAX_FACES_REQUEST = "javax.faces.request";
69
70
71
72
73
74
75 private static final String PARTIAL_IFRAME = "org.apache.myfaces.partial.iframe";
76
77 private static final Set<VisitHint> PARTIAL_EXECUTE_HINTS = Collections.unmodifiableSet(
78 EnumSet.of(VisitHint.EXECUTE_LIFECYCLE, VisitHint.SKIP_UNRENDERED));
79
80
81 private static final Set<VisitHint> PARTIAL_RENDER_HINTS =
82 Collections.unmodifiableSet(EnumSet.of(VisitHint.SKIP_UNRENDERED));
83
84 private FacesContext _facesContext = null;
85 private boolean _released = false;
86
87
88
89 private Boolean _ajaxRequest = null;
90
91
92
93
94
95
96 private Boolean _iframeRequest = null;
97
98 private Collection<String> _executeClientIds = null;
99 private Collection<String> _renderClientIds = null;
100
101 private Boolean _partialRequest = null;
102 private Boolean _renderAll = null;
103 private PartialResponseWriter _partialResponseWriter = null;
104 private VisitContextFactory _visitContextFactory = null;
105 private Boolean _resetValues = null;
106
107 public PartialViewContextImpl(FacesContext context)
108 {
109 _facesContext = context;
110 }
111
112 public PartialViewContextImpl(FacesContext context,
113 VisitContextFactory visitContextFactory)
114 {
115 _facesContext = context;
116 _visitContextFactory = visitContextFactory;
117 }
118
119 @Override
120 public boolean isAjaxRequest()
121 {
122 assertNotReleased();
123 if (_ajaxRequest == null)
124 {
125 String requestType = _facesContext.getExternalContext().
126 getRequestHeaderMap().get(FACES_REQUEST);
127 _ajaxRequest = (requestType != null && PARTIAL_AJAX.equals(requestType));
128 String reqParmamterPartialAjax = _facesContext.getExternalContext().
129 getRequestParameterMap().get(PARTIAL_AJAX_REQ);
130
131
132 _ajaxRequest = _ajaxRequest || reqParmamterPartialAjax != null;
133 }
134 return _ajaxRequest;
135 }
136
137 @Override
138 public boolean isExecuteAll()
139 {
140 assertNotReleased();
141
142 if (isAjaxRequest())
143 {
144 String executeMode = _facesContext.getExternalContext().
145 getRequestParameterMap().get(
146 PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
147 if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
148 {
149 return true;
150 }
151 }
152 return false;
153 }
154
155 @Override
156 public boolean isPartialRequest()
157 {
158 assertNotReleased();
159
160 if (_partialRequest == null)
161 {
162 String requestType = _facesContext.getExternalContext().
163 getRequestHeaderMap().get(FACES_REQUEST);
164 _partialRequest = (requestType != null && PARTIAL_PROCESS.equals(requestType));
165 }
166 return _partialRequest || isAjaxRequest();
167 }
168
169 @Override
170 public boolean isRenderAll()
171 {
172 assertNotReleased();
173
174 if (_renderAll == null)
175 {
176 if (isAjaxRequest())
177 {
178 String executeMode = _facesContext.getExternalContext().
179 getRequestParameterMap().get(
180 PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
181 if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
182 {
183 _renderAll = true;
184 }
185 }
186 if (_renderAll == null)
187 {
188 _renderAll = false;
189 }
190 }
191 return _renderAll;
192 }
193
194
195
196
197
198
199
200
201
202 public boolean isIFrameRequest()
203 {
204 if (_iframeRequest == null)
205 {
206 _iframeRequest = _facesContext.getExternalContext().getRequestParameterMap().containsKey(PARTIAL_IFRAME);
207 }
208 return _iframeRequest;
209 }
210
211 @Override
212 public void setPartialRequest(boolean isPartialRequest)
213 {
214 assertNotReleased();
215
216 _partialRequest = isPartialRequest;
217
218 }
219
220 @Override
221 public void setRenderAll(boolean renderAll)
222 {
223 assertNotReleased();
224
225 _renderAll = renderAll;
226 }
227
228 @Override
229 public Collection<String> getExecuteIds()
230 {
231 assertNotReleased();
232
233 if (_executeClientIds == null)
234 {
235 String executeMode = _facesContext.getExternalContext().
236 getRequestParameterMap().get(
237 PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
238
239 if (executeMode != null && !"".equals(executeMode) &&
240
241 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
242 {
243
244 String[] clientIds
245 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(executeMode), ' ');
246
247
248 List<String> tempList = new ArrayList<String>();
249 for (String clientId : clientIds)
250 {
251 if (clientId.length() > 0)
252 {
253 tempList.add(clientId);
254 }
255 }
256
257
258
259
260 String source = _facesContext.getExternalContext().getRequestParameterMap().get
261 (PartialViewContextImpl.SOURCE_PARAM_NAME);
262
263 if (source != null)
264 {
265 source = source.trim();
266
267 if (!tempList.contains(source))
268 {
269 tempList.add(source);
270 }
271 }
272
273 _executeClientIds = tempList;
274 }
275 else
276 {
277 _executeClientIds = new ArrayList<String>();
278 }
279 }
280 return _executeClientIds;
281 }
282
283 private String _replaceTabOrEnterCharactersWithSpaces(String mode)
284 {
285 StringBuilder builder = new StringBuilder(mode.length());
286 for (int i = 0; i < mode.length(); i++)
287 {
288 if (mode.charAt(i) == '\t' ||
289 mode.charAt(i) == '\n')
290 {
291 builder.append(' ');
292 }
293 else
294 {
295 builder.append(mode.charAt(i));
296 }
297 }
298 return builder.toString();
299 }
300
301 @Override
302 public Collection<String> getRenderIds()
303 {
304 assertNotReleased();
305
306 if (_renderClientIds == null)
307 {
308 String renderMode = _facesContext.getExternalContext().
309 getRequestParameterMap().get(
310 PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
311
312 if (renderMode != null && !"".equals(renderMode) &&
313
314 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
315 {
316 String[] clientIds
317 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
318
319
320 List<String> tempList = new ArrayList<String>();
321 for (String clientId : clientIds)
322 {
323 if (clientId.length() > 0)
324 {
325 tempList.add(clientId);
326 }
327 }
328 _renderClientIds = tempList;
329 }
330 else
331 {
332 _renderClientIds = new ArrayList<String>();
333
334 if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
335 {
336 _renderClientIds.add(PartialResponseWriter.RENDER_ALL_MARKER);
337 }
338 }
339 }
340 return _renderClientIds;
341 }
342
343 @Override
344 public PartialResponseWriter getPartialResponseWriter()
345 {
346 assertNotReleased();
347
348 if (_partialResponseWriter == null)
349 {
350 ResponseWriter responseWriter = _facesContext.getResponseWriter();
351 if (responseWriter == null)
352 {
353
354
355
356 try
357 {
358 RenderKit renderKit = _facesContext.getRenderKit();
359 if (renderKit == null)
360 {
361
362
363
364
365 String renderKitId
366 = _facesContext.getApplication().getViewHandler().calculateRenderKitId(_facesContext);
367 RenderKitFactory rkf
368 = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
369 renderKit = rkf.getRenderKit(_facesContext, renderKitId);
370 }
371 responseWriter = renderKit.createResponseWriter(
372 _facesContext.getExternalContext().getResponseOutputWriter(), "text/xml",
373 _facesContext.getExternalContext().getRequestCharacterEncoding());
374 }
375 catch (IOException e)
376 {
377 throw new IllegalStateException("Cannot create Partial Response Writer", e);
378 }
379 }
380
381
382 if (responseWriter instanceof PartialResponseWriter)
383 {
384 _partialResponseWriter = (PartialResponseWriter) responseWriter;
385 }
386 else
387 {
388 _partialResponseWriter = new PartialResponseWriterImpl(responseWriter);
389 }
390 }
391 return _partialResponseWriter;
392 }
393
394
395
396
397
398
399
400 @Override
401 public void processPartial(PhaseId phaseId)
402 {
403 assertNotReleased();
404
405 UIViewRoot viewRoot = _facesContext.getViewRoot();
406
407 if (phaseId == PhaseId.APPLY_REQUEST_VALUES
408 || phaseId == PhaseId.PROCESS_VALIDATIONS
409 || phaseId == PhaseId.UPDATE_MODEL_VALUES)
410 {
411 processPartialExecute(viewRoot, phaseId);
412 }
413 else if (phaseId == PhaseId.RENDER_RESPONSE)
414 {
415 processPartialRendering(viewRoot, phaseId);
416 }
417 }
418
419 private void processPartialExecute(UIViewRoot viewRoot, PhaseId phaseId)
420 {
421 PartialViewContext pvc = _facesContext.getPartialViewContext();
422 Collection<String> executeIds = pvc.getExecuteIds();
423 if (executeIds == null || executeIds.isEmpty())
424 {
425 return;
426 }
427
428 VisitContext visitCtx = getVisitContextFactory().getVisitContext(_facesContext, executeIds,
429 PARTIAL_EXECUTE_HINTS);
430 viewRoot.visitTree(visitCtx, new PhaseAwareVisitCallback(_facesContext, phaseId));
431 }
432
433 private void processPartialRendering(UIViewRoot viewRoot, PhaseId phaseId)
434 {
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 PartialResponseWriter writer = _facesContext.getPartialViewContext().getPartialResponseWriter();
450 PartialViewContext pvc = _facesContext.getPartialViewContext();
451
452 ResponseWriter oldWriter = _facesContext.getResponseWriter();
453 boolean inDocument = false;
454
455
456
457 ExternalContext externalContext = _facesContext.getExternalContext();
458 externalContext.setResponseContentType("text/xml");
459 externalContext.addResponseHeader("Pragma", "no-cache");
460 externalContext.addResponseHeader("Cache-control", "no-cache");
461
462
463
464 externalContext.addResponseHeader("Expires", "-1");
465
466 try
467 {
468 String currentEncoding = writer.getCharacterEncoding();
469 writer.writePreamble("<?xml version=\"1.0\" encoding=\""+
470 (currentEncoding == null ? "UTF-8" : currentEncoding) +"\"?>");
471 writer.startDocument();
472
473 writer.writeAttribute("id", viewRoot.getContainerClientId(_facesContext),"id");
474
475 inDocument = true;
476 _facesContext.setResponseWriter(writer);
477
478 if (isResetValues())
479 {
480 viewRoot.resetValues(_facesContext, getRenderIds());
481 }
482
483 if (pvc.isRenderAll())
484 {
485 processRenderAll(viewRoot, writer);
486 }
487 else
488 {
489 Collection<String> renderIds = pvc.getRenderIds();
490
491 if (renderIds != null && !renderIds.isEmpty())
492 {
493
494 if (renderIds.contains(PartialResponseWriter.RENDER_ALL_MARKER))
495 {
496 processRenderAll(viewRoot, writer);
497 }
498 else
499 {
500 List<UIComponent> updatedComponents = null;
501 if (!ExternalContextUtils.isPortlet(_facesContext.getExternalContext()) &&
502 MyfacesConfig.getCurrentInstance(externalContext).isStrictJsf2RefreshTargetAjax())
503 {
504 RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
505 if (rvc.isRenderTarget("head"))
506 {
507 UIComponent head = findHeadComponent(viewRoot);
508 if (head != null)
509 {
510 writer.startUpdate("javax.faces.ViewHead");
511 head.encodeAll(_facesContext);
512 writer.endUpdate();
513 if (updatedComponents == null)
514 {
515 updatedComponents = new ArrayList<UIComponent>();
516 }
517 updatedComponents.add(head);
518 }
519 }
520 if (rvc.isRenderTarget("body") || rvc.isRenderTarget("form"))
521 {
522 UIComponent body = findBodyComponent(viewRoot);
523 if (body != null)
524 {
525 writer.startUpdate("javax.faces.ViewBody");
526 body.encodeAll(_facesContext);
527 writer.endUpdate();
528 if (updatedComponents == null)
529 {
530 updatedComponents = new ArrayList<UIComponent>();
531 }
532 updatedComponents.add(body);
533 }
534 }
535 }
536
537 VisitContext visitCtx = getVisitContextFactory().getVisitContext(
538 _facesContext, renderIds, PARTIAL_RENDER_HINTS);
539 viewRoot.visitTree(visitCtx,
540 new PhaseAwareVisitCallback(_facesContext, phaseId, updatedComponents));
541 }
542 }
543 else if (!ExternalContextUtils.isPortlet(_facesContext.getExternalContext()) &&
544 MyfacesConfig.getCurrentInstance(externalContext).isStrictJsf2RefreshTargetAjax())
545 {
546 RequestViewContext rvc = RequestViewContext.getCurrentInstance(_facesContext);
547 if (rvc.isRenderTarget("head"))
548 {
549 UIComponent head = findHeadComponent(viewRoot);
550 if (head != null)
551 {
552 writer.startUpdate("javax.faces.ViewHead");
553 head.encodeAll(_facesContext);
554 writer.endUpdate();
555 }
556 }
557 if (rvc.isRenderTarget("body") || rvc.isRenderTarget("form"))
558 {
559 UIComponent body = findBodyComponent(viewRoot);
560 if (body != null)
561 {
562 writer.startUpdate("javax.faces.ViewBody");
563 body.encodeAll(_facesContext);
564 writer.endUpdate();
565 }
566 }
567 }
568 }
569
570
571
572
573
574 Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(viewRoot);
575 if (!viewParams.isEmpty())
576 {
577 for (UIViewParameter param : viewParams)
578 {
579 param.encodeAll(_facesContext);
580 }
581 }
582
583
584 String viewState = _facesContext.getApplication().getStateManager().getViewState(_facesContext);
585 if (viewState != null)
586 {
587 writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
588 _facesContext));
589 writer.write(viewState);
590 writer.endUpdate();
591 }
592 else if (viewRoot.isTransient())
593 {
594
595 writer.startUpdate(HtmlResponseStateManager.generateUpdateViewStateId(
596 _facesContext));
597 writer.write("stateless");
598 writer.endUpdate();
599
600 }
601
602
603 ClientWindow cw = _facesContext.getExternalContext().getClientWindow();
604 if (cw != null)
605 {
606 writer.startUpdate(HtmlResponseStateManager.generateUpdateClientWindowId(
607 _facesContext));
608 writer.write(cw.getId());
609 writer.endUpdate();
610 }
611 }
612 catch (IOException ex)
613 {
614 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
615 if (log.isLoggable(Level.SEVERE))
616 {
617 log.log(Level.SEVERE, "", ex);
618 }
619
620 }
621 finally
622 {
623 try
624 {
625 if (inDocument)
626 {
627 writer.endDocument();
628 }
629 writer.flush();
630 }
631 catch (IOException ex)
632 {
633 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
634 if (log.isLoggable(Level.SEVERE))
635 {
636 log.log(Level.SEVERE, "", ex);
637 }
638 }
639
640 _facesContext.setResponseWriter(oldWriter);
641 }
642
643 }
644
645 private void processRenderAll(UIViewRoot viewRoot, PartialResponseWriter writer) throws IOException
646 {
647
648 writer.startUpdate(PartialResponseWriter.RENDER_ALL_MARKER);
649
650
651
652
653
654
655
656
657
658
659
660 for (int i = 0, childCount = viewRoot.getChildCount(); i < childCount; i++)
661 {
662 UIComponent comp = viewRoot.getChildren().get(i);
663 comp.encodeAll(_facesContext);
664 }
665 writer.endUpdate();
666 }
667
668
669
670
671 private void assertNotReleased()
672 {
673 if (_released)
674 {
675 throw new IllegalStateException("Error the FacesContext is already released!");
676 }
677 }
678
679 @Override
680 public void release()
681 {
682 assertNotReleased();
683 _visitContextFactory = null;
684 _executeClientIds = null;
685 _renderClientIds = null;
686 _ajaxRequest = null;
687 _partialRequest = null;
688 _renderAll = null;
689 _facesContext = null;
690 _released = true;
691 }
692
693 private UIComponent findHeadComponent(UIViewRoot root)
694 {
695 for (UIComponent child : root.getChildren())
696 {
697 if (child instanceof HtmlHead)
698 {
699 return child;
700 }
701 else
702 {
703 for (UIComponent grandchild : child.getChildren())
704 {
705 if (grandchild instanceof HtmlHead)
706 {
707 return grandchild;
708 }
709 }
710 }
711 }
712 return null;
713 }
714
715 private UIComponent findBodyComponent(UIViewRoot root)
716 {
717 for (UIComponent child : root.getChildren())
718 {
719 if (child instanceof HtmlBody)
720 {
721 return child;
722 }
723 else
724 {
725 for (UIComponent grandchild : child.getChildren())
726 {
727 if (grandchild instanceof HtmlBody)
728 {
729 return grandchild;
730 }
731 }
732 }
733 }
734 return null;
735 }
736
737 private VisitContextFactory getVisitContextFactory()
738 {
739 if (_visitContextFactory == null)
740 {
741 _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
742 }
743 return _visitContextFactory;
744 }
745
746 @Override
747 public boolean isResetValues()
748 {
749 if (_resetValues == null)
750 {
751 String value = _facesContext.getExternalContext().getRequestParameterMap().
752 get(RESET_VALUES_PARAM_NAME);
753 _resetValues = "true".equals(value);
754 }
755 return _resetValues;
756 }
757
758 private class PhaseAwareVisitCallback implements VisitCallback
759 {
760
761 private PhaseId _phaseId;
762 private FacesContext _facesContext;
763 private List<UIComponent> _alreadyUpdatedComponents;
764
765 public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId)
766 {
767 this._phaseId = phaseId;
768 this._facesContext = facesContext;
769 this._alreadyUpdatedComponents = null;
770 }
771
772 public PhaseAwareVisitCallback(FacesContext facesContext, PhaseId phaseId,
773 List<UIComponent> alreadyUpdatedComponents)
774 {
775 this._phaseId = phaseId;
776 this._facesContext = facesContext;
777 this._alreadyUpdatedComponents = alreadyUpdatedComponents;
778 }
779
780 public VisitResult visit(VisitContext context, UIComponent target)
781 {
782 if (_phaseId == PhaseId.APPLY_REQUEST_VALUES)
783 {
784 target.processDecodes(_facesContext);
785 }
786 else if (_phaseId == PhaseId.PROCESS_VALIDATIONS)
787 {
788 target.processValidators(_facesContext);
789 }
790 else if (_phaseId == PhaseId.UPDATE_MODEL_VALUES)
791 {
792 target.processUpdates(_facesContext);
793 }
794 else if (_phaseId == PhaseId.RENDER_RESPONSE)
795 {
796 processRenderComponent(target);
797 }
798 else
799 {
800 throw new IllegalStateException("PPR Response, illegale phase called");
801 }
802
803
804 return VisitResult.REJECT;
805 }
806
807
808
809
810
811
812
813
814 private void processRenderComponent(UIComponent target)
815 {
816 boolean inUpdate = false;
817 PartialResponseWriter writer = (PartialResponseWriter) _facesContext.getResponseWriter();
818 if (this._alreadyUpdatedComponents != null)
819 {
820
821 UIComponent parent = target;
822 while (parent != null)
823 {
824 if (this._alreadyUpdatedComponents.contains(parent))
825 {
826 return;
827 }
828 parent = parent.getParent();
829 }
830 }
831 try
832 {
833 writer.startUpdate(target.getClientId(_facesContext));
834 inUpdate = true;
835 target.encodeAll(_facesContext);
836 }
837 catch (IOException ex)
838 {
839 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
840 if (log.isLoggable(Level.SEVERE))
841 {
842 log.log(Level.SEVERE, "IOException for rendering component", ex);
843 }
844 }
845 finally
846 {
847 if (inUpdate)
848 {
849 try
850 {
851 writer.endUpdate();
852 }
853 catch (IOException ex)
854 {
855 Logger log = Logger.getLogger(PartialViewContextImpl.class.getName());
856 if (log.isLoggable(Level.SEVERE))
857 {
858 log.log(Level.SEVERE, "IOException for rendering component, stopping update rendering", ex);
859 }
860 }
861 }
862 }
863 }
864 }
865 }