You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mm...@apache.org on 2007/07/24 07:05:34 UTC

svn commit: r558932 - in /myfaces/core/branches/1_2_1/api/src/main: java/javax/faces/webapp/FacesServlet.java java/javax/faces/webapp/_ErrorPageWriter.java resources/rsc/ resources/rsc/facelet-dev-debug.xml resources/rsc/facelet-dev-error.xml

Author: mmarinschek
Date: Mon Jul 23 22:05:33 2007
New Revision: 558932

URL: http://svn.apache.org/viewvc?view=rev&rev=558932
Log:
commit for https://issues.apache.org/jira/browse/MYFACES-1685: Enabled exception handling like in Facelets for the rest of the MyFaces LifeCycle

Added:
    myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/_ErrorPageWriter.java
    myfaces/core/branches/1_2_1/api/src/main/resources/rsc/
    myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-debug.xml
    myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-error.xml
Modified:
    myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/FacesServlet.java

Modified: myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/FacesServlet.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/FacesServlet.java?view=diff&rev=558932&r1=558931&r2=558932
==============================================================================
--- myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/FacesServlet.java (original)
+++ myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/FacesServlet.java Mon Jul 23 22:05:33 2007
@@ -16,6 +16,8 @@
 package javax.faces.webapp;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
 
 import javax.faces.FactoryFinder;
 import javax.faces.context.FacesContext;
@@ -47,6 +49,9 @@
     public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID";
 
     private static final String SERVLET_INFO = "FacesServlet of the MyFaces API implementation";
+    private static final String ERROR_HANDLING_PARAMETER = "org.apache.myfaces.ERROR_HANDLING";
+    private static final String ERROR_HANDLER_PARAMETER = "org.apache.myfaces.ERROR_HANDLER";
+
     private ServletConfig _servletConfig;
     private FacesContextFactory _facesContextFactory;
     private Lifecycle _lifecycle;
@@ -111,17 +116,16 @@
         {
             StringBuffer buffer = new StringBuffer();
 
-            buffer.append(" Someone is trying to access a secure resource : "
-                    + pathInfo);
-            buffer.append("\n remote address is " + httpRequest.getRemoteAddr());
-            buffer.append("\n remote host is " + httpRequest.getRemoteHost());
-            buffer.append("\n remote user is " + httpRequest.getRemoteUser());
-            buffer.append("\n request URI is " + httpRequest.getRequestURI());
+            buffer.append(" Someone is trying to access a secure resource : ").append(pathInfo);
+            buffer.append("\n remote address is ").append(httpRequest.getRemoteAddr());
+            buffer.append("\n remote host is ").append(httpRequest.getRemoteHost());
+            buffer.append("\n remote user is ").append(httpRequest.getRemoteUser());
+            buffer.append("\n request URI is ").append(httpRequest.getRequestURI());
 
             log.warn(buffer.toString());
 
             // Why does RI return a 404 and not a 403, SC_FORBIDDEN ?
-            
+
             ((HttpServletResponse) response)
                     .sendError(HttpServletResponse.SC_NOT_FOUND);
             return;
@@ -137,29 +141,54 @@
 			_lifecycle.execute(facesContext);
 			_lifecycle.render(facesContext);
 		}
-        catch (Throwable e)
+        catch (Exception e)
         {
-            if (e instanceof IOException)
-            {
-                throw (IOException)e;
-            }
-            else if (e instanceof ServletException)
-            {
-                throw (ServletException)e;
-            }
-            else if (e.getMessage() != null)
-            {
-                throw new ServletException(e.getMessage(), e);
-            }
-            else
-            {
-                throw new ServletException(e);
-            }
+            handleLifecycleException(facesContext, e);
         }
         finally
         {
             facesContext.release();
         }
 		if(log.isTraceEnabled()) log.trace("service end");
+    }
+
+    private void handleLifecycleException(FacesContext facesContext, Exception e) throws IOException, ServletException {
+
+        boolean errorHandling = getBooleanValue(facesContext.getExternalContext().getInitParameter(ERROR_HANDLING_PARAMETER), true);
+
+        if(errorHandling) {
+            String errorHandlerClass = facesContext.getExternalContext().getInitParameter(ERROR_HANDLER_PARAMETER);
+            if(errorHandlerClass != null) {
+                try {
+                    Class clazz = Class.forName(errorHandlerClass);
+
+                    Object errorHandler = clazz.newInstance();
+
+                    Method m = clazz.getMethod("handleException", new Class[]{FacesContext.class,Exception.class});
+                    m.invoke(errorHandler, new Object[]{facesContext, e});
+                }
+                catch(ClassNotFoundException ex) {
+                    throw new ServletException("Error-Handler : " +errorHandlerClass+ " was not found. Fix your web.xml-parameter : "+ERROR_HANDLER_PARAMETER,ex);
+                } catch (IllegalAccessException ex) {
+                    throw new ServletException("Constructor of error-Handler : " +errorHandlerClass+ " is not accessible. Error-Handler is specified in web.xml-parameter : "+ERROR_HANDLER_PARAMETER,ex);
+                } catch (InstantiationException ex) {
+                    throw new ServletException("Error-Handler : " +errorHandlerClass+ " could not be instantiated. Error-Handler is specified in web.xml-parameter : "+ERROR_HANDLER_PARAMETER,ex);
+                } catch (NoSuchMethodException ex) {
+                    throw new ServletException("Error-Handler : " +errorHandlerClass+ " did not have a method with name : handleException and parameters : javax.faces.context.FacesContext, java.lang.Exception. Error-Handler is specified in web.xml-parameter : "+ERROR_HANDLER_PARAMETER,ex);
+                } catch (InvocationTargetException ex) {
+                    throw new ServletException("Excecution of method handleException in Error-Handler : " +errorHandlerClass+ " caused an exception. Error-Handler is specified in web.xml-parameter : "+ERROR_HANDLER_PARAMETER,ex);
+                }
+            }
+            else {
+                _ErrorPageWriter.handleException(facesContext, e);
+            }
+        }
+        else {
+            _ErrorPageWriter.throwException(e);
+        }
+    }
+
+    private boolean getBooleanValue(String initParameter, boolean defaultVal) {
+        return initParameter != null && (initParameter.equalsIgnoreCase("on") || initParameter.equals("1") || initParameter.equalsIgnoreCase("true")) || defaultVal;
     }
 }

Added: myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/_ErrorPageWriter.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/_ErrorPageWriter.java?view=auto&rev=558932
==============================================================================
--- myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/_ErrorPageWriter.java (added)
+++ myfaces/core/branches/1_2_1/api/src/main/java/javax/faces/webapp/_ErrorPageWriter.java Mon Jul 23 22:05:33 2007
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javax.faces.webapp;
+
+import javax.faces.context.FacesContext;
+import java.io.Writer;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.el.Expression;
+import javax.el.ValueExpression;
+import javax.el.MethodExpression;
+import javax.faces.component.UIComponent;
+import javax.faces.context.ExternalContext;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+/**
+ * @author Jacob Hookom (ICLA with ASF filed).
+ */
+public final class _ErrorPageWriter {
+
+    private final static String TS = "<";
+
+    private static final String ERROR_TEMPLATE = "META-INF/rsc/facelet-dev-error.xml";
+
+    private static String[] ERROR_PARTS;
+
+    private static final String DEBUG_TEMPLATE = "META-INF/rsc/facelet-dev-debug.xml";
+
+    private static String[] DEBUG_PARTS;
+
+    public _ErrorPageWriter() {
+        super();
+    }
+
+    public static void main(String[] argv) throws Exception {
+        init();
+    }
+
+    private static void init() throws IOException {
+        if (ERROR_PARTS == null) {
+            ERROR_PARTS = splitTemplate(ERROR_TEMPLATE);
+        }
+
+        if (DEBUG_PARTS == null) {
+            DEBUG_PARTS = splitTemplate(DEBUG_TEMPLATE);
+        }
+    }
+
+    private static String[] splitTemplate(String rsc) throws IOException {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(rsc);
+        if (is == null) {
+            throw new FileNotFoundException(rsc);
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buff = new byte[512];
+        int read;
+        while ((read = is.read(buff)) != -1) {
+            baos.write(buff, 0, read);
+        }
+        String str = baos.toString();
+        return str.split("@@");
+    }
+
+    public static void debugHtml(Writer writer, FacesContext faces, Exception e) throws IOException {
+        init();
+        Date now = new Date();
+        for (int i = 0; i < ERROR_PARTS.length; i++) {
+            if ("message".equals(ERROR_PARTS[i])) {
+                String msg = e.getMessage();
+                if (msg != null) {
+                    writer.write(msg.replaceAll("<", TS));
+                } else {
+                    writer.write(e.getClass().getName());
+                }
+            } else if ("trace".equals(ERROR_PARTS[i])) {
+                writeException(writer, e);
+            } else if ("now".equals(ERROR_PARTS[i])) {
+                writer.write(DateFormat.getDateTimeInstance().format(now));
+            } else if ("tree".equals(ERROR_PARTS[i])) {
+                writeComponent(writer, faces.getViewRoot());
+            } else if ("vars".equals(ERROR_PARTS[i])) {
+                writeVariables(writer, faces);
+            } else {
+                writer.write(ERROR_PARTS[i]);
+            }
+        }
+    }
+
+    private static void writeException(Writer writer, Exception e) throws IOException {
+        StringWriter str = new StringWriter(256);
+        PrintWriter pstr = new PrintWriter(str);
+        e.printStackTrace(pstr);
+        pstr.close();
+        writer.write(str.toString().replaceAll("<", TS));
+    }
+
+    public static void debugHtml(Writer writer, FacesContext faces) throws IOException {
+        init();
+        Date now = new Date();
+        for (int i = 0; i < DEBUG_PARTS.length; i++) {
+            if ("message".equals(DEBUG_PARTS[i])) {
+                writer.write(faces.getViewRoot().getViewId());
+            } else if ("now".equals(DEBUG_PARTS[i])) {
+                writer.write(DateFormat.getDateTimeInstance().format(now));
+            } else if ("tree".equals(DEBUG_PARTS[i])) {
+                writeComponent(writer, faces.getViewRoot());
+            } else if ("vars".equals(DEBUG_PARTS[i])) {
+                writeVariables(writer, faces);
+            } else {
+                writer.write(DEBUG_PARTS[i]);
+            }
+        }
+    }
+
+    private static void writeVariables(Writer writer, FacesContext faces) throws IOException {
+        ExternalContext ctx = faces.getExternalContext();
+        writeVariables(writer, ctx.getRequestParameterMap(), "Request Parameters");
+        writeVariables(writer, ctx.getRequestMap(), "Request Attributes");
+        if (ctx.getSession(false) != null) {
+            writeVariables(writer, ctx.getSessionMap(), "Session Attributes");
+        }
+        writeVariables(writer, ctx.getApplicationMap(), "Application Attributes");
+    }
+
+    private static void writeVariables(Writer writer, Map vars, String caption) throws IOException {
+        writer.write("<table><caption>");
+        writer.write(caption);
+        writer.write("</caption><thead><tr><th style=\"width: 10%; \">Name</th><th style=\"width: 90%; \">Value</th></tr></thead><tbody>");
+        boolean written = false;
+        if (!vars.isEmpty()) {
+            SortedMap map = new TreeMap(vars);
+            Map.Entry entry = null;
+            String key = null;
+            for (Iterator itr = map.entrySet().iterator(); itr.hasNext(); ) {
+                entry = (Map.Entry) itr.next();
+                key = entry.getKey().toString();
+                if (key.indexOf('.') == -1) {
+                    writer.write("<tr><td>");
+                    writer.write(key.replaceAll("<", TS));
+                    writer.write("</td><td>");
+                    writer.write(entry.getValue().toString().replaceAll("<", TS));
+                    writer.write("</td></tr>");
+                    written = true;
+                }
+            }
+        }
+        if (!written) {
+            writer.write("<tr><td colspan=\"2\"><em>None</em></td></tr>");
+        }
+        writer.write("</tbody></table>");
+    }
+
+    private static void writeComponent(Writer writer, UIComponent c) throws IOException {
+        writer.write("<dl><dt");
+        if (isText(c)) {
+            writer.write(" class=\"uicText\"");
+        }
+        writer.write(">");
+
+        boolean hasChildren = c.getChildCount() > 0 || c.getFacets().size() > 0;
+
+        writeStart(writer, c, hasChildren);
+        writer.write("</dt>");
+        if (hasChildren) {
+            if (c.getFacets().size() > 0) {
+                Map.Entry entry;
+                for (Iterator itr = c.getFacets().entrySet().iterator(); itr.hasNext(); ) {
+                    entry = (Map.Entry) itr.next();
+                    writer.write("<dd class=\"uicFacet\">");
+                    writer.write("<span>");
+                    writer.write((String) entry.getKey());
+                    writer.write("</span>");
+                    writeComponent(writer, (UIComponent) entry.getValue());
+                    writer.write("</dd>");
+                }
+            }
+            if (c.getChildCount() > 0) {
+                for (Iterator itr = c.getChildren().iterator(); itr.hasNext(); ) {
+                    writer.write("<dd>");
+                    writeComponent(writer, (UIComponent) itr.next());
+                    writer.write("</dd>");
+                }
+            }
+            writer.write("<dt>");
+            writeEnd(writer, c);
+            writer.write("</dt>");
+        }
+        writer.write("</dl>");
+    }
+
+    private static void writeEnd(Writer writer, UIComponent c) throws IOException {
+        if (!isText(c)) {
+            writer.write(TS);
+            writer.write('/');
+            writer.write(getName(c));
+            writer.write('>');
+        }
+    }
+
+    private final static String[] IGNORE = new String[] { "parent", "rendererType" };
+
+    private static void writeAttributes(Writer writer, UIComponent c) {
+        try {
+            BeanInfo info = Introspector.getBeanInfo(c.getClass());
+            PropertyDescriptor[] pd = info.getPropertyDescriptors();
+            Method m = null;
+            Object v = null;
+            String str = null;
+            for (int i = 0; i < pd.length; i++) {
+                if (pd[i].getWriteMethod() != null && Arrays.binarySearch(IGNORE, pd[i].getName()) < 0) {
+                    m = pd[i].getReadMethod();
+                    try {
+                        v = m.invoke(c, null);
+                        if (v != null) {
+                            if (v instanceof Collection || v instanceof Map || v instanceof Iterator) {
+                                continue;
+                            }
+                            writer.write(" ");
+                            writer.write(pd[i].getName());
+                            writer.write("=\"");
+                            if (v instanceof Expression) {
+                                str = ((Expression) v).getExpressionString();
+                            }
+                            else if(v instanceof MethodExpression) {
+                                str = ((Expression) v).getExpressionString();
+                            }
+                            else {
+                                ValueExpression ve = c.getValueExpression(pd[i].getName());
+                                str = ve!=null?(ve.getExpressionString()+"="+v.toString()):v.toString();
+                            }
+                            writer.write(str.replaceAll("<", TS));
+                            writer.write("\"");
+                        }
+                    } catch (Exception e) {
+                        // do nothing
+                    }
+                }
+            }
+
+            ValueExpression binding = c.getValueExpression("binding");
+            if (binding != null) {
+                writer.write(" binding=\"");
+                writer.write(binding.getExpressionString().replaceAll("<", TS));
+                writer.write("\"");
+            }
+        } catch (Exception e) {
+            // do nothing
+        }
+    }
+
+    private static void writeStart(Writer writer, UIComponent c, boolean children) throws IOException {
+        if (isText(c)) {
+            String str = c.toString().trim();
+            writer.write(str.replaceAll("<", TS));
+        } else {
+            writer.write(TS);
+            writer.write(getName(c));
+            writeAttributes(writer, c);
+            if (children) {
+                writer.write('>');
+            } else {
+                writer.write("/>");
+            }
+        }
+    }
+
+    private static String getName(UIComponent c) {
+        String nm = c.getClass().getName();
+        return nm.substring(nm.lastIndexOf('.') + 1);
+    }
+
+    private static boolean isText(UIComponent c) {
+        return (c.getClass().getName().startsWith("com.sun.facelets.compiler"));
+    }
+
+    public static void handleException(FacesContext facesContext, Exception ex) throws ServletException, IOException {
+
+        prepareExceptionStack(ex);
+
+        Object response = facesContext.getExternalContext().getResponse();
+        if(response instanceof HttpServletResponse) {
+            HttpServletResponse httpResp = (HttpServletResponse) response;
+            if (!httpResp.isCommitted()) {
+                httpResp.reset();
+                httpResp.setContentType("text/html; charset=UTF-8");
+                Writer writer = httpResp.getWriter();
+
+                debugHtml(writer, facesContext, ex);
+            }
+        }
+        else {
+            throwException(ex);
+        }
+    }
+
+    private static void prepareExceptionStack(Throwable ex) {
+
+        if(ex==null)
+            return;
+
+        if(ex instanceof ServletException) {
+            try
+            {
+                Throwable rootCause = ((ServletException) ex).getRootCause();
+
+                if(rootCause != null) {
+                    Method m = ServletException.class.getMethod("initCause",new Class[]{Throwable.class});
+                    m.invoke(ex,new Object[]{rootCause});
+                }
+            } catch (Exception e1) {
+                //ignore if the method is not found - or cause has already been set.
+            }
+        }
+
+        prepareExceptionStack(ex.getCause());
+    }
+
+    static void throwException(Exception e) throws IOException, ServletException {
+        if (e instanceof IOException)
+        {
+            throw (IOException)e;
+        }
+        else if (e instanceof ServletException)
+        {
+            throw (ServletException)e;
+        }
+        else
+        {
+            ServletException ex;
+
+            if (e.getMessage() != null) {
+                ex=new ServletException(e.getMessage(), e);
+            }
+            else {
+                ex=new ServletException(e);
+            }
+
+            try {
+                Method m = ServletException.class.getMethod("initCause",new Class[]{Throwable.class});
+                m.invoke(ex,new Object[]{e});
+            } catch (Exception e1) {
+                //ignore if the method is not found - or cause has already been set.
+            }
+
+            throw ex;
+        }
+    }
+}

Added: myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-debug.xml
URL: http://svn.apache.org/viewvc/myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-debug.xml?view=auto&rev=558932
==============================================================================
--- myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-debug.xml (added)
+++ myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-debug.xml Mon Jul 23 22:05:33 2007
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+<title>Debug - @@message@@</title>
+<style type="text/css">
+body, div, span, td, th, caption { font-family: 'Trebuchet MS', Verdana, Arial, Sans-Serif; font-size: small; }
+ul, li, pre { padding: 0; margin: 0; }
+h1 { color: #090; }
+h2, h2 span { font-size: large; color: #339; }
+h2 a { text-decoration: none; color: #339; }
+.grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9;  }
+#view { color: #090; font-weight: bold; font-size: medium; }
+#tree, #vars { display: none; }
+code { font-size: medium; }
+#tree dl { color: #006; }
+#tree dd { margin-top: 2px; margin-bottom: 2px; }
+#tree dt { border: 1px solid #DDD; padding: 4px; border-left: 2px solid #666; font-family: "Courier New", Courier, mono; font-size: small; }
+.uicText { color: #999;  }
+table { border: 1px solid #CCC; border-collapse: collapse; border-spacing: 0px; width: 100%; text-align: left; }
+td { border: 1px solid #CCC; }
+thead tr th { padding: 2px; color: #030; background-color: #F9F9F9; }
+tbody tr td { padding: 10px 6px; }
+table caption { text-align: left; padding: 10px 0; font-size: large; }
+</style>
+<style type="text/css" media="print">
+#trace, #tree, #vars { display: block; }
+</style>
+<script language="javascript" type="text/javascript">
+function toggle(id) {
+	var style = document.getElementById(id).style;
+	if ("block" == style.display) {
+		style.display = "none";
+		document.getElementById(id+"Off").style.display = "inline";
+		document.getElementById(id+"On").style.display = "none";
+	} else {
+		style.display = "block";
+		document.getElementById(id+"Off").style.display = "none";
+		document.getElementById(id+"On").style.display = "inline";
+	}
+}
+</script>
+</head>
+<body>
+<h1>Debug Output</h1>
+<div id="error" class="grayBox" style="border: 1px solid #090;">@@message@@</div>
+<h2><a href="#" onclick="toggle('tree'); return false;"><span id="treeOff">+</span><span id="treeOn" style="display: none;">-</span> Component Tree</a></h2>
+<div id="tree" class="grayBox"><code>@@tree@@</code></div>
+<h2><a href="#" onclick="toggle('vars'); return false;"><span id="varsOff">+</span><span id="varsOn" style="display: none;">-</span> Scoped Variables</a></h2>
+<div id="vars">@@vars@@</div>
+<div class="grayBox" style="text-align: right; color: #666;">@@now@@ - Generated by Facelets</div>
+</body>
+</html>

Added: myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-error.xml
URL: http://svn.apache.org/viewvc/myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-error.xml?view=auto&rev=558932
==============================================================================
--- myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-error.xml (added)
+++ myfaces/core/branches/1_2_1/api/src/main/resources/rsc/facelet-dev-error.xml Mon Jul 23 22:05:33 2007
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+<title>Error - @@message@@</title>
+<style type="text/css">
+body, div, span, td, th, caption { font-family: 'Trebuchet MS', Verdana, Arial, Sans-Serif; font-size: small; }
+ul, li, pre { padding: 0; margin: 0; }
+h1 { color: #900; }
+h2, h2 span { font-size: large; color: #339; }
+h2 a { text-decoration: none; color: #339; }
+.grayBox { padding: 8px; margin: 10px 0; border: 1px solid #CCC; background-color: #f9f9f9;  }
+#error { color: #900; font-weight: bold; font-size: medium; }
+#trace, #tree, #vars { display: none; }
+code { font-size: medium; }
+#tree dl { color: #006; }
+#tree dd { margin-top: 2px; margin-bottom: 2px; }
+#tree dt { border: 1px solid #DDD; padding: 4px; border-left: 2px solid #666; font-family: "Courier New", Courier, mono; font-size: small; }
+.uicText { color: #999;  }
+table { border: 1px solid #CCC; border-collapse: collapse; border-spacing: 0px; width: 100%; text-align: left; }
+td { border: 1px solid #CCC; }
+thead tr th { padding: 2px; color: #030; background-color: #F9F9F9; }
+tbody tr td { padding: 10px 6px; }
+table caption { text-align: left; padding: 10px 0; font-size: large; }
+</style>
+<style type="text/css" media="print">
+#trace, #tree, #vars { display: block; }
+</style>
+<script language="javascript" type="text/javascript">
+function toggle(id) {
+	var style = document.getElementById(id).style;
+	if ("block" == style.display) {
+		style.display = "none";
+		document.getElementById(id+"Off").style.display = "inline";
+		document.getElementById(id+"On").style.display = "none";
+	} else {
+		style.display = "block";
+		document.getElementById(id+"Off").style.display = "none";
+		document.getElementById(id+"On").style.display = "inline";
+	}
+}
+</script>
+</head>
+<body>
+<h1>An Error Occurred:</h1>
+<div id="error" class="grayBox" style="border: 1px solid #900;">@@message@@</div>
+<h2><a href="#" onclick="toggle('trace'); return false;"><span id="traceOff">+</span><span id="traceOn" style="display: none;">-</span> Stack Trace</a></h2>
+<div id="trace" class="grayBox"><pre><code>@@trace@@</code></pre></div>
+<h2><a href="#" onclick="toggle('tree'); return false;"><span id="treeOff">+</span><span id="treeOn" style="display: none;">-</span> Component Tree</a></h2>
+<div id="tree" class="grayBox"><code>@@tree@@</code></div>
+<h2><a href="#" onclick="toggle('vars'); return false;"><span id="varsOff">+</span><span id="varsOn" style="display: none;">-</span> Scoped Variables</a></h2>
+<div id="vars">@@vars@@</div>
+<div class="grayBox" style="text-align: right; color: #666;">@@now@@ - Generated by Facelets</div>
+</body>
+</html>