Source code: org/apache/catalina/valves/ErrorReportValve.java
1 /*
2 * Copyright 1999-2001,2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18 package org.apache.catalina.valves;
19
20
21 import java.io.IOException;
22 import java.io.Writer;
23
24 import javax.servlet.ServletException;
25 import javax.servlet.ServletRequest;
26 import javax.servlet.ServletResponse;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.catalina.Globals;
30 import org.apache.catalina.connector.Request;
31 import org.apache.catalina.connector.Response;
32 import org.apache.catalina.util.RequestUtil;
33 import org.apache.catalina.util.ServerInfo;
34 import org.apache.catalina.util.StringManager;
35 import org.apache.tomcat.util.IntrospectionUtils;
36 import org.apache.tomcat.util.compat.JdkCompat;
37
38 /**
39 * <p>Implementation of a Valve that outputs HTML error pages.</p>
40 *
41 * <p>This Valve should be attached at the Host level, although it will work
42 * if attached to a Context.</p>
43 *
44 * <p>HTML code from the Cocoon 2 project.</p>
45 *
46 * @author Remy Maucherat
47 * @author Craig R. McClanahan
48 * @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
49 * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
50 * @author Yoav Shapira
51 * @version $Revision: 304023 $ $Date: 2005-07-26 08:45:22 -0400 (Tue, 26 Jul 2005) $
52 */
53
54 public class ErrorReportValve
55 extends ValveBase {
56
57
58 // ----------------------------------------------------- Instance Variables
59
60
61 /**
62 * The descriptive information related to this implementation.
63 */
64 private static final String info =
65 "org.apache.catalina.valves.ErrorReportValve/1.0";
66
67
68 /**
69 * The StringManager for this package.
70 */
71 protected static StringManager sm =
72 StringManager.getManager(Constants.Package);
73
74
75 // ------------------------------------------------------------- Properties
76
77
78 /**
79 * Return descriptive information about this Valve implementation.
80 */
81 public String getInfo() {
82
83 return (info);
84
85 }
86
87
88 // --------------------------------------------------------- Public Methods
89
90
91 /**
92 * Invoke the next Valve in the sequence. When the invoke returns, check
93 * the response state, and output an error report is necessary.
94 *
95 * @param request The servlet request to be processed
96 * @param response The servlet response to be created
97 *
98 * @exception IOException if an input/output error occurs
99 * @exception ServletException if a servlet error occurs
100 */
101 public void invoke(Request request, Response response)
102 throws IOException, ServletException {
103
104 // Perform the request
105 getNext().invoke(request, response);
106
107 ServletRequest sreq = (ServletRequest) request;
108 Throwable throwable =
109 (Throwable) sreq.getAttribute(Globals.EXCEPTION_ATTR);
110
111 ServletResponse sresp = (ServletResponse) response;
112 if (sresp.isCommitted()) {
113 return;
114 }
115
116 if (throwable != null) {
117
118 // The response is an error
119 response.setError();
120
121 // Reset the response (if possible)
122 try {
123 sresp.reset();
124 } catch (IllegalStateException e) {
125 ;
126 }
127
128 response.sendError
129 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
130
131 }
132
133 response.setSuspended(false);
134
135 try {
136 report(request, response, throwable);
137 } catch (Throwable tt) {
138 ;
139 }
140
141 }
142
143
144 // ------------------------------------------------------ Protected Methods
145
146
147 /**
148 * Prints out an error report.
149 *
150 * @param request The request being processed
151 * @param response The response being generated
152 * @param throwable The exception that occurred (which possibly wraps
153 * a root cause exception
154 */
155 protected void report(Request request, Response response,
156 Throwable throwable)
157 throws IOException {
158
159 // Do nothing on non-HTTP responses
160 int statusCode = response.getStatus();
161
162 // Do nothing on a 1xx, 2xx and 3xx status
163 // Do nothing if anything has been written already
164 if ((statusCode < 400) || (response.getContentCount() > 0))
165 return;
166
167 Throwable rootCause = null;
168
169 if (throwable != null) {
170
171 if (throwable instanceof ServletException)
172 rootCause = ((ServletException) throwable).getRootCause();
173
174 }
175
176 String message = RequestUtil.filter(response.getMessage());
177 if (message == null)
178 message = "";
179
180 // Do nothing if there is no report for the specified status code
181 String report = null;
182 try {
183 report = sm.getString("http." + statusCode, message);
184 } catch (Throwable t) {
185 ;
186 }
187 if (report == null)
188 return;
189
190 StringBuffer sb = new StringBuffer();
191
192 sb.append("<html><head><title>");
193 sb.append(ServerInfo.getServerInfo()).append(" - ");
194 sb.append(sm.getString("errorReportValve.errorReport"));
195 sb.append("</title>");
196 sb.append("<style><!--");
197 sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
198 sb.append("--></style> ");
199 sb.append("</head><body>");
200 sb.append("<h1>");
201 sb.append(sm.getString("errorReportValve.statusHeader",
202 "" + statusCode, message)).append("</h1>");
203 sb.append("<HR size=\"1\" noshade=\"noshade\">");
204 sb.append("<p><b>type</b> ");
205 if (throwable != null) {
206 sb.append(sm.getString("errorReportValve.exceptionReport"));
207 } else {
208 sb.append(sm.getString("errorReportValve.statusReport"));
209 }
210 sb.append("</p>");
211 sb.append("<p><b>");
212 sb.append(sm.getString("errorReportValve.message"));
213 sb.append("</b> <u>");
214 sb.append(message).append("</u></p>");
215 sb.append("<p><b>");
216 sb.append(sm.getString("errorReportValve.description"));
217 sb.append("</b> <u>");
218 sb.append(report);
219 sb.append("</u></p>");
220
221 if (throwable != null) {
222
223 String stackTrace = JdkCompat.getJdkCompat()
224 .getPartialServletStackTrace(throwable);
225 sb.append("<p><b>");
226 sb.append(sm.getString("errorReportValve.exception"));
227 sb.append("</b> <pre>");
228 sb.append(RequestUtil.filter(stackTrace));
229 sb.append("</pre></p>");
230
231 while (rootCause != null) {
232 stackTrace = JdkCompat.getJdkCompat()
233 .getPartialServletStackTrace(rootCause);
234 sb.append("<p><b>");
235 sb.append(sm.getString("errorReportValve.rootCause"));
236 sb.append("</b> <pre>");
237 sb.append(RequestUtil.filter(stackTrace));
238 sb.append("</pre></p>");
239 // In case root cause is somehow heavily nested
240 try {
241 rootCause = (Throwable)IntrospectionUtils.getProperty
242 (rootCause, "rootCause");
243 } catch (ClassCastException e) {
244 rootCause = null;
245 }
246 }
247
248 sb.append("<p><b>");
249 sb.append(sm.getString("errorReportValve.note"));
250 sb.append("</b> <u>");
251 sb.append(sm.getString("errorReportValve.rootCauseInLogs",
252 ServerInfo.getServerInfo()));
253 sb.append("</u></p>");
254
255 }
256
257 sb.append("<HR size=\"1\" noshade=\"noshade\">");
258 sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
259 sb.append("</body></html>");
260
261 try {
262 try {
263 response.setContentType("text/html");
264 response.setCharacterEncoding("utf-8");
265 } catch (Throwable t) {
266 if (container.getLogger().isDebugEnabled())
267 container.getLogger().debug("status.setContentType", t);
268 }
269 Writer writer = response.getReporter();
270 if (writer != null) {
271 // If writer is null, it's an indication that the response has
272 // been hard committed already, which should never happen
273 writer.write(sb.toString());
274 }
275 } catch (IOException e) {
276 ;
277 } catch (IllegalStateException e) {
278 ;
279 }
280
281 }
282
283
284 }