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
240 private static Map<UIComponent, Integer> visitedFacetCount = new HashMap<UIComponent, Integer>();
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.getFacets().size() > 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.getFacets().size() > 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 }
912
913
914
915
916
917
918 private static class ExtendedComponentTreeVisitCallback implements VisitCallback
919 {
920
921 private Writer _writer;
922
923 public ExtendedComponentTreeVisitCallback(Writer writer)
924 {
925 _writer = writer;
926 }
927
928 @SuppressWarnings("unchecked")
929 public VisitResult visit(VisitContext context, UIComponent target)
930 {
931 final Map<String, Object> requestMap = context.getFacesContext()
932 .getExternalContext().getRequestMap();
933
934 try
935 {
936 if (!(target instanceof UIViewRoot))
937 {
938 _writer.write("<dd>");
939 }
940
941 UIComponent parent = target.getParent();
942 boolean hasChildren = (target.getChildCount() > 0 || target.getFacets().size() > 0);
943 String facetName = _getFacetName(target);
944
945 if (!(target instanceof UIColumn))
946 {
947 if (parent instanceof UIColumn
948 && ((parent.getChildCount() > 0 && parent.getChildren().get(0) == target)
949 || (facetName != null &&_getVisitedFacetCount(parent) == 0)))
950 {
951 if (parent.getParent() instanceof UIData
952 && _isFirstUIColumn(parent.getParent(), (UIColumn) parent))
953 {
954 _writer.write("<span>Row: ");
955 int rowIndex = ((UIData) parent.getParent()).getRowIndex();
956 _writer.write("" + rowIndex);
957 if (rowIndex == -1)
958 {
959
960 _writer.write(" (all column facets)");
961 }
962 _writer.write("</span>");
963 }
964 _writer.write("<dl><dt>");
965 _writeStart(_writer, parent, true, false);
966 _writer.write("</dt><dd>");
967 }
968
969 if (facetName != null)
970 {
971 _writer.write("<span>" + facetName + "</span>");
972 _incrementVisitedFacetCount(parent);
973 }
974 _writer.write("<dl><dt");
975 if (_isText(target))
976 {
977 _writer.write(" class=\"uicText\"");
978 }
979 _writer.write(">");
980
981 Map<String, List<Object[]>> debugInfos = null;
982
983
984 if (target instanceof EditableValueHolder)
985 {
986
987 debugInfos = (Map<String, List<Object[]>>) requestMap
988 .get(DEBUG_INFO_KEY + target.getClientId());
989 }
990
991
992
993
994 Renderer renderer = null;
995 try
996 {
997 Method getRenderer = UIComponent.class.getDeclaredMethod(
998 "getRenderer", FacesContext.class);
999
1000 getRenderer.setAccessible(true);
1001 renderer = (Renderer) getRenderer.invoke(target, context.getFacesContext());
1002 }
1003 catch (Exception e)
1004 {
1005
1006 }
1007
1008
1009 _writeStart(_writer, target, (hasChildren || debugInfos != null || renderer != null), false);
1010 _writer.write("</dt>");
1011
1012 if (renderer != null)
1013 {
1014
1015 _writer.write("<div class=\"renderer\">Rendered by ");
1016 _writer.write(renderer.getClass().getCanonicalName());
1017 _writer.write("</div>");
1018
1019 if (!hasChildren && debugInfos == null)
1020 {
1021
1022 _writer.write("<dt>");
1023 _writeEnd(_writer, target);
1024 _writer.write("</dt>");
1025 }
1026 }
1027
1028 if (debugInfos != null)
1029 {
1030 final String fieldid = target.getClientId() + "_lifecycle";
1031 _writer.write("<div class=\"lifecycle_values_wrapper\">");
1032 _writer.write("<a href=\"#\" onclick=\"toggle('");
1033 _writer.write(fieldid);
1034 _writer.write("'); return false;\"><span id=\"");
1035 _writer.write(fieldid);
1036 _writer.write("Off\">+</span><span id=\"");
1037 _writer.write(fieldid);
1038 _writer.write("On\" style=\"display: none;\">-</span> Value Lifecycle</a>");
1039 _writer.write("<div id=\"");
1040 _writer.write(fieldid);
1041 _writer.write("\" class=\"lifecycle_values\">");
1042
1043
1044 for (Map.Entry<String, List<Object[]>> entry : debugInfos.entrySet())
1045 {
1046 _writer.write("<span>");
1047 _writer.write(entry.getKey());
1048 _writer.write("</span><ol>");
1049 int i = 0;
1050 for (Object[] debugInfo : entry.getValue())
1051 {
1052
1053
1054
1055
1056
1057
1058
1059 String oldValue = debugInfo[1] == null ? "null" : debugInfo[1].toString();
1060 String newValue = debugInfo[2] == null ? "null" : debugInfo[2].toString();
1061 _writer.write("<li><b>");
1062 _writer.write(entry.getKey());
1063 _writer.write("</b> set from <b>");
1064 _writer.write(oldValue);
1065 _writer.write("</b> to <b>");
1066 _writer.write(newValue);
1067 _writer.write("</b> in Phase ");
1068 _writer.write(debugInfo[0].toString());
1069
1070
1071 if (debugInfo[3] != null)
1072 {
1073 final String stackTraceId = fieldid + "_" + entry.getKey() + "_" + i;
1074 _writer.write("<div class=\"stacktrace_wrapper\">");
1075 _writer.write("<a href=\"#\" onclick=\"toggle('");
1076 _writer.write(stackTraceId);
1077 _writer.write("'); return false;\"><span id=\"");
1078 _writer.write(stackTraceId);
1079 _writer.write("Off\">+</span><span id=\"");
1080 _writer.write(stackTraceId);
1081 _writer.write("On\" style=\"display: none;\">-</span> Call Stack</a>");
1082 _writer.write("<div id=\"");
1083 _writer.write(stackTraceId);
1084 _writer.write("\" class=\"stacktrace_values\">");
1085 _writer.write("<ul>");
1086 for (StackTraceElement stackTraceElement
1087 : (List<StackTraceElement>) debugInfo[3])
1088 {
1089 _writer.write("<li>");
1090 _writer.write(stackTraceElement.toString());
1091 _writer.write("</li>");
1092 }
1093 _writer.write("</ul></div></div>");
1094 }
1095
1096 _writer.write("</li>");
1097
1098 i++;
1099 }
1100 _writer.write("</ol>");
1101 }
1102
1103 _writer.write("</div></div>");
1104
1105
1106
1107 requestMap.remove(DEBUG_INFO_KEY + target.getClientId());
1108
1109 if (!hasChildren)
1110 {
1111
1112 _writer.write("<dt>");
1113 _writeEnd(_writer, target);
1114 _writer.write("</dt>");
1115 }
1116 }
1117 }
1118
1119 if (!hasChildren)
1120 {
1121 _writer.write("</dl>");
1122
1123 while (parent != null &&
1124 ((parent.getChildCount()>0 && parent.getChildren().get(parent.getChildCount()-1) == target)
1125 || (parent.getFacetCount() != 0
1126 && _getVisitedFacetCount(parent) == parent.getFacetCount())))
1127 {
1128
1129
1130
1131 _removeVisitedFacetCount(parent);
1132
1133
1134 if (parent instanceof UIData)
1135 {
1136 UIData uidata = (UIData) parent;
1137 if (uidata.getRowIndex() != uidata.getRowCount() - 1)
1138 {
1139
1140 break;
1141 }
1142 }
1143 else if (parent instanceof UIRepeat)
1144 {
1145 UIRepeat uirepeat = (UIRepeat) parent;
1146 if (uirepeat.getIndex() + uirepeat.getStep() < uirepeat.getRowCount())
1147 {
1148
1149 break;
1150 }
1151 }
1152
1153 _writer.write("</dd><dt>");
1154 _writeEnd(_writer, parent);
1155 _writer.write("</dt></dl>");
1156
1157 if (!(parent instanceof UIViewRoot))
1158 {
1159 _writer.write("</dd>");
1160 }
1161
1162 target = parent;
1163 parent = target.getParent();
1164 }
1165 }
1166 }
1167 catch (IOException ioe)
1168 {
1169 throw new FacesException(ioe);
1170 }
1171
1172 return VisitResult.ACCEPT;
1173 }
1174
1175 }
1176
1177 private static boolean _isFirstUIColumn(UIComponent uidata, UIColumn uicolumn)
1178 {
1179 for (int i = 0, childCount = uidata.getChildCount(); i < childCount; i++)
1180 {
1181 UIComponent child = uidata.getChildren().get(i);
1182 if (child instanceof UIColumn)
1183 {
1184 return (child == uicolumn);
1185 }
1186 }
1187 return false;
1188 }
1189
1190 private static String _getFacetName(UIComponent component)
1191 {
1192 UIComponent parent = component.getParent();
1193 if (parent != null)
1194 {
1195 if (parent.getFacetCount() > 0)
1196 {
1197 for (Map.Entry<String, UIComponent> entry : parent.getFacets().entrySet())
1198 {
1199 if (entry.getValue() == component)
1200 {
1201 return entry.getKey();
1202 }
1203 }
1204 }
1205 }
1206 return null;
1207 }
1208
1209 private static int _getVisitedFacetCount(UIComponent component)
1210 {
1211 Integer count = visitedFacetCount.get(component);
1212 if (count != null)
1213 {
1214 return count;
1215 }
1216 return 0;
1217 }
1218
1219 private static void _incrementVisitedFacetCount(UIComponent component)
1220 {
1221 visitedFacetCount.put(component, _getVisitedFacetCount(component) + 1);
1222 }
1223
1224 private static void _removeVisitedFacetCount(UIComponent component)
1225 {
1226 visitedFacetCount.remove(component);
1227 }
1228
1229 private static void _writeEnd(Writer writer, UIComponent c) throws IOException
1230 {
1231 if (!_isText(c))
1232 {
1233 writer.write(TS);
1234 writer.write('/');
1235 writer.write(_getName(c));
1236 writer.write('>');
1237 }
1238 }
1239
1240 private static void _writeAttributes(Writer writer, UIComponent c, boolean valueExpressionValues)
1241 {
1242 try
1243 {
1244 BeanInfo info = Introspector.getBeanInfo(c.getClass());
1245 PropertyDescriptor[] pd = info.getPropertyDescriptors();
1246 Method m = null;
1247 Object v = null;
1248 ValueExpression valueExpression = null;
1249 String str = null;
1250 for (int i = 0; i < pd.length; i++)
1251 {
1252 if ((pd[i].getWriteMethod() != null || Arrays.binarySearch(ALWAYS_WRITE, pd[i].getName()) > -1)
1253 && Arrays.binarySearch(IGNORE, pd[i].getName()) < 0)
1254 {
1255 m = pd[i].getReadMethod();
1256 try
1257 {
1258
1259 valueExpression = c.getValueExpression(pd[i].getName());
1260 if (valueExpressionValues && valueExpression != null)
1261 {
1262 String expressionString = valueExpression.getExpressionString();
1263 if (null == expressionString)
1264 {
1265 expressionString = "";
1266 }
1267 _writeAttribute(writer, pd[i].getName(), expressionString);
1268 }
1269 else
1270 {
1271 v = m.invoke(c, null);
1272 if (v != null)
1273 {
1274 if (v instanceof Collection || v instanceof Map || v instanceof Iterator)
1275 {
1276 continue;
1277 }
1278 if (v instanceof Expression)
1279 {
1280 str = ((Expression)v).getExpressionString();
1281 }
1282 else if (v instanceof ValueBinding)
1283 {
1284 str = ((ValueBinding) v).getExpressionString();
1285 }
1286 else if (v instanceof MethodBinding)
1287 {
1288 str = ((MethodBinding) v).getExpressionString();
1289 }
1290 else
1291 {
1292 str = v.toString();
1293 }
1294
1295 _writeAttribute(writer, pd[i].getName(), str);
1296 }
1297 }
1298 }
1299 catch (Exception e)
1300 {
1301
1302 }
1303 }
1304 }
1305
1306 ValueExpression binding = c.getValueExpression("binding");
1307 if (binding != null)
1308 {
1309 _writeAttribute(writer, "binding", binding.getExpressionString());
1310 }
1311
1312
1313 String location = _getComponentLocation(c);
1314 if (location != null)
1315 {
1316 _writeAttribute(writer, "location", location);
1317 }
1318 }
1319 catch (Exception e)
1320 {
1321
1322 }
1323 }
1324
1325 private static void _writeAttribute(Writer writer, String name, String value) throws IOException
1326 {
1327 writer.write(" ");
1328 writer.write(name);
1329 writer.write("=\"");
1330 writer.write(value.replaceAll("<", TS));
1331 writer.write("\"");
1332 }
1333
1334 private static void _writeStart(Writer writer, UIComponent c,
1335 boolean children, boolean valueExpressionValues) throws IOException
1336 {
1337 if (_isText(c))
1338 {
1339 String str = c.toString().trim();
1340 writer.write(str.replaceAll("<", TS));
1341 }
1342 else
1343 {
1344 writer.write(TS);
1345 writer.write(_getName(c));
1346 _writeAttributes(writer, c, valueExpressionValues);
1347 if (children)
1348 {
1349 writer.write('>');
1350 }
1351 else
1352 {
1353 writer.write("/>");
1354 }
1355 }
1356 }
1357
1358 private static String _getName(UIComponent c)
1359 {
1360 String nm = c.getClass().getName();
1361 return nm.substring(nm.lastIndexOf('.') + 1);
1362 }
1363
1364 private static boolean _isText(UIComponent c)
1365 {
1366 return (c.getClass().getName().startsWith("org.apache.myfaces.view.facelets.compiler"));
1367 }
1368
1369 private static void _prepareExceptionStack(Throwable ex)
1370 {
1371
1372 if (ex == null)
1373 {
1374 return;
1375 }
1376
1377
1378 if (!_initCausePerReflection(ex, "getRootCause"))
1379 {
1380 _initCausePerReflection(ex, "getCause");
1381 }
1382
1383 _prepareExceptionStack(ex.getCause());
1384 }
1385
1386 private static boolean _initCausePerReflection(Throwable ex, String methodName)
1387 {
1388 try
1389 {
1390 Method causeGetter = ex.getClass().getMethod(methodName, (Class[])null);
1391 Throwable rootCause = (Throwable)causeGetter.invoke(ex, (Object[])null);
1392 return _initCauseIfAvailable(ex, rootCause);
1393 }
1394 catch (Exception e1)
1395 {
1396 return false;
1397 }
1398 }
1399
1400 private static boolean _initCauseIfAvailable(Throwable th, Throwable cause)
1401 {
1402 if (cause == null)
1403 {
1404 return false;
1405 }
1406
1407 try
1408 {
1409 Method m = Throwable.class.getMethod("initCause", new Class[] { Throwable.class });
1410 m.invoke(th, new Object[] { cause });
1411 return true;
1412 }
1413 catch (Exception e)
1414 {
1415 return false;
1416 }
1417 }
1418
1419
1420
1421
1422
1423
1424 private static String _getComponentLocation(UIComponent component)
1425 {
1426 Location location = (Location) component.getAttributes()
1427 .get(UIComponent.VIEW_LOCATION_KEY);
1428 if (location != null)
1429 {
1430 return location.toString();
1431 }
1432 return null;
1433 }
1434 }