All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gargoylesoftware.htmlunit.Cache Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright (c) 2002-2011 Gargoyle Software Inc.
 *
 * 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 com.gargoylesoftware.htmlunit;

import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.w3c.dom.css.CSSStyleSheet;

/**
 * 

Simple cache implementation which caches compiled JavaScript files and parsed CSS snippets. Caching * compiled JavaScript files avoids unnecessary web requests and additional compilation overhead, while * caching parsed CSS snippets avoids very expensive CSS parsing.

* * @version $Revision: 6204 $ * @author Marc Guillemot * @author Daniel Gredler */ public class Cache implements Serializable { /** The maximum size of the cache. */ private int maxSize_ = 40; /** * The map which holds the cached responses. Note that when keying on URLs, we key on the string version * of the URLs, rather than on the URLs themselves. This is done for performance, because a) the * {@link java.net.URL#hashCode()} method is synchronized, and b) the {@link java.net.URL#hashCode()} * method triggers DNS lookups of the URL hostnames' IPs. As of this writing, the HtmlUnit unit tests * run ~20% faster whey keying on strings rather than on {@link java.net.URL} instances. */ private final Map entries_ = Collections.synchronizedMap(new HashMap(maxSize_)); /** * A cache entry. */ private static class Entry implements Comparable, Serializable { private final String key_; private final Object value_; private long lastAccess_; Entry(final String key, final Object value) { key_ = key; value_ = value; lastAccess_ = System.currentTimeMillis(); } /** * {@inheritDoc} */ public int compareTo(final Entry other) { return NumberUtils.compare(lastAccess_, other.lastAccess_); } /** * Updates the last access date. */ public void touch() { lastAccess_ = System.currentTimeMillis(); } } /** * Caches the specified object, if the corresponding request and response objects indicate * that it is cacheable. * * @param request the request corresponding to the specified compiled script * @param response the response corresponding to the specified compiled script * @param toCache the object that is to be cached, if possible (may be for instance a compiled script or * simply a WebResponse) */ public void cacheIfPossible(final WebRequest request, final WebResponse response, final Object toCache) { if (isCacheable(request, response)) { final String url = response.getWebRequest().getUrl().toString(); final Entry entry = new Entry(url, toCache); entries_.put(entry.key_, entry); deleteOverflow(); } } /** * Caches the parsed version of the specified CSS snippet. We key the cache based on CSS snippets (rather * than requests and responses as is done above) because a) this allows us to cache inline CSS, b) CSS is * extremely expensive to parse, so we want to avoid it as much as possible, c) CSS files aren't usually * nearly as large as JavaScript files, so memory bloat won't be too bad, and d) caching on requests and * responses requires checking dynamicity (see {@link #isDynamicContent(WebResponse)}), and headers often * aren't set up correctly, disallowing caching when in fact it should be allowed. * * @param css the CSS snippet from which styleSheet is derived * @param styleSheet the parsed version of css */ public void cache(final String css, final CSSStyleSheet styleSheet) { final Entry entry = new Entry(css, styleSheet); entries_.put(entry.key_, entry); deleteOverflow(); } /** * Truncates the cache to the maximal number of entries. */ protected void deleteOverflow() { synchronized (entries_) { while (entries_.size() > maxSize_) { final Entry oldestEntry = Collections.min(entries_.values()); entries_.remove(oldestEntry.key_); } } } /** * Determines if the specified response can be cached. * * @param request the performed request * @param response the received response * @return true if the response can be cached */ protected boolean isCacheable(final WebRequest request, final WebResponse response) { return HttpMethod.GET == response.getWebRequest().getHttpMethod() && !isDynamicContent(response); } /** *

Tries to guess if the content is dynamic or not.

* *

"Since origin servers do not always provide explicit expiration times, HTTP caches typically * assign heuristic expiration times, employing algorithms that use other header values (such as the * Last-Modified time) to estimate a plausible expiration time".

* *

The current implementation considers as dynamic content everything except responses with a * Last-Modified header with a date older than 10 minutes or with an Expires header * specifying expiration in more than 10 minutes.

* * @see RFC 2616 * @param response the response to examine * @return true if the response should be considered as dynamic and therefore uncacheable */ protected boolean isDynamicContent(final WebResponse response) { final Date lastModified = parseDateHeader(response, "Last-Modified"); final Date expires = parseDateHeader(response, "Expires"); final long delay = 10 * org.apache.commons.lang.time.DateUtils.MILLIS_PER_MINUTE; final long now = getCurrentTimestamp(); final boolean cacheableContent = (expires != null && (expires.getTime() - now > delay) || (expires == null && lastModified != null && (now - lastModified.getTime() > delay))); return !cacheableContent; } /** * Gets the current time stamp. As method to allow overriding it, when simulating an other time. * @return the current time stamp */ protected long getCurrentTimestamp() { return System.currentTimeMillis(); } /** * Parses and returns the specified date header of the specified response. This method * returns null if the specified header cannot be found or cannot be parsed as * a date. * * @param response the response * @param headerName the header name * @return the specified date header of the specified response */ protected Date parseDateHeader(final WebResponse response, final String headerName) { final String value = response.getResponseHeaderValue(headerName); if (value == null) { return null; } else if (value.matches("-?\\d+")) { return new Date(); } try { return DateUtils.parseDate(value); } catch (final DateParseException e) { return null; } } /** * Returns the cached object corresponding to the specified request. If there is * no corresponding cached object, this method returns null. * * @param request the request whose corresponding cached compiled script is sought * @return the cached object corresponding to the specified request if any */ public Object getCachedObject(final WebRequest request) { if (HttpMethod.GET != request.getHttpMethod()) { return null; } final Entry cachedEntry = entries_.get(request.getUrl().toString()); if (cachedEntry == null) { return null; } synchronized (entries_) { cachedEntry.touch(); } return cachedEntry.value_; } /** * Returns the cached parsed version of the specified CSS snippet. If there is no * corresponding cached stylesheet, this method returns null. * * @param css the CSS snippet whose cached stylesheet is sought * @return the cached stylesheet corresponding to the specified CSS snippet */ public CSSStyleSheet getCachedStyleSheet(final String css) { final Entry cachedEntry = entries_.get(css); if (cachedEntry == null) { return null; } synchronized (entries_) { cachedEntry.touch(); } return (CSSStyleSheet) cachedEntry.value_; } /** * Returns the cache's maximum size. This is the maximum number of files that will * be cached. The default is 25. * * @return the cache's maximum size */ public int getMaxSize() { return maxSize_; } /** * Sets the cache's maximum size. This is the maximum number of files that will * be cached. The default is 25. * * @param maxSize the cache's maximum size (must be >= 0) */ public void setMaxSize(final int maxSize) { if (maxSize < 0) { throw new IllegalArgumentException("Illegal value for maxSize: " + maxSize); } maxSize_ = maxSize; deleteOverflow(); } /** * Returns the number of entries in the cache. * * @return the number of entries in the cache */ public int getSize() { return entries_.size(); } /** * Clears the cache. */ public void clear() { synchronized (entries_) { entries_.clear(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy