Package net.sourceforge.stripes.controller

Source Code of net.sourceforge.stripes.controller.HttpCacheInterceptor$CacheKey

/* Copyright 2007 Ben Gunter
*
* Licensed 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 net.sourceforge.stripes.controller;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletResponse;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.HttpCache;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.util.Log;

/**
* Looks for an {@link HttpCache} annotation on the event handler method, the {@link ActionBean}
* class or the {@link ActionBean}'s superclasses. If an {@link HttpCache} is found, then the
* appropriate response headers are set to control client-side caching.
*
* @author Ben Gunter
* @since Stripes 1.5
*/
@Intercepts(LifecycleStage.ResolutionExecution)
public class HttpCacheInterceptor implements Interceptor {
    private static final Log logger = Log.getInstance(HttpCacheInterceptor.class);

    @HttpCache
    private static final class CacheKey {
        final Method method;
        final Class<?> beanClass;
        final int hashCode;

        /** Create a cache key for the given event handler method and {@link ActionBean} class. */
        public CacheKey(Method method, Class<? extends ActionBean> beanClass) {
            this.method = method;
            this.beanClass = beanClass;
            this.hashCode = method.hashCode() * 37 + beanClass.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            final CacheKey that = (CacheKey) obj;
            return this.method.equals(that.method) && this.beanClass.equals(that.beanClass);
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

        @Override
        public String toString() {
            return beanClass.getName() + "." + method.getName() + "()";
        }
    }

    private Map<CacheKey, HttpCache> cache = new ConcurrentHashMap<CacheKey, HttpCache>(128);

    /** Null values are not allowed by {@link ConcurrentHashMap} so use this reference instead. */
    private static final HttpCache NULL_CACHE = CacheKey.class.getAnnotation(HttpCache.class);

    public Resolution intercept(ExecutionContext ctx) throws Exception {
        final ActionBean actionBean = ctx.getActionBean();
        final Method handler = ctx.getHandler();
        if (actionBean != null && handler != null) {
            final Class<? extends ActionBean> beanClass = actionBean.getClass();
            // if caching is disabled, then set the appropriate response headers
            logger.debug("Looking for ", HttpCache.class.getSimpleName(), " on ",
                    beanClass.getName(), ".", handler.getName(), "()");
            final HttpCache annotation = getAnnotation(handler, beanClass);
            if (annotation != null) {
                final HttpServletResponse response = ctx.getActionBeanContext().getResponse();
                if (annotation.allow()) {
                    long expires = annotation.expires();
                    if (expires != HttpCache.DEFAULT_EXPIRES) {
                        logger.debug("Response expires in ", expires, " seconds");
                        expires = expires * 1000 + System.currentTimeMillis();
                        response.setDateHeader("Expires", expires);
                    }
                }
                else {
                    logger.debug("Disabling client-side caching for response");
                    response.setDateHeader("Expires", 0);
                    response.setHeader("Cache-control", "no-store, no-cache, must-revalidate");
                    response.setHeader("Pragma", "no-cache");
                }
            }
        }

        return ctx.proceed();
    }

    /**
     * Look for a {@link HttpCache} annotation on the method first and then on the class and its
     * superclasses.
     *
     * @param method an event handler method
     * @param beanClass the class to inspect for annotations if none is found on the method
     * @return The first {@link HttpCache} annotation found. If none is found then null.
     */
    protected HttpCache getAnnotation(Method method, Class<? extends ActionBean> beanClass) {
        // check cache first
        final CacheKey cacheKey = new CacheKey(method, beanClass);
        HttpCache annotation = cache.get(cacheKey);
        if (annotation != null) {
            return annotation == NULL_CACHE ? null : annotation;
        }

        // not found in cache so figure it out
        annotation = method.getAnnotation(HttpCache.class);
        if (annotation == null) {
            // search the method's class and its superclasses
            Class<?> clazz = beanClass;
            do {
                annotation = clazz.getAnnotation(HttpCache.class);
                clazz = clazz.getSuperclass();
            } while (clazz != null && annotation == null);
        }

        // check for weirdness
        if (annotation != null) {
            logger.debug("Found ", HttpCache.class.getSimpleName(), " for ", beanClass.getName(),
                    ".", method.getName(), "()");
            final int expires = annotation.expires();
            if (annotation.allow() && expires != HttpCache.DEFAULT_EXPIRES && expires < 0) {
                logger.warn(HttpCache.class.getSimpleName(), " for ", beanClass.getName(), ".",
                        method.getName(), "() allows caching but expires in the past");
            }
            else if (!annotation.allow() && expires != HttpCache.DEFAULT_EXPIRES) {
                logger.warn(HttpCache.class.getSimpleName(), " for ", beanClass.getName(), ".",
                        method.getName(), "() disables caching but explicitly sets expires");
            }
        }
        else {
            annotation = NULL_CACHE;
        }

        cache.put(cacheKey, annotation);
        return annotation;
    }
}
TOP

Related Classes of net.sourceforge.stripes.controller.HttpCacheInterceptor$CacheKey

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.