001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/valves/ErrorReportValve.java,v 1.11 2002/05/10 03:41:13 remm Exp $
003: * $Revision: 1.11 $
004: * $Date: 2002/05/10 03:41:13 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.valves;
065:
066: import java.io.IOException;
067: import java.io.PrintWriter;
068: import java.io.StringWriter;
069: import java.io.Writer;
070: import java.util.ArrayList;
071: import java.util.Enumeration;
072: import java.util.Iterator;
073: import javax.servlet.ServletException;
074: import javax.servlet.ServletRequest;
075: import javax.servlet.ServletResponse;
076: import javax.servlet.http.Cookie;
077: import javax.servlet.http.HttpServletRequest;
078: import javax.servlet.http.HttpServletResponse;
079: import org.apache.catalina.Container;
080: import org.apache.catalina.Globals;
081: import org.apache.catalina.HttpRequest;
082: import org.apache.catalina.HttpResponse;
083: import org.apache.catalina.Logger;
084: import org.apache.catalina.Request;
085: import org.apache.catalina.Response;
086: import org.apache.catalina.Valve;
087: import org.apache.catalina.ValveContext;
088: import org.apache.catalina.connector.HttpResponseWrapper;
089: import org.apache.catalina.util.RequestUtil;
090: import org.apache.catalina.util.ServerInfo;
091: import org.apache.catalina.util.StringManager;
092:
093: /**
094: * <p>Implementation of a Valve that outputs HTML error pages.</p>
095: *
096: * <p>This Valve should be attached at the Host level, although it will work
097: * if attached to a Context.</p>
098: *
099: * <p>HTML code from the Cocoon 2 project.</p>
100: *
101: * @author Remy Maucherat
102: * @author Craig R. McClanahan
103: * @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
104: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
105: * @version $Revision: 1.11 $ $Date: 2002/05/10 03:41:13 $
106: */
107:
108: public class ErrorReportValve extends ValveBase {
109:
110: // ----------------------------------------------------- Instance Variables
111:
112: /**
113: * The debugging detail level for this component.
114: */
115: private int debug = 0;
116:
117: /**
118: * The descriptive information related to this implementation.
119: */
120: private static final String info = "org.apache.catalina.valves.ErrorReportValve/1.0";
121:
122: /**
123: * The StringManager for this package.
124: */
125: protected static StringManager sm = StringManager
126: .getManager(Constants.Package);
127:
128: // ------------------------------------------------------------- Properties
129:
130: /**
131: * Return descriptive information about this Valve implementation.
132: */
133: public String getInfo() {
134:
135: return (info);
136:
137: }
138:
139: // --------------------------------------------------------- Public Methods
140:
141: /**
142: * Invoke the next Valve in the sequence. When the invoke returns, check
143: * the response state, and output an error report is necessary.
144: *
145: * @param request The servlet request to be processed
146: * @param response The servlet response to be created
147: * @param context The valve context used to invoke the next valve
148: * in the current processing pipeline
149: *
150: * @exception IOException if an input/output error occurs
151: * @exception ServletException if a servlet error occurs
152: */
153: public void invoke(Request request, Response response,
154: ValveContext context) throws IOException, ServletException {
155:
156: // Perform the request
157: context.invokeNext(request, response);
158:
159: ServletRequest sreq = (ServletRequest) request;
160: Throwable throwable = (Throwable) sreq
161: .getAttribute(Globals.EXCEPTION_ATTR);
162:
163: ServletResponse sresp = (ServletResponse) response;
164: if (sresp.isCommitted()) {
165: return;
166: }
167:
168: if (throwable != null) {
169:
170: // The response is an error
171: response.setError();
172:
173: // Reset the response (if possible)
174: try {
175: sresp.reset();
176: } catch (IllegalStateException e) {
177: ;
178: }
179:
180: ServletResponse sresponse = (ServletResponse) response;
181: if (sresponse instanceof HttpServletResponse)
182: ((HttpServletResponse) sresponse)
183: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
184:
185: }
186:
187: response.setSuspended(false);
188:
189: try {
190: report(request, response, throwable);
191: } catch (Throwable tt) {
192: tt.printStackTrace();
193: }
194:
195: }
196:
197: /**
198: * Return a String rendering of this object.
199: */
200: public String toString() {
201:
202: StringBuffer sb = new StringBuffer("ErrorReportValve[");
203: sb.append(container.getName());
204: sb.append("]");
205: return (sb.toString());
206:
207: }
208:
209: // ------------------------------------------------------ Protected Methods
210:
211: /**
212: * Prints out an error report.
213: *
214: * @param request The request being processed
215: * @param response The response being generated
216: * @param exception The exception that occurred (which possibly wraps
217: * a root cause exception
218: */
219: protected void report(Request request, Response response,
220: Throwable throwable) throws IOException {
221:
222: // Do nothing on non-HTTP responses
223: if (!(response instanceof HttpResponse))
224: return;
225: HttpResponse hresponse = (HttpResponse) response;
226: if (!(response instanceof HttpServletResponse))
227: return;
228: HttpServletResponse hres = (HttpServletResponse) response;
229: int statusCode = hresponse.getStatus();
230: String message = RequestUtil.filter(hresponse.getMessage());
231: if (message == null)
232: message = "";
233:
234: // Do nothing on a 1xx and 2xx status
235: if (statusCode < 300)
236: return;
237: // Do nothing on a NOT MODIFIED status
238: if (statusCode == HttpServletResponse.SC_NOT_MODIFIED)
239: return;
240:
241: // FIXME: Reset part of the request
242: /*
243: try {
244: if (hresponse.isError())
245: hresponse.reset(statusCode, message);
246: } catch (IllegalStateException e) {
247: ;
248: }
249: */
250:
251: Throwable rootCause = null;
252:
253: if (throwable != null) {
254:
255: if (throwable instanceof ServletException)
256: rootCause = ((ServletException) throwable)
257: .getRootCause();
258:
259: }
260:
261: // Do nothing if there is no report for the specified status code
262: String report = null;
263: try {
264: report = sm.getString("http." + statusCode, message);
265: } catch (Throwable t) {
266: ;
267: }
268: if (report == null)
269: return;
270:
271: StringBuffer sb = new StringBuffer();
272:
273: sb.append("<html><head><title>");
274: sb.append(ServerInfo.getServerInfo()).append(" - ");
275: sb.append(sm.getString("errorReportValve.errorReport"));
276: sb.append("</title>");
277: sb.append("<STYLE><!--");
278: sb
279: .append("H1{font-family : sans-serif,Arial,Tahoma;color : white;background-color : #0086b2;} ");
280: sb
281: .append("H3{font-family : sans-serif,Arial,Tahoma;color : white;background-color : #0086b2;} ");
282: sb
283: .append("BODY{font-family : sans-serif,Arial,Tahoma;color : black;background-color : white;} ");
284: sb.append("B{color : white;background-color : #0086b2;} ");
285: sb.append("HR{color : #0086b2;} ");
286: sb.append("--></STYLE> ");
287: sb.append("</head><body>");
288: sb.append("<h1>");
289: sb.append(
290: sm.getString("errorReportValve.statusHeader", ""
291: + statusCode, message)).append("</h1>");
292: sb.append("<HR size=\"1\" noshade>");
293: sb.append("<p><b>type</b> ");
294: if (throwable != null) {
295: sb.append(sm.getString("errorReportValve.exceptionReport"));
296: } else {
297: sb.append(sm.getString("errorReportValve.statusReport"));
298: }
299: sb.append("</p>");
300: sb.append("<p><b>");
301: sb.append(sm.getString("errorReportValve.message"));
302: sb.append("</b> <u>");
303: sb.append(message).append("</u></p>");
304: sb.append("<p><b>");
305: sb.append(sm.getString("errorReportValve.description"));
306: sb.append("</b> <u>");
307: sb.append(report);
308: sb.append("</u></p>");
309:
310: if (throwable != null) {
311: StringWriter stackTrace = new StringWriter();
312: throwable.printStackTrace(new PrintWriter(stackTrace));
313: sb.append("<p><b>");
314: sb.append(sm.getString("errorReportValve.exception"));
315: sb.append("</b> <pre>");
316: sb.append(stackTrace.toString());
317: sb.append("</pre></p>");
318: if (rootCause != null) {
319: stackTrace = new StringWriter();
320: rootCause.printStackTrace(new PrintWriter(stackTrace));
321: sb.append("<p><b>");
322: sb.append(sm.getString("errorReportValve.rootCause"));
323: sb.append("</b> <pre>");
324: sb.append(stackTrace.toString());
325: sb.append("</pre></p>");
326: }
327: }
328:
329: sb.append("<HR size=\"1\" noshade>");
330: sb.append("<h3>").append(ServerInfo.getServerInfo()).append(
331: "</h3>");
332: sb.append("</body></html>");
333:
334: try {
335:
336: Writer writer = response.getReporter();
337:
338: if (writer != null) {
339:
340: try {
341: hres.setContentType("text/html");
342: } catch (Throwable t) {
343: if (debug >= 1)
344: log("status.setContentType", t);
345: }
346:
347: // If writer is null, it's an indication that the response has
348: // been hard committed already, which should never happen
349: writer.write(sb.toString());
350: writer.flush();
351:
352: }
353:
354: } catch (IOException e) {
355: ;
356: } catch (IllegalStateException e) {
357: ;
358: }
359:
360: }
361:
362: /**
363: * Log a message on the Logger associated with our Container (if any).
364: *
365: * @param message Message to be logged
366: */
367: protected void log(String message) {
368:
369: Logger logger = container.getLogger();
370: if (logger != null)
371: logger.log(this .toString() + ": " + message);
372: else
373: System.out.println(this .toString() + ": " + message);
374:
375: }
376:
377: /**
378: * Log a message on the Logger associated with our Container (if any).
379: *
380: * @param message Message to be logged
381: * @param throwable Associated exception
382: */
383: protected void log(String message, Throwable throwable) {
384:
385: Logger logger = container.getLogger();
386: if (logger != null)
387: logger.log(this .toString() + ": " + message, throwable);
388: else {
389: System.out.println(this .toString() + ": " + message);
390: throwable.printStackTrace(System.out);
391: }
392:
393: }
394:
395: }
|