1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.tomahawk.util;
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.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.PrintWriter;
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.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.SortedMap;
41 import java.util.TreeMap;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44
45 import javax.faces.component.UIComponent;
46 import javax.faces.context.ExternalContext;
47 import javax.faces.context.FacesContext;
48 import javax.faces.el.MethodBinding;
49 import javax.faces.el.ValueBinding;
50 import javax.servlet.ServletException;
51 import javax.servlet.http.HttpServletResponse;
52
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55
56
57
58
59
60
61
62
63
64 public class ErrorPageWriter {
65
66 private static final Log log = LogFactory.getLog(ErrorPageWriter.class);
67 private static final Class[] NO_ARGS = new Class[0];
68
69 private final static String TS = "<";
70
71 private static final String ERROR_TEMPLATE = "META-INF/rsc/myfaces-dev-error.xml";
72
73 private static final String ERROR_TEMPLATE_RESOURCE = "org.apache.myfaces.ERROR_TEMPLATE_RESOURCE";
74
75 private static String[] ERROR_PARTS;
76
77 private static final String DEBUG_TEMPLATE = "META-INF/rsc/myfaces-dev-debug.xml";
78
79 private static final String DEBUG_TEMPLATE_RESOURCE = "org.apache.myfaces.DEBUG_TEMPLATE_RESOURCE";
80
81 private static String[] DEBUG_PARTS;
82
83 public ErrorPageWriter() {
84 super();
85 }
86
87 private static String getErrorTemplate(FacesContext context)
88 {
89 String errorTemplate = context.getExternalContext().getInitParameter(ERROR_TEMPLATE_RESOURCE);
90 if (errorTemplate != null)
91 {
92 return errorTemplate;
93 }
94 return ERROR_TEMPLATE;
95 }
96
97 private static String getDebugTemplate(FacesContext context)
98 {
99 String debugTemplate = context.getExternalContext().getInitParameter(DEBUG_TEMPLATE_RESOURCE);
100 if (debugTemplate != null)
101 {
102 return debugTemplate;
103 }
104 return DEBUG_TEMPLATE;
105 }
106
107 private static void init(FacesContext context) throws IOException {
108 if (ERROR_PARTS == null) {
109 ERROR_PARTS = splitTemplate(getErrorTemplate(context));
110 }
111
112 if (DEBUG_PARTS == null) {
113 DEBUG_PARTS = splitTemplate(getDebugTemplate(context));
114 }
115 }
116
117 private static String[] splitTemplate(String rsc) throws IOException {
118 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(rsc);
119 if (is == null) {
120 throw new FileNotFoundException(rsc);
121 }
122 ByteArrayOutputStream baos = new ByteArrayOutputStream();
123 byte[] buff = new byte[512];
124 int read;
125 while ((read = is.read(buff)) != -1) {
126 baos.write(buff, 0, read);
127 }
128 String str = baos.toString();
129 return str.split("@@");
130 }
131
132 static ArrayList getErrorId(Throwable e){
133 String message = e.getMessage();
134
135 if(message==null)
136 return null;
137
138 ArrayList list = new ArrayList();
139 Pattern pattern = Pattern.compile(".*?\\Q,Id:\\E\\s*(\\S+)\\s*\\].*?");
140 Matcher matcher = pattern.matcher(message);
141
142 while (matcher.find()){
143 list.add(matcher.group(1));
144 }
145 if (list.size()>0) return list;
146 return null;
147 }
148
149 public static void writeCause(Writer writer, Throwable ex) throws IOException {
150 String msg = ex.getMessage();
151 for(;;) {
152 Throwable t = getCause(ex);
153 if (t == null)
154 break;
155
156 ex = t;
157 if (ex.getMessage()!=null) msg = ex.getMessage();
158 }
159
160 if (msg != null) {
161 msg =ex.getClass().getName() + " - " + msg;
162 writer.write(msg.replaceAll("<", TS));
163 } else {
164 writer.write(ex.getClass().getName());
165 }
166 }
167
168 public static void debugHtml(Writer writer, FacesContext faces, Throwable e) throws IOException {
169 init(faces);
170 Date now = new Date();
171 for (int i = 0; i < ERROR_PARTS.length; i++) {
172 if ("message".equals(ERROR_PARTS[i])) {
173 String msg = e.getMessage();
174 if (msg != null) {
175 writer.write(msg.replaceAll("<", TS));
176 } else {
177 writer.write(e.getClass().getName());
178 }
179 } else if ("trace".equals(ERROR_PARTS[i])) {
180 writeException(writer, e);
181 } else if ("now".equals(ERROR_PARTS[i])) {
182 writer.write(DateFormat.getDateTimeInstance().format(now));
183 } else if ("tree".equals(ERROR_PARTS[i])) {
184 if (faces.getViewRoot() != null) {
185 writeComponent(writer, faces.getViewRoot(), getErrorId(e));
186 }
187 } else if ("vars".equals(ERROR_PARTS[i])) {
188 writeVariables(writer, faces);
189 } else if ("cause".equals(ERROR_PARTS[i])) {
190 writeCause(writer, e);
191 } else {
192 writer.write(ERROR_PARTS[i]);
193 }
194 }
195 }
196
197 public static void debugHtml(Writer writer, FacesContext faces, List exceptionList) throws IOException
198 {
199 init(faces);
200 Date now = new Date();
201 for (int i = 0; i < ERROR_PARTS.length; i++)
202 {
203 if ("message".equals(ERROR_PARTS[i]))
204 {
205 for (int j = 0; j < exceptionList.size(); j++)
206 {
207 Exception e = (Exception) exceptionList.get(j);
208 String msg = e.getMessage();
209 if (msg != null)
210 {
211 writer.write(msg.replaceAll("<", TS));
212 }
213 else
214 {
215 writer.write(e.getClass().getName());
216 }
217 if (!(j+1==exceptionList.size()))
218 {
219 writer.write("<br>");
220 }
221 }
222 }
223 else if ("trace".equals(ERROR_PARTS[i]))
224 {
225 for (int j = 0; j < exceptionList.size(); j++)
226 {
227 Exception e = (Exception) exceptionList.get(j);
228 writeException(writer, e);
229 }
230 }
231 else if ("now".equals(ERROR_PARTS[i]))
232 {
233 writer.write(DateFormat.getDateTimeInstance().format(now));
234 }
235 else if ("tree".equals(ERROR_PARTS[i]))
236 {
237 if (faces.getViewRoot() != null)
238 {
239 List highlightId = null;
240 for (int j = 0; j < exceptionList.size(); j++)
241 {
242 Exception e = (Exception) exceptionList.get(j);
243 if (highlightId == null)
244 {
245 highlightId = getErrorId(e);
246 }
247 else
248 {
249 highlightId.addAll(getErrorId(e));
250 }
251 }
252 writeComponent(writer, faces.getViewRoot(), highlightId);
253 }
254 }
255 else if ("vars".equals(ERROR_PARTS[i]))
256 {
257 writeVariables(writer, faces);
258 }
259 else if ("cause".equals(ERROR_PARTS[i]))
260 {
261 for (int j = 0; j < exceptionList.size(); j++)
262 {
263 Exception e = (Exception) exceptionList.get(j);
264 writeCause(writer, e);
265 if (!(j+1==exceptionList.size()))
266 {
267 writer.write("<br>");
268 }
269 }
270 }
271 else
272 {
273 writer.write(ERROR_PARTS[i]);
274 }
275 }
276 }
277
278 private static void writeException(Writer writer, Throwable e) throws IOException {
279 StringWriter str = new StringWriter(256);
280 PrintWriter pstr = new PrintWriter(str);
281 e.printStackTrace(pstr);
282 pstr.close();
283 writer.write(str.toString().replaceAll("<", TS));
284 }
285
286 public static void debugHtml(Writer writer, FacesContext faces) throws IOException {
287 init(faces);
288 Date now = new Date();
289 for (int i = 0; i < DEBUG_PARTS.length; i++) {
290 if ("message".equals(DEBUG_PARTS[i])) {
291 writer.write(faces.getViewRoot().getViewId());
292 } else if ("now".equals(DEBUG_PARTS[i])) {
293 writer.write(DateFormat.getDateTimeInstance().format(now));
294 } else if ("tree".equals(DEBUG_PARTS[i])) {
295 writeComponent(writer, faces.getViewRoot(), null);
296 } else if ("vars".equals(DEBUG_PARTS[i])) {
297 writeVariables(writer, faces);
298 } else {
299 writer.write(DEBUG_PARTS[i]);
300 }
301 }
302 }
303
304 static void writeVariables(Writer writer, FacesContext faces) throws IOException {
305 ExternalContext ctx = faces.getExternalContext();
306 writeVariables(writer, ctx.getRequestParameterMap(), "Request Parameters");
307 writeVariables(writer, ctx.getRequestMap(), "Request Attributes");
308 if (ctx.getSession(false) != null) {
309 writeVariables(writer, ctx.getSessionMap(), "Session Attributes");
310 }
311 writeVariables(writer, ctx.getApplicationMap(), "Application Attributes");
312 }
313
314 private static void writeVariables(Writer writer, Map vars, String caption) throws IOException {
315 writer.write("<table><caption>");
316 writer.write(caption);
317 writer.write("</caption><thead><tr><th style=\"width: 10%; \">Name</th><th style=\"width: 90%; \">Value</th></tr></thead><tbody>");
318 boolean written = false;
319 if (!vars.isEmpty()) {
320 SortedMap map = new TreeMap(vars);
321 Map.Entry entry = null;
322 String key = null;
323 for (Iterator itr = map.entrySet().iterator(); itr.hasNext(); ) {
324 entry = (Map.Entry) itr.next();
325 key = entry.getKey().toString();
326 if (key.indexOf('.') == -1) {
327 writer.write("<tr><td>");
328 writer.write(key.replaceAll("<", TS));
329 writer.write("</td><td>");
330 writer.write(entry.getValue().toString().replaceAll("<", TS));
331 writer.write("</td></tr>");
332 written = true;
333 }
334 }
335 }
336 if (!written) {
337 writer.write("<tr><td colspan=\"2\"><em>None</em></td></tr>");
338 }
339 writer.write("</tbody></table>");
340 }
341
342 static void writeComponent(Writer writer, UIComponent c, List highlightId) throws IOException {
343 writer.write("<dl><dt");
344 if (isText(c)) {
345 writer.write(" class=\"uicText\"");
346 }
347 if (highlightId != null){
348 if ((highlightId.size() > 0) && (highlightId.get(0).equals(c.getId()))){
349 highlightId.remove(0);
350 if (highlightId.size()==0){
351 writer.write(" class=\"highlightComponent\"");
352 }
353 }
354 }
355 writer.write(">");
356
357 boolean hasChildren = c.getChildCount() > 0 || c.getFacets().size() > 0;
358
359 writeStart(writer, c, hasChildren);
360 writer.write("</dt>");
361 if (hasChildren) {
362 if (c.getFacets().size() > 0) {
363 Map.Entry entry;
364 for (Iterator itr = c.getFacets().entrySet().iterator(); itr.hasNext(); ) {
365 entry = (Map.Entry) itr.next();
366 writer.write("<dd class=\"uicFacet\">");
367 writer.write("<span>");
368 writer.write((String) entry.getKey());
369 writer.write("</span>");
370 writeComponent(writer, (UIComponent) entry.getValue(), highlightId);
371 writer.write("</dd>");
372 }
373 }
374 if (c.getChildCount() > 0) {
375 for (Iterator itr = c.getChildren().iterator(); itr.hasNext(); ) {
376 writer.write("<dd>");
377 writeComponent(writer, (UIComponent) itr.next(), highlightId);
378 writer.write("</dd>");
379 }
380 }
381 writer.write("<dt>");
382 writeEnd(writer, c);
383 writer.write("</dt>");
384 }
385 writer.write("</dl>");
386 }
387
388 private static void writeEnd(Writer writer, UIComponent c) throws IOException {
389 if (!isText(c)) {
390 writer.write(TS);
391 writer.write('/');
392 writer.write(getName(c));
393 writer.write('>');
394 }
395 }
396
397 private final static String[] IGNORE = new String[] { "parent", "rendererType" };
398
399 private static void writeAttributes(Writer writer, UIComponent c) {
400 try {
401 BeanInfo info = Introspector.getBeanInfo(c.getClass());
402 PropertyDescriptor[] pd = info.getPropertyDescriptors();
403 Method m = null;
404 Object v = null;
405 String str = null;
406 for (int i = 0; i < pd.length; i++) {
407 if (pd[i].getWriteMethod() != null && Arrays.binarySearch(IGNORE, pd[i].getName()) < 0) {
408 m = pd[i].getReadMethod();
409 try {
410 v = m.invoke(c, null);
411 if (v != null) {
412 if (v instanceof Collection || v instanceof Map || v instanceof Iterator) {
413 continue;
414 }
415 writer.write(" ");
416 writer.write(pd[i].getName());
417 writer.write("=\"");
418 if (v instanceof ValueBinding) {
419 str = ((ValueBinding) v).getExpressionString();
420 } else if (v instanceof MethodBinding) {
421 str = ((MethodBinding) v).getExpressionString();
422 } else {
423 ValueBinding vb = c.getValueBinding(pd[i].getName());
424 str = vb!=null?(vb.getExpressionString()+"="+v.toString()):v.toString();
425 }
426 writer.write(str.replaceAll("<", TS));
427 writer.write("\"");
428 }
429 } catch (Exception e) {
430
431 }
432 }
433 }
434
435 ValueBinding binding = c.getValueBinding("binding");
436 if (binding != null) {
437 writer.write(" binding=\"");
438 writer.write(binding.getExpressionString().replaceAll("<", TS));
439 writer.write("\"");
440 }
441 } catch (Exception e) {
442
443 }
444 }
445
446 private static void writeStart(Writer writer, UIComponent c, boolean children) throws IOException {
447 if (isText(c)) {
448 String str = c.toString().trim();
449 writer.write(str.replaceAll("<", TS));
450 } else {
451 writer.write(TS);
452 writer.write(getName(c));
453 writeAttributes(writer, c);
454 if (children) {
455 writer.write('>');
456 } else {
457 writer.write("/>");
458 }
459 }
460 }
461
462 private static String getName(UIComponent c) {
463 String nm = c.getClass().getName();
464 return nm.substring(nm.lastIndexOf('.') + 1);
465 }
466
467 private static boolean isText(UIComponent c) {
468 return (c.getClass().getName().startsWith("com.sun.facelets.compiler"));
469 }
470
471 public static void handleException(FacesContext facesContext, Exception ex) throws ServletException, IOException
472 {
473 handleThrowable(facesContext, ex);
474 }
475
476 public static void handleThrowable(FacesContext facesContext, Throwable ex) throws ServletException, IOException {
477
478 prepareExceptionStack(ex);
479
480 Object response = facesContext.getExternalContext().getResponse();
481 if(response instanceof HttpServletResponse) {
482 HttpServletResponse httpResp = (HttpServletResponse) response;
483 if (!httpResp.isCommitted()) {
484 httpResp.reset();
485 httpResp.setContentType("text/html; charset=UTF-8");
486 Writer writer = httpResp.getWriter();
487
488 debugHtml(writer, facesContext, ex);
489
490 log.error("An exception occurred", ex);
491 }
492 else {
493 throwException(ex);
494 }
495 }
496 else {
497 throwException(ex);
498 }
499 }
500
501 public static void handleExceptionList(FacesContext facesContext, List exceptionList) throws ServletException, IOException
502 {
503 for (int i = 0; i < exceptionList.size(); i++)
504 {
505 prepareExceptionStack( (Exception) exceptionList.get(i));
506 }
507
508 Object response = facesContext.getExternalContext().getResponse();
509 if(response instanceof HttpServletResponse)
510 {
511 HttpServletResponse httpResp = (HttpServletResponse) response;
512 if (!httpResp.isCommitted())
513 {
514 httpResp.reset();
515 httpResp.setContentType("text/html; charset=UTF-8");
516 Writer writer = httpResp.getWriter();
517
518 debugHtml(writer, facesContext, exceptionList);
519
520 for (int i = 0; i < exceptionList.size(); i++)
521 {
522 log.error("An exception occurred", (Exception) exceptionList.get(i));
523 }
524 }
525 else
526 {
527 throwException((Exception)exceptionList.get(0));
528 }
529 }
530 else
531 {
532 throwException((Exception)exceptionList.get(0));
533 }
534 }
535
536 private static void prepareExceptionStack(Throwable ex) {
537
538 if(ex==null)
539 return;
540
541
542 if(!initCausePerReflection(ex,"getRootCause")) {
543 initCausePerReflection(ex,"getCause");
544 }
545
546 prepareExceptionStack(getCause(ex));
547 }
548
549
550
551
552
553 private static Throwable getCause(Throwable ex) {
554 try {
555 Method causeGetter = ex.getClass().getMethod("getCause", NO_ARGS);
556 Throwable cause = (Throwable) causeGetter.invoke(ex, NO_ARGS);
557 return cause;
558 } catch (Exception e1) {
559 return null;
560 }
561 }
562
563 private static boolean initCausePerReflection(Throwable ex, String methodName) {
564 try {
565 Method causeGetter = ex.getClass().getMethod(methodName, NO_ARGS);
566 Throwable rootCause = (Throwable) causeGetter.invoke(ex, NO_ARGS);
567 return initCauseIfAvailable(ex,rootCause);
568 } catch (Exception e1) {
569 return false;
570 }
571 }
572
573 static void throwException(Throwable e) throws IOException, ServletException {
574
575 prepareExceptionStack(e);
576
577 if (e instanceof IOException)
578 {
579 throw (IOException)e;
580 }
581 else if (e instanceof ServletException)
582 {
583 throw (ServletException)e;
584 }
585 else
586 {
587 ServletException ex;
588
589 if (e.getMessage() != null) {
590 ex=new ServletException(e.getMessage(), e);
591 }
592 else {
593 ex=new ServletException(e);
594 }
595
596 initCauseIfAvailable(ex, e);
597
598 throw ex;
599 }
600 }
601
602 private static boolean initCauseIfAvailable(Throwable th, Throwable cause) {
603
604 if(cause == null)
605 return false;
606
607 try {
608 Method m = Throwable.class.getMethod("initCause",new Class[]{Throwable.class});
609 m.invoke(th,new Object[]{cause});
610 return true;
611 }
612 catch(Exception e) {
613 return false;
614 }
615 }
616 }
617