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.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
67
68
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
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
82
83
84 private Boolean _ajaxRequest = null;
85
86
87
88
89
90
91 private Boolean _iframeRequest = null;
92
93 private Collection<String> _executeClientIds = null;
94 private Collection<String> _renderClientIds = null;
95
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
119
120
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
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
193
194
195
196
197
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
238 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode))
239 {
240
241 String[] clientIds
242 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(executeMode), ' ');
243
244
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
254
255
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
311 !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode))
312 {
313 String[] clientIds
314 = StringUtils.splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
315
316
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
351
352
353 try
354 {
355 RenderKit renderKit = _facesContext.getRenderKit();
356 if (renderKit == null)
357 {
358
359
360
361
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
378
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
393
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
433
434
435
436
437
438
439
440
441
442
443
444
445
446 PartialResponseWriter writer = _facesContext.getPartialViewContext().getPartialResponseWriter();
447 PartialViewContext pvc = _facesContext.getPartialViewContext();
448
449 ResponseWriter oldWriter = _facesContext.getResponseWriter();
450 boolean inDocument = false;
451
452
453
454 ExternalContext externalContext = _facesContext.getExternalContext();
455 externalContext.setResponseContentType("text/xml");
456 externalContext.addResponseHeader("Pragma", "no-cache");
457 externalContext.addResponseHeader("Cache-control", "no-cache");
458
459
460
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
477 if (renderIds != null && !renderIds.isEmpty())
478 {
479
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
557
558
559
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
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
614 writer.startUpdate(PartialResponseWriter.RENDER_ALL_MARKER);
615
616
617
618
619
620
621
622
623
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
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 (child instanceof HtmlHead)
672 {
673 return child;
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 (child instanceof HtmlBody)
694 {
695 return child;
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
758 return VisitResult.REJECT;
759 }
760
761
762
763
764
765
766
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
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 }