View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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   * @author Jacob Hookom (ICLA with ASF filed)
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                         // do nothing
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             // do nothing
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         //check for getRootCause and getCause-methods
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