001 // Copyright 2006, 2008, 2009, 2010, 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.internal.services; 016 017 import org.apache.tapestry5.SymbolConstants; 018 import org.apache.tapestry5.ioc.annotations.Symbol; 019 import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration; 020 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 021 import org.apache.tapestry5.services.ClasspathAssetAliasManager; 022 import org.apache.tapestry5.services.Dispatcher; 023 import org.apache.tapestry5.services.Request; 024 import org.apache.tapestry5.services.Response; 025 import org.apache.tapestry5.services.assets.AssetRequestHandler; 026 027 import javax.servlet.http.HttpServletResponse; 028 import java.io.IOException; 029 import java.util.Collections; 030 import java.util.Comparator; 031 import java.util.List; 032 import java.util.Map; 033 034 /** 035 * Recognizes requests where the path begins with "/asset/" and delivers the content therein as a bytestream. Also 036 * handles requests that are simply polling for a change to the file. 037 * 038 * @see ResourceStreamer 039 * @see ClasspathAssetAliasManager 040 * @see AssetRequestHandler 041 */ 042 @UsesMappedConfiguration(AssetRequestHandler.class) 043 public class AssetDispatcher implements Dispatcher 044 { 045 /** 046 * Keyed on extended path name, which includes the pathPrefix first and a trailing slash. 047 */ 048 private final Map<String, AssetRequestHandler> pathToHandler = CollectionFactory.newMap(); 049 050 /** 051 * List of path prefixes in the pathToHandler, sorted be descending length. 052 */ 053 private final List<String> assetPaths = CollectionFactory.newList(); 054 055 private final String pathPrefix; 056 057 public AssetDispatcher(Map<String, AssetRequestHandler> configuration, 058 059 @Symbol(SymbolConstants.APPLICATION_VERSION) 060 String applicationVersion, 061 062 @Symbol(SymbolConstants.APPLICATION_FOLDER) 063 String applicationFolder, 064 065 @Symbol(SymbolConstants.ASSET_PATH_PREFIX) 066 String assetPathPrefix 067 ) 068 { 069 String folder = applicationFolder.equals("") ? "" : "/" + applicationFolder; 070 071 this.pathPrefix = folder + assetPathPrefix + applicationVersion + "/"; 072 073 for (String path : configuration.keySet()) 074 { 075 String extendedPath = this.pathPrefix + path + "/"; 076 077 pathToHandler.put(extendedPath, configuration.get(path)); 078 079 assetPaths.add(extendedPath); 080 } 081 082 // Sort by descending length 083 084 Collections.sort(assetPaths, new Comparator<String>() 085 { 086 public int compare(String o1, String o2) 087 { 088 return o2.length() - o1.length(); 089 } 090 }); 091 } 092 093 public boolean dispatch(Request request, Response response) throws IOException 094 { 095 String path = request.getPath(); 096 097 // Remember that the request path does not include the context path, so we can simply start 098 // looking for the asset path prefix right off the bat. 099 100 if (!path.startsWith(pathPrefix)) 101 { 102 return false; 103 } 104 105 for (String extendedPath : assetPaths) 106 { 107 108 if (path.startsWith(extendedPath)) 109 { 110 AssetRequestHandler handler = pathToHandler.get(extendedPath); 111 112 String extraPath = path.substring(extendedPath.length()); 113 114 boolean handled = handler.handleAssetRequest(request, response, extraPath); 115 116 if (handled) 117 { 118 return true; 119 } 120 } 121 } 122 123 response.sendError(HttpServletResponse.SC_NOT_FOUND, path); 124 125 return true; 126 } 127 }