001: /*
002: * Copyright 1999-2001,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.valves;
018:
019: import java.io.IOException;
020: import java.io.Writer;
021: import java.util.Locale;
022:
023: import javax.servlet.ServletException;
024: import javax.servlet.ServletRequest;
025: import javax.servlet.ServletResponse;
026: import javax.servlet.http.HttpServletResponse;
027:
028: import org.apache.catalina.Globals;
029: import org.apache.catalina.HttpResponse;
030: import org.apache.catalina.Logger;
031: import org.apache.catalina.Request;
032: import org.apache.catalina.Response;
033: import org.apache.catalina.ValveContext;
034: import org.apache.catalina.util.RequestUtil;
035: import org.apache.catalina.util.ServerInfo;
036: import org.apache.catalina.util.StringManager;
037: import org.apache.commons.beanutils.PropertyUtils;
038: import org.apache.tomcat.util.compat.JdkCompat;
039:
040: /**
041: * <p>Implementation of a Valve that outputs HTML error pages.</p>
042: *
043: * <p>This Valve should be attached at the Host level, although it will work
044: * if attached to a Context.</p>
045: *
046: * <p>HTML code from the Cocoon 2 project.</p>
047: *
048: * @author Remy Maucherat
049: * @author Craig R. McClanahan
050: * @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
051: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
052: * @author Yoav Shapira
053: * @version $Revision: 1.17.2.1 $ $Date: 2004/08/21 15:49:55 $
054: */
055:
056: public class ErrorReportValve extends ValveBase {
057:
058: // ----------------------------------------------------- Instance Variables
059:
060: /**
061: * The debugging detail level for this component.
062: */
063: private int debug = 0;
064:
065: /**
066: * The descriptive information related to this implementation.
067: */
068: private static final String info = "org.apache.catalina.valves.ErrorReportValve/1.0";
069:
070: /**
071: * The StringManager for this package.
072: */
073: protected static StringManager sm = StringManager
074: .getManager(Constants.Package);
075:
076: // ------------------------------------------------------------- Properties
077:
078: /**
079: * Return descriptive information about this Valve implementation.
080: */
081: public String getInfo() {
082:
083: return (info);
084:
085: }
086:
087: // --------------------------------------------------------- Public Methods
088:
089: /**
090: * Invoke the next Valve in the sequence. When the invoke returns, check
091: * the response state, and output an error report is necessary.
092: *
093: * @param request The servlet request to be processed
094: * @param response The servlet response to be created
095: * @param context The valve context used to invoke the next valve
096: * in the current processing pipeline
097: *
098: * @exception IOException if an input/output error occurs
099: * @exception ServletException if a servlet error occurs
100: */
101: public void invoke(Request request, Response response,
102: ValveContext context) throws IOException, ServletException {
103:
104: // Perform the request
105: context.invokeNext(request, response);
106:
107: ServletRequest sreq = (ServletRequest) request;
108: Throwable throwable = (Throwable) sreq
109: .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: ServletResponse sresponse = (ServletResponse) response;
129: if (sresponse instanceof HttpServletResponse)
130: ((HttpServletResponse) sresponse)
131: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
132:
133: }
134:
135: response.setSuspended(false);
136:
137: try {
138: report(request, response, throwable);
139: } catch (Throwable tt) {
140: tt.printStackTrace();
141: }
142:
143: }
144:
145: /**
146: * Return a String rendering of this object.
147: */
148: public String toString() {
149:
150: StringBuffer sb = new StringBuffer("ErrorReportValve[");
151: sb.append(container.getName());
152: sb.append("]");
153: return (sb.toString());
154:
155: }
156:
157: // ------------------------------------------------------ Protected Methods
158:
159: /**
160: * Prints out an error report.
161: *
162: * @param request The request being processed
163: * @param response The response being generated
164: * @param throwable The exception that occurred (which possibly wraps
165: * a root cause exception
166: */
167: protected void report(Request request, Response response,
168: Throwable throwable) throws IOException {
169:
170: // Do nothing on non-HTTP responses
171: if (!(response instanceof HttpResponse))
172: return;
173: HttpResponse hresponse = (HttpResponse) response;
174: if (!(response instanceof HttpServletResponse))
175: return;
176: HttpServletResponse hres = (HttpServletResponse) response;
177: int statusCode = hresponse.getStatus();
178:
179: // Do nothing on a 1xx, 2xx and 3xx status
180: if (statusCode < 400)
181: return;
182:
183: // FIXME: Reset part of the request
184: /*
185: try {
186: if (hresponse.isError())
187: hresponse.reset(statusCode, message);
188: } catch (IllegalStateException e) {
189: ;
190: }
191: */
192:
193: Throwable rootCause = null;
194:
195: if (throwable != null) {
196:
197: if (throwable instanceof ServletException)
198: rootCause = ((ServletException) throwable)
199: .getRootCause();
200:
201: }
202:
203: String message = RequestUtil.filter(hresponse.getMessage());
204: if (message == null)
205: message = "";
206:
207: // Do nothing if there is no report for the specified status code
208: String report = null;
209: try {
210: report = sm.getString("http." + statusCode, message);
211: } catch (Throwable t) {
212: ;
213: }
214: if (report == null)
215: return;
216:
217: StringBuffer sb = new StringBuffer();
218:
219: sb.append("<html><head><title>");
220: sb.append(ServerInfo.getServerInfo()).append(" - ");
221: sb.append(sm.getString("errorReportValve.errorReport"));
222: sb.append("</title>");
223: sb.append("<style><!--");
224: sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
225: sb.append("--></style> ");
226: sb.append("</head><body>");
227: sb.append("<h1>");
228: sb.append(
229: sm.getString("errorReportValve.statusHeader", ""
230: + statusCode, message)).append("</h1>");
231: sb.append("<HR size=\"1\" noshade=\"noshade\">");
232: sb.append("<p><b>type</b> ");
233: if (throwable != null) {
234: sb.append(sm.getString("errorReportValve.exceptionReport"));
235: } else {
236: sb.append(sm.getString("errorReportValve.statusReport"));
237: }
238: sb.append("</p>");
239: sb.append("<p><b>");
240: sb.append(sm.getString("errorReportValve.message"));
241: sb.append("</b> <u>");
242: sb.append(message).append("</u></p>");
243: sb.append("<p><b>");
244: sb.append(sm.getString("errorReportValve.description"));
245: sb.append("</b> <u>");
246: sb.append(report);
247: sb.append("</u></p>");
248:
249: if (throwable != null) {
250:
251: String stackTrace = JdkCompat.getJdkCompat()
252: .getPartialServletStackTrace(throwable);
253: sb.append("<p><b>");
254: sb.append(sm.getString("errorReportValve.exception"));
255: sb.append("</b> <pre>");
256: sb.append(RequestUtil.filter(stackTrace));
257: sb.append("</pre></p>");
258:
259: while (rootCause != null) {
260: stackTrace = JdkCompat.getJdkCompat()
261: .getPartialServletStackTrace(rootCause);
262: sb.append("<p><b>");
263: sb.append(sm.getString("errorReportValve.rootCause"));
264: sb.append("</b> <pre>");
265: sb.append(RequestUtil.filter(stackTrace));
266: sb.append("</pre></p>");
267: // In case root cause is somehow heavily nested
268: try {
269: rootCause = (Throwable) PropertyUtils.getProperty(
270: rootCause, "rootCause");
271: } catch (ClassCastException e) {
272: rootCause = null;
273: } catch (IllegalAccessException e) {
274: rootCause = null;
275: } catch (NoSuchMethodException e) {
276: rootCause = null;
277: } catch (java.lang.reflect.InvocationTargetException e) {
278: rootCause = null;
279: }
280: }
281:
282: sb.append("<p><b>");
283: sb.append(sm.getString("errorReportValve.note"));
284: sb.append("</b> <u>");
285: sb.append(sm.getString("errorReportValve.rootCauseInLogs",
286: ServerInfo.getServerInfo()));
287: sb.append("</u></p>");
288:
289: }
290:
291: sb.append("<HR size=\"1\" noshade=\"noshade\">");
292: sb.append("<h3>").append(ServerInfo.getServerInfo()).append(
293: "</h3>");
294: sb.append("</body></html>");
295:
296: try {
297:
298: try {
299: hres.setContentType("text/html");
300: hres.setCharacterEncoding("utf-8");
301: } catch (Throwable t) {
302: if (debug >= 1)
303: log("status.setContentType", t);
304: }
305:
306: Writer writer = response.getReporter();
307:
308: if (writer != null) {
309:
310: // If writer is null, it's an indication that the response has
311: // been hard committed already, which should never happen
312: writer.write(sb.toString());
313:
314: }
315:
316: } catch (IOException e) {
317: ;
318: } catch (IllegalStateException e) {
319: ;
320: }
321:
322: }
323:
324: /**
325: * Log a message on the Logger associated with our Container (if any).
326: *
327: * @param message Message to be logged
328: */
329: protected void log(String message) {
330:
331: Logger logger = container.getLogger();
332: if (logger != null)
333: logger.log(this .toString() + ": " + message);
334: else
335: System.out.println(this .toString() + ": " + message);
336:
337: }
338:
339: /**
340: * Log a message on the Logger associated with our Container (if any).
341: *
342: * @param message Message to be logged
343: * @param throwable Associated exception
344: */
345: protected void log(String message, Throwable throwable) {
346:
347: Logger logger = container.getLogger();
348: if (logger != null)
349: logger.log(this .toString() + ": " + message, throwable);
350: else {
351: System.out.println(this .toString() + ": " + message);
352: throwable.printStackTrace(System.out);
353: }
354:
355: }
356:
357: }
|