/* * © Copyright IBM Corp. 2012 * * 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 com.ibm.commons.runtime.servlet; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.sql.SQLException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ibm.commons.Platform; import com.ibm.commons.runtime.Application; import com.ibm.commons.runtime.util.UrlUtil; import com.ibm.commons.util.HtmlTextUtil; import com.ibm.commons.util.StringUtil; /** * Provides a base class for HTTP servlets. <code>BaseHttpServlet</code> provides utility methods * for returning standard responses and handling exceptions. * * @author priand * @author mwallace * */ @SuppressWarnings("deprecation") abstract public class BaseHttpServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @param config * @param paramToolkitUrl * @param defaultToolkitUrl * @return */ protected String getInitParameter(ServletConfig config, String name, String defaultValue) { String value = config.getInitParameter(name); return StringUtil.isEmpty(value) ? defaultValue : value; } /** * * @param application * @param name * @param defaultValue * @return */ protected String getAppParameter(Application application, String name, String defaultValue) { String value = application.getProperty(name); return StringUtil.isEmpty(value) ? defaultValue : value; } protected static PrintWriter getWriter(HttpServletResponse response) throws IOException { // Use what is available try { return response.getWriter(); } catch (IllegalStateException ex) { } return new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "utf-8")); } protected void infoServletContext(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { response.setStatus(HttpServletResponse.SC_OK); boolean rtl = isErrorPageRTL(request, response); String title = getErrorPageTitle(request, response); String lang = getErrorPageLang(request, response); PrintWriter w = getWriter(response); try { writeServletContext(request, w, title, lang, rtl); } finally { w.flush(); } } catch (Throwable t) { Platform.getInstance().log(t); } } private void writeServletContext(HttpServletRequest request, PrintWriter w, String title, String lang, boolean isRTL) throws IOException { String dir = isRTL ? "rtl" : "ltr"; if (lang != null) { w.println("<html lang=\"" + lang + "\" dir=\"" + dir + "\">"); } else { w.println("<html dir=\"" + dir + "\">"); } w.println("<head>"); w.println("<title>" + title + "</title>"); w.println("</head>"); w.println("<body>"); w.println("<table>"); w.println(" <tr>"); w.println(" <td>ServletPath</td>"); w.print(" <td>"); w.print(HtmlTextUtil.toHTMLContentString(request.getServletPath(), true)); w.println(" </td>"); w.println(" </tr>"); w.println(" <tr>"); w.println(" <td>ContextPath</td>"); w.print(" <td>"); w.print(HtmlTextUtil.toHTMLContentString(request.getContextPath(), true)); w.println(" </td>"); w.println(" </tr>"); w.println(" <tr>"); w.println(" <td>PathInfo</td>"); w.print(" <td>"); w.print(HtmlTextUtil.toHTMLContentString(request.getPathInfo(), true)); w.println(" </td>"); w.println(" </tr>"); w.println(" <tr>"); w.println(" <td>QueryString</td>"); w.print(" <td>"); w.print(HtmlTextUtil.toHTMLContentString(request.getQueryString(), true)); w.println(" </td>"); w.println(" </tr>"); w.println("</table>"); w.println("</body>"); w.println("</html>"); } public static boolean isErrorPageRTL(HttpServletRequest request, HttpServletResponse response) { return false; } public static String getErrorPageLang(HttpServletRequest request, HttpServletResponse response) { return null; } public static String getErrorPageTitle(HttpServletRequest request, HttpServletResponse response) { return "Error Page"; } /** * Send a 404 error response with the specified formatted message. * * @param request * @param response * @param fmt * @param parameters * * @throws ServletException * @throws IOException */ public static void service400(HttpServletRequest request, HttpServletResponse response, String fmt, Object...parameters) throws ServletException, IOException { String s = StringUtil.format(fmt, parameters); boolean rtl = isErrorPageRTL(request, response); String title = getErrorPageTitle(request, response); String lang = getErrorPageLang(request, response); response.reset(); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType( "text/html" ); PrintWriter w = getWriter(response); try { w.println("<html>"); w.println("<body>"); w.println("<h1>Error 400</h1>"); w.println(StringUtil.format("URL {0}",UrlUtil.getRequestUrl(request))); w.println("<br/>"); w.println("<br/>"); w.println(HtmlTextUtil.toHTMLContentString(s, true)); w.println("</body>"); w.println("</html>"); } finally { w.flush(); } } /** * Send a 404 error response with the specified formatted message. * * @param request * @param response * @param fmt * @param parameters * * @throws ServletException * @throws IOException */ public static void service404(HttpServletRequest request, HttpServletResponse response, String fmt, Object...parameters) throws ServletException, IOException { String s = StringUtil.format(fmt, parameters); response.reset(); response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.setContentType( "text/html" ); PrintWriter w = getWriter(response); try { w.println("<html>"); w.println("<body>"); w.println("<h1>Error 404</h1>"); w.println(StringUtil.format("URL {0}",UrlUtil.getRequestUrl(request))); w.println("<br/>"); w.println("<br/>"); w.println(HtmlTextUtil.toHTMLContentString(s, true)); w.println("</body>"); w.println("</html>"); } finally { w.flush(); } } /** * Send a 500 error response with the specified formatted message. * * @param request * @param response * @param fmt * @param parameters * * @throws ServletException * @throws IOException */ public static void service500(HttpServletRequest request, HttpServletResponse response, String fmt, Object...parameters) throws ServletException, IOException { String s = StringUtil.format(fmt, parameters); response.reset(); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setContentType( "text/html" ); //$NON-NLS-1$ PrintWriter w = getWriter(response); try { w.println("<html>"); w.println("<body>"); w.println("<h1>Error 500</h1>"); w.println(StringUtil.format("URL {0}",UrlUtil.getRequestUrl(request))); w.println("<br/>"); w.println("<br/>"); w.println(HtmlTextUtil.toHTMLContentString(s, true)); w.println("</body>"); w.println("</html>"); } finally { w.flush(); } } /** * Send a service exception response message. * * @param request * @param response * @param t * @param lang * @param isRTL * * @throws IOException * @throws ServletException */ public static void serviceException(HttpServletRequest request, HttpServletResponse response, Throwable t) throws IOException, ServletException { boolean rtl = isErrorPageRTL(request, response); String lang = getErrorPageLang(request, response); serviceException(request, response, t, lang, rtl); } public static void serviceException(HttpServletRequest request, HttpServletResponse response, Throwable t, String lang, boolean isRTL) throws IOException, ServletException { response.reset(); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setContentType( "text/html" ); //$NON-NLS-1$ PrintWriter writer = getWriter(response); writeErrorPageHeader(writer, "Runtime Error", lang, isRTL); writer.println("<h1>"+"Unexpected runtime error"+"</h1>"); writer.println("The runtime has encountered an unexpected error."+"<br/>"); writer.println(StringUtil.format("URL {0}",UrlUtil.getRequestUrl(request))); writer.println("<br/>"); writer.println("<br/>"); boolean verbose = true; if(verbose) { writeExceptionMessage(writer, t); writeExceptionTrace(writer, t, isRTL); } writeErrorPageFooter(writer); writer.flush(); } // // Internals // private static void writeErrorPageHeader(PrintWriter printWriter, String title, String lang, boolean isRTL) throws IOException { String leading = "left"; String dir = "ltr"; if(isRTL){ leading = "right"; dir = "rtl"; } if(lang != null){ printWriter.println("<html lang=\"" + lang + "\" dir=\"" + dir + "\">"); }else{ printWriter.println("<html dir=\"" + dir + "\">"); } printWriter.println("<head>"); printWriter.println("<title>"+title+"</title>"); printWriter.println("<style type=\"text/css\">"); printWriter.println("body {margin-" + leading + ": 2em;font-family: 'Verdana', 'Arial', 'Helvetica', 'sans-serif';font-size: 9pt}"); printWriter.println("h1 {color: #352BFF; font-size : 1.5em; background: #ddd; line-height:2.0em;}"); printWriter.println("h2 {color: #352BFF; font-size : 1.2em;}"); printWriter.println("pre { font-family: 'Courier new', 'Courier', 'monospace'; font-size : 10pt; line-height:1.2em;}"); printWriter.println("pre { border : 1px solid #ddd; margin-" + leading + ": 2em; }"); printWriter.println(".row { margin-" + leading + ": 2em; }"); printWriter.println("</style>"); printWriter.println("</head>"); printWriter.println("<body>"); printWriter.println("<script>"); printWriter.println("function toggleElement(id){"); printWriter.println(" var e=document.getElementById(id);"); printWriter.println(" var e1=document.getElementById(id+'_collapse');"); printWriter.println(" var e2=document.getElementById(id+'_expand');"); printWriter.println(" if(!e || !e1 || !e2)return true;"); printWriter.println(" if(e.style.display==\"none\"){"); printWriter.println(" e.style.display=\"block\""); printWriter.println(" e1.style.display=\"inline\""); printWriter.println(" e2.style.display=\"none\""); printWriter.println(" } else {"); printWriter.println(" e.style.display=\"none\""); printWriter.println(" e1.style.display=\"none\""); printWriter.println(" e2.style.display=\"inline\""); printWriter.println(" }"); printWriter.println(" return true;"); printWriter.println("}"); printWriter.println("</script>"); } private static void writeErrorPageFooter(PrintWriter printWriter) throws IOException { printWriter.println("</body>"); printWriter.println("</html>"); } private static void writeExceptionMessage(PrintWriter printWriter, Throwable t) { printWriter.println("<br/><h2>"+"Exception"+"</h2>"); String lastMessage = null; // Do not duplicate messages... for (Throwable tt=t; tt != null; tt = getCause(tt)) { String s = tt.getLocalizedMessage(); if (StringUtil.isNotEmpty(s) && !StringUtil.equals(lastMessage, s)) { printWriter.print("<span class=\"row\">"+s+"</span>"); printWriter.println("<br/>"); lastMessage = s; } } } private static void writeExceptionTrace(PrintWriter writer, Throwable e, boolean isRTL) { String leading = "left"; String stackIcon = "►"; if(isRTL){ leading = "right"; stackIcon = "◄"; } writer.println("<p style=\"margin-" + leading + ": 2em;\">"); writer.println("<a id=\"stacktrace_expand\" onclick=\"toggleElement('stacktrace')\" style=\"cursor:pointer\">" + stackIcon + "</a>"); writer.println("<a id=\"stacktrace_collapse\" onclick=\"toggleElement('stacktrace')\" style=\"display:none;cursor:pointer\">▼ </a>"); writer.println("<b>"+"Stack Trace"+"</b><br/>"); writer.println("<pre id='stacktrace' style='display:none'>"); writeExceptionTrace2(writer, e, 0); writer.println("</pre>"); writer.println("</p>"); } private static void writeExceptionTrace2(PrintWriter writer, Throwable e, int rows) { if(e!=null) { StackTraceElement[] elt = e.getStackTrace(); writer.println(e.toString()); for(int i=0; i<elt.length; i++) { writer.println(" "+elt[i].toString()); // Limit the size of the trace, in case of an stack overflow exception... rows++; if(rows>200) { return; } } writeExceptionTrace2(writer, getCause(e), rows); } } private static Throwable getCause(Throwable ext) { if(ext instanceof ServletException) { Throwable cause = ((ServletException)ext).getRootCause(); if(cause!=null) { return cause; } } if(ext instanceof SQLException) { Throwable cause = ((SQLException)ext).getNextException(); if(cause!=null) { return cause; } } Throwable cause = ext.getCause(); return cause; } }