tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Alex Cruikshank <a...@epitonic.com>
Subject [PATCH] JspWriter "... already been flushed" Error if page throws exception after 8192 bytes
Date Fri, 10 Dec 1999 21:03:35 GMT
PROBLEM:
If an Exception is thrown from a Jsp page after 8192 characters have been 
written to "out",  the user will get a Servlet Exception wrapped around an 
IOException that says "Error: Attempt to clear a buffer that's already been 
flushed".  The user never sees the exception thrown within the JspPage.  If 
8191 characters or less have been written, the exception is processed 
correctly.

STEPS TO REPRODUCE:
Run the jsp attached file.  If you remove the last "1" in the last 
out.print() statement (or any other number) it works as expected.  There 
are exactly 8192 numbers that get printed to the writer.

CAUSE:
When an exception is thrown within a Jsp page, the JspWriter's clear() 
method is called before the exception is handled.  The clear() method in 
JspWriterImpl checks to see if the "flushed" variable is true, and throws 
an exception if it has (I'm not sure why).  The flushed variable is set to 
true when the JspWriterImpl's internal buffer is filled and must be written 
to the underlying stream.  The internal buffer defaults to 8192 
characters.  The flushed variable is never reset (so any calls to clear() 
after the buffer has been flushed are guaranteed to throw an 
IOException).  The flushed variable seems to be there to prevent 
concurrency problems (I don't know why a JspWriter would be accessed by 
more than one thread, but the flushBuffer() method is synchronized to make 
it thread safe).  The fact that flushed variable will never be reset to 
false, is clearly a bug.

SOLUTIONS:
On the assumption that the flushed variable in 
org.apache.jasper.commJspWriterImpl is a flag to prevent clear() from being 
called by anther thread while the flushBuffer() method is running (since 
these are the only methods that access flushed), I modified flushBuffer() 
to reset flushed once it's done.  It seems to solve the problem with no ill 
side effects:

change:
     /**
      * Flush the output buffer to the underlying character stream, without
      * flushing the stream itself.  This method is non-private only so that it
      * may be invoked by PrintStream.
      */
     protected final void flushBuffer() throws IOException {
         synchronized (lock) {
             if (bufferSize == 0)
                 return;
             flushed = true;
             ensureOpen();
             if (nextChar == 0)
                 return;
             initOut();
             out.write(cb, 0, nextChar);
             nextChar = 0;
         }
     }

to:
     /**
      * Flush the output buffer to the underlying character stream, without
      * flushing the stream itself.  This method is non-private only so that it
      * may be invoked by PrintStream.
      */
     protected final void flushBuffer() throws IOException {
         synchronized (lock) {
             if (bufferSize == 0)
                 return;
             flushed = true;
             ensureOpen();
             if (nextChar == 0)
             {
                 flushed = false;
                 return;
             }
             initOut();
             out.write(cb, 0, nextChar);
             nextChar = 0;
             flushed = false;
         }
     }


Mime
View raw message