Author: pete
Date: Wed Jun 29 15:52:08 2011
New Revision: 1141139

URL: http://svn.apache.org/viewvc?rev=1141139&view=rev
Log:
WICKET-3846 add one more caching strategy using a fixed version string (this 
will work in environments where no stable timestamps for resources are 
available)

Added:
    
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/AbstractFilenameWithVersionResourceCachingStrategy.java
    
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithStaticVersionResourceCachingStrategy.java
Modified:
    
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithTimestampResourceCachingStrategy.java
    
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/BasicResourceReferenceMapperTest.java

Added: 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/AbstractFilenameWithVersionResourceCachingStrategy.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/AbstractFilenameWithVersionResourceCachingStrategy.java?rev=1141139&view=auto
==============================================================================
--- 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/AbstractFilenameWithVersionResourceCachingStrategy.java
 (added)
+++ 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/AbstractFilenameWithVersionResourceCachingStrategy.java
 Wed Jun 29 15:52:08 2011
@@ -0,0 +1,157 @@
+/*
+ * 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.wicket.request.resource.caching;
+
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * base resource caching strategy that adds a version string for the 
+ * requested resource to the filename.
+ * <p/>
+ * versioned_filename := [basename][version-suffix][version](.extension)
+ * <p/>
+ * Since browsers and proxies use this versioned filename of the resource as a 
+ * cache key a change to the version will cause a cache miss and subsequent 
reload.
+ * <p/>
+ * 
+ * @author Peter Ertl
+ */
+public abstract class AbstractFilenameWithVersionResourceCachingStrategy 
+       extends AbstractResourceCachingStrategy
+{
+       /** suffix that uniquely identifies beginning of the version 
+        * string inside the resource filename */
+       private final String versionSuffix;
+
+       /**
+        * Constructor
+        * 
+        * @param versionSuffix
+        *            string appended to the filename before the version string
+        */
+       public AbstractFilenameWithVersionResourceCachingStrategy(String 
versionSuffix)
+       {
+               this.versionSuffix = Args.notEmpty(versionSuffix, 
"versionSuffix");
+       }
+
+       /**
+        * @return string appended to the filename before the version string
+        */
+       public final String getVersionSuffix()
+       {
+               return versionSuffix;
+       }
+
+       public void decorateUrl(ResourceUrl url, ResourceReference reference)
+       {
+               // get version string for requested resource
+               final String version = getVersionStringForResource(reference);
+               
+               // ignore resource if no version information is available
+               if (version == null)
+               {
+                       return;
+               }
+
+               // get undecorated filename
+               final String filename = url.getFileName();
+
+               // check if resource name has extension
+               final int extensionAt = filename.lastIndexOf('.');
+
+               // create filename with version:
+               //
+               // filename :=
+               // [basename][version-suffix][version](.extension)
+               //
+               final StringBuilder versionedFilename = new StringBuilder();
+               
+               // add filename
+               if (extensionAt == -1)
+               {
+                       versionedFilename.append(filename);
+               }
+               else
+               {
+                       versionedFilename.append(filename.substring(0, 
extensionAt));
+               }
+               // add version suffix
+               versionedFilename.append(versionSuffix);
+               
+               // add version
+               versionedFilename.append(version);
+
+               // add extension if present
+               if (extensionAt != -1)
+               {
+                       
versionedFilename.append(filename.substring(extensionAt));
+               }
+               // set versioned filename
+               url.setFileName(versionedFilename.toString());
+       }
+
+       public void undecorateUrl(ResourceUrl url)
+       {
+               final String filename = url.getFileName();
+               
+               // check for extension
+               int pos = filename.lastIndexOf('.');
+
+               // get name of file without extension (but with version string)
+               final String fullname = pos == -1 ? filename : 
filename.substring(0, pos);
+               
+               // get extension of file if present
+               final String extension = pos == -1 ? null : 
filename.substring(pos);
+
+               // get position of version string
+               pos = fullname.lastIndexOf(versionSuffix);
+
+               // remove version string if it exists
+               if (pos != -1)
+               {
+                       // get filename before version string
+                       final String basename = fullname.substring(0, pos);
+
+                       // create filename without version string 
+                       // (required for working resource lookup)
+                       url.setFileName(extension == null? basename : basename 
+ extension);
+               }
+       }
+
+       /**
+        * get string that uniquely identifies the current version of the 
resource
+        * 
+        * @param reference
+        *          resource reference
+        * @return string that uniquely identifies the current version of the 
resource
+        */
+       protected abstract String getVersionStringForResource(ResourceReference 
reference);
+
+       /**
+        * set resource caching to maximum and set cache-visibility to 'public'
+        * 
+        * @param response
+        */
+       public void decorateResponse(AbstractResource.ResourceResponse response)
+       {
+               response.setCacheDurationToMaximum();
+               response.setCacheScope(WebResponse.CacheScope.PUBLIC);
+       }
+}

Added: 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithStaticVersionResourceCachingStrategy.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithStaticVersionResourceCachingStrategy.java?rev=1141139&view=auto
==============================================================================
--- 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithStaticVersionResourceCachingStrategy.java
 (added)
+++ 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithStaticVersionResourceCachingStrategy.java
 Wed Jun 29 15:52:08 2011
@@ -0,0 +1,59 @@
+/*
+ * 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.wicket.request.resource.caching;
+
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * resource caching strategy that adds a static version string to all 
resources. This
+ * is a recommended solution if your deployment environment does not support 
stable timestamps
+ * as used by {@link FilenameWithStaticVersionResourceCachingStrategy}. 
+ * This is for example the case in some cluster environments.
+ * When deploying an updated version of the application the version string 
should be 
+ * changed. The version string could be hardcoded, taken from some build-tool 
generated 
+ * properties file, injected by spring, etc.
+ * <p/>
+ * Tt is recommended to use only numbers and characters for the version string 
to avoid 
+ * ambiguities when parsing resource filenames.
+ * 
+ * @author Peter Ertl
+ */
+public class FilenameWithStaticVersionResourceCachingStrategy 
+       extends AbstractFilenameWithVersionResourceCachingStrategy
+{
+       private static final String DEFAULT_VERSION_SUFFIX = "-ver_"; // 'ver' 
= version
+       
+       private final String version;
+
+       public FilenameWithStaticVersionResourceCachingStrategy(String version)
+       {
+               this(DEFAULT_VERSION_SUFFIX, version);
+       }
+
+       public FilenameWithStaticVersionResourceCachingStrategy(String 
versionSuffix, String version)
+       {
+               super(versionSuffix);
+               this.version = Args.notEmpty(version, "version");
+       }
+
+       @Override
+       protected String getVersionStringForResource(ResourceReference 
reference)
+       {
+               return version;
+       }
+}

Modified: 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithTimestampResourceCachingStrategy.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithTimestampResourceCachingStrategy.java?rev=1141139&r1=1141138&r2=1141139&view=diff
==============================================================================
--- 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithTimestampResourceCachingStrategy.java
 (original)
+++ 
wicket/trunk/wicket-core/src/main/java/org/apache/wicket/request/resource/caching/FilenameWithTimestampResourceCachingStrategy.java
 Wed Jun 29 15:52:08 2011
@@ -16,17 +16,14 @@
  */
 package org.apache.wicket.request.resource.caching;
 
-import org.apache.wicket.request.http.WebResponse;
-import org.apache.wicket.request.resource.AbstractResource;
 import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.time.Time;
 
 /**
  * resource caching strategy that adds a last-modified timestamp to the 
filename
  * <p/>
  * timestamped_filename := 
[basename][timestamp-prefix][last-modified-milliseconds](.extension)
- * 
+ *
  * Normally the resource names won't change when the resource ifself changes, 
for example when you
  * add a new style to your CSS sheet. This can be very annoying as browsers 
(and proxies) usally
  * cache resources in their cache based on the filename and therefore won't 
update. Unless you
@@ -44,112 +41,32 @@ import org.apache.wicket.util.time.Time;
  * Since browsers and proxies use the filename of the resource as a cache key 
the changed filename
  * will not hit the cache and the page gets rendered with the changed file.
  * <p/>
- * 
+ *
  * @author Peter Ertl
  */
-public class FilenameWithTimestampResourceCachingStrategy extends 
AbstractResourceCachingStrategy
+public class FilenameWithTimestampResourceCachingStrategy extends 
AbstractFilenameWithVersionResourceCachingStrategy
 {
-       protected static final String DEFAULT_TIMESTAMP_SUFFIX = "-ts";
+       private static final String DEFAULT_VERSION_SUFFIX = "-ts_"; // 'ts' = 
timestamp
 
-       private final String timestampPrefix;
-
-       /**
-        * Constructor
-        */
        public FilenameWithTimestampResourceCachingStrategy()
        {
-               this(DEFAULT_TIMESTAMP_SUFFIX);
+               super(DEFAULT_VERSION_SUFFIX);
        }
 
-       /**
-        * Constructor
-        * 
-        * @param timestampSuffix
-        *            string appended to the filename before the timestamp
-        */
-       public FilenameWithTimestampResourceCachingStrategy(String 
timestampSuffix)
+       public FilenameWithTimestampResourceCachingStrategy(String 
versionSuffix)
        {
-               Args.notEmpty(timestampSuffix, "timestampPrefix");
-               timestampPrefix = timestampSuffix;
+               super(versionSuffix);
        }
 
-       /**
-        * @return string appended to the filename before the timestamp
-        */
-       public final String getTimestampPrefix()
+       @Override
+       protected String getVersionStringForResource(ResourceReference 
reference)
        {
-               return timestampPrefix;
-       }
-
-       public void decorateUrl(ResourceUrl url, ResourceReference reference)
-       {
-               Time lastModified = getLastModified(reference);
-
-               final String filename = url.getFileName();
+               final Time lastModified = getLastModified(reference);
 
                if (lastModified == null)
-                       return;
-
-               // check if resource name has extension
-               int extensionAt = filename.lastIndexOf('.');
-
-               // create timestamped version of filename:
-               //
-               // filename :=
-               // 
[basename][timestamp-prefix][last-modified-milliseconds](.extension)
-               //
-               StringBuilder timestampedFilename = new StringBuilder();
-               timestampedFilename.append(extensionAt == -1 ? filename
-                       : filename.substring(0, extensionAt));
-               timestampedFilename.append(timestampPrefix);
-               timestampedFilename.append(lastModified.getMilliseconds());
-
-               if (extensionAt != -1)
-                       
timestampedFilename.append(filename.substring(extensionAt));
-
-               url.setFileName(timestampedFilename.toString());
-       }
-
-       public void undecorateUrl(ResourceUrl url)
-       {
-               final String filename = url.getFileName();
-               int pos = filename.lastIndexOf('.');
-
-               final String fullname = pos == -1 ? filename : 
filename.substring(0, pos);
-               final String extension = pos == -1 ? null : 
filename.substring(pos);
-
-               pos = fullname.lastIndexOf(timestampPrefix);
-
-               if (pos != -1)
                {
-                       final String timestamp = fullname.substring(pos + 
timestampPrefix.length());
-                       final String basename = fullname.substring(0, pos);
-
-                       try
-                       {
-                               Long.parseLong(timestamp); // just check the 
timestamp is numeric
-
-                               // create filename without timestamp for 
resource lookup
-                               url.setFileName(extension == null ? basename : 
basename + extension);
-                               return;
-                       }
-                       catch (NumberFormatException e)
-                       {
-                               // some strange case of coincidence where the 
filename contains the timestamp prefix
-                               // but the timestamp itself is non-numeric - we 
interpret this situation as
-                               // "file has no timestamp"
-                       }
+                       return null;
                }
-       }
-
-       /**
-        * set resource caching to maximum and set cache-visibility to 'public'
-        * 
-        * @param response
-        */
-       public void decorateResponse(AbstractResource.ResourceResponse response)
-       {
-               response.setCacheDurationToMaximum();
-               response.setCacheScope(WebResponse.CacheScope.PUBLIC);
+               return String.valueOf(lastModified.getMilliseconds());
        }
 }

Modified: 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/BasicResourceReferenceMapperTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/BasicResourceReferenceMapperTest.java?rev=1141139&r1=1141138&r2=1141139&view=diff
==============================================================================
--- 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/BasicResourceReferenceMapperTest.java
 (original)
+++ 
wicket/trunk/wicket-core/src/test/java/org/apache/wicket/request/mapper/BasicResourceReferenceMapperTest.java
 Wed Jun 29 15:52:08 2011
@@ -23,11 +23,16 @@ import org.apache.wicket.request.IReques
 import org.apache.wicket.request.Url;
 import org.apache.wicket.request.cycle.RequestCycle;
 import 
org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.request.mapper.parameter.INamedParameters;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import 
org.apache.wicket.request.resource.caching.FilenameWithStaticVersionResourceCachingStrategy;
 import 
org.apache.wicket.request.resource.caching.FilenameWithTimestampResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
 import org.apache.wicket.request.resource.caching.NoOpResourceCachingStrategy;
+import org.apache.wicket.request.resource.caching.ResourceUrl;
 import org.apache.wicket.util.IProvider;
 import org.apache.wicket.util.ValueProvider;
 import org.apache.wicket.util.tester.WicketTester;
@@ -456,7 +461,7 @@ public class BasicResourceReferenceMappe
                Url url = encoderWithTimestamps.mapHandler(handler);
 
                // check that url contains timestamp
-               String timestampPart = 
CACHE_FILENAME_WITH_TIMESTAMP.get().getTimestampPrefix() +
+               String timestampPart = 
CACHE_FILENAME_WITH_TIMESTAMP.get().getVersionSuffix() +
                        Long.toString(millis) + "?";
                assertTrue(url.toString().contains(timestampPart));
        }
@@ -496,4 +501,59 @@ public class BasicResourceReferenceMappe
                assertEquals(url1, url3);
                tester.destroy();
        }
+
+       public void testVersionStringInResourceFilename()
+       {
+               final IResource resource = new IResource()
+               {
+                       public void respond(Attributes attributes)
+                       {
+                       }
+               };
+
+               final ResourceReference reference =
+                       new ResourceReference(getClass(), "versioned", 
Locale.ENGLISH, "style", null)
+                       {
+                               @Override
+                               public IResource getResource()
+                               {
+                                       return resource;
+                               }
+                       };
+
+               FilenameWithStaticVersionResourceCachingStrategy strategy =
+                       new 
FilenameWithStaticVersionResourceCachingStrategy("-version-", "foobar");
+
+               INamedParameters params = new PageParameters();
+               ResourceUrl url = new ResourceUrl("test.js", params);
+               strategy.decorateUrl(url, reference);
+               assertEquals("test-version-foobar.js", url.getFileName());
+               strategy.undecorateUrl(url);
+               assertEquals("test.js", url.getFileName());
+
+               url = new ResourceUrl("test", params);
+               strategy.decorateUrl(url, reference);
+               assertEquals("test-version-foobar", url.getFileName());
+               strategy.undecorateUrl(url);
+               assertEquals("test", url.getFileName());
+
+               // this behavior is o.k. since a browser could request an 
+               // previous version of the resource. for example we
+               // could first have 'test-alpha.txt' which would be later 
replaced
+               // by 'test-beta.txt' but in any case will point to 
+               // internal resource 'test.txt'
+               url = new ResourceUrl("test-version-older.txt", params);
+               strategy.undecorateUrl(url);
+               assertEquals("test.txt", url.getFileName());
+
+               // weird but valid
+               url = new ResourceUrl("test-version-.txt", params);
+               strategy.undecorateUrl(url);
+               assertEquals("test.txt", url.getFileName());
+
+               // weird but valid
+               url = new ResourceUrl("test-version--------", params);
+               strategy.undecorateUrl(url);
+               assertEquals("test", url.getFileName());
+       }
 }


Reply via email to