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