larryi 00/12/05 06:02:41 Modified: src/facade22/org/apache/tomcat/facade RequestDispatcherImpl.java ServletWrapper.java src/facade22/org/apache/tomcat/modules/facade22 JspInterceptor.java src/share/org/apache/tomcat/context ErrorHandler.java src/share/org/apache/tomcat/core Handler.java Request.java Response.java Log: Port exception propagation behavior from Tomcat 3.2. Exceptions that occur in included servlets are throw back to their callers. The exception is handled only if it occurs or when it reaches the top level handler. The implementation differs from that of Tomcat 3.2. Where Tomcat 3.2 catches and rethrows the exception at each level, this implementation tries to catch and save the exception. I think this makes the flow of execution easier to control. We can give interceptors a chance to examine the exceptions. Now, only RequestDispatcherImpl rethrows the exceptions. There are several places that exceptions are saved. Init exceptions and UnavailableExceptions thrown in the service() method are saved in Handler along with a "permanent" indication. postServletInit interceptors can find it here. Also, postServletInit is called even if the init() method throws an exception. The first exception that occurs during request processing is saved on the Response along with the "include" URI, if present. This exception will not be overwritten if subsequent exceptions occur. Exceptions during request processing are also saved on the Request. But unlike the Response exception, the Request exception is overwritten with each new exception that occurs. In RequestDispatcherImpl, setting of the "included" flag was restored in includeNamed() and forwardNamed() since it is used to control error handling behavior. JspInterceptor has been updated to set an initial exception if translation or compilation fails. This way these error won't always show up as a 404 error. Also, setting the compileTime from the java file was removed. After a restart, this prevented the error from being recreated. Thus, a new java file will be created after each restart for a non-compiling JSP, but this is better than creating one on each request. Watchdog still passes, but these changes need more review and testing. Handling of destroy() wasn't addressed in these changes. This needs review in the context of these changes. Revision Changes Path 1.5 +87 -12 jakarta-tomcat/src/facade22/org/apache/tomcat/facade/RequestDispatcherImpl.java Index: RequestDispatcherImpl.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/facade22/org/apache/tomcat/facade/RequestDispatcherImpl.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- RequestDispatcherImpl.java 2000/11/02 21:51:37 1.4 +++ RequestDispatcherImpl.java 2000/12/05 14:02:37 1.5 @@ -188,11 +188,26 @@ // CM should have set the wrapper - call it Handler wr=realRequest.getHandler(); - if( wr!=null ) wr.service(realRequest, realResponse); + if( wr!=null ) + wr.service(realRequest, realResponse); + // Clean up the request and response as needed + ; // No action required + + // Rethrow original error if present + if ( realResponse.isExceptionPresent() ) { + Exception ex = realResponse.getErrorException(); + if ( ex instanceof IOException ) + throw (IOException) ex; + else if ( ex instanceof ServletException ) + throw (ServletException) ex; + else + throw new ServletException + (sm.getString("dispatcher.forwardException", ex)); + } + // close the response - output after this point will be discarded. realResponse.finish(); - } public void include(ServletRequest request, ServletResponse response) @@ -313,7 +328,8 @@ // for the realRequest ( since the real request will still have the // original handler/wrapper ) Handler wr=subRequest.getHandler(); - if( wr!=null ) wr.service(realRequest, realResponse); + if( wr!=null ) + wr.service(realRequest, realResponse); // After request, we want to restore the include attributes - for // chained includes. @@ -335,6 +351,18 @@ if( ! old_included ) { realResponse.setIncluded( false ); } + + // Rethrow original error if present + if ( realResponse.isExceptionPresent() ) { + Exception ex = realResponse.getErrorException(); + if ( ex instanceof IOException ) + throw (IOException) ex; + else if ( ex instanceof ServletException ) + throw (ServletException) ex; + else + throw new ServletException + (sm.getString("dispatcher.includeException", ex)); + } } @@ -345,13 +373,36 @@ public void includeNamed(ServletRequest request, ServletResponse response) throws ServletException, IOException { + // We got here if name!=null, so assert it + Handler wr = context.getServletByName( name ); // Use the original request - as in specification ! + Request realRequest=((HttpServletRequestFacade)request).getRealRequest(); + Response realResponse = realRequest.getResponse(); - // We got here if name!=null, so assert it - Handler wrapper = context.getServletByName( name ); - Request realR=((HttpServletRequestFacade)request).getRealRequest(); - if( wrapper!=null) - wrapper.service( realR, realR.getResponse()); + // Set the "included" flag so that things like header setting in the + // included servlet will be correctly ignored + boolean old_included=realResponse.isIncluded(); + if( ! old_included ) realResponse.setIncluded( true ); + + if( wr!=null) + wr.service( realRequest, realResponse); + + // Clean up the request and response as needed + if( ! old_included ) { + realResponse.setIncluded( false ); + } + + // Rethrow original error if present + if ( realResponse.isExceptionPresent() ) { + Exception ex = realResponse.getErrorException(); + if ( ex instanceof IOException ) + throw (IOException) ex; + else if ( ex instanceof ServletException ) + throw (ServletException) ex; + else + throw new ServletException + (sm.getString("dispatcher.includeException", ex)); + } } /** Named forward @@ -359,11 +410,35 @@ public void forwardNamed(ServletRequest request, ServletResponse response) throws ServletException, IOException { - Handler wrapper = context.getServletByName( name ); - Request realR=((HttpServletRequestFacade)request).getRealRequest(); + // We got here if name!=null, so assert it + Handler wr = context.getServletByName( name ); - if( wrapper!=null) - wrapper.service( realR, realR.getResponse()); + // Use the original request - as in specification ! + Request realRequest=((HttpServletRequestFacade)request).getRealRequest(); + Response realResponse = realRequest.getResponse(); + + // Set the "included" flag so that things like header setting in the + // included servlet will be correctly ignored + boolean old_included=realResponse.isIncluded(); + if( ! old_included ) realResponse.setIncluded( true ); + + if( wr!=null) + wr.service( realRequest, realResponse); + + // Clean up the request and response as needed + ; // No action required + + // Rethrow original error if present + if ( realResponse.isExceptionPresent() ) { + Exception ex = realResponse.getErrorException(); + if ( ex instanceof IOException ) + throw (IOException) ex; + else if ( ex instanceof ServletException ) + throw (ServletException) ex; + else + throw new ServletException + (sm.getString("dispatcher.forwardException", ex)); + } } /** 1.11 +119 -91 jakarta-tomcat/src/facade22/org/apache/tomcat/facade/ServletWrapper.java Index: ServletWrapper.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/facade22/org/apache/tomcat/facade/ServletWrapper.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- ServletWrapper.java 2000/11/06 15:07:40 1.10 +++ ServletWrapper.java 2000/12/05 14:02:37 1.11 @@ -90,10 +90,9 @@ // optional informations protected String description = null; - // If init() fails, this will keep the reason. - // init may be called when the servlet starts, but we need to - // report the error later, to the client - Exception unavailable=null; + // If init() fails, Handler.errorException will hold the reason. + // In the case of an UnavailableException, this field will hold + // the expiration time if UnavailableException is not permanent. long unavailableTime=-1; // Usefull info for class reloading @@ -180,6 +179,23 @@ servletClass=null; } + public Exception getErrorException() { + Exception ex = super.getErrorException(); + if ( ex != null ) { + if ( ex instanceof UnavailableException && + ! ((UnavailableException)ex).isPermanent()) { + // make updated UnavailableException, reporting a minimum of 1 second + int secs=1; + long moreWaitTime=unavailableTime - System.currentTimeMillis(); + if( moreWaitTime > 0 ) + secs = (int)((moreWaitTime + 999) / 1000); + ex = new UnavailableException(ex.getMessage(), secs); + } + } + return ex; + } + + public void reload() { if( initialized ) { try { @@ -308,20 +324,32 @@ public void init() throws Exception { - // make sure the servlet is loaded before calling preInit + // if initialized, then we were sync blocked when first init() succeeded + if( initialized ) return; + // if exception present, then we were sync blocked when first init() failed + // or an interceptor set an inital exeception + // in the latter case, preServletInit() and postServletInit() interceptors + // don't get called + if ( isExceptionPresent() ) return; + + // make sure the servlet is loaded before calling preInit // Jsp case - maybe another Jsp engine is used if( servlet==null && path != null && servletClassName == null) { log("Calling handleJspInit " + servletClassName); handleJspInit(); + } + + if( servlet==null ) { + // try to load servlet, save exception if one occurs + try { + loadServlet(); + } catch ( Exception ex ) { + // save init exception + setErrorException( ex ); + setExceptionPermanent( true ); + return; + } } - // Will throw exception if it can't load, let upper - // levels handle this - // try { - if( servlet==null ) loadServlet(); - // } catch( ClassNotFoundException ex ) { - // } catch( InstantiationException ex ) { - //} catch( IllegalStateException ex ) { - //} // Call pre, doInit and post BaseInterceptor cI[]=context.getContainer().getInterceptors(); @@ -334,9 +362,8 @@ } doInit(); - - // if an exception is thrown in init, no end interceptors will - // be called. that was in the origianl code J2EE used + + // doInit() catches exceptions, so post interceptors are always called for( int i=0; i< cI.length; i++ ) { try { cI[i].postServletInit( context, this ); @@ -349,32 +376,21 @@ protected void doInit() throws Exception { - // The servlet is loaded and not null - otherwise init() - // throws exception - if( initialized ) - return; + // ASSERT synchronized at higher level, initialized must be false try { - // if multiple threads will call the same servlet at once, - // we should have only one init - synchronized( this ) { - // we may have 2 threads entering doInit, - // the first one may init the servlet - if( initialized ) - return; - final Servlet sinstance = servlet; - final ServletConfig servletConfig = configF; - - // init - only if unavailable was null or - // unavailable period expired - servlet.init(servletConfig); - initialized=true; - } + final Servlet sinstance = servlet; + final ServletConfig servletConfig = configF; + servlet.init(servletConfig); + initialized=true; } catch( UnavailableException ex ) { - unavailable=ex; - unavailableTime=System.currentTimeMillis(); - unavailableTime += ex.getUnavailableSeconds() * 1000; + setServletUnavailable( ex ); + servlet=null; } catch( Exception ex ) { - unavailable=ex; + // other init exceptions are treated as permanent + // XXX need a context setting for unavailable time + setErrorException(ex); + setExceptionPermanent(true); + servlet=null; } } @@ -383,6 +399,7 @@ JspHandler */ public void service(Request req, Response res) + throws IOException, ServletException { // <servlet><jsp-file> case if( path!=null ) { @@ -395,23 +412,22 @@ req.setAttribute( "javax.servlet.include.servlet_path", path ); } - if( unavailable!=null ) { - // Don't load at all if permanently unavailable - if (((UnavailableException) unavailable).getUnavailableSeconds() == -1) { - initialized = false; - return; - } - - // Don't load if Unavailable timer is in place - if( stillUnavailable() ) { - handleUnavailable( req, res ); - initialized=false; - return; + // if servlet is not available + if ( isExceptionPresent() ) { + // get if error has expired + checkErrorExpired( req, res ); + // if we have an error on this request + if ( req.isExceptionPresent()) { + // if in included, defer handling to higher level + if ( res.isIncluded() ) + return; + // otherwise handle error + contextM.handleError( req, res, getErrorException()); } - unavailable=null;// timer expired + context.log(getServletName() + " unavailable time expired, trying again "); } - // called only if unavailable==null or timer expired. + // we reach here of there is no error or the exception has expired // will do an init super.service( req, res ); } @@ -438,7 +454,25 @@ res.setFacade( resF ); } - doService( reqF, resF ); + try { + doService( reqF, resF ); + } catch ( Exception ex ) { + if ( ex instanceof UnavailableException ) { + // if error not set + if ( ! isExceptionPresent() ) { + synchronized(this) { + if ( ! isExceptionPresent() ) { + setServletUnavailable( (UnavailableException)ex ); + // XXX if the UnavailableException is permanent we are supposed + // to destroy the servlet. Synchronization of this destruction + // needs review before adding this. + } + } + } + } + // save error state on request and response + saveError( req, res, ex ); + } } protected void doService(HttpServletRequest req, HttpServletResponse res) @@ -466,50 +500,44 @@ ServletWrapper jspServletW = (ServletWrapper)context.getServletByName("jsp"); servletClassName = jspServletW.getServletClass(); } - - // -------------------- Unavailable -------------------- - /** Check if we can try again an init - */ - private boolean stillUnavailable() { - // we have a timer - maybe we can try again - how much - // do we have to wait - (in mSec) - long moreWaitTime=unavailableTime - System.currentTimeMillis(); - if( unavailableTime > 0 && ( moreWaitTime < 0 )) { - // we can try again - unavailable=null; - unavailableTime=-1; - log(getServletName() + " unavailable time expired," + - " try again "); - return false; + // -------------------- Utility methods -------------------- + + private void setServletUnavailable( UnavailableException ex ) { + // servlet exception state + setErrorException( ex ); + if ( ex.isPermanent() ) { + setExceptionPermanent( true ); } else { - return true; + setExceptionPermanent( false ); + unavailableTime=System.currentTimeMillis(); + unavailableTime += ex.getUnavailableSeconds() * 1000; } } - - /** Send 503. Should be moved in ErrorHandling + + /** Check if error exception is present and if so, has the error + expired. Sets error on request and response if un-expired + error found. */ - private void handleUnavailable( Request req, Response res ) { - if( unavailable instanceof UnavailableException ) { - int unavailableTime = ((UnavailableException)unavailable). - getUnavailableSeconds(); - if( unavailableTime > 0 ) { - res.setHeader("Retry-After", - Integer.toString(unavailableTime)); + + private void checkErrorExpired( Request req, Response res ) { + // synchronize so another request can't expire the error + synchronized(this) { + // if error still present + if ( isExceptionPresent() ) { + // if permanent exception or timer not expired + if ( isExceptionPermanent() || + (unavailableTime - System.currentTimeMillis()) > 0) { + // save error state on request and response + saveError( req, res, getErrorException() ); + } else { + // we can try again + setErrorException(null); + setExceptionPermanent(false); + unavailableTime=-1; + } } } - - String msg=unavailable.getMessage(); - long moreWaitTime=unavailableTime - System.currentTimeMillis(); - log( "Error in " + getServletName() + - "init(), error happened at " + - unavailableTime + " wait " + moreWaitTime + - " : " + msg, unavailable); - req.setAttribute("javax.servlet.error.message", msg ); - res.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); // 503 - contextM.handleStatus( req, res, - HttpServletResponse.SC_SERVICE_UNAVAILABLE ); - return; } } 1.12 +23 -4 jakarta-tomcat/src/facade22/org/apache/tomcat/modules/facade22/JspInterceptor.java Index: JspInterceptor.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/facade22/org/apache/tomcat/modules/facade22/JspInterceptor.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- JspInterceptor.java 2000/12/03 01:51:27 1.11 +++ JspInterceptor.java 2000/12/05 14:02:38 1.12 @@ -221,6 +221,11 @@ } wrapper.setServletClass( classN ); wrapper.setNote( jspInfoNOTE, jspInfo ); + // set initial exception on the servlet if one is present + if ( jspInfo.isExceptionPresent() ) { + wrapper.setErrorException(jspInfo.getCompileException()); + wrapper.setExceptionPermanent(true); + } } catch( TomcatException ex ) { log("mapJspPage: request=" + req + ", jspInfo=" + jspInfo + ", servletName=" + servletName + ", classN=" + classN, ex); return ; @@ -263,6 +268,7 @@ if(debug>0)log( "Compiled to " + jspInfo.realClassPath ); } catch( Exception ex ) { log("compile: req="+req+", jspInfo=" + jspInfo, ex); + jspInfo.setCompileException(ex); } } @@ -386,6 +392,8 @@ File jspSource; // used to avoid File allocation for lastModified long compileTime;// tstamp of last compile attemp + Exception compileException; // translation/compile exception + JspInfo( Request req ) { init( req ); } @@ -465,6 +473,7 @@ */ void init(Request req ) { this.request = req; + compileException = null; // String includeUri // = (String) req.getAttribute(Constants.INC_SERVLET_PATH); uri=req.getServletPath(); @@ -516,6 +525,7 @@ } else { version=0; updateVersionedPaths(); + compileTime=0; } if( debug>0 ) @@ -576,11 +586,20 @@ void updateCompileTime() { File f=new File( realClassPath ); compileTime=0; - if( ! f.exists() ) { - f=new File( javaFilePath ); - if ( ! f.exists() ) return; - } + if ( ! f.exists() ) return; compileTime=f.lastModified(); + } + + void setCompileException(Exception ex) { + compileException = ex; + } + + Exception getCompileException() { + return compileException; + } + + boolean isExceptionPresent() { + return ( compileException != null ); } void log(String s) { 1.8 +15 -2 jakarta-tomcat/src/share/org/apache/tomcat/context/ErrorHandler.java Index: ErrorHandler.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/context/ErrorHandler.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- ErrorHandler.java 2000/12/03 08:19:02 1.7 +++ ErrorHandler.java 2000/12/05 14:02:39 1.8 @@ -67,6 +67,7 @@ import java.net.*; import java.util.*; import java.security.*; +import javax.servlet.*; import org.apache.tomcat.util.log.*; @@ -179,7 +180,13 @@ if( debug>0 ) ctx.log( "Handler " + errorServlet + " " + errorPath); - errorServlet.service( req, res ); + try { + errorServlet.service( req, res ); + } catch( IOException e ) { + ; // ASSERT: Only thrown by included servlets + } catch( ServletException e) { + ; // ASSERT: Only thrown by included servlets + } } // XXX XXX Security - we should log the message, but nothing @@ -270,7 +277,13 @@ if( debug>0 ) ctx.log( "Handler " + errorServlet + " " + errorPath); - errorServlet.service( req, res ); + try { + errorServlet.service( req, res ); + } catch( IOException e ) { + ; // ASSERT: Only thrown by included servlets + } catch( ServletException e) { + ; // ASSERT: Only thrown by included servlets + } } public final Handler getHandlerForPath( ContextManager cm, 1.22 +101 -24 jakarta-tomcat/src/share/org/apache/tomcat/core/Handler.java Index: Handler.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Handler.java,v retrieving revision 1.21 retrieving revision 1.22 diff -u -r1.21 -r1.22 --- Handler.java 2000/11/06 15:16:30 1.21 +++ Handler.java 2000/12/05 14:02:40 1.22 @@ -62,6 +62,7 @@ import java.io.*; import java.net.*; import java.util.*; +import javax.servlet.*; /** * The class that will generate the actual response. @@ -129,6 +130,9 @@ protected int loadOnStartup=-1; protected boolean loadingOnStartup=false; + + protected Exception errorException=null; + protected boolean exceptionPermanent=false; // Debug protected int debug=0; @@ -224,6 +228,34 @@ return loadingOnStartup; } + /** Sets an exception that relates to the ability of the + servlet to execute. An exception may be set by an + interceptor if there is an error during the creation + of the servlet. + */ + public void setErrorException(Exception ex) { + errorException = ex; + } + + /** Gets the exception that relates to the servlet's + ability to execute. + */ + public Exception getErrorException() { + return errorException; + } + + public boolean isExceptionPresent() { + return ( errorException != null ); + } + + public void setExceptionPermanent( boolean permanent ) { + exceptionPermanent = permanent; + } + + public boolean isExceptionPermanent() { + return exceptionPermanent; + } + // -------------------- Jsp specific code public String getPath() { @@ -264,13 +296,17 @@ /** Destroy a handler, and notify all the interested interceptors */ public void destroy() throws Exception { - if ( ! initialized ) return;// already destroyed or not init. + if ( ! initialized ) { + errorException = null; + return;// already destroyed or not init. + } initialized=false; // XXX post will not be called if any error happens in destroy. // That's how tomcat worked before - I think it's a bug ! doDestroy(); + errorException=null; } @@ -279,63 +315,93 @@ public void init() throws Exception { + // if initialized, then we were sync blocked when first init() succeeded + if( initialized ) return; + // if exception present, then we were sync blocked when first init() failed + // or an interceptor set an inital exeception + if (errorException != null) throw errorException; try { - if( initialized ) return; doInit(); - return; } catch( Exception ex ) { - initialized=false; + // save error, assume permanent + setErrorException(ex); + setExceptionPermanent(true); } } /** Call the service method, and notify all listeners + * + * @exception IOException if an input/output error occurs and we are + * processing an included servlet (otherwise it is swallowed and + * handled by the top level error handler mechanism) + * @exception ServletException if a servlet throws an exception and + * we are processing an included servlet (otherwise it is swallowed + * and handled by the top level error handler mechanism) */ - public void service(Request req, Response res) + public void service(Request req, Response res) + throws IOException, ServletException { if( ! initialized ) { - try { - synchronized( this ) { - // we may be initialized when we enter the sync block - if( ! initialized ) + Exception ex=null; + synchronized( this ) { + // we may be initialized when we enter the sync block + if( ! initialized ) { + try { init(); + } catch ( Exception e ) { + errorException = e; + exceptionPermanent = true; + } } - } catch( Exception ex ) { - initialized=false; - if( ex instanceof ClassNotFoundException ) { - contextM.handleStatus( req, res, 404); - return; - } + // get copy of exception, if any, before leaving sync lock + ex=errorException; + } + // if error occurred + if ( ex != null ) { + // save error state on request and response + saveError( req, res, ex ); context.log("Exception in init " + ex.getMessage(), ex ); - contextM.handleError( req, res, ex ); + // if in included, defer handling to higher level + if (res.isIncluded()) return; + // handle init error since at top level + if( ex instanceof ClassNotFoundException ) + contextM.handleStatus( req, res, 404 ); + else + contextM.handleError( req, res, ex ); return; } } - BaseInterceptor reqI[]= - req.getContainer().getInterceptors(Container.H_postService); - if( ! internal ) { + BaseInterceptor reqI[]= + req.getContainer().getInterceptors(Container.H_preService); for( int i=0; i< reqI.length; i++ ) { reqI[i].preService( req, res ); } } - Throwable t=null; try { doService( req, res ); - } catch( Throwable t1 ) { - t=t1; + } catch( Exception ex ) { + // save error state on request and response + saveError( req, res, ex ); } // continue with the postService if( ! internal ) { + BaseInterceptor reqI[]= + req.getContainer().getInterceptors(Container.H_postService); for( int i=0; i< reqI.length; i++ ) { reqI[i].postService( req, res ); } } - if( t==null ) return; - contextM.handleError( req, res, t ); + // if no error + if( ! res.isExceptionPresent() ) return; + // if in included, defer handling to higher level + if (res.isIncluded()) return; + // handle original error since at top level + contextM.handleError( req, res, res.getErrorException() ); } // -------------------- Abstract methods -------------------- @@ -385,6 +451,17 @@ context.log(s, t); } + // --------------- Error Handling ---------------- + + public final void saveError( Request req, Response res, Exception ex ) { + // save current exception on the request + req.setErrorException( ex ); + // if the first exception, save info on the response + if ( ! res.isExceptionPresent() ) { + res.setErrorException( ex ); + res.setErrorURI( (String)req.getAttribute("javax.servlet.include.request_uri") ); + } + } // -------------------- Notes -------------------- public final void setNote( int pos, Object value ) { 1.79 +24 -0 jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java Index: Request.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java,v retrieving revision 1.78 retrieving revision 1.79 diff -u -r1.78 -r1.79 --- Request.java 2000/12/01 19:59:34 1.78 +++ Request.java 2000/12/05 14:02:40 1.79 @@ -186,6 +186,9 @@ Request parent; Request child; + // Error handling support + Exception errorException; + private Object notes[]=new Object[ContextManager.MAX_NOTES]; // Accounting private Counters cntr=new Counters(ACCOUNTS); @@ -817,6 +820,26 @@ //return ((int)b[0]) & 0x000000FF; } + // ----------------- Error State ----------------- + + /** Set most recent exception that occurred while handling + this request. + */ + public void setErrorException( Exception ex ) { + errorException = ex; + } + + /** Get most recent exception that occurred while handling + this request. + */ + public Exception getErrorException() { + return errorException; + } + + public boolean isExceptionPresent() { + return ( errorException != null ); + } + // -------------------- debug -------------------- public String toString() { @@ -863,6 +886,7 @@ didReadFormData = false; container=null; handler=null; + errorException=null; jvmRoute = null; headers.clear(); // XXX use recycle pattern serverNameMB.recycle(); 1.43 +41 -0 jakarta-tomcat/src/share/org/apache/tomcat/core/Response.java Index: Response.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Response.java,v retrieving revision 1.42 retrieving revision 1.43 diff -u -r1.42 -r1.43 --- Response.java 2000/11/06 18:17:26 1.42 +++ Response.java 2000/12/05 14:02:41 1.43 @@ -105,6 +105,12 @@ protected boolean usingWriter = false; protected boolean included=false; + // holds request error exception + // set this just once during request processing + Exception errorException=null; + // holds request error URI + String errorURI=null; + // protected String contentType = DEFAULT_CONTENT_TYPE; protected String contentLanguage = null; @@ -200,6 +206,39 @@ this.commited=v; } + // -----------------Error State -------------------- + + /** Set the error Exception that occurred during + request processing. + */ + public void setErrorException(Exception ex) { + errorException = ex; + } + + /** Get the Exception that occurred during request + processing. + */ + public Exception getErrorException() { + return errorException; + } + + public boolean isExceptionPresent() { + return ( errorException != null ); + } + + /** Set request URI that caused an error during + request processing. + */ + public void setErrorURI(String uri) { + errorURI = uri; + } + + /** Get the request URI that caused the original error. + */ + public String getErrorURI() { + return errorURI; + } + // -------------------- Methods -------------------- @@ -445,6 +484,8 @@ usingStream = false; commited = false; included=false; + errorException=null; + errorURI=null; if ( oBuffer != null ) oBuffer.recycle(); headers.clear(); }