Here's my implementation:
// Copyright 2006, 2008, 2009, 2010, 2011 The Apache Software Foundation
//
// 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 org.apache.tapestry5.internal.services;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.services.ClasspathAssetAliasManager;
import org.apache.tapestry5.services.Dispatcher;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
import org.apache.tapestry5.services.assets.AssetRequestHandler;
/**
* Recognizes requests where the path begins with "/asset/" and
delivers the content therein as a bytestream. Also
* handles requests that are simply polling for a change to the file.
*
* @see ResourceStreamer
* @see ClasspathAssetAliasManager
* @see AssetRequestHandler
*/
// PATCHED incorrect APPLICATION_VERSION 302 temporary redirect to
current APPLICATION_VERSION
// PATCH +
@SuppressWarnings("all")
@UsesMappedConfiguration(AssetRequestHandler.class)
public class AssetDispatcher implements Dispatcher
{
/**
* Keyed on extended path name, which includes the pathPrefix first
and a trailing slash.
*/
private final Map<String, AssetRequestHandler> pathToHandler =
CollectionFactory.newMap();
/**
* List of path prefixes in the pathToHandler, sorted be descending
length.
*/
private final List<String> assetPaths = CollectionFactory.newList();
private final String pathPrefix;
// PATCH +
private final String pathPrefixPattern;
public AssetDispatcher(Map<String, AssetRequestHandler> configuration,
@Symbol(SymbolConstants.APPLICATION_VERSION) String applicationVersion,
@Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
@Symbol(SymbolConstants.ASSET_PATH_PREFIX) String assetPathPrefix)
{
String folder = applicationFolder.equals("") ? "" : "/" +
applicationFolder;
this.pathPrefix = folder + assetPathPrefix + applicationVersion
+ "/";
// PATCH +
this.pathPrefixPattern = "^" + folder + assetPathPrefix +
"[^/]{1,}/(.*)$";
for (String path : configuration.keySet())
{
String extendedPath = this.pathPrefix + path + "/";
pathToHandler.put(extendedPath, configuration.get(path));
assetPaths.add(extendedPath);
}
// Sort by descending length
Collections.sort(assetPaths, new Comparator<String>()
{
public int compare(String o1, String o2)
{
return o2.length() - o1.length();
}
});
}
public boolean dispatch(Request request, Response response) throws
IOException
{
String path = request.getPath();
// Remember that the request path does not include the context
path, so we can simply start
// looking for the asset path prefix right off the bat.
if (!path.startsWith(pathPrefix))
{
// PATCH +
if (path.matches(pathPrefixPattern))
{
// send 302 temporary redirect to the version this
server is using
String newPath = request.getContextPath() +
path.replaceAll(pathPrefixPattern, pathPrefix + "$1");
response.setHeader("Location", newPath);
response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY, newPath);
return true;
}
return false;
}
for (String extendedPath : assetPaths)
{
if (path.startsWith(extendedPath))
{
AssetRequestHandler handler =
pathToHandler.get(extendedPath);
String extraPath = path.substring(extendedPath.length());
boolean handled = handler.handleAssetRequest(request,
response, extraPath);
if (handled)
{
return true;
}
}
}
response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
return true;
}
}
On 22/02/2012 6:05 AM, Howard Lewis Ship wrote:
I suspect the 302 redirect may be the correct solution, and Tapestry
could support this pretty reasonably.
The perfect solution would involve the following:
- The version number if replaced with the SHA1 hash of the resource's
(uncompressed) content
- All CSS is dynamically rewritten to convert relative references into
full paths including the correct SHA1 hash
I wonder if the 302 redirect could act as a way to avoid rewriting the CSS?
On Sun, Feb 19, 2012 at 7:57 PM, Paul Stanton<p...@mapshed.com.au> wrote:
Hi Cezary,
I think I have the same need as you.
We use load balanced servers, and when upgrading we upgrade one at a time so
that there is always a server running.
But the side effect is that the upgraded server can receive requests for the
old assets, and we don't want to fail in this case.
In the past (5.1) I monkey-patched AssetResourceLocatorImpl to allow<=
version number requests.
However in migrating to 5.3 I would like a better solution, and the
implementation is different now anyway.
I like the idea of the 302 redirect.
How did you end up solving this? Is it possible to just contribute your own
AssetDispatcher instead of just replacing the class?
Thanks, Paul.
On 24/03/2011 10:09 AM, Cezary Biernacki wrote:
Perhaps the AssetDispatcher could check the requested version number and
if
it doesn't match the app version, send a 302 redirect to the "correct"
URL?
/assets/1.3/ctx/script.js --> /assets/1.2/ctx/script.js
Or perhaps it should only deliver the asset if the requested version os
older than or equal to the app version?
Determining "older" and "newer" would however assume people name their
application versions in a certain way.
It is exactly what I did in my application. I have overridden
AssetsDispatcher service with my copy based on original one, that differs
only in handling missing resources: if resource actually exists but just
version numbers differ, my dispatcher just sends redirect response with a
correct path.
As a side note, I observed in that - at least in my case - typically a
favicon was a typical asset requested with wrong version number, probably
from bookmarks.
Regards,
Cezary
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org