Improve error reporting. New error page that gives a bit more of an explanation...
[frank.git] / prod / net / jaekl / frank / ErrorHandler.java
diff --git a/prod/net/jaekl/frank/ErrorHandler.java b/prod/net/jaekl/frank/ErrorHandler.java
new file mode 100644 (file)
index 0000000..c1d72bd
--- /dev/null
@@ -0,0 +1,105 @@
+package net.jaekl.frank;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import net.jaekl.qd.http.InvalidResponseException;
+
+public class ErrorHandler {
+       static final String JAVASCRIPT =
+                       "<SCRIPT>\n" +
+                       "  var show_hide = function() {\n" +
+                       "  var theDiv = document.getElementById('details');\n" +
+                       "  var theButton = document.getElementById('details_btn');\n" +
+                       "  if (theDiv.style.display === 'block' || theDiv.style.display === '') {\n" +
+                       "    theDiv.style.display = 'none';\n" +
+                       "  theButton.value = 'Show details';\n" +
+                       "  }\n" +
+                       "  else {\n" +
+                       "    theDiv.style.display = 'block';\n" +
+                       "    theButton.value = 'Hide details';\n" +
+                       "  }\n" +
+                       "}\n" +
+                       "</SCRIPT>";
+       
+       void writeScript(PrintWriter pw) {
+               pw.println(JAVASCRIPT);
+       }
+       
+       void explain(PrintWriter pw, Throwable t, FrankBundle bundle) {
+               Throwable cause = t;
+               if (t instanceof FrankException) {
+                       if (null != t.getCause()) {
+                               cause = t.getCause();
+                       }
+               }
+               
+               pw.println("<P>");
+               if (cause instanceof InvalidResponseException) {
+                       InvalidResponseException ire = (InvalidResponseException)cause;
+                       
+                       pw.println(bundle.get(FrankBundle.INVALID_RESPONSE));
+                       pw.println("<P ID=\"errtable\">");
+                       pw.println("  <TABLE>");
+                       pw.println("    <TR><TD CLASS=\"head\">" + bundle.get(FrankBundle.URL_CONTACTED) 
+                                       + "</TD><TD>" + ire.getUrl() + "</TD></TR>");
+                       pw.println("    <TR CLASS=\"alt\"><TD CLASS=\"head\">" + bundle.get(FrankBundle.REQUEST_MADE) 
+                                       + "</TD><TD>" + ire.getMethod() + "</TD></TR>");
+                       pw.println("    <TR><TD CLASS=\"head\">" + bundle.get(FrankBundle.ANSWER_RECEIVED) 
+                                       + "</TD><TD>" + ire.getResponse() + "</TD></TR>");
+                       pw.println("  </TABLE>");
+                       pw.println("</P>");
+                       pw.println("<P>" + bundle.get(FrankBundle.MAYBE_SERVER_PROBLEM) + "</P>");
+               }
+               else {
+                       pw.println(bundle.get(FrankBundle.UNEXPECTED_EXCEPTION));
+               }
+               pw.println("</P>");
+       }
+       
+       void writeErrorPage(PrintWriter pw, Throwable t, Locale locale) {
+               Style style = new Style();
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               PrintStream ps = new PrintStream(baos);
+               FrankBundle bundle = FrankBundle.getInst(locale);
+               
+               pw.println("<HTML><HEAD>");
+               pw.println("<TITLE>" + 
+                               bundle.get(FrankBundle.FRANK) + ": " +
+                               bundle.get(FrankBundle.ERROR_PAGE) + 
+                               "</TITLE>");
+               style.writeStyle(pw);
+               writeScript(pw);
+               pw.println("</HEAD>");
+               
+               pw.println("<BODY>");
+               pw.println("<TABLE ID=\"errhead\" WIDTH=\"100%\"><TR><TD>" +
+                               bundle.get(FrankBundle.FRANK) + ": " +
+                               bundle.get(FrankBundle.UNEXPECTED_ERROR) + 
+                               "</TD></TR></TABLE>");
+               
+               explain(pw, t, bundle);
+               
+               // Note that, if we cared about security, we would log this stack trace to a
+               // server log, and only report a cross-reference to the log file back to the 
+               // end user's browser, to avoid potentially exposing internal info that we 
+               // don't want to share.
+               // At least at this point, we don't care (that much), and trade off a 
+               // potential information leak in favour of reducing our code complexity
+               // and the administrator's workload.
+               
+               pw.println("<P><INPUT TYPE=\"button\" ID=\"details_btn\" VALUE=\"Show details\" ONCLICK=\"show_hide();\"/></P>");
+               pw.println("<P>");
+               pw.println("<DIV ID=\"details\" STYLE=\"display: none;\"><PRE>");
+               
+               t.printStackTrace(ps);
+               String stackTrace = baos.toString(); 
+               pw.println(stackTrace);
+                               
+               pw.println("</PRE>\n</P>\n</DIV>");
+               pw.println("<P>Click <A HREF=\"/\">here</A> to return to the main page.</P>");
+               pw.println("</BODY></HTML>");
+       }
+}