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