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