[ Team LiB ] Previous Section Next Section

17.1 Buffering

There's one important thing about how a JSP page is processed that has not been covered in any example so far: buffering of the response body. As you may recall from Chapter 2, an HTTP response message contains both headers and a body. The headers tell the browser such things as what type of data the body contains (HTML text, an image), the size of the body, if the body can be cached, and so forth. Headers are also used to set cookies and to tell the browser to automatically get another page (a redirect). All response headers must be sent to the browser before the body is sent.

As soon as a JSP page writes something to the body of the message, the JSP container may start sending the response to the browser. It's then too late to set headers, since they have to be sent first. In a servlet, you have full control over when something is written to the response body, so you can make sure that you set all headers you need before you generate the body. In a JSP page, however, it's not that easy. Everything you put in a JSP page that is not a JSP element is written to the response body automatically by the JSP container. Here's the top part of the autheticate.jsp page from Chapter 13:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="ora" uri="orataglib" %>
  
<%-- Remove the validUser session bean, if any --%>
<c:remove var="validUser" />
...

It doesn't contain any HTML, so you may think that this doesn't add anything to the response body. But it does. This page snippet contains six lines: five lines with JSP elements and one blank line. The JSP elements themselves are evaluated by the JSP container and never show up in the response, but the linefeed character at the end of each line is not a JSP element, so it's added to the response body.

Later in the same page, custom actions are used to set cookies, or in other words, set response headers:

<c:choose>
  <c:when test="${!empty param.remember}">
    <ora:addCookie name="userName" 
      value="${param.userName}"
      maxAge="2592000" />
    <ora:addCookie name="password" 
      value="${param.password}"
      maxAge="2592000" />
  </c:when>
  <c:otherwise>
    <ora:addCookie name="userName" 
      value="${param.userName}"
      maxAge="0" />
    <ora:addCookie name="password" 
      value="$param.password}"
      maxAge="0" />
  </c:otherwise>
</c:choose>

This doesn't work if the linefeed characters added to the body have caused the response to be sent to the browser (the response has been committed, as it's called in the servlet specification). Besides not being able to set headers after the response has been committed, the servlet specification also prohibits a request being forwarded when data has already been written to the response body. This is because when you forward to another JSP page or servlet, the forwarding target should have full control over the request. If the originating page has already started to generate the response body, the target is no longer in charge.

Buffering solves this problem. Instead of sending the response to the browser as soon as something is written to the response body, the JSP container writes everything that's not a JSP element and all dynamic content generated by JSP elements to a buffer. At some point, such as when the buffer is full or the end of the page is reached, the container sends all headers that have been set, followed by the buffered body content. So in this example, all linefeed characters end up in the buffer, and the cookie headers are set. When the whole page has been processed, the JSP container sends all headers first and then the contents of the buffer. Works like a charm.

You can control the size of the buffer and what to do when the buffer is full with two page directive attributes:

<%@ page buffer="12kb" autoFlush="false" %>

The buffer attribute accepts a value that specifies the minimum size of the buffer; the container may choose to use a bigger buffer than specified. The value must be the number of kilobytes followed by kb. A buffer that holds at least 8 KB is used by default. The keyword none is also accepted. If you use this keyword, the JSP container will not perform any buffering of the response body.

The autoFlush attribute can be set to true or false, with true being the default. It specifies what to do when the buffer is full. If the value is true, the buffered content is sent (flushed) to the browser when the buffer is full, and the rest of the page gets buffered until the buffer is full again. If you specify the value false, the JSP container throws an exception when the buffer is full, ending the processing of the page.

In most cases, you want to use the default values. If you have an extremely large page in which you set headers at the end of the page, you may need to increase the buffer size. Eight kilobytes, however, is enough for most pages. Disabling buffering may make sense if you have a page that generates the result slowly, and you want to send what's ready to the browser as soon as possible. But even if you disable the JSP buffering, the servlet container may still do some buffering of the result, so there's no guarantee that it will be sent immediately. No matter what value you use for the buffer attribute, however, you can force the buffer to be flushed with a scriptlet like this:

<% out.flush(  ); %>

Setting the autoFlush attribute to false is rare. A possible use for this is if you have no control over the size of the dynamic content you generate, and you want to make sure the processing is aborted if you reach a certain limit.

    [ Team LiB ] Previous Section Next Section