
com.peterphi.std.guice.web.rest.pagewriter.TwitterBootstrapRestFailurePageRenderer Maven / Gradle / Ivy
package com.peterphi.std.guice.web.rest.pagewriter;
import com.peterphi.std.guice.restclient.jaxb.ExceptionInfo;
import com.peterphi.std.guice.restclient.jaxb.RestFailure;
import com.peterphi.std.guice.web.HttpCallContext;
import com.peterphi.std.util.ListUtility;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.List;
import java.util.Map;
public class TwitterBootstrapRestFailurePageRenderer extends TwitterBootstrapPageWriter
{
private final RestFailure failure;
private boolean jiraEnabled = false;
private String jiraEndpoint;
private int jiraPid;
private int jiraIssueType;
private List highlightTerms = null;
private boolean renderJvmInfo = false;
private boolean renderStackTrace = false;
private boolean renderRequestInfo = false;
private boolean renderRequestAttributes = false;
private boolean renderEnvironmentVariables = true;
public TwitterBootstrapRestFailurePageRenderer(RestFailure failure)
{
this.failure = failure;
}
public void enableJIRA(String endpoint, int pid, int issueType)
{
this.jiraEnabled = true;
this.jiraEndpoint = endpoint;
this.jiraPid = pid;
this.jiraIssueType = issueType;
}
public void setHighlightTerms(List terms)
{
this.highlightTerms = terms;
}
@Override
protected String getTitle()
{
if (failure.exception.detail == null || failure.exception.detail.length() < 1024)
return failure.exception.shortName + ": " + failure.exception.detail;
else
return failure.exception.shortName; // Detail is very long, omit it from the page title
}
@Override
protected void writeCustomHeadContent(StringBuilder sb)
{
super.writeCustomHeadContent(sb);
sb.append("\n");
}
private void writeJIRA(StringBuilder sb)
{
String reportHref;
if (jiraPid != 0)
{
final HttpCallContext ctx = HttpCallContext.get();
final String url = ctx.getRequest().getRequestURL().toString();
reportHref = "javascript:doJiraCreateIssue();";
String summary = failure.exception.shortName + ": " + failure.exception.detail.replaceAll("[\r\n]", "");
String environment = "URL: " + url + "\n";
environment += "Log Id: " + ctx.getLogId() + "\n";
environment += "Server addr: " + ctx.getRequest().getLocalAddr() + "\n";
environment += "Request Info: " + ctx.getRequestInfo() + "\n";
environment += "Referer: " + ctx.getRequest().getHeader("Referer") + "\n";
environment += "Timestamp: " + failure.date + "\n";
environment += "Source: " + failure.source + "\n";
environment += "User agent: " + ctx.getRequest().getHeader("User-Agent") + "\n";
String detail = "**PLEASE DESCRIBE WHAT YOU DID LEADING UP TO THIS ERROR**\n\n\n";
detail += "Technical request details:\n--------------------------\n\nI was accessing " +
url +
" and got an HTTP " +
failure.httpCode +
".\n";
detail += "\nStack trace:\n{code}\n" + failure.exception.stackTrace + "\n{code}\n";
sb.append("\n");
sb.append("\n");
}
else
{
reportHref = jiraEndpoint + "/secure/CreateIssue!default.jspa";
}
// String searchJiraHref = jiraEndpoint + "/secure/QuickSearch.jspa?searchString=" + escape(failure.exception.detail);
// String searchWikiHref = jiraEndpoint + "/secure/QuickSearch.jspa?searchString=" + escape(failure.exception.detail);
sb.append(" Create Issue\n");
}
@Override
protected void writeBodyContent(StringBuilder sb)
{
sb.append(" \n");
sb.append("\n");
sb.append("\n");
appendHeader(sb);
sb.append("There was a problem processing your request:
").append(failure.exception.shortName).append(
"
thrown ").append(failure.date).append("
with id ").append(failure.id).append(
"
and error code ").append(failure.errorCode).append("
returning HTTP Code ").append(
failure.httpCode).append("
.");
if (failure.source != null && !failure.source.equals("unknown"))
sb.append(" Source listed as ").append(failure.source);
sb.append("
");
// Add the exception (and potentially the stack trace)
appendException(sb, failure.exception);
if (renderRequestInfo)
{
// Add the request info
appendRequestDetails(sb);
}
if (renderJvmInfo)
{
// Add JV details
appendJVMDetails(sb);
}
sb.append(""); // div class=container
}
protected void appendHeader(StringBuilder sb)
{
sb.append("A problem occurred
");
}
@SuppressWarnings("unchecked")
private void appendRequestDetails(StringBuilder sb)
{
final HttpCallContext ctx = HttpCallContext.get();
sb.append("HTTP Request
\n\n");
final HttpServletRequest r = ctx.getRequest();
// HttpServletRequest information
sb.append("Properties:
\n");
sb.append("\n");
{
appendKeyValueListElement(sb, "Log Id", ctx.getLogId());
appendKeyValueListElement(sb, "Request", ctx.getRequestInfo());
appendAllSimpleGetters(sb, r);
}
sb.append("
\n");
if (renderRequestAttributes)
{
// Request attributes
sb.append("Request Attributes
\n");
sb.append("\n");
{
for (Object nameObj : ListUtility.iterate(r.getAttributeNames()))
{
final String name = (String) nameObj;
appendKeyValueListElement(sb, name, r.getAttribute(name));
}
}
sb.append("
\n");
}
// HTTP headers
sb.append("Headers
\n");
sb.append("\n");
{
for (Object headerObj : ListUtility.iterate(r.getHeaderNames()))
{
final String header = (String) headerObj;
for (Object val : ListUtility.iterate(r.getHeaders(header)))
{
appendKeyValueListElement(sb, header, val);
}
}
}
sb.append("
\n");
// HTTP cookies
sb.append("Cookies
\n");
if (r.getCookies() != null && r.getCookies().length > 0)
{
sb.append("\n");
for (Cookie cookie : r.getCookies())
{
appendKeyValueListElement(sb, cookie.getName(), cookie.getValue());
}
sb.append("
\n");
}
else
{
sb.append("No cookies
");
}
}
private void appendJVMDetails(StringBuilder sb)
{
// HTTP cookies
sb.append("Java Virtual Machine
\n");
sb.append("Properties\n");
sb.append("\n");
for (String name : System.getProperties().stringPropertyNames())
{
appendKeyValueListElement(sb, name, System.getProperty(name));
}
sb.append("
\n");
if (renderEnvironmentVariables)
{
sb.append("Environment Variables\n");
sb.append("\n");
for (Map.Entry entry : System.getenv().entrySet())
{
appendKeyValueListElement(sb, entry.getKey(), entry.getValue());
}
sb.append("
\n");
}
}
private void appendAllSimpleGetters(StringBuilder sb, Object o)
{
try
{
for (Method method : o.getClass().getMethods())
{
if (method.getParameterTypes().length == 0 &&
method.getName().startsWith("get") &&
(method.getReturnType().isPrimitive() ||
method.getReturnType() == String.class ||
method.getReturnType() == URI.class ||
method.getReturnType() == StringBuffer.class))
{
final String name = method.getName().substring(3);
final Object result = method.invoke(o);
appendKeyValueListElement(sb, name, result);
}
}
}
catch (Exception e)
{
// Take no action
}
}
private void appendKeyValueListElement(StringBuilder sb, String key, Object value)
{
sb.append("").append(escape(key)).append(" ").append(escape(String.valueOf(value))).append(" \n");
}
private void appendException(StringBuilder sb, ExceptionInfo info)
{
if (info != null)
{
sb.append("
");
sb.append("").append(escape(info.shortName)).append("
");
// Maintain any whitespace in the exception detail (e.g. for guice CreationExceptions)
sb.append("
").append(escape(info.detail)).append("
");
if (renderStackTrace)
{
appendStacktrace(sb, info);
}
}
}
private void appendStacktrace(StringBuilder sb, ExceptionInfo info)
{
if (!StringUtils.isEmpty(info.stackTrace))
{
sb.append("");
if (this.highlightTerms != null)
{
String[] lines = info.stackTrace.split("\n");
for (String line : lines)
{
appendStacktraceLine(sb, line);
}
}
else
{
// Just append the stack trace
sb.append(escape(info.stackTrace));
}
sb.append("
");
}
}
private void appendStacktraceLine(StringBuilder sb, String line)
{
// Only consider lines starting in whitespace (stack trace elements looking like " at com.company.x(MyClass.java:123)"
if (!line.isEmpty() && Character.isWhitespace(line.charAt(0)))
{
for (String highlightTerm : highlightTerms)
{
if (line.contains(highlightTerm))
{
// Highlight line (also, make sure the whitespace is outside the tag so things line up nicely)
sb.append("\t");
sb.append(escape(line.substring(1)));
sb.append("");
sb.append("\n");
return;
}
}
// Line did not meet highlight criteria, mute it (also, make sure the whitespace is outside the tag so things line up nicely)
sb.append("\t");
sb.append(escape(line.substring(1)));
sb.append("");
sb.append("\n");
}
else
{
// Line does not start with whitespace, do not highlight or mute
sb.append(escape(line)).append("\n");
}
}
public void enableJVMInfo()
{
this.renderJvmInfo = true;
}
public void enableEnvironmentVariables()
{
this.renderEnvironmentVariables = true;
}
public void enableStackTrace()
{
this.renderStackTrace = true;
}
public void enableRequestInfo()
{
this.renderRequestInfo = true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy