1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.renderkit;
20
21 import java.beans.BeanInfo;
22 import java.beans.Introspector;
23 import java.beans.PropertyDescriptor;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.PrintWriter;
28 import java.io.Serializable;
29 import java.io.StringWriter;
30 import java.io.Writer;
31 import java.lang.reflect.Method;
32 import java.text.DateFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Date;
37 import java.util.EnumSet;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.SortedMap;
43 import java.util.TreeMap;
44 import java.util.logging.Level;
45 import java.util.logging.Logger;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
48
49 import javax.el.Expression;
50 import javax.el.ValueExpression;
51 import javax.faces.FacesException;
52 import javax.faces.component.EditableValueHolder;
53 import javax.faces.component.UIColumn;
54 import javax.faces.component.UIComponent;
55 import javax.faces.component.UIData;
56 import javax.faces.component.UIViewRoot;
57 import javax.faces.component.visit.VisitCallback;
58 import javax.faces.component.visit.VisitContext;
59 import javax.faces.component.visit.VisitHint;
60 import javax.faces.component.visit.VisitResult;
61 import javax.faces.context.ExternalContext;
62 import javax.faces.context.FacesContext;
63 import javax.faces.context.PartialResponseWriter;
64 import javax.faces.context.ResponseWriter;
65 import javax.faces.el.MethodBinding;
66 import javax.faces.el.ValueBinding;
67 import javax.faces.render.Renderer;
68 import javax.faces.view.Location;
69 import javax.servlet.http.HttpServletResponse;
70
71 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
72 import org.apache.myfaces.lifecycle.ViewNotFoundException;
73 import org.apache.myfaces.shared.config.MyfacesConfig;
74 import org.apache.myfaces.shared.renderkit.html.HtmlResponseWriterImpl;
75 import org.apache.myfaces.shared.util.ClassUtils;
76 import org.apache.myfaces.shared.util.StateUtils;
77 import org.apache.myfaces.spi.WebConfigProvider;
78 import org.apache.myfaces.spi.WebConfigProviderFactory;
79 import org.apache.myfaces.view.facelets.component.UIRepeat;
80 import org.apache.myfaces.view.facelets.el.ContextAware;
81
82
83
84
85
86
87
88
89 public final class ErrorPageWriter
90 {
91
92
93
94
95
96
97
98
99
100
101 public static class ErrorPageBean implements Serializable
102 {
103
104 private static final long serialVersionUID = -79513324193326616L;
105
106 public String getErrorPageHtml() throws IOException
107 {
108 FacesContext facesContext = FacesContext.getCurrentInstance();
109 Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
110
111 Throwable t = (Throwable) requestMap.get(EXCEPTION_KEY);
112 if (t == null)
113 {
114 throw new IllegalStateException("No Exception to handle");
115 }
116
117 UIViewRoot view = (UIViewRoot) requestMap.get(VIEW_KEY);
118
119 StringWriter writer = new StringWriter();
120 ErrorPageWriter.debugHtml(writer, facesContext, view, null, t);
121 String html = writer.toString();
122
123
124 String body;
125 try
126 {
127 body = html.substring(html.indexOf("<body>") + "<body>".length(), html.indexOf("</body>"));
128 }
129 catch (Exception e)
130 {
131
132 return html;
133 }
134
135 String head;
136 try
137 {
138 head = html.substring(html.indexOf("<head>") + "<head>".length(), html.indexOf("</head>"));
139 }
140 catch (Exception e)
141 {
142
143 return body;
144 }
145
146
147 StringBuilder builder = new StringBuilder(body);
148
149 int startIndex = 0;
150 while (true)
151 {
152 try
153 {
154 int endIndex = head.indexOf("</style>", startIndex) + "</style>".length();
155 builder.append(head.substring(head.indexOf("<style", startIndex), endIndex));
156 startIndex = endIndex;
157 }
158 catch (Exception e)
159 {
160
161 break;
162 }
163 }
164
165 startIndex = 0;
166 while (true)
167 {
168 try
169 {
170 int endIndex = head.indexOf("</script>", startIndex) + "</script>".length();
171 builder.append(head.substring(head.indexOf("<script", startIndex), endIndex));
172 startIndex = endIndex;
173 }
174 catch (Exception e)
175 {
176
177 break;
178 }
179 }
180
181 return builder.toString();
182 }
183
184 }
185
186
187
188
189 public static final String ERROR_PAGE_BEAN_KEY = "__myFacesErrorPageBean";
190
191 private static final String EXCEPTION_KEY = "javax.servlet.error.exception";
192 public static final String VIEW_KEY = "org.apache.myfaces.error.UIViewRoot";
193
194 private static final Logger log = Logger.getLogger(ErrorPageWriter.class.getName());
195
196 private final static String TS = "<";
197
198 private static final String ERROR_TEMPLATE = "META-INF/rsc/myfaces-dev-error.xml";
199
200
201
202
203
204
205
206
207 @JSFWebConfigParam(defaultValue="META-INF/rsc/myfaces-dev-error.xml", since="1.2.4")
208 private static final String ERROR_TEMPLATE_RESOURCE = "org.apache.myfaces.ERROR_TEMPLATE_RESOURCE";
209
210 private static String[] errorParts;
211
212 private static final String DEBUG_TEMPLATE = "META-INF/rsc/myfaces-dev-debug.xml";
213
214
215
216
217 @JSFWebConfigParam(defaultValue="META-INF/rsc/myfaces-dev-debug.xml", since="1.2.4")
218 private static final String DEBUG_TEMPLATE_RESOURCE = "org.apache.myfaces.DEBUG_TEMPLATE_RESOURCE";
219
220 private static String[] debugParts;
221
222 private static final String REGEX_PATTERN = ".*?\\Q,Id:\\E\\s*(\\S+)\\s*\\].*?";
223
224 private final static String[] IGNORE = new String[] { "parent", "rendererType" };
225
226 private final static String[] ALWAYS_WRITE = new String[] { "class", "clientId" };
227
228
229
230
231
232
233 public static final String DEBUG_INFO_KEY = "org.apache.myfaces.debug.DEBUG_INFO";
234
235
236
237
238
239
240 private static final String VISITED_FACET_COUNT_KEY = "org.apache.myfaces.debug.VISITED_FACET_COUNT";
241
242
243
244
245
246
247 @JSFWebConfigParam(defaultValue="false, on Development Project stage: true",
248 expectedValues="true,false", since="1.2.4")
249 public static final String ERROR_HANDLING_PARAMETER = "org.apache.myfaces.ERROR_HANDLING";
250
251 public ErrorPageWriter()
252 {
253 super();
254 }
255
256
257
258
259
260
261
262
263
264 public static void debugHtml(Writer writer, FacesContext faces, Throwable e) throws IOException
265 {
266 debugHtml(writer, faces, faces.getViewRoot(), null, e);
267 }
268
269 private static void debugHtml(Writer writer, FacesContext faces, UIViewRoot view,
270 Collection<UIComponent> components, Throwable... exs) throws IOException
271 {
272 _init(faces);
273 Date now = new Date();
274
275 for (int i = 0; i < errorParts.length; i++)
276 {
277 if ("view".equals((errorParts[i])))
278 {
279 if (faces.getViewRoot() != null)
280 {
281 String viewId = faces.getViewRoot().getViewId();
282 writer.write("viewId=" + viewId);
283 writer.write("<br/>");
284 String realPath = null;
285 try
286 {
287
288 realPath = faces.getExternalContext().getRealPath(viewId);
289 }
290 catch(Throwable e)
291 {
292
293 }
294 if (realPath != null)
295 {
296 writer.write("location=" + realPath);
297 writer.write("<br/>");
298 }
299 writer.write("phaseId=" + faces.getCurrentPhaseId());
300 writer.write("<br/>");
301 writer.write("<br/>");
302 }
303 }
304 else if ("message".equals(errorParts[i]))
305 {
306 boolean printed = false;
307
308
309
310
311
312 for (Throwable e : exs)
313 {
314 String msg = e.getMessage();
315 if (printed)
316 {
317 writer.write("<br/>");
318 }
319 if (msg != null)
320 {
321 writer.write(msg.replaceAll("<", TS));
322 }
323 else
324 {
325 writer.write(e.getClass().getName());
326 }
327 printed = true;
328 }
329 }
330 else if ("trace".equals(errorParts[i]))
331 {
332 boolean printed = false;
333 for (Throwable e : exs)
334 {
335 if (printed)
336 {
337 writer.write("\n");
338 }
339 _writeException(writer, e);
340 printed = true;
341 }
342 }
343 else if ("now".equals(errorParts[i]))
344 {
345 writer.write(DateFormat.getDateTimeInstance().format(now));
346 }
347 else if ("tree".equals(errorParts[i]))
348 {
349 if (view != null)
350 {
351 List<String> errorIds = _getErrorId(components, exs);
352 _writeComponent(faces, writer, view, errorIds, true);
353 }
354 }
355 else if ("vars".equals(errorParts[i]))
356 {
357 _writeVariables(writer, faces, view);
358 }
359 else if ("cause".equals(errorParts[i]))
360 {
361 boolean printed = false;
362 Iterator<UIComponent> iterator = null;
363 if (components != null)
364 {
365 iterator = components.iterator();
366 }
367 for (Throwable e : exs)
368 {
369 if (printed)
370 {
371 writer.write("<br/>");
372 }
373 _writeCause(writer, e);
374 if (iterator != null)
375 {
376 UIComponent uiComponent = iterator.next();
377 if (uiComponent != null)
378 {
379 _writeComponent(faces, writer, uiComponent, null,
380 }
381 }
382 printed = true;
383 }
384 }
385 else
386 {
387 writer.write(errorParts[i]);
388 }
389 }
390 }
391
392
393
394
395
396
397
398
399 public static void debugHtml(Writer writer, FacesContext faces) throws IOException
400 {
401 _init(faces);
402 Date now = new Date();
403 for (int i = 0; i < debugParts.length; i++)
404 {
405 if ("message".equals(debugParts[i]))
406 {
407 writer.write(faces.getViewRoot().getViewId());
408 }
409 else if ("now".equals(debugParts[i]))
410 {
411 writer.write(DateFormat.getDateTimeInstance().format(now));
412 }
413 else if ("tree".equals(debugParts[i]))
414 {
415 _writeComponent(faces, writer, faces.getViewRoot(), null, true);
416 }
417 else if ("extendedtree".equals(debugParts[i]))
418 {
419 _writeExtendedComponentTree(writer, faces);
420 }
421 else if ("vars".equals(debugParts[i]))
422 {
423 _writeVariables(writer, faces, faces.getViewRoot());
424 }
425 else
426 {
427 writer.write(debugParts[i]);
428 }
429 }
430 }
431
432 public static void handle(FacesContext facesContext, Collection<UIComponent> components,
433 Throwable... exs) throws FacesException
434 {
435 for (Throwable ex : exs)
436 {
437 _prepareExceptionStack(ex);
438 }
439
440 if (!facesContext.getExternalContext().isResponseCommitted())
441 {
442 facesContext.getExternalContext().responseReset();
443 }
444
445 int responseStatus = -1;
446 for (Throwable ex : exs)
447 {
448 if (ex instanceof ViewNotFoundException)
449 {
450 responseStatus = HttpServletResponse.SC_NOT_FOUND;
451 break;
452 }
453 else
454 {
455 responseStatus = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
456 }
457 }
458 if (responseStatus != -1)
459 {
460 facesContext.getExternalContext().setResponseStatus(responseStatus);
461 }
462
463
464 facesContext.getExternalContext().setResponseContentType("text/html");
465 facesContext.getExternalContext().setResponseCharacterEncoding("UTF-8");
466 try
467 {
468
469
470 Writer writer = facesContext.getExternalContext().getResponseOutputWriter();
471 debugHtml(writer, facesContext, facesContext.getViewRoot(), components, exs);
472 }
473 catch(IOException ioe)
474 {
475 throw new FacesException("Could not write the error page", ioe);
476 }
477
478
479 facesContext.responseComplete();
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494 @Deprecated
495 public static void handleThrowable(FacesContext facesContext, Throwable ex) throws FacesException
496 {
497 _prepareExceptionStack(ex);
498
499 boolean errorPageWritten = false;
500
501
502
503
504
505 WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
506 facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
507
508 if(webConfigProvider.isErrorPagePresent(facesContext.getExternalContext()))
509 {
510
511 facesContext.getExternalContext().getRequestMap().put(VIEW_KEY, facesContext.getViewRoot());
512 }
513 else
514 {
515
516
517 String errorHandling = facesContext.getExternalContext().getInitParameter(ERROR_HANDLING_PARAMETER);
518 boolean errorHandlingDisabled = (errorHandling != null && errorHandling.equalsIgnoreCase("false"));
519 if (!errorHandlingDisabled)
520 {
521
522 Object response = facesContext.getExternalContext().getResponse();
523 if (response instanceof HttpServletResponse)
524 {
525 HttpServletResponse httpResp = (HttpServletResponse) response;
526 if (!httpResp.isCommitted())
527 {
528 httpResp.reset();
529 if (facesContext.getPartialViewContext().isAjaxRequest())
530 {
531
532 httpResp.setContentType("text/xml; charset=UTF-8");
533 try
534 {
535 Writer writer = httpResp.getWriter();
536
537 ResponseWriter responseWriter = new HtmlResponseWriterImpl(writer, "text/xml", "utf-8");
538 PartialResponseWriter partialWriter = new PartialResponseWriter(responseWriter);
539 partialWriter.startDocument();
540 partialWriter.startError(ex.getClass().getName());
541 if (ex.getCause() != null)
542 {
543 partialWriter.write(ex.getCause().toString());
544 }
545 else if (ex.getMessage() != null)
546 {
547 partialWriter.write(ex.getMessage());
548 }
549 partialWriter.endError();
550 partialWriter.endDocument();
551 }
552 catch(IOException ioe)
553 {
554 throw new FacesException("Could not write the error page", ioe);
555 }
556 }
557 else
558 {
559
560 httpResp.setContentType("text/html; charset=UTF-8");
561 try
562 {
563 Writer writer = httpResp.getWriter();
564 debugHtml(writer, facesContext, ex);
565 }
566 catch(IOException ioe)
567 {
568 throw new FacesException("Could not write the error page", ioe);
569 }
570 }
571 log.log(Level.SEVERE, "An exception occurred", ex);
572
573
574 facesContext.responseComplete();
575
576 errorPageWritten = true;
577 }
578 }
579 }
580 }
581
582
583 if (!errorPageWritten)
584 {
585 if (ex instanceof FacesException)
586 {
587 throw (FacesException) ex;
588 }
589 if (ex instanceof RuntimeException)
590 {
591 throw (RuntimeException) ex;
592 }
593 throw new FacesException(ex);
594 }
595
596 }
597
598 private static String _getErrorTemplate(FacesContext context)
599 {
600 String errorTemplate = context.getExternalContext().getInitParameter(ERROR_TEMPLATE_RESOURCE);
601 if (errorTemplate != null)
602 {
603 return errorTemplate;
604 }
605 return ERROR_TEMPLATE;
606 }
607
608 private static String _getDebugTemplate(FacesContext context)
609 {
610 String debugTemplate = context.getExternalContext().getInitParameter(DEBUG_TEMPLATE_RESOURCE);
611 if (debugTemplate != null)
612 {
613 return debugTemplate;
614 }
615 return DEBUG_TEMPLATE;
616 }
617
618 private static void _init(FacesContext context) throws IOException
619 {
620 if (errorParts == null)
621 {
622 errorParts = _splitTemplate(_getErrorTemplate(context));
623 }
624
625 if (debugParts == null)
626 {
627 debugParts = _splitTemplate(_getDebugTemplate(context));
628 }
629 }
630
631 private static String[] _splitTemplate(String rsc) throws IOException
632 {
633 InputStream is = ClassUtils.getContextClassLoader().getResourceAsStream(rsc);
634 if (is == null)
635 {
636
637 is = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(rsc);
638 if (is == null)
639 {
640
641 is = ErrorPageWriter.class.getClassLoader().getResourceAsStream(rsc);
642 }
643 }
644
645 if (is == null)
646 {
647
648
649
650
651 throw new IllegalArgumentException("Could not find resource " + rsc);
652 }
653 ByteArrayOutputStream baos = new ByteArrayOutputStream();
654 byte[] buff = new byte[512];
655 int read;
656 while ((read = is.read(buff)) != -1)
657 {
658 baos.write(buff, 0, read);
659 }
660 String str = baos.toString();
661 return str.split("@@");
662 }
663
664 private static List<String> _getErrorId(Collection<UIComponent> components, Throwable... exs)
665 {
666 List<String> list = null;
667 for (Throwable e : exs)
668 {
669 String message = e.getMessage();
670
671 if (message == null)
672 {
673 continue;
674 }
675
676 Pattern pattern = Pattern.compile(REGEX_PATTERN);
677 Matcher matcher = pattern.matcher(message);
678
679 while (matcher.find())
680 {
681 if (list == null)
682 {
683 list = new ArrayList<String>();
684 }
685 list.add(matcher.group(1));
686 }
687 }
688 if (list != null && list.size() > 0)
689 {
690 return list;
691 }
692 else if (components != null)
693 {
694 list = new ArrayList<String>();
695 for (UIComponent uiComponent : components)
696 {
697 if (uiComponent != null)
698 {
699 list.add(uiComponent.getId());
700 }
701 }
702 return list;
703 }
704 return null;
705 }
706
707 private static void _writeException(Writer writer, Throwable e) throws IOException
708 {
709 StringWriter str = new StringWriter(256);
710 PrintWriter pstr = new PrintWriter(str);
711 e.printStackTrace(pstr);
712 pstr.close();
713 writer.write(str.toString().replaceAll("<", TS));
714 }
715
716 private static void _writeCause(Writer writer, Throwable ex) throws IOException
717 {
718 String msg = ex.getMessage();
719 String contextAwareLocation = null;
720 if (ex instanceof ContextAware)
721 {
722 ContextAware caex = (ContextAware) ex;
723 contextAwareLocation = caex.getLocation().toString() + " " +
724 caex.getQName() + "=\"" +
725 caex.getExpressionString() + "\"";
726 }
727 while (ex.getCause() != null)
728 {
729 ex = ex.getCause();
730 if (ex instanceof ContextAware)
731 {
732 ContextAware caex = (ContextAware) ex;
733 contextAwareLocation = caex.getLocation().toString() + " " +
734 caex.getQName() + "=\"" +
735 caex.getExpressionString() + "\"";
736 }
737 if (ex.getMessage() != null)
738 {
739 msg = ex.getMessage();
740 }
741 }
742
743 if (msg != null)
744 {
745 msg = ex.getClass().getName() + " - " + msg;
746 writer.write(msg.replaceAll("<", TS));
747 }
748 else
749 {
750 writer.write(ex.getClass().getName());
751 }
752 StackTraceElement stackTraceElement = ex.getStackTrace()[0];
753 writer.write("<br/> at " + stackTraceElement.toString());
754
755 if (contextAwareLocation != null)
756 {
757 writer.write("<br/> <br/>");
758 writer.write(contextAwareLocation);
759 writer.write("<br/>");
760 }
761 }
762
763 private static void _writeVariables(Writer writer, FacesContext faces, UIViewRoot view) throws IOException
764 {
765 ExternalContext ctx = faces.getExternalContext();
766 _writeVariables(writer, ctx.getRequestParameterMap(), "Request Parameters");
767 _writeVariables(writer, ctx.getRequestMap(), "Request Attributes");
768 if (view != null)
769 {
770 _writeVariables(writer, view.getViewMap(), "View Attributes");
771 }
772 if (ctx.getSession(false) != null)
773 {
774 _writeVariables(writer, ctx.getSessionMap(), "Session Attributes");
775 }
776 MyfacesConfig config = MyfacesConfig.getCurrentInstance(ctx);
777 if(config!=null && !config.isFlashScopeDisabled() && ctx.getFlash() != null)
778 {
779 _writeVariables(writer, ctx.getFlash(), "Flash Attributes");
780 }
781 _writeVariables(writer, ctx.getApplicationMap(), "Application Attributes");
782 }
783
784 private static void _writeVariables(Writer writer, Map<String, ? extends Object> vars, String caption)
785 throws IOException
786 {
787 writer.write("<table><caption>");
788 writer.write(caption);
789 writer.write("</caption><thead><tr><th style=\"width: 10%; \">Name</th>"
790 + "<th style=\"width: 90%; \">Value</th></tr></thead><tbody>");
791 boolean written = false;
792 if (!vars.isEmpty())
793 {
794 SortedMap<String, Object> sortedMap = new TreeMap<String, Object>(vars);
795 for (Map.Entry<String, Object> entry : sortedMap.entrySet())
796 {
797 String key = entry.getKey();
798 if (key.indexOf('.') == -1)
799 {
800 writer.write("<tr><td>");
801 writer.write(key.replaceAll("<", TS));
802 writer.write("</td><td>");
803 Object value = entry.getValue();
804
805
806 if (value != null && value.toString() != null)
807 {
808 writer.write(value.toString().replaceAll("<", TS));
809 }
810 else
811 {
812 writer.write("null");
813 }
814 writer.write("</td></tr>");
815 written = true;
816 }
817 }
818 }
819 if (!written)
820 {
821 writer.write("<tr><td colspan=\"2\"><em>None</em></td></tr>");
822 }
823 writer.write("</tbody></table>");
824 }
825
826 private static void _writeComponent(FacesContext faces, Writer writer, UIComponent c, List<String> highlightId,
827 boolean writeChildren) throws IOException
828 {
829 writer.write("<dl><dt");
830 if (_isText(c))
831 {
832 writer.write(" class=\"uicText\"");
833 }
834 if (highlightId != null)
835 {
836 if ((highlightId.size() > 0))
837 {
838 String id = c.getId();
839 if (highlightId.contains(id))
840 {
841 writer.write(" class=\"highlightComponent\"");
842 }
843 }
844 }
845 writer.write(">");
846
847 boolean hasChildren = (c.getChildCount() > 0 || c.getFacetCount() > 0) && writeChildren;
848
849 int stateSize = 0;
850
851 Object state = c.saveState(faces);
852 if (state != null)
853 {
854 try
855 {
856 byte[] stateBytes = StateUtils.getAsByteArray(state, faces.getExternalContext());
857 stateSize = stateBytes.length;
858 }
859 catch (Exception e)
860 {
861 stateSize = -1;
862 if (log.isLoggable(Level.FINEST))
863 {
864 log.fine("Could not determine state size: " + e.getMessage());
865 }
866 }
867 }
868 _writeStart(writer, c, hasChildren, true);
869 writer.write(" - State size:" + stateSize + " bytes");
870 writer.write("</dt>");
871 if (hasChildren)
872 {
873 if (c.getFacetCount() > 0)
874 {
875 for (Map.Entry<String, UIComponent> entry : c.getFacets().entrySet())
876 {
877 writer.write("<dd class=\"uicFacet\">");
878 writer.write("<span>");
879 writer.write(entry.getKey());
880 writer.write("</span>");
881 _writeComponent(faces, writer, entry.getValue(), highlightId, true);
882 writer.write("</dd>");
883 }
884 }
885 if (c.getChildCount() > 0)
886 {
887 for (int i = 0, childCount = c.getChildCount(); i < childCount; i++)
888 {
889 UIComponent child = c.getChildren().get(i);
890 writer.write("<dd>");
891 _writeComponent(faces, writer, child, highlightId, writeChildren);
892 writer.write("</dd>");
893 }
894 }
895 writer.write("<dt>");
896 _writeEnd(writer, c);
897 writer.write("</dt>");
898 }
899 writer.write("</dl>");
900 }
901
902
903
904
905
906
907
908
909
910 private static void _writeExtendedComponentTree(Writer writer,
911 FacesContext facesContext) throws IOException
912 {
913 VisitContext visitContext = VisitContext.createVisitContext(
914 facesContext, null, EnumSet.of(VisitHint.SKIP_UNRENDERED));
915 facesContext.getViewRoot().visitTree(visitContext, new ExtendedComponentTreeVisitCallback(writer));
916 _clearVisitedFacetCountMap(facesContext);
917 }
918
919
920
921
922
923
924 private static class ExtendedComponentTreeVisitCallback implements VisitCallback
925 {
926
927 private Writer _writer;
928
929 public ExtendedComponentTreeVisitCallback(Writer writer)
930 {
931 _writer = writer;
932 }
933
934 @SuppressWarnings("unchecked")
935 public VisitResult visit(VisitContext context, UIComponent target)
936 {
937 final Map<String, Object> requestMap = context.getFacesContext()
938 .getExternalContext().getRequestMap();
939
940 try
941 {
942 if (!(target instanceof UIViewRoot))
943 {
944 _writer.write("<dd>");
945 }
946
947 UIComponent parent = target.getParent();
948 boolean hasChildren = (target.getChildCount() > 0 || target.getFacetCount() > 0);
949 String facetName = _getFacetName(target);
950
951 if (!(target instanceof UIColumn))
952 {
953 if (parent instanceof UIColumn
954 && ((parent.getChildCount() > 0 && parent.getChildren().get(0) == target)
955 || (facetName != null &&
956 _getVisitedFacetCount(context.getFacesContext(), parent) == 0)))
957 {
958 if (parent.getParent() instanceof UIData
959 && _isFirstUIColumn(parent.getParent(), (UIColumn) parent))
960 {
961 _writer.write("<span>Row: ");
962 int rowIndex = ((UIData) parent.getParent()).getRowIndex();
963 _writer.write("" + rowIndex);
964 if (rowIndex == -1)
965 {
966
967 _writer.write(" (all column facets)");
968 }
969 _writer.write("</span>");
970 }
971 _writer.write("<dl><dt>");
972 _writeStart(_writer, parent, true, false);
973 _writer.write("</dt><dd>");
974 }
975
976 if (facetName != null)
977 {
978 _writer.write("<span>" + facetName + "</span>");
979 _incrementVisitedFacetCount(context.getFacesContext(), parent);
980 }
981 _writer.write("<dl><dt");
982 if (_isText(target))
983 {
984 _writer.write(" class=\"uicText\"");
985 }
986 _writer.write(">");
987
988 Map<String, List<Object[]>> debugInfos = null;
989
990
991 if (target instanceof EditableValueHolder)
992 {
993
994 debugInfos = (Map<String, List<Object[]>>) requestMap
995 .get(DEBUG_INFO_KEY + target.getClientId());
996 }
997
998
999
1000
1001 Renderer renderer = null;
1002 try
1003 {
1004 Method getRenderer = UIComponent.class.getDeclaredMethod(
1005 "getRenderer", FacesContext.class);
1006
1007 getRenderer.setAccessible(true);
1008 renderer = (Renderer) getRenderer.invoke(target, context.getFacesContext());
1009 }
1010 catch (Exception e)
1011 {
1012
1013 }
1014
1015
1016 _writeStart(_writer, target, (hasChildren || debugInfos != null || renderer != null), false);
1017 _writer.write("</dt>");
1018
1019 if (renderer != null)
1020 {
1021
1022 _writer.write("<div class=\"renderer\">Rendered by ");
1023 _writer.write(renderer.getClass().getCanonicalName());
1024 _writer.write("</div>");
1025
1026 if (!hasChildren && debugInfos == null)
1027 {
1028
1029 _writer.write("<dt>");
1030 _writeEnd(_writer, target);
1031 _writer.write("</dt>");
1032 }
1033 }
1034
1035 if (debugInfos != null)
1036 {
1037 final String fieldid = target.getClientId() + "_lifecycle";
1038 _writer.write("<div class=\"lifecycle_values_wrapper\">");
1039 _writer.write("<a href=\"#\" onclick=\"toggle('");
1040 _writer.write(fieldid);
1041 _writer.write("'); return false;\"><span id=\"");
1042 _writer.write(fieldid);
1043 _writer.write("Off\">+</span><span id=\"");
1044 _writer.write(fieldid);
1045 _writer.write("On\" style=\"display: none;\">-</span> Value Lifecycle</a>");
1046 _writer.write("<div id=\"");
1047 _writer.write(fieldid);
1048 _writer.write("\" class=\"lifecycle_values\">");
1049
1050
1051 for (Map.Entry<String, List<Object[]>> entry : debugInfos.entrySet())
1052 {
1053 _writer.write("<span>");
1054 _writer.write(entry.getKey());
1055 _writer.write("</span><ol>");
1056 int i = 0;
1057 for (Object[] debugInfo : entry.getValue())
1058 {
1059
1060
1061
1062
1063
1064
1065
1066 String oldValue = debugInfo[1] == null ? "null" : debugInfo[1].toString();
1067 String newValue = debugInfo[2] == null ? "null" : debugInfo[2].toString();
1068 _writer.write("<li><b>");
1069 _writer.write(entry.getKey());
1070 _writer.write("</b> set from <b>");
1071 _writer.write(oldValue);
1072 _writer.write("</b> to <b>");
1073 _writer.write(newValue);
1074 _writer.write("</b> in Phase ");
1075 _writer.write(debugInfo[0].toString());
1076
1077
1078 if (debugInfo[3] != null)
1079 {
1080 final String stackTraceId = fieldid + "_" + entry.getKey() + "_" + i;
1081 _writer.write("<div class=\"stacktrace_wrapper\">");
1082 _writer.write("<a href=\"#\" onclick=\"toggle('");
1083 _writer.write(stackTraceId);
1084 _writer.write("'); return false;\"><span id=\"");
1085 _writer.write(stackTraceId);
1086 _writer.write("Off\">+</span><span id=\"");
1087 _writer.write(stackTraceId);
1088 _writer.write("On\" style=\"display: none;\">-</span> Call Stack</a>");
1089 _writer.write("<div id=\"");
1090 _writer.write(stackTraceId);
1091 _writer.write("\" class=\"stacktrace_values\">");
1092 _writer.write("<ul>");
1093 for (StackTraceElement stackTraceElement
1094 : (List<StackTraceElement>) debugInfo[3])
1095 {
1096 _writer.write("<li>");
1097 _writer.write(stackTraceElement.toString());
1098 _writer.write("</li>");
1099 }
1100 _writer.write("</ul></div></div>");
1101 }
1102
1103 _writer.write("</li>");
1104
1105 i++;
1106 }
1107 _writer.write("</ol>");
1108 }
1109
1110 _writer.write("</div></div>");
1111
1112
1113
1114 requestMap.remove(DEBUG_INFO_KEY + target.getClientId());
1115
1116 if (!hasChildren)
1117 {
1118
1119 _writer.write("<dt>");
1120 _writeEnd(_writer, target);
1121 _writer.write("</dt>");
1122 }
1123 }
1124 }
1125
1126 if (!hasChildren)
1127 {
1128 _writer.write("</dl>");
1129
1130 while (parent != null &&
1131 ((parent.getChildCount()>0 && parent.getChildren().get(parent.getChildCount()-1) == target)
1132 || (parent.getFacetCount() != 0
1133 && _getVisitedFacetCount(context.getFacesContext(), parent) ==
1134 parent.getFacetCount())))
1135 {
1136
1137
1138
1139 _removeVisitedFacetCount(context.getFacesContext(), parent);
1140
1141
1142 if (parent instanceof UIData)
1143 {
1144 UIData uidata = (UIData) parent;
1145 if (uidata.getRowIndex() != uidata.getRowCount() - 1)
1146 {
1147
1148 break;
1149 }
1150 }
1151 else if (parent instanceof UIRepeat)
1152 {
1153 UIRepeat uirepeat = (UIRepeat) parent;
1154 if (uirepeat.getIndex() + uirepeat.getStep() < uirepeat.getRowCount())
1155 {
1156
1157 break;
1158 }
1159 }
1160
1161 _writer.write("</dd><dt>");
1162 _writeEnd(_writer, parent);
1163 _writer.write("</dt></dl>");
1164
1165 if (!(parent instanceof UIViewRoot))
1166 {
1167 _writer.write("</dd>");
1168 }
1169
1170 target = parent;
1171 parent = target.getParent();
1172 }
1173 }
1174 }
1175 catch (IOException ioe)
1176 {
1177 throw new FacesException(ioe);
1178 }
1179
1180 return VisitResult.ACCEPT;
1181 }
1182
1183 }
1184
1185 private static boolean _isFirstUIColumn(UIComponent uidata, UIColumn uicolumn)
1186 {
1187 for (int i = 0, childCount = uidata.getChildCount(); i < childCount; i++)
1188 {
1189 UIComponent child = uidata.getChildren().get(i);
1190 if (child instanceof UIColumn)
1191 {
1192 return (child == uicolumn);
1193 }
1194 }
1195 return false;
1196 }
1197
1198 private static String _getFacetName(UIComponent component)
1199 {
1200 UIComponent parent = component.getParent();
1201 if (parent != null)
1202 {
1203 if (parent.getFacetCount() > 0)
1204 {
1205 for (Map.Entry<String, UIComponent> entry : parent.getFacets().entrySet())
1206 {
1207 if (entry.getValue() == component)
1208 {
1209 return entry.getKey();
1210 }
1211 }
1212 }
1213 }
1214 return null;
1215 }
1216
1217 private static int _getVisitedFacetCount(FacesContext facesContext, UIComponent component)
1218 {
1219 Map<UIComponent, Integer> visitedFacetCount = (Map<UIComponent, Integer>)
1220 facesContext.getAttributes().get(VISITED_FACET_COUNT_KEY);
1221 if (visitedFacetCount == null)
1222 {
1223 return 0;
1224 }
1225 Integer count = visitedFacetCount.get(component);
1226 if (count != null)
1227 {
1228 return count;
1229 }
1230 return 0;
1231 }
1232
1233 private static void _incrementVisitedFacetCount(FacesContext facesContext, UIComponent component)
1234 {
1235 Map<UIComponent, Integer> visitedFacetCount = (Map<UIComponent, Integer>)
1236 facesContext.getAttributes().get(VISITED_FACET_COUNT_KEY);
1237 if (visitedFacetCount == null)
1238 {
1239 visitedFacetCount = new HashMap<UIComponent, Integer>();
1240 facesContext.getAttributes().put(VISITED_FACET_COUNT_KEY, visitedFacetCount);
1241 }
1242 visitedFacetCount.put(component, _getVisitedFacetCount(facesContext, component) + 1);
1243 }
1244
1245 private static void _removeVisitedFacetCount(FacesContext facesContext, UIComponent component)
1246 {
1247 Map<UIComponent, Integer> visitedFacetCount = (Map<UIComponent, Integer>)
1248 facesContext.getAttributes().get(VISITED_FACET_COUNT_KEY);
1249 if (visitedFacetCount == null)
1250 {
1251 return;
1252 }
1253 visitedFacetCount.remove(component);
1254 }
1255
1256 private static void _clearVisitedFacetCountMap(FacesContext facesContext)
1257 {
1258 Map<UIComponent, Integer> visitedFacetCount = (Map<UIComponent, Integer>)
1259 facesContext.getAttributes().get(VISITED_FACET_COUNT_KEY);
1260 if (visitedFacetCount != null)
1261 {
1262 visitedFacetCount.clear();
1263 facesContext.getAttributes().remove(VISITED_FACET_COUNT_KEY);
1264 }
1265 }
1266
1267 private static void _writeEnd(Writer writer, UIComponent c) throws IOException
1268 {
1269 if (!_isText(c))
1270 {
1271 writer.write(TS);
1272 writer.write('/');
1273 writer.write(_getName(c));
1274 writer.write('>');
1275 }
1276 }
1277
1278 private static void _writeAttributes(Writer writer, UIComponent c, boolean valueExpressionValues)
1279 {
1280 try
1281 {
1282 BeanInfo info = Introspector.getBeanInfo(c.getClass());
1283 PropertyDescriptor[] pd = info.getPropertyDescriptors();
1284 Method m = null;
1285 Object v = null;
1286 ValueExpression valueExpression = null;
1287 String str = null;
1288 for (int i = 0; i < pd.length; i++)
1289 {
1290 if ((pd[i].getWriteMethod() != null || Arrays.binarySearch(ALWAYS_WRITE, pd[i].getName()) > -1)
1291 && Arrays.binarySearch(IGNORE, pd[i].getName()) < 0)
1292 {
1293 m = pd[i].getReadMethod();
1294 if (m != null)
1295 {
1296 try
1297 {
1298
1299 valueExpression = c.getValueExpression(pd[i].getName());
1300 if (valueExpressionValues && valueExpression != null)
1301 {
1302 String expressionString = valueExpression.getExpressionString();
1303 if (null == expressionString)
1304 {
1305 expressionString = "";
1306 }
1307 _writeAttribute(writer, pd[i].getName(), expressionString);
1308 }
1309 else
1310 {
1311 v = m.invoke(c, null);
1312 if (v != null)
1313 {
1314 if (v instanceof Collection || v instanceof Map || v instanceof Iterator)
1315 {
1316 continue;
1317 }
1318 if (v instanceof Expression)
1319 {
1320 str = ((Expression)v).getExpressionString();
1321 }
1322 else if (v instanceof ValueBinding)
1323 {
1324 str = ((ValueBinding) v).getExpressionString();
1325 }
1326 else if (v instanceof MethodBinding)
1327 {
1328 str = ((MethodBinding) v).getExpressionString();
1329 }
1330 else
1331 {
1332 str = v.toString();
1333 }
1334
1335 _writeAttribute(writer, pd[i].getName(), str);
1336 }
1337 }
1338 }
1339 catch (Exception e)
1340 {
1341
1342 }
1343 }
1344 }
1345 }
1346
1347 ValueExpression binding = c.getValueExpression("binding");
1348 if (binding != null)
1349 {
1350 _writeAttribute(writer, "binding", binding.getExpressionString());
1351 }
1352
1353
1354 String location = _getComponentLocation(c);
1355 if (location != null)
1356 {
1357 _writeAttribute(writer, "location", location);
1358 }
1359 }
1360 catch (Exception e)
1361 {
1362
1363 }
1364 }
1365
1366 private static void _writeAttribute(Writer writer, String name, String value) throws IOException
1367 {
1368 writer.write(" ");
1369 writer.write(name);
1370 writer.write("=\"");
1371 writer.write(value.replaceAll("<", TS));
1372 writer.write("\"");
1373 }
1374
1375 private static void _writeStart(Writer writer, UIComponent c,
1376 boolean children, boolean valueExpressionValues) throws IOException
1377 {
1378 if (_isText(c))
1379 {
1380 String str = c.toString().trim();
1381 writer.write(str.replaceAll("<", TS));
1382 }
1383 else
1384 {
1385 writer.write(TS);
1386 writer.write(_getName(c));
1387 _writeAttributes(writer, c, valueExpressionValues);
1388 if (children)
1389 {
1390 writer.write('>');
1391 }
1392 else
1393 {
1394 writer.write("/>");
1395 }
1396 }
1397 }
1398
1399 private static String _getName(UIComponent c)
1400 {
1401 String nm = c.getClass().getName();
1402 return nm.substring(nm.lastIndexOf('.') + 1);
1403 }
1404
1405 private static boolean _isText(UIComponent c)
1406 {
1407 return (c.getClass().getName().startsWith("org.apache.myfaces.view.facelets.compiler"));
1408 }
1409
1410 private static void _prepareExceptionStack(Throwable ex)
1411 {
1412
1413 if (ex == null)
1414 {
1415 return;
1416 }
1417
1418
1419 if (!_initCausePerReflection(ex, "getRootCause"))
1420 {
1421 _initCausePerReflection(ex, "getCause");
1422 }
1423
1424 _prepareExceptionStack(ex.getCause());
1425 }
1426
1427 private static boolean _initCausePerReflection(Throwable ex, String methodName)
1428 {
1429 try
1430 {
1431 Method causeGetter = ex.getClass().getMethod(methodName, (Class[])null);
1432 Throwable rootCause = (Throwable)causeGetter.invoke(ex, (Object[])null);
1433 return _initCauseIfAvailable(ex, rootCause);
1434 }
1435 catch (Exception e1)
1436 {
1437 return false;
1438 }
1439 }
1440
1441 private static boolean _initCauseIfAvailable(Throwable th, Throwable cause)
1442 {
1443 if (cause == null)
1444 {
1445 return false;
1446 }
1447
1448 try
1449 {
1450 Method m = Throwable.class.getMethod("initCause", new Class[] { Throwable.class });
1451 m.invoke(th, new Object[] { cause });
1452 return true;
1453 }
1454 catch (Exception e)
1455 {
1456 return false;
1457 }
1458 }
1459
1460
1461
1462
1463
1464
1465 private static String _getComponentLocation(UIComponent component)
1466 {
1467 Location location = (Location) component.getAttributes()
1468 .get(UIComponent.VIEW_LOCATION_KEY);
1469 if (location != null)
1470 {
1471 return location.toString();
1472 }
1473 return null;
1474 }
1475 }