You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/10/08 03:02:31 UTC
cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/core BaseInterceptor.java Container.java ContextManager.java TomcatException.java
costin 00/10/07 18:02:31
Modified: src/etc server.xml
src/share/org/apache/tomcat/context DefaultCMSetter.java
src/share/org/apache/tomcat/core BaseInterceptor.java
Container.java ContextManager.java
TomcatException.java
Added: src/share/org/apache/tomcat/context ErrorHandler.java
Log:
Moved error handling in a normal interceptor.
- allow customization of error handling ( including what to show, etc)
- allow multiple "facades" ( that's a particular case of customized
error handling - i.e. we customize based on interpretations specific to
a particular servlet API version )
- "evolution by mini-revolutions" - the code for error handling is complex,
anyone can experiment with better implementations.
The change involves a new callback ( where we hook the exeception handling
code ).
Also, moved the standard error handlers in the new ErrorHandler ( from
DefaultCMSetter )
Revision Changes Path
1.49 +6 -0 jakarta-tomcat/src/etc/server.xml
Index: server.xml
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/etc/server.xml,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -r1.48 -r1.49
--- server.xml 2000/10/06 05:07:06 1.48
+++ server.xml 2000/10/08 01:02:24 1.49
@@ -124,6 +124,12 @@
<ContextInterceptor
className="org.apache.tomcat.context.LoaderInterceptor11" />
+ <!-- Default Error handler
+ -->
+ <ContextInterceptor
+ className="org.apache.tomcat.context.ErrorHandler" />
+
+
<ContextInterceptor
className="org.apache.tomcat.context.WorkDirInterceptor" />
1.56 +0 -296 jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java
Index: DefaultCMSetter.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -r1.55 -r1.56
--- DefaultCMSetter.java 2000/10/06 05:18:02 1.55
+++ DefaultCMSetter.java 2000/10/08 01:02:25 1.56
@@ -177,301 +177,5 @@
loghelperServlet.getLogger() != null)
cm.addLogger( loghelperServlet.getLogger() );
}
-
- /** Add default error handlers
- */
- public void contextInit( Context ctx)
- throws TomcatException
- {
- ctx.addServlet( new ExceptionHandler());
- ctx.addServlet( new StatusHandler());
-
- // Default status handlers
- ctx.addServlet( new RedirectHandler());
- ctx.addErrorPage( "302", "tomcat.redirectHandler");
- ctx.addServlet( new NotFoundHandler());
- ctx.addErrorPage( "404", "tomcat.notFoundHandler");
- }
-}
-
-class NotFoundHandler extends Handler {
- static StringManager sm=StringManager.
- getManager("org.apache.tomcat.resources");
- int sbNote=0;
-
- NotFoundHandler() {
- initialized=true;
- internal=true;
- name="tomcat.notFoundHandler";
- }
-
- public void doService(Request req, Response res)
- throws Exception
- {
- res.setContentType("text/html"); // ISO-8859-1 default
-
- String requestURI = (String)req.
- getAttribute("javax.servlet.include.request_uri");
-
- if (requestURI == null) {
- requestURI = req.getRequestURI();
- }
-
- if( sbNote==0 ) {
- sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
- "NotFoundHandler.buff");
- }
-
- // we can recycle it because
- // we don't call toString();
- StringBuffer buf=(StringBuffer)req.getNote( sbNote );
- if( buf==null ) {
- buf = new StringBuffer();
- req.setNote( sbNote, buf );
- }
-
- buf.append("<head><title>")
- .append(sm.getString("defaulterrorpage.notfound404"))
- .append("</title></head>\r\n");
- buf.append("<body><h1>")
- .append(sm.getString("defaulterrorpage.notfound404"))
- .append("</h1>\r\n");
- buf.append(sm.getString("defaulterrorpage.originalrequest"))
- .append( requestURI );
- buf.append("</body>\r\n");
-
- res.setContentLength(buf.length());
-
- res.getBuffer().write( buf );
- buf.setLength(0);
- }
-}
-
-class ExceptionHandler extends Handler {
- static StringManager sm=StringManager.
- getManager("org.apache.tomcat.resources");
- int sbNote=0;
-
- ExceptionHandler() {
- initialized=true;
- internal=true;
- name="tomcat.exceptionHandler";
- }
-
- public void doService(Request req, Response res)
- throws Exception
- {
- String msg=(String)req.getAttribute("javax.servlet.error.message");
-
- Throwable e= (Throwable)req.
- getAttribute("tomcat.servlet.error.throwable");
- if( e==null ) {
- log("Exception handler called without an exception", new Throwable("trace"));
- return;
- }
-
- res.setContentType("text/html");
- res.setStatus( 500 );
-
- if( sbNote==0 ) {
- sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
- "ExceptionHandler.buff");
- }
-
- // we can recycle it because
- // we don't call toString();
- StringBuffer buf=(StringBuffer)req.getNote( sbNote );
- if( buf==null ) {
- buf = new StringBuffer();
- req.setNote( sbNote, buf );
- }
- buf.append("<h1>");
- if( res.isIncluded() ) {
- buf.append(sm.getString("defaulterrorpage.includedservlet") ).
- append(" ");
- } else {
- buf.append("Error: ");
- }
-
- buf.append( 500 );
- buf.append("</h1>\r\n");
-
- // More info - where it happended"
- buf.append("<h2>")
- .append(sm.getString("defaulterrorpage.location"))
- .append(req.getRequestURI())
- .append("</h2>");
-
- buf.append("<b>")
- .append(sm.getString("defaulterrorpage.internalservleterror"))
- .append("</b><br>");
-
- buf.append("<pre>");
- // prints nested exceptions too, including SQLExceptions, recursively
- String trace = Logger.throwableToString
- (e, "<b>" + sm.getString("defaulterrorpage.rootcause") + "</b>");
- buf.append(trace);
-
- buf.append("</pre>\r\n");
-
- buf.append("\r\n");
-
- res.getBuffer().write( buf );
- buf.setLength(0);
- }
-}
-
-class StatusHandler extends Handler {
- static StringManager sm=StringManager.
- getManager("org.apache.tomcat.resources");
- int sbNote=0;
-
- StatusHandler() {
- initialized=true;
- internal=true;
- name="tomcat.statusHandler";
- }
-
- // We don't want interceptors called for redirect
- // handler
- public void doService(Request req, Response res)
- throws Exception
- {
- String msg=(String)req.getAttribute("javax.servlet.error.message");
-
- res.setContentType("text/html");
- // res is reset !!!
- // status is already set
- int sc=res.getStatus();
-
- if( sbNote==0 ) {
- sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
- "StatusHandler.buff");
- }
-
- // we can recycle it because
- // we don't call toString();
- StringBuffer buf=(StringBuffer)req.getNote( sbNote );
- if( buf==null ) {
- buf = new StringBuffer();
- req.setNote( sbNote, buf );
- }
- buf.append("<h1>");
- if( res.isIncluded() ) {
- buf.append(sm.getString("defaulterrorpage.includedservlet") );
- } else {
- buf.append("Error: ");
- }
-
- buf.append( sc );
- buf.append("</h1>\r\n");
-
- // More info - where it happended"
- buf.append("<h2>")
- .append(sm.getString("defaulterrorpage.location"))
- .append(req.getRequestURI())
- .append("</h2>");
-
- buf.append("<b>")
- .append(msg)
- .append("</b><br>");
-
- res.setContentLength(buf.length());
- res.getBuffer().write( buf );
- buf.setLength(0);
- }
}
-
-class RedirectHandler extends Handler {
- static StringManager sm=StringManager.
- getManager("org.apache.tomcat.resources");
- int sbNote=0;
-
- RedirectHandler() {
- initialized=true;
- internal=true;
- name="tomcat.redirectHandler";
- }
- // We don't want interceptors called for redirect
- // handler
- public void doService(Request req, Response res)
- throws Exception
- {
- String location = (String)
- req.getAttribute("javax.servlet.error.message");
- Context ctx=req.getContext();
-
- location = makeAbsolute(req, location);
-
- if( debug>0) ctx.log("Redirect " + location + " " + req );
-
- res.setContentType("text/html"); // ISO-8859-1 default
- res.setHeader("Location", location);
-
- if( sbNote==0 ) {
- sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
- "RedirectHandler.buff");
- }
-
- // we can recycle it because
- // we don't call toString();
- StringBuffer buf=(StringBuffer)req.getNote( sbNote );
- if( buf==null ) {
- buf = new StringBuffer();
- req.setNote( sbNote, buf );
- }
- buf.append("<head><title>").
- append(sm.getString("defaulterrorpage.documentmoved")).
- append("</title></head>\r\n<body><h1>").
- append(sm.getString("defaulterrorpage.documentmoved")).
- append("</h1>\r\n").
- append(sm.getString("defaulterrorpage.thisdocumenthasmoved")).
- append(" <a href=\"").
- append(location).
- append("\">here</a>.<p>\r\n</body>\r\n");
-
- res.setContentLength(buf.length());
- res.getBuffer().write( buf );
- buf.setLength(0);
-
- }
-
- // XXX Move it to URLUtil !!!
- private String makeAbsolute(Request req, String location) {
- URL url = null;
- try {
- // Try making a URL out of the location
- // Throws an exception if the location is relative
- url = new URL(location);
- } catch (MalformedURLException e) {
- String requrl = getRequestURL(req);
- try {
- url = new URL(new URL(requrl), location);
- }
- catch (MalformedURLException ignored) {
- // Give up
- return location;
- }
- }
- return url.toString();
- }
-
- static String getRequestURL( Request req ) {
- StringBuffer url = new StringBuffer ();
- String scheme = req.getScheme ();
- int port = req.getServerPort ();
- String urlPath = req.getRequestURI();
-
- url.append (scheme); // http, https
- url.append ("://");
- url.append (req.getServerName ());
- if ((scheme.equals ("http") && port != 80)
- || (scheme.equals ("https") && port != 443)) {
- url.append (':');
- url.append (req.getServerPort ());
- }
- url.append(urlPath);
- return url.toString();
- }
-}
1.1 jakarta-tomcat/src/share/org/apache/tomcat/context/ErrorHandler.java
Index: ErrorHandler.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.context;
import org.apache.tomcat.core.*;
import org.apache.tomcat.request.*;
import org.apache.tomcat.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.*;
import org.apache.tomcat.util.log.*;
// don't extend - replace !
/**
* Handle errors - this is the default handler, you can replace it
* with customized versions
*
* @author Costin Manolache
*/
public final class ErrorHandler extends BaseInterceptor {
public ErrorHandler() {
}
public void engineInit( ContextManager cm )
throws TomcatException
{
}
public void addContext( ContextManager cm, Context ctx)
throws TomcatException
{
}
/** Add default error handlers
*/
public void contextInit( Context ctx)
throws TomcatException
{
ctx.addServlet( new ExceptionHandler());
ctx.addServlet( new StatusHandler());
// Default status handlers
ctx.addServlet( new RedirectHandler());
ctx.addErrorPage( "302", "tomcat.redirectHandler");
ctx.addServlet( new NotFoundHandler());
ctx.addErrorPage( "404", "tomcat.notFoundHandler");
}
public int handleError( Request req, Response res, Throwable t ) {
ContextManager cm=req.getContextManager();
Context ctx = req.getContext();
if(ctx==null) ctx=cm.getDefaultContext();
if( t==null ) {
handleStatusImpl( cm, ctx, req, res, res.getStatus() );
} else {
handleErrorImpl( cm, ctx, req, res, t );
}
return 200;
}
// -------------------- Implementation of handleError
// Originally in ContextManager.
private final void handleStatusImpl( ContextManager cm, Context ctx,
Request req, Response res,
int code )
{
String errorPath=null;
Handler errorServlet=null;
// don't log normal cases ( redirect and need_auth ), they are not
// error
// XXX this log was intended to debug the status code generation.
// it can be removed for all cases.
if( code != 302 && code != 401 && code!=400 ) // tuneme
ctx.log( "Status code:" + code + " request:" + req + " msg:" +
req.getAttribute("javax.servlet.error.message"));
errorPath = ctx.getErrorPage( code );
if( errorPath != null ) {
errorServlet=getHandlerForPath( cm, ctx, errorPath );
// Make sure Jsps will work
req.setAttribute( "javax.servlet.include.request_uri",
ctx.getPath() + "/" + errorPath );
req.setAttribute( "javax.servlet.include.servlet_path", errorPath );
}
boolean isDefaultHandler = false;
if( errorServlet==null ) {
errorServlet=ctx.getServletByName( "tomcat.statusHandler");
isDefaultHandler = true;
}
if (errorServlet == null) {
ctx.log( "Handler errorServlet is null! errorPath:" + errorPath);
return;
}
if (!isDefaultHandler)
res.resetBuffer();
req.setAttribute("javax.servlet.error.status_code",new Integer( code));
req.setAttribute("tomcat.servlet.error.request", req);
if( debug>0 )
ctx.log( "Handler " + errorServlet + " " + errorPath);
errorServlet.service( req, res );
}
// XXX XXX Security - we should log the message, but nothing
// should show up to the user - it gives up information
// about the internal system !
// Developers can/should use the logs !!!
/** General error handling mechanism. It will try to find an error handler
* or use the default handler.
*/
void handleErrorImpl( ContextManager cm, Context ctx,
Request req, Response res , Throwable t )
{
/** The exception must be available to the user.
Note that it is _WRONG_ to send the trace back to
the client. AFAIK the trace is the _best_ debugger.
*/
if( t instanceof IllegalStateException ) {
ctx.log("IllegalStateException in " + req, t);
// Nothing special in jasper exception treatement, no deps
//} else if( t instanceof org.apache.jasper.JasperException ) {
// ctx.log("JasperException in " + req, t);
} else if( t instanceof IOException ) {
if( "Broken pipe".equals(t.getMessage()))
{
ctx.log("Broken pipe in " + req, t, Logger.DEBUG); // tuneme
return;
}
ctx.log("IOException in " + req, t );
} else {
ctx.log("Exception in " + req , t );
}
if(null!=req.getAttribute("tomcat.servlet.error.defaultHandler")){
// we are in handleRequest for the "default" error handler
log("ERROR: can't find default error handler, or error in default error page", t);
}
String errorPath=null;
Handler errorServlet=null;
// Scan the exception's inheritance tree looking for a rule
// that this type of exception should be forwarded
Class clazz = t.getClass();
while (errorPath == null && clazz != null) {
String name = clazz.getName();
errorPath = ctx.getErrorPage(name);
clazz = clazz.getSuperclass();
}
if( errorPath != null ) {
errorServlet=getHandlerForPath( cm, ctx, errorPath );
// Make sure Jsps will work
req.setAttribute( "javax.servlet.include.request_uri",
ctx.getPath() + "/" + errorPath );
req.setAttribute( "javax.servlet.include.servlet_path", errorPath );
}
boolean isDefaultHandler = false;
if( errorLoop( ctx, req ) || errorServlet==null) {
errorServlet = ctx.getServletByName("tomcat.exceptionHandler");
isDefaultHandler = true;
}
if (errorServlet == null) {
ctx.log( "Handler errorServlet is null! errorPath:" + errorPath);
return;
}
if (!isDefaultHandler)
res.resetBuffer();
req.setAttribute("javax.servlet.error.exception_type", t.getClass());
req.setAttribute("javax.servlet.error.message", t.getMessage());
req.setAttribute("tomcat.servlet.error.throwable", t);
req.setAttribute("tomcat.servlet.error.request", req);
if( debug>0 )
ctx.log( "Handler " + errorServlet + " " + errorPath);
errorServlet.service( req, res );
}
public final Handler getHandlerForPath( ContextManager cm,
Context ctx, String path ) {
if( ! path.startsWith( "/" ) ) {
return ctx.getServletByName( path );
}
Request req1=new Request();
Response res1=new Response();
cm.initRequest( req1, res1 );
req1.setRequestURI( ctx.getPath() + path );
cm.processRequest( req1 );
return req1.getWrapper();
}
/** Handle the case of error handler generating an error or special status
*/
private boolean errorLoop( Context ctx, Request req ) {
if( req.getAttribute("javax.servlet.error.status_code") != null
|| req.getAttribute("javax.servlet.error.exception_type")!=null) {
if( ctx.getDebug() > 0 )
ctx.log( "Error: exception inside exception servlet " +
req.getAttribute("javax.servlet.error.status_code") +
" " + req.
getAttribute("javax.servlet.error.exception_type"));
return true;
}
return false;
}
}
class NotFoundHandler extends Handler {
static StringManager sm=StringManager.
getManager("org.apache.tomcat.resources");
int sbNote=0;
NotFoundHandler() {
initialized=true;
internal=true;
name="tomcat.notFoundHandler";
}
public void doService(Request req, Response res)
throws Exception
{
res.setContentType("text/html"); // ISO-8859-1 default
String requestURI = (String)req.
getAttribute("javax.servlet.include.request_uri");
if (requestURI == null) {
requestURI = req.getRequestURI();
}
if( sbNote==0 ) {
sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
"NotFoundHandler.buff");
}
// we can recycle it because
// we don't call toString();
StringBuffer buf=(StringBuffer)req.getNote( sbNote );
if( buf==null ) {
buf = new StringBuffer();
req.setNote( sbNote, buf );
}
buf.append("<head><title>")
.append(sm.getString("defaulterrorpage.notfound404"))
.append("</title></head>\r\n");
buf.append("<body><h1>")
.append(sm.getString("defaulterrorpage.notfound404"))
.append("</h1>\r\n");
buf.append(sm.getString("defaulterrorpage.originalrequest"))
.append( requestURI );
buf.append("</body>\r\n");
res.setContentLength(buf.length());
res.getBuffer().write( buf );
buf.setLength(0);
}
}
class ExceptionHandler extends Handler {
static StringManager sm=StringManager.
getManager("org.apache.tomcat.resources");
int sbNote=0;
ExceptionHandler() {
initialized=true;
internal=true;
name="tomcat.exceptionHandler";
}
public void doService(Request req, Response res)
throws Exception
{
String msg=(String)req.getAttribute("javax.servlet.error.message");
Throwable e= (Throwable)req.
getAttribute("tomcat.servlet.error.throwable");
if( e==null ) {
log("Exception handler called without an exception", new Throwable("trace"));
return;
}
res.setContentType("text/html");
res.setStatus( 500 );
if( sbNote==0 ) {
sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
"ExceptionHandler.buff");
}
// we can recycle it because
// we don't call toString();
StringBuffer buf=(StringBuffer)req.getNote( sbNote );
if( buf==null ) {
buf = new StringBuffer();
req.setNote( sbNote, buf );
}
buf.append("<h1>");
if( res.isIncluded() ) {
buf.append(sm.getString("defaulterrorpage.includedservlet") ).
append(" ");
} else {
buf.append("Error: ");
}
buf.append( 500 );
buf.append("</h1>\r\n");
// More info - where it happended"
buf.append("<h2>")
.append(sm.getString("defaulterrorpage.location"))
.append(req.getRequestURI())
.append("</h2>");
buf.append("<b>")
.append(sm.getString("defaulterrorpage.internalservleterror"))
.append("</b><br>");
buf.append("<pre>");
// prints nested exceptions too, including SQLExceptions, recursively
String trace = Logger.throwableToString
(e, "<b>" + sm.getString("defaulterrorpage.rootcause") + "</b>");
buf.append(trace);
buf.append("</pre>\r\n");
buf.append("\r\n");
res.getBuffer().write( buf );
buf.setLength(0);
}
}
class StatusHandler extends Handler {
static StringManager sm=StringManager.
getManager("org.apache.tomcat.resources");
int sbNote=0;
StatusHandler() {
initialized=true;
internal=true;
name="tomcat.statusHandler";
}
// We don't want interceptors called for redirect
// handler
public void doService(Request req, Response res)
throws Exception
{
String msg=(String)req.getAttribute("javax.servlet.error.message");
res.setContentType("text/html");
// res is reset !!!
// status is already set
int sc=res.getStatus();
if( sbNote==0 ) {
sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
"StatusHandler.buff");
}
// we can recycle it because
// we don't call toString();
StringBuffer buf=(StringBuffer)req.getNote( sbNote );
if( buf==null ) {
buf = new StringBuffer();
req.setNote( sbNote, buf );
}
buf.append("<h1>");
if( res.isIncluded() ) {
buf.append(sm.getString("defaulterrorpage.includedservlet") );
} else {
buf.append("Error: ");
}
buf.append( sc );
buf.append("</h1>\r\n");
// More info - where it happended"
buf.append("<h2>")
.append(sm.getString("defaulterrorpage.location"))
.append(req.getRequestURI())
.append("</h2>");
buf.append("<b>")
.append(msg)
.append("</b><br>");
res.setContentLength(buf.length());
res.getBuffer().write( buf );
buf.setLength(0);
}
}
class RedirectHandler extends Handler {
static StringManager sm=StringManager.
getManager("org.apache.tomcat.resources");
int sbNote=0;
RedirectHandler() {
initialized=true;
internal=true;
name="tomcat.redirectHandler";
}
// We don't want interceptors called for redirect
// handler
public void doService(Request req, Response res)
throws Exception
{
String location = (String)
req.getAttribute("javax.servlet.error.message");
Context ctx=req.getContext();
location = makeAbsolute(req, location);
if( debug>0) ctx.log("Redirect " + location + " " + req );
res.setContentType("text/html"); // ISO-8859-1 default
res.setHeader("Location", location);
if( sbNote==0 ) {
sbNote=req.getContextManager().getNoteId(ContextManager.REQUEST_NOTE,
"RedirectHandler.buff");
}
// we can recycle it because
// we don't call toString();
StringBuffer buf=(StringBuffer)req.getNote( sbNote );
if( buf==null ) {
buf = new StringBuffer();
req.setNote( sbNote, buf );
}
buf.append("<head><title>").
append(sm.getString("defaulterrorpage.documentmoved")).
append("</title></head>\r\n<body><h1>").
append(sm.getString("defaulterrorpage.documentmoved")).
append("</h1>\r\n").
append(sm.getString("defaulterrorpage.thisdocumenthasmoved")).
append(" <a href=\"").
append(location).
append("\">here</a>.<p>\r\n</body>\r\n");
res.setContentLength(buf.length());
res.getBuffer().write( buf );
buf.setLength(0);
}
// XXX Move it to URLUtil !!!
private String makeAbsolute(Request req, String location) {
URL url = null;
try {
// Try making a URL out of the location
// Throws an exception if the location is relative
url = new URL(location);
} catch (MalformedURLException e) {
String requrl = getRequestURL(req);
try {
url = new URL(new URL(requrl), location);
}
catch (MalformedURLException ignored) {
// Give up
return location;
}
}
return url.toString();
}
static String getRequestURL( Request req ) {
StringBuffer url = new StringBuffer ();
String scheme = req.getScheme ();
int port = req.getServerPort ();
String urlPath = req.getRequestURI();
url.append (scheme); // http, https
url.append ("://");
url.append (req.getServerName ());
if ((scheme.equals ("http") && port != 80)
|| (scheme.equals ("https") && port != 443)) {
url.append (':');
url.append (req.getServerPort ());
}
url.append(urlPath);
return url.toString();
}
}
1.24 +23 -3 jakarta-tomcat/src/share/org/apache/tomcat/core/BaseInterceptor.java
Index: BaseInterceptor.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/BaseInterceptor.java,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -r1.23 -r1.24
--- BaseInterceptor.java 2000/10/01 06:37:44 1.23
+++ BaseInterceptor.java 2000/10/08 01:02:28 1.24
@@ -267,10 +267,30 @@
return 0;
}
- public String []getMethods() {
- return methods;
- }
+ /** This callback is called whenever an exception happen.
+ * If t is null assume this is a "status" report ( 500, 404, etc).
+ *
+ * During this hook it is possible to create a sub-request
+ * and call the handler, and it is possible that the sub-request
+ * will also generate an exception. The handler must insure
+ * no loops will happen - but it's free to choose whatever method
+ * it wants.
+ *
+ * It's also the handler responsiblity to insure correct
+ * servlet API semantics - if the spec becomes incopmatible
+ * with previous versions ( or multiple interpretations are
+ * possible) that can be made a context-specific handler.
+ *
+ * @returns 200 if the error was handled ( similar with Apache's
+ * OK )
+ * 0 if this handler can't deal with the error ( to
+ * allow chaining )
+ */
+ public int handleError( Request request, Response response, Throwable t) {
+ return 0;
+ }
+
// -------------------- Context notifications --------------------
/** Notify when a context is initialized.
* The first interceptor in the chain for contextInit must read web.xml
1.38 +3 -1 jakarta-tomcat/src/share/org/apache/tomcat/core/Container.java
Index: Container.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Container.java,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -r1.37 -r1.38
--- Container.java 2000/10/01 22:21:44 1.37
+++ Container.java 2000/10/08 01:02:29 1.38
@@ -390,13 +390,15 @@
public static final int H_afterBody=8;
public static final int H_postService=9;
public static final int H_postRequest=10;
- public static final int H_engineInit=11;
+ public static final int H_handleError=11;
+ public static final int H_engineInit=12;
public static final String PREDEFINED_I[]= {
"requestMap", "contextMap", "authenticate",
"authorize", "preService", "beforeBody",
"newSessionRequest", "beforeCommit",
"afterBody", "postService", "postRequest",
+ "handleError",
// special case - all interceptors will be added to the "context"
// chain. We plan to use a simpler Event/Listener model for
// all context hooks, since they don't have any performance requirement
1.144 +24 -159 jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java
Index: ContextManager.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java,v
retrieving revision 1.143
retrieving revision 1.144
diff -u -r1.143 -r1.144
--- ContextManager.java 2000/10/06 05:18:12 1.143
+++ ContextManager.java 2000/10/08 01:02:29 1.144
@@ -524,6 +524,12 @@
}
}
+ /** Return the "default" context for the "default" host.
+ */
+ public Context getDefaultContext() {
+ return rootContext;
+ }
+
/** Return the list of contexts managed by this server
*/
public final Enumeration getContexts() {
@@ -817,173 +823,32 @@
/** Called for error-codes
*/
public final void handleStatus( Request req, Response res, int code ) {
- String errorPath=null;
- Handler errorServlet=null;
-
- if( code==0 )
- code=res.getStatus();
- else
- res.setStatus(code);
-
- Context ctx = req.getContext();
- if(ctx==null) ctx=rootContext;
-
- // don't log normal cases ( redirect and need_auth ), they are not
- // error
- // XXX this log was intended to debug the status code generation.
- // it can be removed for all cases.
- if( code != 302 && code != 401 && code!=400 ) // tuneme
- ctx.log( "Status code:" + code + " request:" + req + " msg:" +
- req.getAttribute("javax.servlet.error.message"));
+ if( code!=0 )
+ res.setStatus( code );
- errorPath = ctx.getErrorPage( code );
- if( errorPath != null ) {
- errorServlet=getHandlerForPath( ctx, errorPath );
-
- // Make sure Jsps will work
- req.setAttribute( "javax.servlet.include.request_uri",
- ctx.getPath() + "/" + errorPath );
- req.setAttribute( "javax.servlet.include.servlet_path", errorPath );
- }
-
- boolean isDefaultHandler = false;
- if( errorServlet==null ) {
- errorServlet=ctx.getServletByName( "tomcat.statusHandler");
- isDefaultHandler = true;
- }
-
- if (errorServlet == null) {
- ctx.log( "Handler errorServlet is null! errorPath:" + errorPath);
- return;
+ BaseInterceptor ri[];
+ int status;
+ ri=getInterceptors( req, Container.H_handleError );
+
+ for( int i=0; i< ri.length; i++ ) {
+ status=ri[i].handleError( req, res, null );
+ if( status!=0 ) return;
}
-
- if (!isDefaultHandler)
- res.resetBuffer();
-
- req.setAttribute("javax.servlet.error.status_code",new Integer( code));
- req.setAttribute("tomcat.servlet.error.request", req);
-
- if( debug>0 )
- ctx.log( "Handler " + errorServlet + " " + errorPath);
-
- errorServlet.service( req, res );
}
-
- // XXX XXX Security - we should log the message, but nothing
- // should show up to the user - it gives up information
- // about the internal system !
- // Developers can/should use the logs !!!
- /** General error handling mechanism. It will try to find an error handler
- * or use the default handler.
+ /**
+ * Call error hook
*/
void handleError( Request req, Response res , Throwable t ) {
- Context ctx = req.getContext();
- if(ctx==null) {
- ctx=rootContext;
- }
-
- /** The exception must be available to the user.
- Note that it is _WRONG_ to send the trace back to
- the client. AFAIK the trace is the _best_ debugger.
- */
- if( t instanceof IllegalStateException ) {
- ctx.log("IllegalStateException in " + req, t);
- // Nothing special in jasper exception treatement, no deps
- //} else if( t instanceof org.apache.jasper.JasperException ) {
- // ctx.log("JasperException in " + req, t);
- } else if( t instanceof IOException ) {
- if( "Broken pipe".equals(t.getMessage()))
- {
- ctx.log("Broken pipe in " + req, t, Logger.DEBUG); // tuneme
- return;
- }
- ctx.log("IOException in " + req, t );
- } else {
- ctx.log("Exception in " + req , t );
- }
-
- if(null!=req.getAttribute("tomcat.servlet.error.defaultHandler")){
- // we are in handleRequest for the "default" error handler
- log("ERROR: can't find default error handler, or error in default error page", t);
- }
-
- String errorPath=null;
- Handler errorServlet=null;
-
- // Scan the exception's inheritance tree looking for a rule
- // that this type of exception should be forwarded
- Class clazz = t.getClass();
- while (errorPath == null && clazz != null) {
- String name = clazz.getName();
- errorPath = ctx.getErrorPage(name);
- clazz = clazz.getSuperclass();
- }
-
- if( errorPath != null ) {
- errorServlet=getHandlerForPath( ctx, errorPath );
-
- // Make sure Jsps will work
- req.setAttribute( "javax.servlet.include.request_uri",
- ctx.getPath() + "/" + errorPath );
- req.setAttribute( "javax.servlet.include.servlet_path", errorPath );
- }
-
- boolean isDefaultHandler = false;
- if( errorLoop( ctx, req ) || errorServlet==null) {
- errorServlet = ctx.getServletByName("tomcat.exceptionHandler");
- isDefaultHandler = true;
- }
-
- if (errorServlet == null) {
- ctx.log( "Handler errorServlet is null! errorPath:" + errorPath);
- return;
- }
-
- if (!isDefaultHandler)
- res.resetBuffer();
-
- req.setAttribute("javax.servlet.error.exception_type", t.getClass());
- req.setAttribute("javax.servlet.error.message", t.getMessage());
- req.setAttribute("tomcat.servlet.error.throwable", t);
- req.setAttribute("tomcat.servlet.error.request", req);
-
- if( debug>0 )
- ctx.log( "Handler " + errorServlet + " " + errorPath);
-
- errorServlet.service( req, res );
- }
-
- public final Handler getHandlerForPath( Context ctx, String path ) {
- if( ! path.startsWith( "/" ) ) {
- return ctx.getServletByName( path );
- }
- Request req1=new Request();
- Response res1=new Response();
- initRequest( req1, res1 );
-
- req1.setRequestURI( ctx.getPath() + path );
- processRequest( req1 );
- return req1.getWrapper();
- }
-
- /** Handle the case of error handler generating an error or special status
- */
- private boolean errorLoop( Context ctx, Request req ) {
- if( req.getAttribute("javax.servlet.error.status_code") != null
- || req.getAttribute("javax.servlet.error.exception_type")!=null) {
-
- if( ctx.getDebug() > 0 )
- ctx.log( "Error: exception inside exception servlet " +
- req.getAttribute("javax.servlet.error.status_code") +
- " " + req.
- getAttribute("javax.servlet.error.exception_type"));
-
- return true;
+ BaseInterceptor ri[];
+ int status;
+ ri=getInterceptors( req, Container.H_handleError );
+
+ for( int i=0; i< ri.length; i++ ) {
+ status=ri[i].handleError( req, res, t );
+ if( status!=0 ) return;
}
- return false;
}
-
// -------------------- Support for notes --------------------
1.2 +12 -0 jakarta-tomcat/src/share/org/apache/tomcat/core/TomcatException.java
Index: TomcatException.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/TomcatException.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TomcatException.java 2000/01/25 01:11:55 1.1
+++ TomcatException.java 2000/10/08 01:02:29 1.2
@@ -101,6 +101,18 @@
* that caused this connector exception
*/
public Throwable getRootCause() {
+ // Does any other Exception have a "getRootCause" ?
+ return rootCause;
+ }
+
+ /**
+ * Get the underlying exception that caused this ConnectorException.
+ * Follow the pattern from SQLException, etc.
+ *
+ * @return the <code>Throwable</code>
+ * that caused this connector exception
+ */
+ public Throwable getNextException() {
return rootCause;
}
}