JCacheFilter.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *   http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.jcs3.jcache.extras.web;

  20. import javax.cache.Cache;
  21. import javax.cache.CacheManager;
  22. import javax.cache.Caching;
  23. import javax.cache.configuration.FactoryBuilder;
  24. import javax.cache.configuration.MutableConfiguration;
  25. import javax.cache.expiry.ExpiryPolicy;
  26. import javax.cache.integration.CacheLoader;
  27. import javax.cache.integration.CacheWriter;
  28. import javax.cache.spi.CachingProvider;
  29. import javax.servlet.Filter;
  30. import javax.servlet.FilterChain;
  31. import javax.servlet.FilterConfig;
  32. import javax.servlet.ServletException;
  33. import javax.servlet.ServletRequest;
  34. import javax.servlet.ServletResponse;
  35. import javax.servlet.http.Cookie;
  36. import javax.servlet.http.HttpServletRequest;
  37. import javax.servlet.http.HttpServletResponse;
  38. import java.io.BufferedOutputStream;
  39. import java.io.ByteArrayOutputStream;
  40. import java.io.IOException;
  41. import java.io.Serializable;
  42. import java.net.URI;
  43. import java.util.Arrays;
  44. import java.util.Collection;
  45. import java.util.Enumeration;
  46. import java.util.List;
  47. import java.util.Map;
  48. import java.util.Properties;
  49. import java.util.zip.GZIPOutputStream;

  50. import static java.util.Collections.list;
  51. import static javax.servlet.http.HttpServletResponse.SC_OK;

  52. public class JCacheFilter implements Filter
  53. {
  54.     private Cache<PageKey, Page> cache;
  55.     private CachingProvider provider;
  56.     private CacheManager manager;

  57.     @Override
  58.     public void init(final FilterConfig filterConfig) throws ServletException
  59.     {
  60.         final ClassLoader classLoader = filterConfig.getServletContext().getClassLoader();
  61.         provider = Caching.getCachingProvider(classLoader);

  62.         String uri = filterConfig.getInitParameter("configuration");
  63.         if (uri == null)
  64.         {
  65.             uri = provider.getDefaultURI().toString();
  66.         }
  67.         final Properties properties = new Properties();
  68.         for (final String key : list(filterConfig.getInitParameterNames()))
  69.         {
  70.             final String value = filterConfig.getInitParameter(key);
  71.             if (value != null)
  72.             {
  73.                 properties.put(key, value);
  74.             }
  75.         }
  76.         manager = provider.getCacheManager(URI.create(uri), classLoader, properties);

  77.         String cacheName = filterConfig.getInitParameter("cache-name");
  78.         if (cacheName == null)
  79.         {
  80.             cacheName = JCacheFilter.class.getName();
  81.         }
  82.         cache = manager.getCache(cacheName);
  83.         if (cache == null)
  84.         {
  85.             final MutableConfiguration<PageKey, Page> configuration = new MutableConfiguration<PageKey, Page>()
  86.                     .setStoreByValue(false);
  87.             configuration.setReadThrough("true".equals(properties.getProperty("read-through", "false")));
  88.             configuration.setWriteThrough("true".equals(properties.getProperty("write-through", "false")));
  89.             if (configuration.isReadThrough())
  90.             {
  91.                 configuration.setCacheLoaderFactory(new FactoryBuilder.ClassFactory<CacheLoader<PageKey, Page>>(properties.getProperty("cache-loader-factory")));
  92.             }
  93.             if (configuration.isWriteThrough())
  94.             {
  95.                 configuration.setCacheWriterFactory(new FactoryBuilder.ClassFactory<CacheWriter<? super PageKey, ? super Page>>(properties.getProperty("cache-writer-factory")));
  96.             }
  97.             final String expirtyPolicy = properties.getProperty("expiry-policy-factory");
  98.             if (expirtyPolicy != null)
  99.             {
  100.                 configuration.setExpiryPolicyFactory(new FactoryBuilder.ClassFactory<ExpiryPolicy>(expirtyPolicy));
  101.             }
  102.             configuration.setManagementEnabled("true".equals(properties.getProperty("management-enabled", "false")));
  103.             configuration.setStatisticsEnabled("true".equals(properties.getProperty("statistics-enabled", "false")));
  104.             cache = manager.createCache(cacheName, configuration);
  105.         }
  106.     }

  107.     @Override
  108.     public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException
  109.     {
  110.         boolean gzip = false;
  111.         if (HttpServletRequest.class.isInstance(servletRequest))
  112.         {
  113.             final Enumeration<String> acceptEncoding = HttpServletRequest.class.cast(servletRequest).getHeaders("Accept-Encoding");
  114.             while (acceptEncoding != null && acceptEncoding.hasMoreElements())
  115.             {
  116.                 if ("gzip".equals(acceptEncoding.nextElement()))
  117.                 {
  118.                     gzip = true;
  119.                     break;
  120.                 }
  121.             }
  122.         }

  123.         final HttpServletResponse httpServletResponse = HttpServletResponse.class.cast(servletResponse);
  124.         checkResponse(httpServletResponse);

  125.         final PageKey key = new PageKey(key(servletRequest), gzip);
  126.         Page page = cache.get(key);
  127.         if (page == null)
  128.         {
  129.             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  130.             final InMemoryResponse response;
  131.             if (gzip)
  132.             {
  133.                 response = new InMemoryResponse(httpServletResponse, new GZIPOutputStream(baos));
  134.             }
  135.             else
  136.             {
  137.                 response = new InMemoryResponse(httpServletResponse, baos);
  138.             }
  139.             filterChain.doFilter(servletRequest, response);
  140.             response.flushBuffer();

  141.             page = new Page(
  142.                     response.getStatus(),
  143.                     response.getContentType(),
  144.                     response.getContentLength(),
  145.                     response.getCookies(),
  146.                     response.getHeaders(),
  147.                     baos.toByteArray());
  148.             cache.put(key, page);
  149.         }

  150.         if (page.status == SC_OK) {
  151.             checkResponse(httpServletResponse);

  152.             if (gzip)
  153.             {
  154.                 httpServletResponse.setHeader("Content-Encoding", "gzip");
  155.             }

  156.             httpServletResponse.setStatus(page.status);
  157.             if (page.contentType != null)
  158.             {
  159.                 httpServletResponse.setContentType(page.contentType);
  160.             }
  161.             if (page.contentLength > 0)
  162.             {
  163.                 httpServletResponse.setContentLength(page.contentLength);
  164.             }
  165.             for (final Cookie c : page.cookies)
  166.             {
  167.                 httpServletResponse.addCookie(c);
  168.             }
  169.             for (final Map.Entry<String, List<Serializable>> entry : page.headers.entrySet())
  170.             {
  171.                 for (final Serializable value : entry.getValue())
  172.                 {
  173.                     if (Integer.class.isInstance(value))
  174.                     {
  175.                         httpServletResponse.addIntHeader(entry.getKey(), Integer.class.cast(value));
  176.                     }
  177.                     else if (String.class.isInstance(value))
  178.                     {
  179.                         httpServletResponse.addHeader(entry.getKey(), String.class.cast(value));
  180.                     }
  181.                     else if (Long.class.isInstance(value))
  182.                     {
  183.                         httpServletResponse.addDateHeader(entry.getKey(), Long.class.cast(value));
  184.                     }
  185.                 }
  186.             }
  187.             httpServletResponse.setContentLength(page.out.length);
  188.             final BufferedOutputStream bos = new BufferedOutputStream(httpServletResponse.getOutputStream());
  189.             if (page.out.length != 0)
  190.             {
  191.                 bos.write(page.out);
  192.             }
  193.             else
  194.             {
  195.                 bos.write(new byte[0]);
  196.             }
  197.             bos.flush();
  198.         }
  199.     }

  200.     protected String key(final ServletRequest servletRequest)
  201.     {
  202.         if (HttpServletRequest.class.isInstance(servletRequest))
  203.         {
  204.             final HttpServletRequest request = HttpServletRequest.class.cast(servletRequest);
  205.             return request.getMethod() + '_' + request.getRequestURI() + '_' + request.getQueryString();
  206.         }
  207.         return servletRequest.toString();
  208.     }

  209.     private static void checkResponse(final ServletResponse servletResponse)
  210.     {
  211.         if (servletResponse.isCommitted()) {
  212.             throw new IllegalStateException("Response committed");
  213.         }
  214.     }

  215.     @Override
  216.     public void destroy()
  217.     {
  218.         if (!cache.isClosed())
  219.         {
  220.             cache.close();
  221.         }
  222.         if (!manager.isClosed())
  223.         {
  224.             manager.close();
  225.         }
  226.         provider.close();
  227.     }

  228.     protected static class PageKey implements Serializable {
  229.         private final String uri;
  230.         private boolean gzip;

  231.         public PageKey(final String uri, final boolean gzip)
  232.         {
  233.             this.uri = uri;
  234.             this.gzip = gzip;
  235.         }

  236.         public void setGzip(final boolean gzip)
  237.         {
  238.             this.gzip = gzip;
  239.         }

  240.         @Override
  241.         public boolean equals(final Object o)
  242.         {
  243.             if (this == o)
  244.             {
  245.                 return true;
  246.             }
  247.             if (o == null || getClass() != o.getClass())
  248.             {
  249.                 return false;
  250.             }

  251.             final PageKey pageKey = PageKey.class.cast(o);
  252.             return gzip == pageKey.gzip && uri.equals(pageKey.uri);

  253.         }

  254.         @Override
  255.         public int hashCode()
  256.         {
  257.             int result = uri.hashCode();
  258.             result = 31 * result + (gzip ? 1 : 0);
  259.             return result;
  260.         }
  261.     }

  262.     protected static class Page implements Serializable {
  263.         private final int status;
  264.         private final String contentType;
  265.         private final int contentLength;
  266.         private final Collection<Cookie> cookies;
  267.         private final Map<String, List<Serializable>> headers;
  268.         private final byte[] out;

  269.         public Page(final int status,
  270.                     final String contentType, final int contentLength,
  271.                     final Collection<Cookie> cookies, final Map<String, List<Serializable>> headers,
  272.                     final byte[] out)
  273.         {
  274.             this.status = status;
  275.             this.contentType = contentType;
  276.             this.contentLength = contentLength;
  277.             this.cookies = cookies;
  278.             this.headers = headers;
  279.             this.out = out;
  280.         }

  281.         @Override
  282.         public boolean equals(final Object o)
  283.         {
  284.             if (this == o)
  285.             {
  286.                 return true;
  287.             }
  288.             if (o == null || getClass() != o.getClass())
  289.             {
  290.                 return false;
  291.             }

  292.             final Page page = Page.class.cast(o);
  293.             return contentLength == page.contentLength
  294.                     && status == page.status
  295.                     && !(contentType != null ? !contentType.equals(page.contentType) : page.contentType != null)
  296.                     && cookies.equals(page.cookies)
  297.                     && headers.equals(page.headers)
  298.                     && Arrays.equals(out, page.out);

  299.         }

  300.         @Override
  301.         public int hashCode()
  302.         {
  303.             int result = status;
  304.             result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
  305.             result = 31 * result + contentLength;
  306.             result = 31 * result + cookies.hashCode();
  307.             result = 31 * result + headers.hashCode();
  308.             result = 31 * result + Arrays.hashCode(out);
  309.             return result;
  310.         }
  311.     }
  312. }