Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » filter » [javadoc | source]
    1   /* This software is published under the terms of the OpenSymphony Software
    2    * License version 1.1, of which a copy has been included with this
    3    * distribution in the LICENSE.txt file. */
    4   package com.opensymphony.module.sitemesh.filter;
    5   
    6   import com.opensymphony.module.sitemesh.Factory;
    7   import com.opensymphony.module.sitemesh.Page;
    8   
    9   import javax.servlet.ServletOutputStream;
   10   import javax.servlet.http.HttpServletResponse;
   11   import javax.servlet.http.HttpServletResponseWrapper;
   12   import java.io.IOException;
   13   import java.io.PrintWriter;
   14   
   15   /**
   16    * Implementation of HttpServletResponseWrapper that captures page data instead of
   17    * sending to the writer.
   18    *
   19    * <p>Should be used in filter-chains or when forwarding/including pages
   20    * using a RequestDispatcher.</p>
   21    *
   22    * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
   23    * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
   24    * @version $Revision: 1.11 $
   25    */
   26   public final class PageResponseWrapper extends HttpServletResponseWrapper {
   27   
   28       private final RoutablePrintWriter routablePrintWriter;
   29       private final RoutableServletOutputStream routableServletOutputStream;
   30       private final Factory factory;
   31   
   32       private Buffer buffer;
   33       private boolean aborted = false;
   34       private boolean parseablePage = false;
   35   
   36       public PageResponseWrapper(final HttpServletResponse response, Factory factory) {
   37           super(response);
   38           this.factory = factory;
   39   
   40           routablePrintWriter = new RoutablePrintWriter(new RoutablePrintWriter.DestinationFactory() {
   41               public PrintWriter activateDestination() throws IOException {
   42                   return response.getWriter();
   43               }
   44           });
   45           routableServletOutputStream = new RoutableServletOutputStream(new RoutableServletOutputStream.DestinationFactory() {
   46               public ServletOutputStream create() throws IOException {
   47                   return response.getOutputStream();
   48               }
   49           });
   50       }
   51   
   52       /**
   53        * Set the content-type of the request and store it so it can
   54        * be passed to the {@link com.opensymphony.module.sitemesh.PageParser}.
   55        */
   56       public void setContentType(String type) {
   57           super.setContentType(type);
   58   
   59           if (type != null) {
   60               // this is the content type + charset. eg: text/html;charset=UTF-8
   61               int offset = type.lastIndexOf("charset=");
   62               String encoding = null;
   63               if (offset != -1)
   64                  encoding = extractContentTypeValue(type, offset + 8);
   65               String contentType = extractContentTypeValue(type, 0);
   66   
   67               if (factory.shouldParsePage(contentType)) {
   68                   activateSiteMesh(contentType, encoding);
   69               }
   70           }
   71   
   72       }
   73   
   74       private void activateSiteMesh(String contentType, String encoding) {
   75           if (parseablePage) {
   76               return; // already activated
   77           }
   78           parseablePage = true;
   79           buffer = new Buffer(factory, contentType, encoding);
   80           routablePrintWriter.updateDestination(new RoutablePrintWriter.DestinationFactory() {
   81               public PrintWriter activateDestination() {
   82                   return buffer.getWriter();
   83               }
   84           });
   85           routableServletOutputStream.updateDestination(new RoutableServletOutputStream.DestinationFactory() {
   86               public ServletOutputStream create() {
   87                   return buffer.getOutputStream();
   88               }
   89           });
   90       }
   91   
   92       private String extractContentTypeValue(String type, int startIndex) {
   93           if (startIndex < 0)
   94               return null;
   95   
   96           // Skip over any leading spaces
   97           while (startIndex < type.length() && type.charAt(startIndex) == ' ')
   98               startIndex++;
   99   
  100           if (startIndex >= type.length())
  101               return null;
  102   
  103           int endIndex = startIndex;
  104   
  105           if (type.charAt(startIndex) == '"') {
  106               startIndex++;
  107               endIndex = type.indexOf('"', startIndex);
  108               if (endIndex == -1)
  109                   endIndex = type.length();
  110           } else {
  111               // Scan through until we hit either  the end of the string or a
  112               // special character (as defined in RFC-2045). Note that we ignore '/'
  113               // since we want to capture it as part of the value.
  114               char ch;
  115               while (endIndex < type.length() && (ch = type.charAt(endIndex)) != ' ' && ch != ';'
  116                     && ch != '(' && ch != ')' && ch != '[' && ch != ']' && ch != '<' && ch != '>'
  117                     && ch != ':' && ch != ',' && ch != '=' && ch != '?' && ch != '@' && ch!= '"'
  118                     && ch !='\\')
  119                  endIndex++;
  120           }
  121           return type.substring(startIndex, endIndex);
  122       }
  123   
  124       /** Prevent content-length being set if page is parseable. */
  125       public void setContentLength(int contentLength) {
  126           if (!parseablePage) super.setContentLength(contentLength);
  127       }
  128   
  129       /** Prevent content-length being set if page is parseable. */
  130       public void setHeader(String name, String value) {
  131           if (name.toLowerCase().equals("content-type")) { // ensure ContentType is always set through setContentType()
  132               setContentType(value);
  133           } else if (!parseablePage || !name.toLowerCase().equals("content-length")) {
  134               super.setHeader(name, value);
  135           }
  136       }
  137   
  138       /** Prevent content-length being set if page is parseable. */
  139       public void addHeader(String name, String value) {
  140           if (name.toLowerCase().equals("content-type")) { // ensure ContentType is always set through setContentType()
  141               setContentType(value);
  142           } else if (!parseablePage || !name.toLowerCase().equals("content-length")) {
  143               super.addHeader(name, value);
  144           }
  145       }
  146   
  147       /**
  148        * Prevent 'not modified' (304) HTTP status from being sent if page is parseable
  149        * (so web-server/browser doesn't cache contents).
  150        */
  151       public void setStatus(int sc) {
  152           if (!parseablePage || sc != HttpServletResponse.SC_NOT_MODIFIED) {
  153               super.setStatus(sc);
  154           }
  155       }
  156   
  157       public ServletOutputStream getOutputStream() {
  158           return routableServletOutputStream;
  159       }
  160   
  161       public PrintWriter getWriter() {
  162           return routablePrintWriter;
  163       }
  164   
  165       public Page getPage() throws IOException {
  166           if (aborted || !parseablePage) {
  167               return null;
  168           } else {
  169               return buffer.parse();
  170           }
  171       }
  172   
  173       public void sendError(int sc) throws IOException {
  174           aborted = true;
  175           super.sendError(sc);
  176       }
  177   
  178       public void sendError(int sc, String msg) throws IOException {
  179           aborted = true;
  180           super.sendError(sc, msg);
  181       }
  182   
  183       public void sendRedirect(String location) throws IOException {
  184           aborted = true;
  185           super.sendRedirect(location);
  186       }
  187   
  188       public boolean isUsingStream() {
  189           return buffer != null && buffer.isUsingStream();
  190       }
  191   }

Save This Page
Home » sitemesh-2.3 » com.opensymphony.module » sitemesh » filter » [javadoc | source]