Package com.oreilly.servlet

Source Code of com.oreilly.servlet.CacheServletOutputStream

// Copyright (C) 1999-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
// All rights reserved.  Use of this class is limited.
// Please see the LICENSE for more information.

package com.oreilly.servlet;

import java.io.*;
import java.util.*;

import javax.servlet.http.Cookie;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* <p>A superclass for HTTP servlets</p>
* Use it when the servlet must have its output
* cached and automatically resent as appropriate according to the
* servlet's getLastModified() method.  To take advantage of this class,
* a servlet must:<br>
* <ul>
* <li>Extend <tt>CacheHttpServlet</tt> instead of <tt>HttpServlet</tt>
* <li>Implement a <tt>getLastModified(HttpServletRequest)</tt> method as usual
* </ul>
* This class uses the value returned by <tt>getLastModified()</tt> to manage
* an internal cache of the servlet's output.  Before handling a request,
* this class checks the value of <tt>getLastModified()</tt>, and if the
* output cache is at least as current as the servlet's last modified time,
* the cached output is sent without calling the servlet's <tt>doGet()</tt>
* method.
* <p>
* In order to be safe, if this class detects that the servlet's query
* string, extra path info, or servlet path has changed, the cache is
* invalidated and recreated.  However, this class does not invalidate
* the cache based on differing request headers or cookies; for
* servlets that vary their output based on these values (i.e. a session
* tracking servlet) this class should probably not be used.
* <p>
* No caching is performed for POST requests.
* <p>
* <tt>CacheHttpServletResponse</tt> and <tt>CacheServletOutputStream</tt>
* are helper classes to this class and should not be used directly.
* <p>
* This class has been built against Servlet API 2.2.  Using it with previous
* Servlet API versions should work; using it with future API versions likely
* won't work.
* <p>
* If you get the error Cannot resolve symbol method getContentType()
* Then include library /lib/compile/servlet.jar when compiling the project.
*
* @author <b>Jason Hunter</b>, Copyright &#169; 1999
* @version 0.92, 00/03/16, added synchronization blocks to make thread safe
* @version 0.91, 99/12/28, made support classes package protected
* @version 0.90, 99/12/19
*/

public abstract class CacheHttpServlet extends HttpServlet {

  // ---------------------------------------------------------------------------

  CacheHttpServletResponse cacheResponse;
  long cacheLastMod = -1;
  String cacheQueryString = null;
  String cachePathInfo = null;
  String cacheServletPath = null;
  Object lock = new Object();

  // ---------------------------------------------------------------------------

  protected void service(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    // Only do caching for GET requests
    String method = req.getMethod();
    if (!method.equals("GET")) {
      super.service(req, res);
      return;
    }

    // Check the last modified time for this servlet
    long servletLastMod = getLastModified(req);

    // A last modified of -1 means we shouldn't use any cache logic
    if (servletLastMod == -1) {
      super.service(req, res);
      return;
    }

    // If the client sent an If-Modified-Since header equal or after the
    // servlet's last modified time, send a short "Not Modified" status code
    // Round down to the nearest second since client headers are in seconds
    if ((servletLastMod / 1000 * 1000) <=
             req.getDateHeader("If-Modified-Since")) {
      res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      return;
    }

    // Use the existing cache if it's current and valid
    CacheHttpServletResponse localResponseCopy = null;
    synchronized (lock) {
      if (servletLastMod <= cacheLastMod &&
               cacheResponse.isValid() &&
               equal(cacheQueryString, req.getQueryString()) &&
               equal(cachePathInfo, req.getPathInfo()) &&
               equal(cacheServletPath, req.getServletPath())) {
        localResponseCopy = cacheResponse;
      }
    }
    if (localResponseCopy != null) {
      localResponseCopy.writeTo(res);
      return;
    }

    // Otherwise make a new cache to capture the response
    localResponseCopy = new CacheHttpServletResponse(res);
    super.service(req, localResponseCopy);
    synchronized (lock) {
      cacheResponse = localResponseCopy;
      cacheLastMod = servletLastMod;
      cacheQueryString = req.getQueryString();
      cachePathInfo = req.getPathInfo();
      cacheServletPath = req.getServletPath();
    }
  }

  // ---------------------------------------------------------------------------

  private static boolean equal(String s1, String s2) {
    if (s1 == null && s2 == null) {
      return true;
    }
    else if (s1 == null || s2 == null) {
      return false;
    }
    else {
      return s1.equals(s2);
    }
  }
}

// -----------------------------------------------------------------------------

class CacheHttpServletResponse implements HttpServletResponse {
  // Store key response variables so they can be set later
  private int status;
  private Hashtable headers;
  private int contentLength;
  private String contentType;
  private Locale locale;
  private Vector cookies;
  private boolean didError;
  private boolean didRedirect;
  private boolean gotStream;
  private boolean gotWriter;

  private HttpServletResponse delegate;
  private CacheServletOutputStream out;
  private PrintWriter writer;

  // ---------------------------------------------------------------------------

  CacheHttpServletResponse(HttpServletResponse res) {
    delegate = res;
    try {
      out = new CacheServletOutputStream(res.getOutputStream());
    }
    catch (IOException e) {
      System.out.println(
        "Got IOException constructing cached response: " + e.getMessage());
    }
    internalReset();
  }

  // ---------------------------------------------------------------------------

  private void internalReset() {
    status = 200;
    headers = new Hashtable();
    contentLength = -1;
    contentType = null;
    locale = null;
    cookies = new Vector();
    didError = false;
    didRedirect = false;
    gotStream = false;
    gotWriter = false;
    out.getBuffer().reset();
  }

  public boolean isValid() {
    // We don't cache error pages or redirects
    return (!didError) && (!didRedirect);
  }

  // ---------------------------------------------------------------------------

  private void internalSetHeader(String name, Object value) {
    Vector v = new Vector();
    v.addElement(value);
    headers.put(name, v);
  }

  // ---------------------------------------------------------------------------

  private void internalAddHeader(String name, Object value) {
    Vector v = (Vector) headers.get(name);
    if (v == null) {
      v = new Vector();
    }
    v.addElement(value);
    headers.put(name, v);
  }

  // ---------------------------------------------------------------------------

  public void writeTo(HttpServletResponse res) {
    // Write status code
    res.setStatus(status);
    // Write convenience headers
    if (contentType != null) res.setContentType(contentType);
    if (locale != null) res.setLocale(locale);
    // Write cookies
    Enumeration cookieenum = cookies.elements();
    while (cookieenum.hasMoreElements()) {
      Cookie c = (Cookie) cookieenum.nextElement();
      res.addCookie(c);
    }
    // Write standard headers
    Enumeration headerenum = headers.keys();
    while (headerenum.hasMoreElements()) {
      String name = (String) headerenum.nextElement();
      Vector values = (Vector) headers.get(name); // may have multiple values
      Enumeration enum2 = values.elements();
      while (enum2.hasMoreElements()) {
        Object value = enum2.nextElement();
        if (value instanceof String) {
          res.setHeader(name, (String)value);
        }
        if (value instanceof Integer) {
          res.setIntHeader(name, ((Integer)value).intValue());
        }
        if (value instanceof Long) {
          res.setDateHeader(name, ((Long)value).longValue());
        }
      }
    }
    // Write content length
    res.setContentLength(out.getBuffer().size());
    // Write body
    try {
      out.getBuffer().writeTo(res.getOutputStream());
    }
    catch (IOException e) {
      System.out.println(
        "Got IOException writing cached response: " + e.getMessage());
    }
  }

  // ---------------------------------------------------------------------------

  public String getContentType() {
    return delegate.getContentType();
  }

  // ---------------------------------------------------------------------------

  public ServletOutputStream getOutputStream()
    throws IOException,IllegalStateException {
    if (gotWriter) {
      throw new IllegalStateException(
        "Cannot get output stream after getting writer");
    }
    gotStream = true;
    return out;
  }

  // ---------------------------------------------------------------------------

  public PrintWriter getWriter()
    throws UnsupportedEncodingException,IllegalStateException {
    if (gotStream) {
      throw new IllegalStateException(
        "Cannot get writer after getting output stream");
    }
    gotWriter = true;
    if (writer == null) {
      OutputStreamWriter w =
        new OutputStreamWriter(out, getCharacterEncoding());
      writer = new PrintWriter(w, true)// autoflush is necessary
    }
    return writer;
  }

  // ---------------------------------------------------------------------------

  public void setContentLength(int len) {
    delegate.setContentLength(len);
    // No need to save the length; we can calculate it later
  }

  // ---------------------------------------------------------------------------

  public void setContentType(String type) {
    delegate.setContentType(type);
    contentType = type;
  }

  // ---------------------------------------------------------------------------

  public String getCharacterEncoding() {
    return delegate.getCharacterEncoding();
  }

  // ---------------------------------------------------------------------------

  public void setBufferSize(int size) throws IllegalStateException {
    delegate.setBufferSize(size);
  }

  // ---------------------------------------------------------------------------

  public int getBufferSize() {
    return delegate.getBufferSize();
  }

  // ---------------------------------------------------------------------------

  public void resetBuffer() throws IllegalStateException {
    delegate.reset();
    internalReset();
  }

  // ---------------------------------------------------------------------------

  public void reset() throws IllegalStateException {
    delegate.reset();
    internalReset();
  }

  // ---------------------------------------------------------------------------

  /*
  public void resetBuffer() throws IllegalStateException {
    delegate.resetBuffer();
    contentLength = -1;
    out.getBuffer().reset();
  }
*/

// ---------------------------------------------------------------------------

  public boolean isCommitted() {
    return delegate.isCommitted();
  }

  // ---------------------------------------------------------------------------

  public void flushBuffer() throws IOException {
    delegate.flushBuffer();
  }

  // ---------------------------------------------------------------------------

  public void setLocale(Locale loc) {
    delegate.setLocale(loc);
    locale = loc;
  }

  // ---------------------------------------------------------------------------

  public Locale getLocale() {
    return delegate.getLocale();
  }

  // ---------------------------------------------------------------------------

  public void addCookie(Cookie cookie) {
    delegate.addCookie(cookie);
    cookies.addElement(cookie);
  }

  // ---------------------------------------------------------------------------

  public boolean containsHeader(String name) {
    return delegate.containsHeader(name);
  }

  // ---------------------------------------------------------------------------

  public void setCharacterEncoding(String enc) {
    delegate.setCharacterEncoding(enc);
  }

  // ---------------------------------------------------------------------------

  /** @deprecated */
  public void setStatus(int sc, String sm) {
    delegate.setStatus(sc, sm);
    status = sc;
  }

  // ---------------------------------------------------------------------------

  public void setStatus(int sc) {
    delegate.setStatus(sc);
    status = sc;
  }

  // ---------------------------------------------------------------------------

  public void setHeader(String name, String value) {
    delegate.setHeader(name, value);
    internalSetHeader(name, value);
  }

  // ---------------------------------------------------------------------------

  public void setIntHeader(String name, int value) {
    delegate.setIntHeader(name, value);
    internalSetHeader(name, new Integer(value));
  }

  // ---------------------------------------------------------------------------

  public void setDateHeader(String name, long date) {
    delegate.setDateHeader(name, date);
    internalSetHeader(name, new Long(date));
  }

  // ---------------------------------------------------------------------------

  public void sendError(int sc, String msg) throws IOException {
    delegate.sendError(sc, msg);
    didError = true;
  }

  // ---------------------------------------------------------------------------

  public void sendError(int sc) throws IOException {
    delegate.sendError(sc);
    didError = true;
  }

  // ---------------------------------------------------------------------------

  public void sendRedirect(String location) throws IOException {
    delegate.sendRedirect(location);
    didRedirect = true;
  }

  // ---------------------------------------------------------------------------

  public String encodeURL(String url) {
    return delegate.encodeURL(url);
  }

  // ---------------------------------------------------------------------------

  public String encodeRedirectURL(String url) {
    return delegate.encodeRedirectURL(url);
  }

  // ---------------------------------------------------------------------------

  public void addHeader(String name, String value) {
    internalAddHeader(name, value);
  }

  // ---------------------------------------------------------------------------

  public void addIntHeader(String name, int value) {
    internalAddHeader(name, new Integer(value));
  }

  // ---------------------------------------------------------------------------

  public void addDateHeader(String name, long value) {
    internalAddHeader(name, new Long(value));
  }

  // ---------------------------------------------------------------------------

  /** @deprecated */
  public String encodeUrl(String url) {
    return this.encodeURL(url);
  }

  // ---------------------------------------------------------------------------

  /** @deprecated */
  public String encodeRedirectUrl(String url) {
    return this.encodeRedirectURL(url);
  }
}

// -----------------------------------------------------------------------------

class CacheServletOutputStream extends ServletOutputStream {

  ServletOutputStream dlgate;
  ByteArrayOutputStream cache;

  CacheServletOutputStream(ServletOutputStream out) {
    dlgate = out;
    cache = new ByteArrayOutputStream(4096);
  }

  public ByteArrayOutputStream getBuffer() {
    return cache;
  }

  public void write(int b) throws IOException {
    dlgate.write(b);
    cache.write(b);
  }

  public void write(byte b[]) throws IOException {
    dlgate.write(b);
    cache.write(b);
  }

  public void write(byte buf[], int offset, int len) throws IOException {
    dlgate.write(buf, offset, len);
    cache.write(buf, offset, len);
  }
} // CacheServletOutputStream
TOP

Related Classes of com.oreilly.servlet.CacheServletOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.