ch.entwine.weblounge.cache.impl.CacheEntry.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.cache.impl.CacheEntry.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.cache.impl;

import ch.entwine.weblounge.common.request.CacheHandle;
import ch.entwine.weblounge.common.request.CacheTag;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * This class implements an entry into the cache.
 */
public final class CacheEntry implements Serializable {

    /** The logging facility */
    private static final Logger logger = LoggerFactory.getLogger(CacheEntry.class);

    /** Serial version uid */
    private static final long serialVersionUID = 5694887351734158681L;

    /** The key for this cache entry */
    private String key = null;

    /** The content buffer */
    private byte[] content;

    /** The content encoding */
    private String encoding = null;

    /** The response metadata */
    private CacheableHttpServletResponseHeaders headers = null;

    /** Date when the entry was added to the cache */
    private long creationDate = 0L;

    /** Date where the entry's contents were last modified */
    private long modificationDate = 0L;

    /** Time in ms for the client to revalidate */
    private long clientRevalidationTime = 0L;

    /** The etag */
    private String eTag = null;

    /**
     * Creates a new cache entry for the given handle, content and metadata.
     * <p>
     * Note that some cache information, such as the etag and the last
     * modification dates will be gathered from the response headers if available.
     * 
     * @param handle
     *          the cache handle
     * @param content
     *          the content
     * @param encoding
     *          the content encoding
     * @param headers
     *          the metadata
     * @throws IllegalArgumentException
     *           if the content or the headers collection is <code>null</code>
     */
    protected CacheEntry(CacheHandle handle, byte[] content, String encoding,
            CacheableHttpServletResponseHeaders headers) {
        if (handle == null)
            throw new IllegalArgumentException("Handle cannot be null");
        if (content == null)
            throw new IllegalArgumentException("Content cannot be null");
        if (headers == null)
            throw new IllegalArgumentException("Headers cannot be null");
        this.key = handle.getKey();
        this.encoding = encoding;
        this.content = content;
        this.creationDate = handle.getCreationDate();
        this.modificationDate = getTimeWithoutMilliseconds(handle.getModificationDate());
        this.clientRevalidationTime = handle.getClientRevalidationTime();
        this.eTag = createETag(modificationDate);
        setHeaders(headers);
    }

    /**
     * Returns the key for this entry.
     * 
     * @return the key
     */
    public String getKey() {
        return key;
    }

    /**
     * Returns the content encoding.
     * 
     * @return the encoding
     */
    public String getEncoding() {
        return encoding;
    }

    /**
     * Returns this entry's etag value.
     * 
     * @return the etag
     */
    public String getETag() {
        return eTag;
    }

    /**
     * Returns the date when this entry was created.
     * 
     * @return the creation date
     */
    public long getCreationDate() {
        return creationDate;
    }

    /**
     * Returns the date when this entry's content was last modified.
     * 
     * @return the modification date
     */
    public long getModificationDate() {
        return modificationDate;
    }

    /**
     * Returns the time in ms until the client needs to revalidate the response.
     * 
     * @return the client revalidation time
     */
    public long getClientRevalidationTime() {
        return clientRevalidationTime;
    }

    /**
     * Returns <code>true</code> if the entry is tagged with <code>tag</code>.
     * 
     * @param tag
     *          the tag
     * @return <code>true</code> if the entry is tagged
     */
    public boolean containsTag(CacheTag tag) {
        String keyPart = tag.getName() + "=" + tag.getValue();
        return key.contains(keyPart);
    }

    /**
     * Sets the response headers, which will be used to extract certain common
     * information such as the etag or the last-modified date.
     * 
     * @param headers
     *          the response headers
     */
    public void setHeaders(CacheableHttpServletResponseHeaders headers) {
        this.headers = headers;

        // Overwrite local Last-Modified?
        if (headers.containsHeader("Last-Modified")) {
            DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
            String lastModifiedHeader = (String) headers.getHeaders().get("Last-Modified");
            try {
                modificationDate = getTimeWithoutMilliseconds(
                        new Date(Math.max(modificationDate, df.parse(lastModifiedHeader).getTime())));
                logger.trace("Changing modification date to '{}' as provided by response headers",
                        lastModifiedHeader);
                eTag = createETag(modificationDate);
                logger.trace("Changing eTag to '{}' as provided by response headers", eTag);
            } catch (ParseException e) {
                logger.error("Unexpected date format for 'Last-Modified' header: {}", lastModifiedHeader);
            }
        }

        // Overwrite local ETag?
        if (headers.containsHeader("ETag")) {
            eTag = (String) headers.getHeaders().get("ETag");
            logger.trace("Changing eTag to '{}' as provided by response headers", eTag);
        }
    }

    /**
     * Returns the cached response headers.
     * 
     * @return the headers
     */
    public CacheableHttpServletResponseHeaders getHeaders() {
        return headers;
    }

    /**
     * Returns the cached content.
     * 
     * @return the content
     */
    public byte[] getContent() {
        return content;
    }

    /**
     * Returns the content type.
     * 
     * @return the content type
     */
    public String getContentType() {
        Object contentType = headers.getHeaders().get("Content-Type");
        if (contentType == null)
            return null;
        if (contentType instanceof String)
            return (String) contentType;
        throw new IllegalStateException("Response contained more than one 'Content-type' header");
    }

    /**
     * Returns <code>true</code> if date is not older than the creation date of
     * this cache entry.
     * 
     * @param date
     *          the date
     * @return <code>true</code> if this entry is older or equally old
     */
    public boolean notModified(long date) {
        return date >= modificationDate;
    }

    /**
     * Returns <code>true</code> if <code>eTag</code> is either blank (not
     * specified) or if it matches this entry's etag.
     * 
     * @param eTag
     *          the etag
     * @return <code>true</code> if the etag is either empty or matches
     */
    public boolean matches(String eTag) {
        return StringUtils.isNotBlank(eTag) && this.eTag.equals(eTag);
    }

    /**
     * Returns the etag (including the extra pair of quotes) for the given value,
     * which will usually be the creation time of the cached entry.
     * 
     * @param value
     *          the value
     * @return the etag
     */
    public static String createETag(long value) {
        return "\"WL-" + Long.toHexString(value) + "\"";
    }

    /**
     * Strips the milliseconds from the date and returns it.
     * 
     * @param date
     *          the data
     * @return the date without the milliseconds
     */
    private long getTimeWithoutMilliseconds(Date date) {
        long millis = date.getTime() / 1000;
        millis *= 1000;
        return millis;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return key;
    }

}