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