Commits

Anonymous committed a7c67bd

Default initialization of the Cache-Control max-age
Issue number: CACHE-240
Submitted by: Lars Torunski

Comments (0)

Files changed (5)

 # OSCache build properties
 name=oscache
 fullname=OSCache
-version=2.3
+version=2.3.1-dev
 status=integration
-cvs.tag=v2_3
+cvs.tag=v2_3_1
 
 # The URL to use for testing the example webapp. Comment this out to disable the webapp tests.
 #test.web.baseURL=http://localhost:7001/oscache-example/
 <?xml-stylesheet type="text/xsl" href="http://www.jayasoft.fr/org/ivyrep/ivy-doc.xsl"?>
 <ivy-module version="1.0">
     <info organisation="opensymphony" module="oscache"
-          revision="2.3"
+          revision="2.3.1"
           status="integration"
-          publication="20060306120000">
+          publication="20060401120000">
         <license name="Apache" url="http://www.apache.org/licenses/LICENSE-2.0.txt"/>
         <ivyauthor name="opensymphony" url="http://www.opensymphony.com/"/>
 

src/java/com/opensymphony/oscache/web/filter/CacheFilter.java

     public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
     public static final String HEADER_EXPIRES = "Expires";
     public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
-    public static final String HEADER_CACHE_CONTROL = "Cache-control";
+    public static final String HEADER_CACHE_CONTROL = "Cache-Control";
     public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
 
     // Fragment parameter
     public static final long EXPIRES_OFF = 0;
     public static final long EXPIRES_ON = 1;
     public static final long EXPIRES_TIME = -1;
+    
+    // Cache Control
+    public static final long MAX_AGE_NO_INIT = Long.MIN_VALUE;
+    public static final long MAX_AGE_TIME = Long.MAX_VALUE;
 
     // request attribute to avoid reentrance
     private final static String REQUEST_FILTERED = "__oscache_filtered";
     private int nocache = NOCACHE_OFF; // defines special no cache option for the requests - default is off
     private long lastModified = LAST_MODIFIED_INITIAL; // defines if the last-modified-header will be sent - default is intial setting
     private long expires = EXPIRES_ON; // defines if the expires-header will be sent - default is on
+    private long cacheControlMaxAge = -60; // defines which max-age in Cache-Control to be set - default is 60 seconds for max-age
     private ICacheKeyProvider cacheKeyProvider = this; // the provider of the cache key - default is the CacheFilter itselfs
     private ICacheGroupsProvider cacheGroupsProvider = this; // the provider of the cache groups - default is the CacheFilter itselfs
 
                     log.info("<cache>: New cache entry, cache stale or cache scope flushed for " + key);
                 }
 
-                CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse) response, fragmentRequest, time * 1000L, lastModified, expires);
+                CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse) response, fragmentRequest, time * 1000L, lastModified, expires, cacheControlMaxAge);
                 chain.doFilter(request, cacheResponse);
                 cacheResponse.flushBuffer();
 
      * <code>on</code> (default) for sending it if it is set in the filter chain and 
      * <code>time</code> the expires information will be intialized based on the time parameter and creation time of the content.</li>
      * 
+     * <li><b>max-age</b> - defines the cache control response header max-age. Acceptable values are
+     * <code>no init</code> for don't initializing the max-age cache control, 
+     * <code>time</code> the max-age information will be based on the time parameter and creation time of the content (expiration timestamp minus current timestamp), and</li>
+     * <code>[positive integer]</code> value constant in seconds to be set in every response, the default value is 60.</li>
+     * 
      * <li><b>ICacheKeyProvider</b> - Class implementing the interface <code>ICacheKeyProvider</code>.
      * A developer can implement a method which provides cache keys based on the request, 
      * the servlect cache administrator and cache.</li>
             log.info("Could not get init parameter 'expires', defaulting to 'on'.");
         }
 
+        // filter parameter Cache-Control
+        try {
+            String cacheControlMaxAgeString = config.getInitParameter("max-age");
+            
+            if (cacheControlMaxAgeString.equals("no init")) {
+            	cacheControlMaxAge = MAX_AGE_NO_INIT;
+            } else if (cacheControlMaxAgeString.equals("time")) {
+            	cacheControlMaxAge = MAX_AGE_TIME;
+            } else {
+            	cacheControlMaxAge = Long.parseLong(cacheControlMaxAgeString);
+            	if (cacheControlMaxAge >= 0) {
+            		// declare the cache control as a constant
+            		cacheControlMaxAge = - cacheControlMaxAge;
+            	} else {
+                    log.warn("Init parameter 'max-age' must be at least a positive integer, defaulting to 'time'. ");
+                	cacheControlMaxAge = 60;
+            	}
+            }
+        } catch (Exception e) {
+            log.info("Could not get init parameter 'max-age', defaulting to 'time'.");
+        }
+
         // filter parameter ICacheKeyProvider
         ICacheKeyProvider cacheKeyProvider = (ICacheKeyProvider)instantiateFromInitParam("ICacheKeyProvider", ICacheKeyProvider.class, this.getClass().getName());
         if (cacheKeyProvider != null) {

src/java/com/opensymphony/oscache/web/filter/CacheHttpServletResponseWrapper.java

     private int status = SC_OK;
     private long expires = CacheFilter.EXPIRES_ON;
     private long lastModified = CacheFilter.LAST_MODIFIED_INITIAL;
+    private long cacheControl = -60;
 
     /**
      * Constructor
      * @param response The servlet response
      */
     public CacheHttpServletResponseWrapper(HttpServletResponse response) {
-        this(response, false, Long.MAX_VALUE, CacheFilter.EXPIRES_ON, CacheFilter.LAST_MODIFIED_INITIAL);
+        this(response, false, Long.MAX_VALUE, CacheFilter.EXPIRES_ON, CacheFilter.LAST_MODIFIED_INITIAL, -60);
     }
 
     /**
      * @param time the refresh time in millis
      * @param lastModified defines if last modified header will be send, @see CacheFilter
      * @param expires defines if expires header will be send, @see CacheFilter
+     * @param cacheControl defines if cache control header will be send, @see CacheFilter
      */
-    public CacheHttpServletResponseWrapper(HttpServletResponse response, boolean fragment, long time, long lastModified, long expires) {
+    public CacheHttpServletResponseWrapper(HttpServletResponse response, boolean fragment, long time, long lastModified, long expires, long cacheControl) {
         super(response);
         result = new ResponseContent();
         this.fragment = fragment;
         this.expires = expires;
         this.lastModified = lastModified;
+        this.cacheControl = cacheControl;
         
         // only set inital values for last modified and expires, when a complete page is cached
         if (!fragment) {
             // setting a default last modified value based on object creation and remove the millis
             if (lastModified == CacheFilter.LAST_MODIFIED_INITIAL) {
-                long current = System.currentTimeMillis() / 1000;
-                result.setLastModified(current * 1000);
+                long current = System.currentTimeMillis();
+    			current = current - (current % 1000);
+                result.setLastModified(current);
                 super.setDateHeader(CacheFilter.HEADER_LAST_MODIFIED, result.getLastModified());
             }
             // setting the expires value
                 result.setExpires(result.getLastModified() + time);
                 super.setDateHeader(CacheFilter.HEADER_EXPIRES, result.getExpires());
             }
+            // setting the cache control with max-age 
+            if (cacheControl == CacheFilter.MAX_AGE_TIME) {
+            	// set the count down
+                long maxAge = System.currentTimeMillis();
+                maxAge = maxAge - (maxAge % 1000) + time;
+                result.setMaxAge(maxAge);
+                super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + time / 1000);
+            } else if (cacheControl != CacheFilter.MAX_AGE_NO_INIT) {
+                result.setMaxAge(cacheControl);
+                super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + (-cacheControl));
+            }
         }
     }
 
             if (!fragment) {
                 result.setExpires(value);
             } // TODO should we return now by fragments to avoid putting the header to the response?
-        } 
+        }
 
         super.setDateHeader(name, value);
     }
             status = SC_OK;
             expires = CacheFilter.EXPIRES_ON;
             lastModified = CacheFilter.LAST_MODIFIED_INITIAL;
+            cacheControl = -60;
         } else {
             throw new IllegalStateException("Can't reset CacheHttpServletResponseWrapper, because it's already committed!");
         }

src/java/com/opensymphony/oscache/web/filter/ResponseContent.java

     private byte[] content = null;
     private long expires = Long.MAX_VALUE;
     private long lastModified = -1;
+    private long maxAge = -60;
 
     public String getContentType() {
         return contentType;
     }
 
     /**
-     * @return the expires date and time in miliseconds when the content is stale
+     * @return the expires date and time in miliseconds when the content will be stale
      */
     public long getExpires() {
         return expires;
         expires = value;
     }
 
+	/**
+	 * Returns the max age of the content in miliseconds. If expires header and cache control are
+	 * enabled both, both will be equal. 
+	 * @return the max age of the content in miliseconds, if -1 max-age is disabled
+	 */
+	public long getMaxAge() {
+		return maxAge;
+	}
+
+	/**
+	 * Sets the max age date and time in miliseconds. If the parameter is -1, the max-age parameter
+	 * won't be set by default in the Cache-Control header.
+	 * @param value sets the intial
+	 */
+	public void setMaxAge(long value) {
+		maxAge = value;
+	}
+
     /**
      * Get an output stream. This is used by the {@link SplitServletOutputStream}
      * to capture the original (uncached) response into a byte array.
                 if (expires != Long.MAX_VALUE) {
                     httpResponse.setDateHeader(CacheFilter.HEADER_EXPIRES, expires);
                 }
+                
+                // add the cache-control header for max-age
+                if (maxAge == CacheFilter.MAX_AGE_NO_INIT || maxAge == CacheFilter.MAX_AGE_TIME) {
+                	// do nothing
+                } else if (maxAge > 0) { // set max-age based on life time
+                	long currentMaxAge = maxAge / 1000 - System.currentTimeMillis() / 1000;
+                	if (currentMaxAge < 0) {
+                		currentMaxAge = 0;
+                	}
+                	httpResponse.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + currentMaxAge);
+                } else {
+                	httpResponse.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + (-maxAge));
+                }
+                
             }
         }
 
     public boolean isContentGZiped() {
         return "gzip".equals(contentEncoding);
     }
+
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.