This is an awesome idea, and works perfectly too.

Thanks.
-------------------------------
"If at first you don't succeed,
   so much for skydiving!"




On 22 February 2012 08:47, Paul Stanton <p...@mapshed.com.au> wrote:
> By the way all,
>
> Here is a solution which does not require any changes to tapestry code. All
> code is free to use.
>
> 1. contribute a dispatcher to MasterDispatcher, just before the regular
> AssetDispatcher:
>
>    public static void bind(ServiceBinder binder)
>    {
>        binder.bind(Dispatcher.class,
> AssetVersionCheckDispatcher.class).withId("AssetVersionCheckDispatcher");
>    }
>
>    public static void
> contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
> //
>            @InjectService("AssetVersionCheckDispatcher") Dispatcher
> assetVersionCheckDispatcher)
>    {
>        configuration.add("AssetVersionCheckDispatcher",
> assetVersionCheckDispatcher, "before:AssetDispatcher");
>    }
>
> 2. Implementation of AssetVersionCheckDispatcher:
>
> public class AssetVersionCheckDispatcher implements Dispatcher
> {
>    private final String pathPrefix;
>    private final String pathPrefixPattern;
>
>    public
> AssetVersionCheckDispatcher(@Symbol(SymbolConstants.APPLICATION_VERSION)
> String appVersion,
>            @Symbol(SymbolConstants.APPLICATION_FOLDER) String appFolder,
>            @Symbol(SymbolConstants.ASSET_PATH_PREFIX) String
> assetPathPrefix)
>    {
>        String folder = appFolder.equals("") ? "" : "/" + appFolder;
>        this.pathPrefix = folder + assetPathPrefix + appVersion + "/";
>
>        this.pathPrefixPattern = "^" + folder + assetPathPrefix +
> "[^/]{1,}/(.*)$";
>    }
>
>    @Override
>
>    public boolean dispatch(Request request, Response response) throws
> IOException
>    {
>        String path = request.getPath();
>
>        if (!path.startsWith(pathPrefix))
>        {
>            if (path.matches(pathPrefixPattern))
>            {
>                // send 302 temporary redirect to the current version
>                String newPath = request.getContextPath() +
> path.replaceFirst(pathPrefixPattern, pathPrefix + "$1");
>
>                response.setHeader("Location", newPath);
>                response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY,
> newPath);
>                return true;
>            }
>        }
>
>        return false;
>    }
> }
>
> On 22/02/2012 11:08 AM, Paul Stanton wrote:
>>
>> Didn't think there was a Jira yet, I'll try to get around to logging it
>> later this week.
>>
>> On 22/02/2012 8:38 AM, Howard Lewis Ship wrote:
>>>
>>> Patches are best provided on JIRA; there's a checkbox that shows you
>>> have granted the rights to the patch&  code to the Apache Software
>>> Foundation.  Thanks!
>>>
>>> On Tue, Feb 21, 2012 at 12:28 PM, Paul Stanton<p...@mapshed.com.au>
>>>  wrote:
>>>>
>>>> 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
>>>>
>>>
>>>
>>
>> ---------------------------------------------------------------------
>> 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
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org

Reply via email to