You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by raulmt <ra...@gmail.com> on 2011/03/23 22:43:55 UTC

Re: Versioned assets

Hi Howard, 

I wanted to know if there is a reason why Tapestry couldn't just answer the
requests for assets independently of the requested version. I believe that
it is better to answer with a newer version than to just not answering at
all… In AssetDispatcher, this could be done by just processing the request
when its path starts with RequestConstants.ASSET_PATH_PREFIX and then to
generate the virtualPath by stripping from this constant to the next "/"
(assuming there is a version in the middle, which will be true for all the
URLs generated by Tapestry).

Could this cause problems in some way?

Regards,
Raul.

--
View this message in context: http://tapestry-users.832.n2.nabble.com/Versioned-assets-tp5421811p6202030.html
Sent from the Tapestry Users mailing list archive at Nabble.com.

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


Re: Versioned assets

Posted by Lenny Primak <lp...@hope.nyc.ny.us>.
Sounds good to me.  
I, for example, use the Mercurial revision hash as the version number,
so in a sense 'earlier/later' version does not make sense in my context,
so if Tapestry would just redirect to the current version it would work great!

On Feb 21, 2012, at 2:05 PM, 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 <pa...@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-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>> 
> 
> 
> 
> -- 
> Howard M. Lewis Ship
> 
> Creator of Apache Tapestry
> 
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
> 
> (971) 678-5210
> http://howardlewisship.com
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
> 


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


Re: Versioned assets

Posted by Howard Lewis Ship <hl...@gmail.com>.
Also, reviewing the codes makes 301 (Moved Permanently) look like a
better response than 302 (Found); the later indicates that the
resource is available under a new URI but future requests should
continue to start using the original URI.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

Thoughts?

On Tue, Feb 21, 2012 at 11:05 AM, Howard Lewis Ship <hl...@gmail.com> 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 <pa...@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-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>
>
>
> --
> Howard M. Lewis Ship
>
> Creator of Apache Tapestry
>
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
>
> (971) 678-5210
> http://howardlewisship.com



-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: Versioned assets

Posted by Paul Stanton <pa...@mapshed.com.au>.
well, it does when you change

"before:AssetDispatcher"

to

"before:Asset"

sorry.

On 24/02/2012 3:19 PM, Steve Eynon wrote:
> 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<pa...@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<pa...@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<pa...@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-unsubscribe@tapestry.apache.org
>>>>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>>
>>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>

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


Re: Versioned assets

Posted by Steve Eynon <st...@alienfactory.co.uk>.
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 <pa...@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<pa...@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<pa...@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-unsubscribe@tapestry.apache.org
>>>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>>>
>>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>
>>>
>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>

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


Re: Versioned assets

Posted by Lance Java <la...@googlemail.com>.
Most of the time, you should avoid using the regex methods on the String
class (split, matches, replace etc) as these will compile the
java.util.regex.Pattern for every invocation.

Instead, you should Pattern.compile(regex) once and save the result then
use Pattern.matches(value).

On Wednesday, 22 February 2012, Paul Stanton <pa...@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<pa...@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();
>            }
>        });

Re: Versioned assets

Posted by Paul Stanton <pa...@mapshed.com.au>.
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<pa...@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<pa...@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-unsubscribe@tapestry.apache.org
>>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>>
>>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>
>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>

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


Re: Versioned assets

Posted by Paul Stanton <pa...@mapshed.com.au>.
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<pa...@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<pa...@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-unsubscribe@tapestry.apache.org
>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>>
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>
>

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


Re: Versioned assets

Posted by Howard Lewis Ship <hl...@gmail.com>.
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 <pa...@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<pa...@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-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>
>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>



-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

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


Re: Versioned assets

Posted by Paul Stanton <pa...@mapshed.com.au>.
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<pa...@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-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>
>

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


Re: Versioned assets

Posted by Howard Lewis Ship <hl...@gmail.com>.
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 <pa...@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-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>



-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

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


Re: Versioned assets

Posted by Paul Stanton <pa...@mapshed.com.au>.
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-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Versioned assets

Posted by Cezary Biernacki <ce...@gmail.com>.
>
>
> 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

Re: Versioned assets

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
On Wed, 23 Mar 2011 20:18:16 -0300, Martin Strand  
<do...@gmail.com> wrote:

> Because that would defeat the purpose of automatic version management  
> for /assets/ and we would have to rename every asset that changed  
> between versions

I'm not following you: if you use some Tapestry way to refer to assets,  
you don't need to change anything when you change the application version.

-- 
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, Ars Machina Tecnologia da Informação Ltda.
http://www.arsmachina.com.br

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


Re: Versioned assets

Posted by Martin Strand <do...@gmail.com>.
Because that would defeat the purpose of automatic version management for  
/assets/ and we would have to rename every asset that changed between  
versions

On Thu, 24 Mar 2011 00:04:29 +0100, Thiago H. de Paula Figueiredo  
<th...@gmail.com> wrote:

> Why don't you set the application version symbol of your application to  
> a given value and never change it?

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


Re: Versioned assets

Posted by Martin Strand <do...@gmail.com>.
On Thu, 24 Mar 2011 00:45:56 +0100, Thiago H. de Paula Figueiredo
<th...@gmail.com> wrote:

> On Wed, 23 Mar 2011 20:26:35 -0300, raulmt <ra...@gmail.com> wrote:
>
>>
>> Cezary Biernacki wrote:
>>>
>>> On Thu, Mar 24, 2011 at 12:04 AM, Thiago H. de Paula Figueiredo &lt;
>>> thiagohp@gmail.com&gt; wrote:
>>>
>>> &gt; Why don't you set the application version symbol of your  
>>> application
>>> to a
>>> &gt; given value and never change it?
>>>
>>>
>>> Really? It will cause all caching problems that version numbers are
>>> supposed
>>> to solve, will not it?
>
> It would. I thought they wanted fixed URLs. Maybe I'm just not  
> understanding what the people in this thread want. Some refer to  
> "current asset". It implies that the previous asset is available, and it  
> isn't.  Maybe they're talking about "current versioned asset *URL*". How  
> would Tapestry know for sure and in an efficient way that the request  
> was for an old version asset URL?
> Any non-matching version would be redirected?


No, not fixed URLs. We want to take advantage of Tapestry's versioned
assets but still let the application respond to requests for "old" asset
URLs that have been published by previous versions of the application.


> Any non-matching version would be redirected?

Yes, that is what I suggested and what Cezary apparently already did.
I believe this should work as long as the redirect is not permanent
or cachable.

If we were to deliver cachable assets (or cachable/permanent redirects)
for a future version that has not existed yet, we could run into problems
once that future version is deployed. Some intermediate proxy could then
have mistakenly cached the 'future' asset.

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


Re: Versioned assets

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
On Wed, 23 Mar 2011 20:26:35 -0300, raulmt <ra...@gmail.com> wrote:

>
> Cezary Biernacki wrote:
>>
>> On Thu, Mar 24, 2011 at 12:04 AM, Thiago H. de Paula Figueiredo &lt;
>> thiagohp@gmail.com&gt; wrote:
>>
>> &gt; Why don't you set the application version symbol of your  
>> application
>> to a
>> &gt; given value and never change it?
>>
>>
>> Really? It will cause all caching problems that version numbers are
>> supposed
>> to solve, will not it?

It would. I thought they wanted fixed URLs. Maybe I'm just not  
understanding what the people in this thread want. Some refer to "current  
asset". It implies that the previous asset is available, and it isn't.   
Maybe they're talking about "current versioned asset *URL*". How would  
Tapestry know for sure and in an efficient way that the request was for an  
old version asset URL? Any non-matching version would be redirected?

-- 
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, Ars Machina Tecnologia da Informação Ltda.
http://www.arsmachina.com.br

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


Re: Versioned assets

Posted by raulmt <ra...@gmail.com>.
Cezary Biernacki wrote:
> 
> On Thu, Mar 24, 2011 at 12:04 AM, Thiago H. de Paula Figueiredo &lt;
> thiagohp@gmail.com&gt; wrote:
> 
> &gt; Why don't you set the application version symbol of your application
> to a
> &gt; given value and never change it?
> 
> 
> Really? It will cause all caching problems that version numbers are
> supposed
> to solve, will not it?
> 
> Cezary
> 
> 

That's exactly what I was going to say ;) the far future expire dates
Tapestry shows for all assets is a great way to maximize caching, and the
version number is also a great solution to force clients to get the new
version of your scripts/css/images, so a fixed version value is not a
feasible solution.

The solution proposed by Martin sounds very good, a redirect to the
"current" URL of the asset. In fact, with
HttpServletResponse.SC_MOVED_PERMANENTLY search engines should also update
its references.

Maybe none of these are ideal solutions, but my point is, to my
understanding, redirects to current versions is a better solution than a not
found.

Regards,
Raul.

--
View this message in context: http://tapestry-users.832.n2.nabble.com/Versioned-assets-tp5421811p6202349.html
Sent from the Tapestry Users mailing list archive at Nabble.com.

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


Re: Versioned assets

Posted by Cezary Biernacki <ce...@gmail.com>.
On Thu, Mar 24, 2011 at 12:04 AM, Thiago H. de Paula Figueiredo <
thiagohp@gmail.com> wrote:

> Why don't you set the application version symbol of your application to a
> given value and never change it?


Really? It will cause all caching problems that version numbers are supposed
to solve, will not it?

Cezary



>
>
> On Wed, 23 Mar 2011 19:50:59 -0300, Martin Strand <
> do.not.eat.yellow.snow@gmail.com> wrote:
>
>  On Wed, 23 Mar 2011 22:43:55 +0100, raulmt <ra...@gmail.com> wrote:
>>
>>  I wanted to know if there is a reason why Tapestry couldn't just answer
>>> the
>>> requests for assets independently of the requested version. I believe
>>> that
>>> it is better to answer with a newer version than to just not answering at
>>> all… In AssetDispatcher, this could be done by just processing the
>>>  request
>>> when its path starts with RequestConstants.ASSET_PATH_PREFIX and then to
>>> generate the virtualPath by stripping from this constant to the next "/"
>>> (assuming there is a version in the middle, which will be true for all
>>> the
>>> URLs generated by Tapestry).
>>>
>>> Could this cause problems in some way?
>>>
>>
>>
>> This sounds like a very good idea. I can think of a few scenarios where it
>> would be helpful:
>>
>> - cached or saved html documents from your application, with old asset
>> URLs
>>
>> - external pages referencing assets on your site
>>
>> - google image search - the search engine might have indexed an old asset
>> URL that no longer works
>>
>> A version upgrade resulting in 404 for all old asset URLs just feels
>> wrong. Cool URIs don't change :)
>> http://www.w3.org/Provider/Style/URI
>>
>>
>> There is however one potential problem I can think of:
>> An application running v1.2 gets a request for a newer version
>> /assets/1.3/ctx/script.js and delivers its own copy of that script.
>> That script will then get cached by clients and proxies (maybe even your
>> own Squid server) so when you later upgrade your app to v1.3, clients will
>> use the cached copy and your app won't even get a chance to deliver the
>> upgraded script.
>>
>> 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.
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>>
>
> --
> Thiago H. de Paula Figueiredo
> Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,
> and instructor
> Owner, Ars Machina Tecnologia da Informação Ltda.
> Consultor, desenvolvedor e instrutor em Java, Tapestry e Hibernate
> Coordenador e professor da Especialização em Engenharia de Software com
> Ênfase em Java da Faculdade Pitágoras
> http://www.arsmachina.com.br
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>

Re: Versioned assets

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
Why don't you set the application version symbol of your application to a  
given value and never change it?

On Wed, 23 Mar 2011 19:50:59 -0300, Martin Strand  
<do...@gmail.com> wrote:

> On Wed, 23 Mar 2011 22:43:55 +0100, raulmt <ra...@gmail.com> wrote:
>
>> I wanted to know if there is a reason why Tapestry couldn't just answer  
>> the
>> requests for assets independently of the requested version. I believe  
>> that
>> it is better to answer with a newer version than to just not answering  
>> at
>> all… In AssetDispatcher, this could be done by just processing the   
>> request
>> when its path starts with RequestConstants.ASSET_PATH_PREFIX and then to
>> generate the virtualPath by stripping from this constant to the next "/"
>> (assuming there is a version in the middle, which will be true for all  
>> the
>> URLs generated by Tapestry).
>>
>> Could this cause problems in some way?
>
>
> This sounds like a very good idea. I can think of a few scenarios where  
> it would be helpful:
>
> - cached or saved html documents from your application, with old asset  
> URLs
>
> - external pages referencing assets on your site
>
> - google image search - the search engine might have indexed an old  
> asset URL that no longer works
>
> A version upgrade resulting in 404 for all old asset URLs just feels  
> wrong. Cool URIs don't change :)
> http://www.w3.org/Provider/Style/URI
>
>
> There is however one potential problem I can think of:
> An application running v1.2 gets a request for a newer version  
> /assets/1.3/ctx/script.js and delivers its own copy of that script.
> That script will then get cached by clients and proxies (maybe even your  
> own Squid server) so when you later upgrade your app to v1.3, clients  
> will use the cached copy and your app won't even get a chance to deliver  
> the upgraded script.
>
> 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.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>


-- 
Thiago H. de Paula Figueiredo
Independent Java, Apache Tapestry 5 and Hibernate consultant, developer,  
and instructor
Owner, Ars Machina Tecnologia da Informação Ltda.
Consultor, desenvolvedor e instrutor em Java, Tapestry e Hibernate
Coordenador e professor da Especialização em Engenharia de Software com  
Ênfase em Java da Faculdade Pitágoras
http://www.arsmachina.com.br

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


Re: Versioned assets

Posted by Martin Strand <do...@gmail.com>.
On Wed, 23 Mar 2011 22:43:55 +0100, raulmt <ra...@gmail.com> wrote:

> I wanted to know if there is a reason why Tapestry couldn't just answer  
> the
> requests for assets independently of the requested version. I believe  
> that
> it is better to answer with a newer version than to just not answering at
> all… In AssetDispatcher, this could be done by just processing the   
> request
> when its path starts with RequestConstants.ASSET_PATH_PREFIX and then to
> generate the virtualPath by stripping from this constant to the next "/"
> (assuming there is a version in the middle, which will be true for all  
> the
> URLs generated by Tapestry).
>
> Could this cause problems in some way?


This sounds like a very good idea. I can think of a few scenarios where it  
would be helpful:

- cached or saved html documents from your application, with old asset URLs

- external pages referencing assets on your site

- google image search - the search engine might have indexed an old asset  
URL that no longer works

A version upgrade resulting in 404 for all old asset URLs just feels  
wrong. Cool URIs don't change :)
http://www.w3.org/Provider/Style/URI


There is however one potential problem I can think of:
An application running v1.2 gets a request for a newer version  
/assets/1.3/ctx/script.js and delivers its own copy of that script.
That script will then get cached by clients and proxies (maybe even your  
own Squid server) so when you later upgrade your app to v1.3, clients will  
use the cached copy and your app won't even get a chance to deliver the  
upgraded script.

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.

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