AcceleratorFilter implementiert
[percentcodec] / cachecontrol / src / main / java / de / halbekunst / juplo / cachecontrol / AcceleratorFilter.java
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java
new file mode 100644 (file)
index 0000000..82c4082
--- /dev/null
@@ -0,0 +1,267 @@
+package de.halbekunst.juplo.cachecontrol;
+
+import de.halbekunst.juplo.cachecontrol.CacheControl.CacheMethodHandle;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+
+
+
+/**
+ *
+ * @author kai
+ */
+@Configurable
+public class AcceleratorFilter implements Filter {
+  private final static Logger log = LoggerFactory.getLogger(AcceleratorFilter.class);
+
+  public final static String REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
+
+
+  @Autowired CacheControl cacheControl;
+  @Autowired(required=true) Integer buffer;
+  @Autowired(required=true) String eTag;
+  @Autowired(required=true) Boolean weak;
+  @Autowired(required=true) Long lastModified;
+  @Autowired(required=true) Integer cacheSeconds;
+
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    if (!(request instanceof HttpServletRequest)) {
+      log.error("AcceleratorFilter can only handle HTTP-requests");
+      chain.doFilter(request, response);
+      return;
+    }
+
+    /** Prüfen, ob es sich um eine Anfrage für einen JSP-Include handelt */
+    if (request.getAttribute(REQUEST_URI_ATTRIBUTE) != null) {
+      log.debug("Includes cannot be accelerated");
+      chain.doFilter(request, response);
+      return;
+    }
+
+    AccelerationWrapper wrapper = new AccelerationWrapper((HttpServletRequest)request, (HttpServletResponse)response);
+    cacheControl.init(wrapper);
+    chain.doFilter(request, wrapper);
+    wrapper.finish();
+  }
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void destroy() {
+  }
+
+
+  class AccelerationWrapper extends HttpServletResponseWrapper implements CacheMethodHandle {
+
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    private boolean zipped;
+    private GZIPServletOutputStream out;
+
+    private long now;
+    private int buffer;
+    private int status;
+    private String type;
+
+
+    AccelerationWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException {
+      super(response);
+
+      this.request = request;
+      this.response = response;
+
+      now = System.currentTimeMillis();
+      buffer = AcceleratorFilter.this.buffer;
+      status = HttpServletResponse.SC_OK;
+
+      zipped = false;
+      Enumeration values = request.getHeaders(HeaderNames.HEADER_ACCEPT_ENCODING);
+      while (values.hasMoreElements()) {
+        String value = (String) values.nextElement();
+        if (value.indexOf("gzip") != -1) {
+          zipped = true;
+          response.addHeader(HeaderNames.HEADER_CONTENT_ENCODING, "gzip");
+          break;
+        }
+      }
+    }
+
+
+    public void finish() throws IOException {
+      if (zipped && out != null)
+        out.zout.finish();
+    }
+
+
+    @Override
+    public void setStatus(int sc) {
+      response.setStatus(sc);
+      status = sc;
+      try {
+        cacheControl.decorate(request, response, this);
+      }
+      catch (Exception e) {
+        log.error("Error while decorating response", e);
+      }
+    }
+
+    @Override
+    public void setStatus(int sc, String sm) {
+      response.setStatus(sc,sm);
+      status = sc;
+      try {
+        cacheControl.decorate(request, response, this);
+      }
+      catch (Exception e) {
+        log.error("Error while decorating response", e);
+      }
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+      if (out == null)
+        out = new GZIPServletOutputStream(response.getOutputStream(), zipped);
+      return out;
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+      return new PrintWriter(getOutputStream());
+    }
+
+    @Override
+    public void setContentType(String type) {
+      this.type = type;
+      response.setContentType(type);
+    }
+
+    @Override
+    public void setBufferSize(int size) {
+      this.buffer = size;
+      response.setBufferSize(size);
+    }
+
+    @Override
+    public int getBufferSize() {
+      return buffer;
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+      cacheControl.decorate(request, response, this);
+      if (zipped && out != null) {
+        out.zout.finish();
+      }
+      response.flushBuffer();
+    }
+
+    @Override
+    public void resetBuffer() {
+      response.resetBuffer();
+    }
+
+    @Override
+    public boolean isCommitted() {
+      return response.isCommitted();
+    }
+
+    @Override
+    public void reset() {
+      response.reset();
+    }
+
+
+
+    @Override
+    public long getTimestamp() {
+      return now;
+    }
+
+    @Override
+    public int accepts(HttpServletRequest request) {
+      return status;
+    }
+
+    @Override
+    public int getCacheSeconds(HttpServletRequest request) {
+      return cacheSeconds;
+    }
+
+    @Override
+    public long getLastModified(HttpServletRequest request) {
+      return lastModified;
+    }
+
+    @Override
+    public String getETag(HttpServletRequest request) {
+      return eTag;
+    }
+
+    @Override
+    public boolean isETagWeak() {
+      return weak;
+    }
+
+    @Override
+    public void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap) {
+    }
+
+
+    class GZIPServletOutputStream extends ServletOutputStream {
+
+      private final OutputStream out;
+      private final GZIPOutputStream zout;
+      private boolean untouched = true;
+
+      public GZIPServletOutputStream(ServletOutputStream out, boolean zipped) throws IOException {
+        if (zipped) {
+          this.zout = new GZIPOutputStream(out, buffer);
+          this.out = this.zout;
+        }
+        else {
+          this.out = out;
+          this.zout = null;
+        }
+      }
+
+
+      @Override
+      public void write(int i) throws IOException {
+        if (untouched) {
+          untouched = false;
+          try {
+            AcceleratorFilter.this.cacheControl.decorate(AccelerationWrapper.this.request, response, buffer);
+          }
+          catch (Exception e) {
+            log.error("Error while guessing Cache-Header's", e);
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+          }
+        }
+        out.write(i);
+      }
+    }
+  }
+}