I am writing to report a case where calling PageContextImpl.getException() can result in a ClassCastException. This occurred in tomcat 5.0.19, but examination of tomcat 5.0.28's source code, the problem could occur there as well. In my application, I have an jsp declared as an error page. This error page uses a tag to record some additional information. The first few lines of this tag are as follows: public void doTag() throws IOException, JspException { try { PageContext context = (PageContext)getJspContext(); JspWriter out = context.getOut(); HttpServletRequest request = (HttpServletRequest) context.getRequest(); Throwable error = context.getException(); // proceed to log the exception and some of the // surrounding contextual information In one case, the `catch' associated with this try caught a ClassCastException from PageContextImpl.getException(): // line 608 in 5.0.19's PageContextImpl.java // line 560 in 5.0.28's PageContextImpl.java public Exception getException() { return (Exception)request.getAttribute(EXCEPTION); } This would occur if the error was a result of a java.lang.Throwable. What possible paths might lead up to this? The _jspService() generated by JspC typically terminates with something like the following: } catch (Throwable t) { if (!(t instanceof SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) out.clearBuffer(); if (pageContext != null) pageContext.handlePageException(t); } } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext); } It's catching a Throwable. PageContextImpl.handlePageException() (lines 730 - 745 in version 5.0.28) does this: public void handlePageException(final Throwable t) throws IOException, ServletException { if (t == null) throw new NullPointerException("null Throwable"); if (System.getSecurityManager() != null){ try{ AccessController.doPrivileged(new PrivilegedExceptionAction(){ public Object run() throws Exception{ doHandlePageException(t); return null; } }); It's dealing with throwables. Continuing on to doHandlePageException(), (lines 763 - 786 in version 5.0.28): private void doHandlePageException(Throwable t) throws IOException, ServletException { if (errorPageURL != null && !errorPageURL.equals("")) { /* * Set request attributes. * Do not set the javax.servlet.error.exception attribute here * (instead, set in the generated servlet code for the error page) * in order to prevent the ErrorReportValve, which is invoked as * part of forwarding the request to the error page, from * throwing it if the response has not been committed (the response * will have been committed if the error page is a JSP page). */ request.setAttribute("javax.servlet.jsp.jspException", t); request.setAttribute("javax.servlet.error.status_code", new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); request.setAttribute("javax.servlet.error.request_uri", ((HttpServletRequest) request).getRequestURI()); request.setAttribute("javax.servlet.error.servlet_name", config.getServletName()); try { forward(errorPageURL); It's also dealing with Throwables. The following two (short) jsp files reproduce the problem consistently. --------------- Page: foo.jsp ----------------------------------- <%@ page session="false" language="java" errorPage="/myerror.jsp" %> <html><body>hello world</body></html> <% if (1 + 1 == 2) { throw new Throwable("The cake fell in the mud"); } %> ------------------------------------------------------------------ ------------------- Page: myerror.jsp ---------------------------- <!-- myerror.jsp --> <%@ page session="false" language="java" isErrorPage="true" import="java.io.PrintWriter" %> An error occurred: <pre> <% out.println(pageContext.getException().toString()); %> </pre> ------------------------------------------------------------------ Perhaps getException() could take extra steps to ensure that the object being returned is indeed an exception (and wrap it if it's not).
Good, interesting catch. It seems like something simple in the PageContext imlpementation, like if(! (exception instanceof Exception)) { exception = new JspException(exception); } would be a good wrap. It only wraps if needed, and the JspException constructor takes a Throwable root cause, so I think we'll be all set. I'll wait to see what more experienced Jasper developers (Kin-Man?) think of this.
Yoav Shapira's suggestion (2004-09-15 18:14) seems like a reasonable way to handle this. If my opinion matters :)
Fixed for 5.0.29 and 5.5.3.