This is an awesome idea, and works perfectly too. Thanks. ------------------------------- "If at first you don't succeed, so much for skydiving!"
On 22 February 2012 08:47, Paul Stanton <p...@mapshed.com.au> wrote: > By the way all, > > Here is a solution which does not require any changes to tapestry code. All > code is free to use. > > 1. contribute a dispatcher to MasterDispatcher, just before the regular > AssetDispatcher: > > public static void bind(ServiceBinder binder) > { > binder.bind(Dispatcher.class, > AssetVersionCheckDispatcher.class).withId("AssetVersionCheckDispatcher"); > } > > public static void > contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration, > // > @InjectService("AssetVersionCheckDispatcher") Dispatcher > assetVersionCheckDispatcher) > { > configuration.add("AssetVersionCheckDispatcher", > assetVersionCheckDispatcher, "before:AssetDispatcher"); > } > > 2. Implementation of AssetVersionCheckDispatcher: > > public class AssetVersionCheckDispatcher implements Dispatcher > { > private final String pathPrefix; > private final String pathPrefixPattern; > > public > AssetVersionCheckDispatcher(@Symbol(SymbolConstants.APPLICATION_VERSION) > String appVersion, > @Symbol(SymbolConstants.APPLICATION_FOLDER) String appFolder, > @Symbol(SymbolConstants.ASSET_PATH_PREFIX) String > assetPathPrefix) > { > String folder = appFolder.equals("") ? "" : "/" + appFolder; > this.pathPrefix = folder + assetPathPrefix + appVersion + "/"; > > this.pathPrefixPattern = "^" + folder + assetPathPrefix + > "[^/]{1,}/(.*)$"; > } > > @Override > > public boolean dispatch(Request request, Response response) throws > IOException > { > String path = request.getPath(); > > if (!path.startsWith(pathPrefix)) > { > if (path.matches(pathPrefixPattern)) > { > // send 302 temporary redirect to the current version > String newPath = request.getContextPath() + > path.replaceFirst(pathPrefixPattern, pathPrefix + "$1"); > > response.setHeader("Location", newPath); > response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY, > newPath); > return true; > } > } > > return false; > } > } > > On 22/02/2012 11:08 AM, Paul Stanton wrote: >> >> Didn't think there was a Jira yet, I'll try to get around to logging it >> later this week. >> >> On 22/02/2012 8:38 AM, Howard Lewis Ship wrote: >>> >>> Patches are best provided on JIRA; there's a checkbox that shows you >>> have granted the rights to the patch& code to the Apache Software >>> Foundation. Thanks! >>> >>> On Tue, Feb 21, 2012 at 12:28 PM, Paul Stanton<p...@mapshed.com.au> >>> wrote: >>>> >>>> Here's my implementation: >>>> >>>> // Copyright 2006, 2008, 2009, 2010, 2011 The Apache Software Foundation >>>> // >>>> // Licensed under the Apache License, Version 2.0 (the "License"); >>>> // you may not use this file except in compliance with the License. >>>> // You may obtain a copy of the License at >>>> // >>>> // http://www.apache.org/licenses/LICENSE-2.0 >>>> // >>>> // Unless required by applicable law or agreed to in writing, software >>>> // distributed under the License is distributed on an "AS IS" BASIS, >>>> // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or >>>> implied. >>>> // See the License for the specific language governing permissions and >>>> // limitations under the License. >>>> >>>> package org.apache.tapestry5.internal.services; >>>> >>>> import java.io.IOException; >>>> import java.util.Collections; >>>> import java.util.Comparator; >>>> import java.util.List; >>>> import java.util.Map; >>>> >>>> import javax.servlet.http.HttpServletResponse; >>>> >>>> import org.apache.tapestry5.SymbolConstants; >>>> import org.apache.tapestry5.ioc.annotations.Symbol; >>>> import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration; >>>> import org.apache.tapestry5.ioc.internal.util.CollectionFactory; >>>> import org.apache.tapestry5.services.ClasspathAssetAliasManager; >>>> import org.apache.tapestry5.services.Dispatcher; >>>> import org.apache.tapestry5.services.Request; >>>> import org.apache.tapestry5.services.Response; >>>> import org.apache.tapestry5.services.assets.AssetRequestHandler; >>>> >>>> /** >>>> * Recognizes requests where the path begins with "/asset/" and delivers >>>> the >>>> content therein as a bytestream. Also >>>> * handles requests that are simply polling for a change to the file. >>>> * >>>> * @see ResourceStreamer >>>> * @see ClasspathAssetAliasManager >>>> * @see AssetRequestHandler >>>> */ >>>> // PATCHED incorrect APPLICATION_VERSION 302 temporary redirect to >>>> current >>>> APPLICATION_VERSION >>>> >>>> // PATCH + >>>> @SuppressWarnings("all") >>>> @UsesMappedConfiguration(AssetRequestHandler.class) >>>> public class AssetDispatcher implements Dispatcher >>>> { >>>> /** >>>> * Keyed on extended path name, which includes the pathPrefix first >>>> and a >>>> trailing slash. >>>> */ >>>> private final Map<String, AssetRequestHandler> pathToHandler = >>>> CollectionFactory.newMap(); >>>> >>>> /** >>>> * List of path prefixes in the pathToHandler, sorted be descending >>>> length. >>>> */ >>>> private final List<String> assetPaths = CollectionFactory.newList(); >>>> >>>> private final String pathPrefix; >>>> >>>> // PATCH + >>>> private final String pathPrefixPattern; >>>> >>>> public AssetDispatcher(Map<String, AssetRequestHandler> >>>> configuration, >>>> >>>> @Symbol(SymbolConstants.APPLICATION_VERSION) String >>>> applicationVersion, >>>> >>>> @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder, >>>> >>>> @Symbol(SymbolConstants.ASSET_PATH_PREFIX) String assetPathPrefix) >>>> { >>>> String folder = applicationFolder.equals("") ? "" : "/" + >>>> applicationFolder; >>>> >>>> this.pathPrefix = folder + assetPathPrefix + applicationVersion + >>>> "/"; >>>> >>>> // PATCH + >>>> this.pathPrefixPattern = "^" + folder + assetPathPrefix + >>>> "[^/]{1,}/(.*)$"; >>>> >>>> for (String path : configuration.keySet()) >>>> { >>>> String extendedPath = this.pathPrefix + path + "/"; >>>> >>>> pathToHandler.put(extendedPath, configuration.get(path)); >>>> >>>> assetPaths.add(extendedPath); >>>> } >>>> >>>> // Sort by descending length >>>> >>>> Collections.sort(assetPaths, new Comparator<String>() >>>> { >>>> public int compare(String o1, String o2) >>>> { >>>> return o2.length() - o1.length(); >>>> } >>>> }); >>>> } >>>> >>>> public boolean dispatch(Request request, Response response) throws >>>> IOException >>>> { >>>> String path = request.getPath(); >>>> >>>> // Remember that the request path does not include the context >>>> path, >>>> so we can simply start >>>> // looking for the asset path prefix right off the bat. >>>> >>>> if (!path.startsWith(pathPrefix)) >>>> { >>>> // PATCH + >>>> if (path.matches(pathPrefixPattern)) >>>> { >>>> // send 302 temporary redirect to the version this server >>>> is >>>> using >>>> String newPath = request.getContextPath() + >>>> path.replaceAll(pathPrefixPattern, pathPrefix + "$1"); >>>> response.setHeader("Location", newPath); >>>> >>>> response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY, >>>> newPath); >>>> return true; >>>> } >>>> return false; >>>> } >>>> >>>> for (String extendedPath : assetPaths) >>>> { >>>> >>>> if (path.startsWith(extendedPath)) >>>> { >>>> AssetRequestHandler handler = >>>> pathToHandler.get(extendedPath); >>>> >>>> String extraPath = path.substring(extendedPath.length()); >>>> >>>> boolean handled = handler.handleAssetRequest(request, >>>> response, extraPath); >>>> >>>> if (handled) >>>> { >>>> return true; >>>> } >>>> } >>>> } >>>> >>>> response.sendError(HttpServletResponse.SC_NOT_FOUND, path); >>>> >>>> return true; >>>> >>>> } >>>> } >>>> >>>> >>>> On 22/02/2012 6:05 AM, Howard Lewis Ship wrote: >>>>> >>>>> I suspect the 302 redirect may be the correct solution, and Tapestry >>>>> could support this pretty reasonably. >>>>> >>>>> The perfect solution would involve the following: >>>>> - The version number if replaced with the SHA1 hash of the resource's >>>>> (uncompressed) content >>>>> - All CSS is dynamically rewritten to convert relative references into >>>>> full paths including the correct SHA1 hash >>>>> >>>>> I wonder if the 302 redirect could act as a way to avoid rewriting the >>>>> CSS? >>>>> >>>>> On Sun, Feb 19, 2012 at 7:57 PM, Paul Stanton<p...@mapshed.com.au> >>>>> wrote: >>>>>> >>>>>> Hi Cezary, >>>>>> >>>>>> I think I have the same need as you. >>>>>> >>>>>> We use load balanced servers, and when upgrading we upgrade one at a >>>>>> time >>>>>> so >>>>>> that there is always a server running. >>>>>> >>>>>> But the side effect is that the upgraded server can receive requests >>>>>> for >>>>>> the >>>>>> old assets, and we don't want to fail in this case. >>>>>> >>>>>> In the past (5.1) I monkey-patched AssetResourceLocatorImpl to allow<= >>>>>> version number requests. >>>>>> >>>>>> However in migrating to 5.3 I would like a better solution, and the >>>>>> implementation is different now anyway. >>>>>> >>>>>> I like the idea of the 302 redirect. >>>>>> >>>>>> How did you end up solving this? Is it possible to just contribute >>>>>> your >>>>>> own >>>>>> AssetDispatcher instead of just replacing the class? >>>>>> >>>>>> Thanks, Paul. >>>>>> >>>>>> >>>>>> On 24/03/2011 10:09 AM, Cezary Biernacki wrote: >>>>>>>> >>>>>>>> >>>>>>>> Perhaps the AssetDispatcher could check the requested version number >>>>>>>> and >>>>>>>> if >>>>>>>> it doesn't match the app version, send a 302 redirect to the >>>>>>>> "correct" >>>>>>>> URL? >>>>>>>> /assets/1.3/ctx/script.js --> /assets/1.2/ctx/script.js >>>>>>>> >>>>>>>> Or perhaps it should only deliver the asset if the requested version >>>>>>>> os >>>>>>>> older than or equal to the app version? >>>>>>>> Determining "older" and "newer" would however assume people name >>>>>>>> their >>>>>>>> application versions in a certain way. >>>>>>> >>>>>>> >>>>>>> It is exactly what I did in my application. I have overridden >>>>>>> AssetsDispatcher service with my copy based on original one, that >>>>>>> differs >>>>>>> only in handling missing resources: if resource actually exists but >>>>>>> just >>>>>>> version numbers differ, my dispatcher just sends redirect response >>>>>>> with >>>>>>> a >>>>>>> correct path. >>>>>>> >>>>>>> As a side note, I observed in that - at least in my case - typically >>>>>>> a >>>>>>> favicon was a typical asset requested with wrong version number, >>>>>>> probably >>>>>>> from bookmarks. >>>>>>> >>>>>>> Regards, >>>>>>> Cezary >>>>>>> >>>>>> --------------------------------------------------------------------- >>>>>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>>>>> For additional commands, e-mail: users-h...@tapestry.apache.org >>>>>> >>>>> >>>> --------------------------------------------------------------------- >>>> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >>>> For additional commands, e-mail: users-h...@tapestry.apache.org >>>> >>> >>> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org >> For additional commands, e-mail: users-h...@tapestry.apache.org >> >> > > --------------------------------------------------------------------- > To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org > For additional commands, e-mail: users-h...@tapestry.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org