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();
       }
  
  
  

Reply via email to