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 }