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());
+ }
}