JCacheFilter.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- package org.apache.commons.jcs3.jcache.extras.web;
- import javax.cache.Cache;
- import javax.cache.CacheManager;
- import javax.cache.Caching;
- import javax.cache.configuration.FactoryBuilder;
- import javax.cache.configuration.MutableConfiguration;
- import javax.cache.expiry.ExpiryPolicy;
- import javax.cache.integration.CacheLoader;
- import javax.cache.integration.CacheWriter;
- import javax.cache.spi.CachingProvider;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.BufferedOutputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.Serializable;
- import java.net.URI;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Enumeration;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.zip.GZIPOutputStream;
- import static java.util.Collections.list;
- import static javax.servlet.http.HttpServletResponse.SC_OK;
- public class JCacheFilter implements Filter
- {
- private Cache<PageKey, Page> cache;
- private CachingProvider provider;
- private CacheManager manager;
- @Override
- public void init(final FilterConfig filterConfig) throws ServletException
- {
- final ClassLoader classLoader = filterConfig.getServletContext().getClassLoader();
- provider = Caching.getCachingProvider(classLoader);
- String uri = filterConfig.getInitParameter("configuration");
- if (uri == null)
- {
- uri = provider.getDefaultURI().toString();
- }
- final Properties properties = new Properties();
- for (final String key : list(filterConfig.getInitParameterNames()))
- {
- final String value = filterConfig.getInitParameter(key);
- if (value != null)
- {
- properties.put(key, value);
- }
- }
- manager = provider.getCacheManager(URI.create(uri), classLoader, properties);
- String cacheName = filterConfig.getInitParameter("cache-name");
- if (cacheName == null)
- {
- cacheName = JCacheFilter.class.getName();
- }
- cache = manager.getCache(cacheName);
- if (cache == null)
- {
- final MutableConfiguration<PageKey, Page> configuration = new MutableConfiguration<PageKey, Page>()
- .setStoreByValue(false);
- configuration.setReadThrough("true".equals(properties.getProperty("read-through", "false")));
- configuration.setWriteThrough("true".equals(properties.getProperty("write-through", "false")));
- if (configuration.isReadThrough())
- {
- configuration.setCacheLoaderFactory(new FactoryBuilder.ClassFactory<CacheLoader<PageKey, Page>>(properties.getProperty("cache-loader-factory")));
- }
- if (configuration.isWriteThrough())
- {
- configuration.setCacheWriterFactory(new FactoryBuilder.ClassFactory<CacheWriter<? super PageKey, ? super Page>>(properties.getProperty("cache-writer-factory")));
- }
- final String expirtyPolicy = properties.getProperty("expiry-policy-factory");
- if (expirtyPolicy != null)
- {
- configuration.setExpiryPolicyFactory(new FactoryBuilder.ClassFactory<ExpiryPolicy>(expirtyPolicy));
- }
- configuration.setManagementEnabled("true".equals(properties.getProperty("management-enabled", "false")));
- configuration.setStatisticsEnabled("true".equals(properties.getProperty("statistics-enabled", "false")));
- cache = manager.createCache(cacheName, configuration);
- }
- }
- @Override
- public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException
- {
- boolean gzip = false;
- if (HttpServletRequest.class.isInstance(servletRequest))
- {
- final Enumeration<String> acceptEncoding = HttpServletRequest.class.cast(servletRequest).getHeaders("Accept-Encoding");
- while (acceptEncoding != null && acceptEncoding.hasMoreElements())
- {
- if ("gzip".equals(acceptEncoding.nextElement()))
- {
- gzip = true;
- break;
- }
- }
- }
- final HttpServletResponse httpServletResponse = HttpServletResponse.class.cast(servletResponse);
- checkResponse(httpServletResponse);
- final PageKey key = new PageKey(key(servletRequest), gzip);
- Page page = cache.get(key);
- if (page == null)
- {
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- final InMemoryResponse response;
- if (gzip)
- {
- response = new InMemoryResponse(httpServletResponse, new GZIPOutputStream(baos));
- }
- else
- {
- response = new InMemoryResponse(httpServletResponse, baos);
- }
- filterChain.doFilter(servletRequest, response);
- response.flushBuffer();
- page = new Page(
- response.getStatus(),
- response.getContentType(),
- response.getContentLength(),
- response.getCookies(),
- response.getHeaders(),
- baos.toByteArray());
- cache.put(key, page);
- }
- if (page.status == SC_OK) {
- checkResponse(httpServletResponse);
- if (gzip)
- {
- httpServletResponse.setHeader("Content-Encoding", "gzip");
- }
- httpServletResponse.setStatus(page.status);
- if (page.contentType != null)
- {
- httpServletResponse.setContentType(page.contentType);
- }
- if (page.contentLength > 0)
- {
- httpServletResponse.setContentLength(page.contentLength);
- }
- for (final Cookie c : page.cookies)
- {
- httpServletResponse.addCookie(c);
- }
- for (final Map.Entry<String, List<Serializable>> entry : page.headers.entrySet())
- {
- for (final Serializable value : entry.getValue())
- {
- if (Integer.class.isInstance(value))
- {
- httpServletResponse.addIntHeader(entry.getKey(), Integer.class.cast(value));
- }
- else if (String.class.isInstance(value))
- {
- httpServletResponse.addHeader(entry.getKey(), String.class.cast(value));
- }
- else if (Long.class.isInstance(value))
- {
- httpServletResponse.addDateHeader(entry.getKey(), Long.class.cast(value));
- }
- }
- }
- httpServletResponse.setContentLength(page.out.length);
- final BufferedOutputStream bos = new BufferedOutputStream(httpServletResponse.getOutputStream());
- if (page.out.length != 0)
- {
- bos.write(page.out);
- }
- else
- {
- bos.write(new byte[0]);
- }
- bos.flush();
- }
- }
- protected String key(final ServletRequest servletRequest)
- {
- if (HttpServletRequest.class.isInstance(servletRequest))
- {
- final HttpServletRequest request = HttpServletRequest.class.cast(servletRequest);
- return request.getMethod() + '_' + request.getRequestURI() + '_' + request.getQueryString();
- }
- return servletRequest.toString();
- }
- private static void checkResponse(final ServletResponse servletResponse)
- {
- if (servletResponse.isCommitted()) {
- throw new IllegalStateException("Response committed");
- }
- }
- @Override
- public void destroy()
- {
- if (!cache.isClosed())
- {
- cache.close();
- }
- if (!manager.isClosed())
- {
- manager.close();
- }
- provider.close();
- }
- protected static class PageKey implements Serializable {
- private final String uri;
- private boolean gzip;
- public PageKey(final String uri, final boolean gzip)
- {
- this.uri = uri;
- this.gzip = gzip;
- }
- public void setGzip(final boolean gzip)
- {
- this.gzip = gzip;
- }
- @Override
- public boolean equals(final Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (o == null || getClass() != o.getClass())
- {
- return false;
- }
- final PageKey pageKey = PageKey.class.cast(o);
- return gzip == pageKey.gzip && uri.equals(pageKey.uri);
- }
- @Override
- public int hashCode()
- {
- int result = uri.hashCode();
- result = 31 * result + (gzip ? 1 : 0);
- return result;
- }
- }
- protected static class Page implements Serializable {
- private final int status;
- private final String contentType;
- private final int contentLength;
- private final Collection<Cookie> cookies;
- private final Map<String, List<Serializable>> headers;
- private final byte[] out;
- public Page(final int status,
- final String contentType, final int contentLength,
- final Collection<Cookie> cookies, final Map<String, List<Serializable>> headers,
- final byte[] out)
- {
- this.status = status;
- this.contentType = contentType;
- this.contentLength = contentLength;
- this.cookies = cookies;
- this.headers = headers;
- this.out = out;
- }
- @Override
- public boolean equals(final Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (o == null || getClass() != o.getClass())
- {
- return false;
- }
- final Page page = Page.class.cast(o);
- return contentLength == page.contentLength
- && status == page.status
- && !(contentType != null ? !contentType.equals(page.contentType) : page.contentType != null)
- && cookies.equals(page.cookies)
- && headers.equals(page.headers)
- && Arrays.equals(out, page.out);
- }
- @Override
- public int hashCode()
- {
- int result = status;
- result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
- result = 31 * result + contentLength;
- result = 31 * result + cookies.hashCode();
- result = 31 * result + headers.hashCode();
- result = 31 * result + Arrays.hashCode(out);
- return result;
- }
- }
- }