Add user-friendly explanation for server timeout.
[frank.git] / prod / net / jaekl / frank / ErrorHandler.java
1 package net.jaekl.frank;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.PrintStream;
5 import java.io.PrintWriter;
6 import java.net.SocketTimeoutException;
7 import java.util.Locale;
8
9 import net.jaekl.qd.http.InvalidResponseException;
10
11 public class ErrorHandler {
12         static final String JAVASCRIPT =
13                         "<SCRIPT>\n" +
14                         "  var show_hide = function() {\n" +
15                         "  var theDiv = document.getElementById('details');\n" +
16                         "  var theButton = document.getElementById('details_btn');\n" +
17                         "  if (theDiv.style.display === 'block' || theDiv.style.display === '') {\n" +
18                         "    theDiv.style.display = 'none';\n" +
19                         "  theButton.value = 'Show details';\n" +
20                         "  }\n" +
21                         "  else {\n" +
22                         "    theDiv.style.display = 'block';\n" +
23                         "    theButton.value = 'Hide details';\n" +
24                         "  }\n" +
25                         "}\n" +
26                         "</SCRIPT>";
27         
28         void writeScript(PrintWriter pw) {
29                 pw.println(JAVASCRIPT);
30         }
31         
32         void explain(PrintWriter pw, Throwable t, FrankBundle bundle) {
33                 Throwable cause = t;
34                 if (t instanceof FrankException) {
35                         if (null != t.getCause()) {
36                                 cause = t.getCause();
37                         }
38                 }
39                 
40                 pw.println("<P>");
41                 if (cause instanceof InvalidResponseException) {
42                         InvalidResponseException ire = (InvalidResponseException)cause;
43                         
44                         pw.println(bundle.get(FrankBundle.INVALID_RESPONSE));
45                         pw.println("<P ID=\"errtable\">");
46                         pw.println("  <TABLE>");
47                         pw.println("    <TR><TD CLASS=\"head\">" + bundle.get(FrankBundle.URL_CONTACTED) 
48                                         + "</TD><TD>" + ire.getUrl() + "</TD></TR>");
49                         pw.println("    <TR CLASS=\"alt\"><TD CLASS=\"head\">" + bundle.get(FrankBundle.REQUEST_MADE) 
50                                         + "</TD><TD>" + ire.getMethod() + "</TD></TR>");
51                         pw.println("    <TR><TD CLASS=\"head\">" + bundle.get(FrankBundle.ANSWER_RECEIVED) 
52                                         + "</TD><TD>" + ire.getResponse() + "</TD></TR>");
53                         pw.println("  </TABLE>");
54                         pw.println("</P>");
55                         pw.println("<P>" + bundle.get(FrankBundle.MAYBE_SERVER_PROBLEM) + "</P>");
56                 }
57                 else if (cause instanceof SocketTimeoutException) {
58                         pw.println(bundle.get(FrankBundle.SERVER_TIMEOUT));
59                 }
60                 else {
61                         pw.println(bundle.get(FrankBundle.UNEXPECTED_EXCEPTION));
62                 }
63                 pw.println("</P>");
64         }
65         
66         void writeErrorPage(PrintWriter pw, Throwable t, Locale locale) {
67                 Style style = new Style();
68                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
69                 PrintStream ps = new PrintStream(baos);
70                 FrankBundle bundle = FrankBundle.getInst(locale);
71                 
72                 pw.println("<HTML><HEAD>");
73                 pw.println("<TITLE>" + 
74                                 bundle.get(FrankBundle.FRANK) + ": " +
75                                 bundle.get(FrankBundle.ERROR_PAGE) + 
76                                 "</TITLE>");
77                 style.writeStyle(pw);
78                 writeScript(pw);
79                 pw.println("</HEAD>");
80                 
81                 pw.println("<BODY>");
82                 pw.println("<TABLE ID=\"errhead\" WIDTH=\"100%\"><TR><TD>" +
83                                 bundle.get(FrankBundle.FRANK) + ": " +
84                                 bundle.get(FrankBundle.UNEXPECTED_ERROR) + 
85                                 "</TD></TR></TABLE>");
86                 
87                 explain(pw, t, bundle);
88                 
89                 // Note that, if we cared about security, we would log this stack trace to a
90                 // server log, and only report a cross-reference to the log file back to the 
91                 // end user's browser, to avoid potentially exposing internal info that we 
92                 // don't want to share.
93                 // At least at this point, we don't care (that much), and trade off a 
94                 // potential information leak in favour of reducing our code complexity
95                 // and the administrator's workload.
96                 
97                 pw.println("<P><INPUT TYPE=\"button\" ID=\"details_btn\" VALUE=\"Show details\" ONCLICK=\"show_hide();\"/></P>");
98                 pw.println("<P>");
99                 pw.println("<DIV ID=\"details\" STYLE=\"display: none;\"><PRE>");
100                 
101                 t.printStackTrace(ps);
102                 String stackTrace = baos.toString(); 
103                 pw.println(stackTrace);
104                                 
105                 pw.println("</PRE>\n</P>\n</DIV>");
106                 pw.println("<P>Click <A HREF=\"/\">here</A> to return to the main page.</P>");
107                 pw.println("</BODY></HTML>");
108         }
109 }