blob: eab477a7ea701b444c4ca96c94cb51323a4abc98 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 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.io.Writer;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Jacob Hookom (ICLA with ASF filed)
*/
class _ErrorPageWriter {
private static final Log log = LogFactory.getLog(_ErrorPageWriter.class);
private static final Class[] NO_ARGS = new Class[0];
private final static String TS = "<";
private static final String ERROR_TEMPLATE = "META-INF/rsc/myfaces-dev-error.xml";
private static final String ERROR_TEMPLATE_RESOURCE = "org.apache.myfaces.ERROR_TEMPLATE_RESOURCE";
private static String[] ERROR_PARTS;
private static final String DEBUG_TEMPLATE = "META-INF/rsc/myfaces-dev-debug.xml";
private static final String DEBUG_TEMPLATE_RESOURCE = "org.apache.myfaces.DEBUG_TEMPLATE_RESOURCE";
private static String[] DEBUG_PARTS;
public _ErrorPageWriter() {
super();
}
private static String getErrorTemplate(FacesContext context)
{
String errorTemplate = context.getExternalContext().getInitParameter(ERROR_TEMPLATE_RESOURCE);
if (errorTemplate != null)
{
return errorTemplate;
}
return ERROR_TEMPLATE;
}
private static String getDebugTemplate(FacesContext context)
{
String debugTemplate = context.getExternalContext().getInitParameter(DEBUG_TEMPLATE_RESOURCE);
if (debugTemplate != null)
{
return debugTemplate;
}
return DEBUG_TEMPLATE;
}
private static void init(FacesContext context) throws IOException {
if (ERROR_PARTS == null) {
ERROR_PARTS = splitTemplate(getErrorTemplate(context));
}
if (DEBUG_PARTS == null) {
DEBUG_PARTS = splitTemplate(getDebugTemplate(context));
}
}
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("@@");
}
private static ArrayList getErrorId(Throwable e){
String message = e.getMessage();
if(message==null)
return null;
ArrayList list = new ArrayList();
Pattern pattern = Pattern.compile(".*?\\Q,Id:\\E\\s*(\\S+)\\s*\\].*?");
Matcher matcher = pattern.matcher(message);
while (matcher.find()){
list.add(matcher.group(1));
}
if (list.size()>0) return list;
return null;
}
public static void writeCause(Writer writer, Throwable ex) throws IOException {
String msg = ex.getMessage();
for(;;) {
Throwable t = getCause(ex);
if (t == null)
break;
ex = t;
if (ex.getMessage()!=null) msg = ex.getMessage();
}
if (msg != null) {
msg =ex.getClass().getName() + " - " + msg;
writer.write(msg.replaceAll("<", TS));
} else {
writer.write(ex.getClass().getName());
}
}
public static void debugHtml(Writer writer, FacesContext faces, Throwable e) throws IOException {
init(faces);
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])) {
if (faces.getViewRoot() != null) {
writeComponent(writer, faces.getViewRoot(), getErrorId(e));
}
} else if ("vars".equals(ERROR_PARTS[i])) {
writeVariables(writer, faces);
} else if ("cause".equals(ERROR_PARTS[i])) {
writeCause(writer, e);
} else {
writer.write(ERROR_PARTS[i]);
}
}
}
public static void debugHtml(Writer writer, FacesContext faces, List exceptionList) throws IOException
{
init(faces);
Date now = new Date();
for (int i = 0; i < ERROR_PARTS.length; i++)
{
if ("message".equals(ERROR_PARTS[i]))
{
for (int j = 0; j < exceptionList.size(); j++)
{
Exception e = (Exception) exceptionList.get(j);
String msg = e.getMessage();
if (msg != null)
{
writer.write(msg.replaceAll("<", TS));
}
else
{
writer.write(e.getClass().getName());
}
if (!(j+1==exceptionList.size()))
{
writer.write("<br>");
}
}
}
else if ("trace".equals(ERROR_PARTS[i]))
{
for (int j = 0; j < exceptionList.size(); j++)
{
Exception e = (Exception) exceptionList.get(j);
writeException(writer, e);
}
}
else if ("now".equals(ERROR_PARTS[i]))
{
writer.write(DateFormat.getDateTimeInstance().format(now));
}
else if ("tree".equals(ERROR_PARTS[i]))
{
if (faces.getViewRoot() != null)
{
List highlightId = null;
for (int j = 0; j < exceptionList.size(); j++)
{
Exception e = (Exception) exceptionList.get(j);
if (highlightId == null)
{
highlightId = getErrorId(e);
}
else
{
highlightId.addAll(getErrorId(e));
}
}
writeComponent(writer, faces.getViewRoot(), highlightId);
}
}
else if ("vars".equals(ERROR_PARTS[i]))
{
writeVariables(writer, faces);
}
else if ("cause".equals(ERROR_PARTS[i]))
{
for (int j = 0; j < exceptionList.size(); j++)
{
Exception e = (Exception) exceptionList.get(j);
writeCause(writer, e);
if (!(j+1==exceptionList.size()))
{
writer.write("<br>");
}
}
}
else
{
writer.write(ERROR_PARTS[i]);
}
}
}
private static void writeException(Writer writer, Throwable 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(faces);
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(), null);
} 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, List highlightId) throws IOException {
writer.write("<dl><dt");
if (isText(c)) {
writer.write(" class=\"uicText\"");
}
if (highlightId != null){
if ((highlightId.size() > 0) && (highlightId.get(0).equals(c.getId()))){
highlightId.remove(0);
if (highlightId.size()==0){
writer.write(" class=\"highlightComponent\"");
}
}
}
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(), highlightId);
writer.write("</dd>");
}
}
if (c.getChildCount() > 0) {
for (Iterator itr = c.getChildren().iterator(); itr.hasNext(); ) {
writer.write("<dd>");
writeComponent(writer, (UIComponent) itr.next(), highlightId);
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 ValueBinding) {
str = ((ValueBinding) v).getExpressionString();
} else if (v instanceof MethodBinding) {
str = ((MethodBinding) v).getExpressionString();
} else {
ValueBinding vb = c.getValueBinding(pd[i].getName());
str = vb!=null?(vb.getExpressionString()+"="+v.toString()):v.toString();
}
writer.write(str.replaceAll("<", TS));
writer.write("\"");
}
} catch (Exception e) {
// do nothing
}
}
}
ValueBinding binding = c.getValueBinding("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
{
handleThrowable(facesContext, ex);
}
public static void handleThrowable(FacesContext facesContext, Throwable 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);
log.error("An exception occurred", ex);
}
else {
throwException(ex);
}
}
else {
throwException(ex);
}
}
public static void handleExceptionList(FacesContext facesContext, List exceptionList) throws ServletException, IOException
{
for (int i = 0; i < exceptionList.size(); i++)
{
prepareExceptionStack( (Exception) exceptionList.get(i));
}
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, exceptionList);
for (int i = 0; i < exceptionList.size(); i++)
{
log.error("An exception occurred", (Exception) exceptionList.get(i));
}
}
else
{
throwException((Exception)exceptionList.get(0));
}
}
else
{
throwException((Exception)exceptionList.get(0));
}
}
private static void prepareExceptionStack(Throwable ex) {
if(ex==null)
return;
//check for getRootCause and getCause-methods
if(!initCausePerReflection(ex,"getRootCause")) {
initCausePerReflection(ex,"getCause");
}
prepareExceptionStack(getCause(ex));
}
/**
* Get the cause of an exception, if available. Reflection must be used because
* JSF11 supports java1.3 but Throwable.getCause was added in java1.4.
*/
private static Throwable getCause(Throwable ex) {
try {
Method causeGetter = ex.getClass().getMethod("getCause", NO_ARGS);
Throwable cause = (Throwable) causeGetter.invoke(ex, NO_ARGS);
return cause;
} catch (Exception e1) {
return null;
}
}
private static boolean initCausePerReflection(Throwable ex, String methodName) {
try {
Method causeGetter = ex.getClass().getMethod(methodName, NO_ARGS);
Throwable rootCause = (Throwable) causeGetter.invoke(ex, NO_ARGS);
return initCauseIfAvailable(ex,rootCause);
} catch (Exception e1) {
return false;
}
}
static void throwException(Throwable e) throws IOException, ServletException {
prepareExceptionStack(e);
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);
}
initCauseIfAvailable(ex, e);
throw ex;
}
}
private static boolean initCauseIfAvailable(Throwable th, Throwable cause) {
if(cause == null)
return false;
try {
Method m = Throwable.class.getMethod("initCause",new Class[]{Throwable.class});
m.invoke(th,new Object[]{cause});
return true;
}
catch(Exception e) {
return false;
}
}
}